@superlogic/spree-pay 0.1.42 → 0.2.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/build/CryptoComTab-WWW23OV5.js +169 -0
- package/build/CryptoTab-HISSGXAC.js +838 -0
- package/build/chunk-2V675UEC.js +56 -0
- package/build/chunk-FGNS5H2Q.js +278 -0
- package/build/chunk-RQX2IOTB.js +795 -0
- package/build/index.cjs +4374 -3869
- package/build/index.css +60 -21
- package/build/index.js +1507 -3510
- package/package.json +1 -1
|
@@ -0,0 +1,838 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CheckoutButton,
|
|
3
|
+
PointsSwitch,
|
|
4
|
+
cn as cn2
|
|
5
|
+
} from "./chunk-FGNS5H2Q.js";
|
|
6
|
+
import {
|
|
7
|
+
Dialog,
|
|
8
|
+
DialogContent,
|
|
9
|
+
DialogDescription,
|
|
10
|
+
DialogTitle,
|
|
11
|
+
InfoBanner,
|
|
12
|
+
PaymentError,
|
|
13
|
+
SlapiPaymentService,
|
|
14
|
+
cn,
|
|
15
|
+
logger,
|
|
16
|
+
useSpreePayConfig,
|
|
17
|
+
useSpreePayRegister,
|
|
18
|
+
useSpreePaymentMethod
|
|
19
|
+
} from "./chunk-RQX2IOTB.js";
|
|
20
|
+
|
|
21
|
+
// src/components/CryptoTab/Crypto/CryptoWrapper.tsx
|
|
22
|
+
import { useMemo as useMemo2 } from "react";
|
|
23
|
+
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
|
24
|
+
import NiceModal3 from "@ebay/nice-modal-react";
|
|
25
|
+
import { RainbowKitProvider, getDefaultConfig, lightTheme } from "@rainbow-me/rainbowkit";
|
|
26
|
+
import "@rainbow-me/rainbowkit/styles.css";
|
|
27
|
+
import { WagmiProvider } from "wagmi";
|
|
28
|
+
import { base } from "wagmi/chains";
|
|
29
|
+
|
|
30
|
+
// src/components/CryptoTab/Crypto/Crypto.tsx
|
|
31
|
+
import { useCallback, useEffect as useEffect2 } from "react";
|
|
32
|
+
import { useAccount as useAccount3 } from "wagmi";
|
|
33
|
+
|
|
34
|
+
// ../../node_modules/@wagmi/core/dist/esm/utils/getAction.js
|
|
35
|
+
function getAction(client, actionFn, name) {
|
|
36
|
+
const action_implicit = client[actionFn.name];
|
|
37
|
+
if (typeof action_implicit === "function")
|
|
38
|
+
return action_implicit;
|
|
39
|
+
const action_explicit = client[name];
|
|
40
|
+
if (typeof action_explicit === "function")
|
|
41
|
+
return action_explicit;
|
|
42
|
+
return (params) => actionFn(client, params);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// ../../node_modules/@wagmi/core/dist/esm/actions/readContract.js
|
|
46
|
+
import { readContract as viem_readContract } from "viem/actions";
|
|
47
|
+
function readContract(config, parameters) {
|
|
48
|
+
const { chainId, ...rest } = parameters;
|
|
49
|
+
const client = config.getClient({ chainId });
|
|
50
|
+
const action = getAction(client, viem_readContract, "readContract");
|
|
51
|
+
return action(rest);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// ../../node_modules/@wagmi/core/dist/esm/actions/waitForTransactionReceipt.js
|
|
55
|
+
import { hexToString } from "viem";
|
|
56
|
+
import { call, getTransaction, waitForTransactionReceipt as viem_waitForTransactionReceipt } from "viem/actions";
|
|
57
|
+
async function waitForTransactionReceipt(config, parameters) {
|
|
58
|
+
const { chainId, timeout = 0, ...rest } = parameters;
|
|
59
|
+
const client = config.getClient({ chainId });
|
|
60
|
+
const action = getAction(client, viem_waitForTransactionReceipt, "waitForTransactionReceipt");
|
|
61
|
+
const receipt = await action({ ...rest, timeout });
|
|
62
|
+
if (receipt.status === "reverted") {
|
|
63
|
+
const action_getTransaction = getAction(client, getTransaction, "getTransaction");
|
|
64
|
+
const { from: account, ...txn } = await action_getTransaction({
|
|
65
|
+
hash: receipt.transactionHash
|
|
66
|
+
});
|
|
67
|
+
const action_call = getAction(client, call, "call");
|
|
68
|
+
const code = await action_call({
|
|
69
|
+
...txn,
|
|
70
|
+
account,
|
|
71
|
+
data: txn.input,
|
|
72
|
+
gasPrice: txn.type !== "eip1559" ? txn.gasPrice : void 0,
|
|
73
|
+
maxFeePerGas: txn.type === "eip1559" ? txn.maxFeePerGas : void 0,
|
|
74
|
+
maxPriorityFeePerGas: txn.type === "eip1559" ? txn.maxPriorityFeePerGas : void 0
|
|
75
|
+
});
|
|
76
|
+
const reason = code?.data ? hexToString(`0x${code.data.substring(138)}`) : "unknown reason";
|
|
77
|
+
throw new Error(reason);
|
|
78
|
+
}
|
|
79
|
+
return {
|
|
80
|
+
...receipt,
|
|
81
|
+
chainId: client.chain.id
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// ../../node_modules/@wagmi/core/dist/esm/exports/index.js
|
|
86
|
+
import { custom, http, webSocket } from "viem";
|
|
87
|
+
|
|
88
|
+
// src/hooks/payments/useCryptoPayment.ts
|
|
89
|
+
import { erc20Abi } from "viem";
|
|
90
|
+
import { useConfig, useWalletClient } from "wagmi";
|
|
91
|
+
|
|
92
|
+
// src/config/baseTokens.ts
|
|
93
|
+
var BASE_CHAIN_ID = 8453;
|
|
94
|
+
var BASE_TOKENS = [
|
|
95
|
+
{
|
|
96
|
+
address: "0x2b11834ed1feaed4b4b3a86a6f571315e25a884d",
|
|
97
|
+
chainId: BASE_CHAIN_ID,
|
|
98
|
+
decimals: 18,
|
|
99
|
+
symbol: "MOCA" /* MOCA */,
|
|
100
|
+
name: "Moca",
|
|
101
|
+
logoURI: "https://assets.coingecko.com/coins/images/30046/standard/moca.png"
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
address: "0x833589fCD6eDb6E08f4c7C32D4f71b54bDA02913",
|
|
105
|
+
chainId: BASE_CHAIN_ID,
|
|
106
|
+
decimals: 6,
|
|
107
|
+
symbol: "USDC" /* USDC */,
|
|
108
|
+
name: "USD Coin",
|
|
109
|
+
logoURI: "https://cryptologos.cc/logos/usd-coin-usdc-logo.png"
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
address: "0x4200000000000000000000000000000000000006",
|
|
113
|
+
chainId: BASE_CHAIN_ID,
|
|
114
|
+
decimals: 18,
|
|
115
|
+
symbol: "WETH" /* WETH */,
|
|
116
|
+
name: "Wrapped Ether",
|
|
117
|
+
logoURI: "https://static.cdnlogo.com/logos/e/84/ethereum-eth_thumb.png"
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
address: "0xfde4c96c8593536e31f229ea8f37b2ada2699bb2",
|
|
121
|
+
chainId: BASE_CHAIN_ID,
|
|
122
|
+
decimals: 6,
|
|
123
|
+
symbol: "USDT" /* USDT */,
|
|
124
|
+
name: "Tether USD",
|
|
125
|
+
logoURI: "https://cryptologos.cc/logos/tether-usdt-logo.png"
|
|
126
|
+
}
|
|
127
|
+
];
|
|
128
|
+
|
|
129
|
+
// src/hooks/payments/useCryptoPayment.ts
|
|
130
|
+
var MAX_UINT256 = BigInt(2) ** BigInt(256) - BigInt(1);
|
|
131
|
+
var cryptoPaymentLogger = logger.child("crypto-payment");
|
|
132
|
+
var useCryptoPayment = () => {
|
|
133
|
+
const { data: walletClient } = useWalletClient();
|
|
134
|
+
const { spreePayConfig } = useSpreePayConfig();
|
|
135
|
+
const config = useConfig();
|
|
136
|
+
const { selectedPaymentMethod } = useSpreePaymentMethod();
|
|
137
|
+
const cryptoPayment = async (params) => {
|
|
138
|
+
if (!walletClient) {
|
|
139
|
+
const error = new Error("Wallet not connected");
|
|
140
|
+
cryptoPaymentLogger.error("Wallet not connected for crypto payment", error);
|
|
141
|
+
throw error;
|
|
142
|
+
}
|
|
143
|
+
if (!spreePayConfig) {
|
|
144
|
+
const error = new Error("Spree Pay config not loaded");
|
|
145
|
+
cryptoPaymentLogger.error("Config not loaded for crypto payment", error);
|
|
146
|
+
throw error;
|
|
147
|
+
}
|
|
148
|
+
if (selectedPaymentMethod.type !== "CRYPTO" /* CRYPTO */ || !selectedPaymentMethod.method?.symbol) {
|
|
149
|
+
const error = new Error("Unsupported payment method");
|
|
150
|
+
cryptoPaymentLogger.error("Invalid payment method for crypto payment", error, {
|
|
151
|
+
actualType: selectedPaymentMethod.type,
|
|
152
|
+
hasMethod: Boolean(selectedPaymentMethod.method)
|
|
153
|
+
});
|
|
154
|
+
throw error;
|
|
155
|
+
}
|
|
156
|
+
const { capture, hash, metadata } = params;
|
|
157
|
+
const TOKEN = selectedPaymentMethod.method.symbol;
|
|
158
|
+
cryptoPaymentLogger.info("Starting crypto payment", {
|
|
159
|
+
hash,
|
|
160
|
+
capture,
|
|
161
|
+
token: TOKEN,
|
|
162
|
+
walletAddress: walletClient.account.address
|
|
163
|
+
});
|
|
164
|
+
if (["MOCA" /* MOCA */, "WETH" /* WETH */, "USDC" /* USDC */, "USDT" /* USDT */].includes(TOKEN)) {
|
|
165
|
+
const tokenAddress = selectedPaymentMethod.method.address;
|
|
166
|
+
if (!tokenAddress) {
|
|
167
|
+
const error = new Error("Token address not found");
|
|
168
|
+
cryptoPaymentLogger.error("Token address missing", error, { token: TOKEN });
|
|
169
|
+
throw error;
|
|
170
|
+
}
|
|
171
|
+
cryptoPaymentLogger.debug("Checking token allowance", {
|
|
172
|
+
token: TOKEN,
|
|
173
|
+
tokenAddress,
|
|
174
|
+
router: spreePayConfig.crypto.oneInchAggregationRouter
|
|
175
|
+
});
|
|
176
|
+
const allowance = await readContract(config, {
|
|
177
|
+
address: tokenAddress,
|
|
178
|
+
abi: erc20Abi,
|
|
179
|
+
functionName: "allowance",
|
|
180
|
+
args: [walletClient.account.address, spreePayConfig.crypto.oneInchAggregationRouter]
|
|
181
|
+
});
|
|
182
|
+
if (allowance <= 0n) {
|
|
183
|
+
cryptoPaymentLogger.info("Requesting token approval", {
|
|
184
|
+
token: TOKEN,
|
|
185
|
+
tokenAddress
|
|
186
|
+
});
|
|
187
|
+
const result = await walletClient.writeContract({
|
|
188
|
+
address: tokenAddress,
|
|
189
|
+
abi: erc20Abi,
|
|
190
|
+
functionName: "approve",
|
|
191
|
+
args: [spreePayConfig.crypto.oneInchAggregationRouter, MAX_UINT256]
|
|
192
|
+
});
|
|
193
|
+
cryptoPaymentLogger.debug("Waiting for approval confirmation", {
|
|
194
|
+
approvalTxHash: result
|
|
195
|
+
});
|
|
196
|
+
await waitForTransactionReceipt(config, {
|
|
197
|
+
hash: result,
|
|
198
|
+
confirmations: 1
|
|
199
|
+
});
|
|
200
|
+
cryptoPaymentLogger.info("Token approval confirmed", {
|
|
201
|
+
approvalTxHash: result
|
|
202
|
+
});
|
|
203
|
+
} else {
|
|
204
|
+
cryptoPaymentLogger.debug("Sufficient allowance exists", {
|
|
205
|
+
allowance: allowance.toString()
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
cryptoPaymentLogger.debug("Creating crypto payment", {
|
|
210
|
+
token: TOKEN,
|
|
211
|
+
slippageBps: Math.round(0.5 * 100)
|
|
212
|
+
});
|
|
213
|
+
const paymentRes = await SlapiPaymentService.createPayment({
|
|
214
|
+
hash,
|
|
215
|
+
capture,
|
|
216
|
+
metadata,
|
|
217
|
+
type: "CRYPTO" /* CRYPTO */,
|
|
218
|
+
crypto: {
|
|
219
|
+
token: TOKEN,
|
|
220
|
+
publicKey: walletClient.account.address,
|
|
221
|
+
slippageType: "fixed",
|
|
222
|
+
slippageBps: Math.round(0.5 * 100)
|
|
223
|
+
}
|
|
224
|
+
});
|
|
225
|
+
cryptoPaymentLogger.info("Crypto payment created", {
|
|
226
|
+
paymentId: paymentRes.data.id,
|
|
227
|
+
txId: paymentRes.data.txId
|
|
228
|
+
});
|
|
229
|
+
const parsedTX = JSON.parse(paymentRes.data.encodedTx);
|
|
230
|
+
cryptoPaymentLogger.debug("Sending transaction", {
|
|
231
|
+
to: parsedTX.to,
|
|
232
|
+
value: parsedTX.value.toString()
|
|
233
|
+
});
|
|
234
|
+
const txHash = await walletClient.sendTransaction({
|
|
235
|
+
account: walletClient.account.address,
|
|
236
|
+
to: parsedTX.to,
|
|
237
|
+
data: parsedTX.data,
|
|
238
|
+
value: parsedTX.value
|
|
239
|
+
});
|
|
240
|
+
cryptoPaymentLogger.info("Transaction sent", {
|
|
241
|
+
txHash,
|
|
242
|
+
paymentId: paymentRes.data.id
|
|
243
|
+
});
|
|
244
|
+
cryptoPaymentLogger.debug("Verifying transaction on chain", {
|
|
245
|
+
txId: paymentRes.data.txId,
|
|
246
|
+
txHash
|
|
247
|
+
});
|
|
248
|
+
const res = await SlapiPaymentService.baseVerify({ id: paymentRes.data.txId, txHash });
|
|
249
|
+
const finalStatus = res.verified ? "CAPTURED" /* CAPTURED */ : "FAILED" /* FAILED */;
|
|
250
|
+
cryptoPaymentLogger.info("Crypto payment completed", {
|
|
251
|
+
paymentId: paymentRes.data.id,
|
|
252
|
+
txHash,
|
|
253
|
+
verified: res.verified,
|
|
254
|
+
status: finalStatus
|
|
255
|
+
});
|
|
256
|
+
return {
|
|
257
|
+
txHash,
|
|
258
|
+
paymentId: paymentRes.data.id,
|
|
259
|
+
txId: paymentRes.data.txId,
|
|
260
|
+
status: finalStatus,
|
|
261
|
+
paymentType: "CRYPTO" /* CRYPTO */
|
|
262
|
+
};
|
|
263
|
+
};
|
|
264
|
+
return { cryptoPayment };
|
|
265
|
+
};
|
|
266
|
+
|
|
267
|
+
// src/components/CryptoTab/Crypto/ConnectButton.tsx
|
|
268
|
+
import { ConnectButton as RainbowButton } from "@rainbow-me/rainbowkit";
|
|
269
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
270
|
+
var ConnectButton = () => {
|
|
271
|
+
const buttonCN = "h-9 rounded-full border border-(--b-brand) px-4 text-sm font-medium text-(--brand-primary)";
|
|
272
|
+
return /* @__PURE__ */ jsx(RainbowButton.Custom, { children: ({ mounted, chain, account, openAccountModal, openChainModal, openConnectModal }) => {
|
|
273
|
+
if (!mounted) return null;
|
|
274
|
+
return /* @__PURE__ */ jsx(Fragment, { children: (() => {
|
|
275
|
+
if (!mounted || !account || !chain) {
|
|
276
|
+
return /* @__PURE__ */ jsx("button", { className: buttonCN, onClick: openConnectModal, children: "Connect wallet" });
|
|
277
|
+
}
|
|
278
|
+
if (chain.unsupported) {
|
|
279
|
+
return /* @__PURE__ */ jsx("button", { className: buttonCN, onClick: openChainModal, children: "Select a Network" });
|
|
280
|
+
}
|
|
281
|
+
return /* @__PURE__ */ jsxs("button", { className: cn(buttonCN, "flex items-center gap-2 pl-1.5"), onClick: openAccountModal, children: [
|
|
282
|
+
chain.hasIcon && /* @__PURE__ */ jsx("div", { className: "size-6 overflow-hidden rounded-full", style: { background: chain.iconBackground }, children: chain.iconUrl && /* @__PURE__ */ jsx("img", { alt: chain.name ?? "Chain icon", src: chain.iconUrl }) }),
|
|
283
|
+
account.displayName
|
|
284
|
+
] });
|
|
285
|
+
})() });
|
|
286
|
+
} });
|
|
287
|
+
};
|
|
288
|
+
|
|
289
|
+
// src/config/symbolLogos.tsx
|
|
290
|
+
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
291
|
+
var MOCA_SVG = /* @__PURE__ */ jsxs2("svg", { xmlns: "http://www.w3.org/2000/svg", fill: "none", className: "size-7", viewBox: "0 0 28 28", children: [
|
|
292
|
+
/* @__PURE__ */ jsx2("circle", { cx: "14", cy: "14", r: "13.5", fill: "#c15f97" }),
|
|
293
|
+
/* @__PURE__ */ jsx2(
|
|
294
|
+
"path",
|
|
295
|
+
{
|
|
296
|
+
fill: "#fff",
|
|
297
|
+
d: "M16.06 6.65c.3 0 .59.16.74.43l6.06 10.5a.85.85 0 1 1-1.47.84L16.06 9.2l-1.51 2.62-.02.03-3.8 6.57a.85.85 0 0 1-1.47-.84l3.57-6.18-1.27-2.2-5.32 9.22a.85.85 0 0 1-1.48-.84l6.07-10.5.06-.1a.85.85 0 0 1 1.4.1l1.52 2.62 1.52-2.62.06-.1c.16-.2.4-.33.67-.33"
|
|
298
|
+
}
|
|
299
|
+
),
|
|
300
|
+
/* @__PURE__ */ jsx2("circle", { cx: "16", cy: "14", r: "1.5", fill: "#fff" })
|
|
301
|
+
] });
|
|
302
|
+
var USDC_SVG = /* @__PURE__ */ jsxs2("svg", { xmlns: "http://www.w3.org/2000/svg", className: "size-7", fill: "none", viewBox: "0 0 28 28", children: [
|
|
303
|
+
/* @__PURE__ */ jsx2("path", { fill: "#2775ca", d: "M14 28c7.76 0 14-6.24 14-14S21.76 0 14 0 0 6.24 0 14s6.24 14 14 14" }),
|
|
304
|
+
/* @__PURE__ */ jsx2(
|
|
305
|
+
"path",
|
|
306
|
+
{
|
|
307
|
+
fill: "#fff",
|
|
308
|
+
d: "M17.85 16.22c0-2.04-1.23-2.74-3.68-3.04-1.75-.23-2.1-.7-2.1-1.51 0-.82.59-1.34 1.75-1.34 1.05 0 1.64.35 1.93 1.22.06.18.23.3.4.3h.94a.4.4 0 0 0 .41-.42v-.05a2.91 2.91 0 0 0-2.63-2.4v-1.4c0-.23-.17-.4-.46-.46h-.88c-.23 0-.4.17-.46.46v1.35c-1.75.23-2.86 1.4-2.86 2.85 0 1.93 1.16 2.69 3.61 2.98 1.64.29 2.16.64 2.16 1.57 0 .94-.81 1.58-1.92 1.58-1.52 0-2.04-.64-2.22-1.52-.06-.23-.23-.35-.4-.35h-1a.4.4 0 0 0-.4.41v.06c.23 1.46 1.16 2.5 3.08 2.8v1.4c0 .23.18.4.47.47h.88c.23 0 .4-.18.46-.47v-1.4c1.75-.3 2.92-1.52 2.92-3.1Z"
|
|
309
|
+
}
|
|
310
|
+
),
|
|
311
|
+
/* @__PURE__ */ jsx2(
|
|
312
|
+
"path",
|
|
313
|
+
{
|
|
314
|
+
fill: "#fff",
|
|
315
|
+
d: "M11.03 22.34a8.69 8.69 0 0 1-5.2-11.2 8.63 8.63 0 0 1 5.2-5.19.6.6 0 0 0 .35-.58v-.82c0-.23-.12-.4-.35-.47-.06 0-.18 0-.24.06a10.48 10.48 0 0 0 0 20.01c.24.12.47 0 .53-.23.06-.06.06-.12.06-.24v-.81c0-.18-.18-.41-.35-.53m6.18-18.2c-.23-.12-.47 0-.53.23-.05.06-.05.12-.05.24v.81c0 .24.17.47.35.59a8.69 8.69 0 0 1 5.19 11.2 8.63 8.63 0 0 1-5.2 5.19c-.23.12-.34.3-.34.58v.82c0 .23.11.4.35.47.05 0 .17 0 .23-.06a10.48 10.48 0 0 0 6.82-13.19 10.58 10.58 0 0 0-6.82-6.88"
|
|
316
|
+
}
|
|
317
|
+
)
|
|
318
|
+
] });
|
|
319
|
+
var USDT_SVG = /* @__PURE__ */ jsxs2("svg", { xmlns: "http://www.w3.org/2000/svg", className: "size-7", fill: "none", viewBox: "0 0 28 28", children: [
|
|
320
|
+
/* @__PURE__ */ jsx2("path", { fill: "#26a17b", d: "M14 28a14 14 0 1 0 0-28 14 14 0 0 0 0 28" }),
|
|
321
|
+
/* @__PURE__ */ jsx2(
|
|
322
|
+
"path",
|
|
323
|
+
{
|
|
324
|
+
fill: "#fff",
|
|
325
|
+
d: "M15.5 15.3v-.01c-.1 0-.6.04-1.72.04-.88 0-1.5-.03-1.73-.04-3.42-.15-5.97-.74-5.97-1.46 0-.7 2.55-1.3 5.97-1.46v2.33a26 26 0 0 0 3.44 0v-2.32c3.42.15 5.96.74 5.96 1.46 0 .7-2.55 1.3-5.96 1.45m0-3.15v-2.08h4.76V6.89H7.3v3.17h4.76v2.08c-3.87.17-6.77.94-6.77 1.86s2.9 1.68 6.77 1.86v6.67h3.45v-6.67c3.86-.18 6.76-.94 6.76-1.86s-2.9-1.68-6.76-1.86"
|
|
326
|
+
}
|
|
327
|
+
)
|
|
328
|
+
] });
|
|
329
|
+
var WETH_SVG = /* @__PURE__ */ jsxs2("svg", { xmlns: "http://www.w3.org/2000/svg", fill: "none", className: "size-7", viewBox: "0 0 24 24", children: [
|
|
330
|
+
/* @__PURE__ */ jsxs2("g", { clipPath: "url(#clip0_528_9173)", children: [
|
|
331
|
+
/* @__PURE__ */ jsx2(
|
|
332
|
+
"path",
|
|
333
|
+
{
|
|
334
|
+
fill: "#000",
|
|
335
|
+
d: "M17.14 20.57c0 .95-1.31 2.01-3.39 2.4h-2.59c-4.65 0-8.42-1.07-8.42-2.4 0-1.32 3.77-2.4 8.42-2.4s5.98 1.08 5.98 2.4Z"
|
|
336
|
+
}
|
|
337
|
+
),
|
|
338
|
+
/* @__PURE__ */ jsx2(
|
|
339
|
+
"path",
|
|
340
|
+
{
|
|
341
|
+
fill: "#F61F7D",
|
|
342
|
+
d: "M23.31 11c0 5.86-5.18 11.63-11.07 11.63-5.9 0-11.9-6.17-11.9-12.03C.34 4.75 5.12 0 11.01 0s12.3 5.15 12.3 11Z"
|
|
343
|
+
}
|
|
344
|
+
),
|
|
345
|
+
/* @__PURE__ */ jsx2(
|
|
346
|
+
"path",
|
|
347
|
+
{
|
|
348
|
+
fill: "#000",
|
|
349
|
+
fillRule: "evenodd",
|
|
350
|
+
d: "M19.19 18.27c1.87-2 3.03-4.65 3.03-7.26 0-2.52-1.39-5-3.56-6.87-2.18-1.88-5-3.05-7.65-3.05a9.54 9.54 0 0 0-9.57 9.51c0 2.57 1.33 5.31 3.42 7.44 2.1 2.13 4.8 3.5 7.38 3.5a9.7 9.7 0 0 0 6.95-3.27Zm-6.95 4.36c5.89 0 11.07-5.77 11.07-11.62C23.31 5.15 16.9 0 11.01 0A10.63 10.63 0 0 0 .34 10.6c0 5.86 6 12.03 11.9 12.03Z",
|
|
351
|
+
clipRule: "evenodd"
|
|
352
|
+
}
|
|
353
|
+
),
|
|
354
|
+
/* @__PURE__ */ jsx2("path", { fill: "#fff", d: "M24 12.17a10.8 10.8 0 1 1-21.6 0 10.8 10.8 0 0 1 21.6 0Z" }),
|
|
355
|
+
/* @__PURE__ */ jsx2(
|
|
356
|
+
"path",
|
|
357
|
+
{
|
|
358
|
+
fill: "#000",
|
|
359
|
+
fillRule: "evenodd",
|
|
360
|
+
d: "M13.2 21.87a9.7 9.7 0 1 0 0-19.4 9.7 9.7 0 0 0 0 19.4Zm0 1.1a10.8 10.8 0 1 0 0-21.6 10.8 10.8 0 0 0 0 21.6Z",
|
|
361
|
+
clipRule: "evenodd"
|
|
362
|
+
}
|
|
363
|
+
),
|
|
364
|
+
/* @__PURE__ */ jsx2("path", { fill: "#000", fillRule: "evenodd", d: "M3.02 10.63.7 8.75l.74-.86 2.34 1.87-.75.87Z", clipRule: "evenodd" }),
|
|
365
|
+
/* @__PURE__ */ jsx2(
|
|
366
|
+
"path",
|
|
367
|
+
{
|
|
368
|
+
fill: "#000",
|
|
369
|
+
d: "M5.83 9.94h.99l.31 3 .4-3h.78l.41 2.98.32-2.98h.98l-.63 5.15H8.37l-.45-3.1-.42 3.1H6.47l-.64-5.15ZM10.91 9.94h2.38v.78h-1.2v1.27H13v.8h-.92v1.53h1.22v.77h-2.39V9.94ZM14.64 10.79h-.81v-.85h2.78v.85h-.8v4.3h-1.17v-4.3ZM17.42 9.94h1.16v2.09h.82V9.94h1.17v5.15H19.4v-2.25h-.82v2.25h-1.16V9.94Z"
|
|
370
|
+
}
|
|
371
|
+
)
|
|
372
|
+
] }),
|
|
373
|
+
/* @__PURE__ */ jsx2("defs", { children: /* @__PURE__ */ jsx2("clipPath", { id: "clip0_528_9173", children: /* @__PURE__ */ jsx2("path", { fill: "#fff", d: "M0 0h24v24H0z" }) }) })
|
|
374
|
+
] });
|
|
375
|
+
var symbolLogos = {
|
|
376
|
+
MOCA: MOCA_SVG,
|
|
377
|
+
USDC: USDC_SVG,
|
|
378
|
+
USDT: USDT_SVG,
|
|
379
|
+
WETH: WETH_SVG
|
|
380
|
+
};
|
|
381
|
+
function getSymbolLogo(symbol) {
|
|
382
|
+
return symbolLogos[symbol] ?? null;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
// src/components/CryptoTab/Crypto/Logos.tsx
|
|
386
|
+
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
387
|
+
var Logos = () => {
|
|
388
|
+
return /* @__PURE__ */ jsxs3("div", { className: "flex", children: [
|
|
389
|
+
/* @__PURE__ */ jsx3("div", { className: "rounded-full border border-(--b-primary)", children: getSymbolLogo("MOCA") }),
|
|
390
|
+
/* @__PURE__ */ jsx3("div", { className: "-ml-2.5 rounded-full border border-(--b-primary)", children: getSymbolLogo("USDC") }),
|
|
391
|
+
/* @__PURE__ */ jsx3("div", { className: "-ml-2.5 rounded-full border border-(--b-primary)", children: getSymbolLogo("USDT") }),
|
|
392
|
+
/* @__PURE__ */ jsx3("div", { className: "-ml-2.5 rounded-full border border-(--b-primary) bg-(--s-primary)", children: getSymbolLogo("WETH") })
|
|
393
|
+
] });
|
|
394
|
+
};
|
|
395
|
+
|
|
396
|
+
// src/components/CryptoTab/Crypto/SelectCoinButton.tsx
|
|
397
|
+
import NiceModal2 from "@ebay/nice-modal-react";
|
|
398
|
+
|
|
399
|
+
// src/modals/CryptoSelectModal.tsx
|
|
400
|
+
import { useMemo, useState as useState2 } from "react";
|
|
401
|
+
import NiceModal, { useModal } from "@ebay/nice-modal-react";
|
|
402
|
+
|
|
403
|
+
// ../ui/src/components/input.tsx
|
|
404
|
+
import { jsx as jsx4 } from "react/jsx-runtime";
|
|
405
|
+
function Input({ className, type, ...props }) {
|
|
406
|
+
return /* @__PURE__ */ jsx4(
|
|
407
|
+
"input",
|
|
408
|
+
{
|
|
409
|
+
type,
|
|
410
|
+
"data-slot": "input",
|
|
411
|
+
className: cn2(
|
|
412
|
+
"file:text-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input flex h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base text-(--primary) shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-(--tertiary) disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
|
|
413
|
+
"focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
|
|
414
|
+
"aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
|
|
415
|
+
className
|
|
416
|
+
),
|
|
417
|
+
...props
|
|
418
|
+
}
|
|
419
|
+
);
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
// ../../node_modules/@radix-ui/react-separator/dist/index.mjs
|
|
423
|
+
import * as React2 from "react";
|
|
424
|
+
|
|
425
|
+
// ../../node_modules/@radix-ui/react-separator/node_modules/@radix-ui/react-primitive/dist/index.mjs
|
|
426
|
+
import * as React from "react";
|
|
427
|
+
import * as ReactDOM from "react-dom";
|
|
428
|
+
import { createSlot } from "@radix-ui/react-slot";
|
|
429
|
+
import { jsx as jsx5 } from "react/jsx-runtime";
|
|
430
|
+
var NODES = [
|
|
431
|
+
"a",
|
|
432
|
+
"button",
|
|
433
|
+
"div",
|
|
434
|
+
"form",
|
|
435
|
+
"h2",
|
|
436
|
+
"h3",
|
|
437
|
+
"img",
|
|
438
|
+
"input",
|
|
439
|
+
"label",
|
|
440
|
+
"li",
|
|
441
|
+
"nav",
|
|
442
|
+
"ol",
|
|
443
|
+
"p",
|
|
444
|
+
"select",
|
|
445
|
+
"span",
|
|
446
|
+
"svg",
|
|
447
|
+
"ul"
|
|
448
|
+
];
|
|
449
|
+
var Primitive = NODES.reduce((primitive, node) => {
|
|
450
|
+
const Slot = createSlot(`Primitive.${node}`);
|
|
451
|
+
const Node = React.forwardRef((props, forwardedRef) => {
|
|
452
|
+
const { asChild, ...primitiveProps } = props;
|
|
453
|
+
const Comp = asChild ? Slot : node;
|
|
454
|
+
if (typeof window !== "undefined") {
|
|
455
|
+
window[/* @__PURE__ */ Symbol.for("radix-ui")] = true;
|
|
456
|
+
}
|
|
457
|
+
return /* @__PURE__ */ jsx5(Comp, { ...primitiveProps, ref: forwardedRef });
|
|
458
|
+
});
|
|
459
|
+
Node.displayName = `Primitive.${node}`;
|
|
460
|
+
return { ...primitive, [node]: Node };
|
|
461
|
+
}, {});
|
|
462
|
+
|
|
463
|
+
// ../../node_modules/@radix-ui/react-separator/dist/index.mjs
|
|
464
|
+
import { jsx as jsx6 } from "react/jsx-runtime";
|
|
465
|
+
var NAME = "Separator";
|
|
466
|
+
var DEFAULT_ORIENTATION = "horizontal";
|
|
467
|
+
var ORIENTATIONS = ["horizontal", "vertical"];
|
|
468
|
+
var Separator = React2.forwardRef((props, forwardedRef) => {
|
|
469
|
+
const { decorative, orientation: orientationProp = DEFAULT_ORIENTATION, ...domProps } = props;
|
|
470
|
+
const orientation = isValidOrientation(orientationProp) ? orientationProp : DEFAULT_ORIENTATION;
|
|
471
|
+
const ariaOrientation = orientation === "vertical" ? orientation : void 0;
|
|
472
|
+
const semanticProps = decorative ? { role: "none" } : { "aria-orientation": ariaOrientation, role: "separator" };
|
|
473
|
+
return /* @__PURE__ */ jsx6(
|
|
474
|
+
Primitive.div,
|
|
475
|
+
{
|
|
476
|
+
"data-orientation": orientation,
|
|
477
|
+
...semanticProps,
|
|
478
|
+
...domProps,
|
|
479
|
+
ref: forwardedRef
|
|
480
|
+
}
|
|
481
|
+
);
|
|
482
|
+
});
|
|
483
|
+
Separator.displayName = NAME;
|
|
484
|
+
function isValidOrientation(orientation) {
|
|
485
|
+
return ORIENTATIONS.includes(orientation);
|
|
486
|
+
}
|
|
487
|
+
var Root = Separator;
|
|
488
|
+
|
|
489
|
+
// ../ui/src/components/separator.tsx
|
|
490
|
+
import { jsx as jsx7 } from "react/jsx-runtime";
|
|
491
|
+
function Separator2({
|
|
492
|
+
className,
|
|
493
|
+
orientation = "horizontal",
|
|
494
|
+
decorative = true,
|
|
495
|
+
...props
|
|
496
|
+
}) {
|
|
497
|
+
return /* @__PURE__ */ jsx7(
|
|
498
|
+
Root,
|
|
499
|
+
{
|
|
500
|
+
"data-slot": "separator",
|
|
501
|
+
decorative,
|
|
502
|
+
orientation,
|
|
503
|
+
className: cn2(
|
|
504
|
+
"shrink-0 bg-(--b-secondary) data-[orientation=horizontal]:h-px data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-px",
|
|
505
|
+
className
|
|
506
|
+
),
|
|
507
|
+
...props
|
|
508
|
+
}
|
|
509
|
+
);
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
// src/hooks/useBaseERC20Token.ts
|
|
513
|
+
import * as React3 from "react";
|
|
514
|
+
import { erc20Abi as erc20Abi2, formatUnits, getAddress } from "viem";
|
|
515
|
+
import { useAccount, usePublicClient } from "wagmi";
|
|
516
|
+
function useBaseERC20Token() {
|
|
517
|
+
const { address } = useAccount();
|
|
518
|
+
const baseClient = usePublicClient({ chainId: BASE_CHAIN_ID });
|
|
519
|
+
const defaultClient = usePublicClient();
|
|
520
|
+
const [rows, setRows] = React3.useState([]);
|
|
521
|
+
const [isLoading, setLoading] = React3.useState(false);
|
|
522
|
+
const [error, setError] = React3.useState(null);
|
|
523
|
+
React3.useEffect(() => {
|
|
524
|
+
let cancelled = false;
|
|
525
|
+
async function run() {
|
|
526
|
+
const client = baseClient ?? defaultClient;
|
|
527
|
+
if (!address || !client) {
|
|
528
|
+
setRows([]);
|
|
529
|
+
return;
|
|
530
|
+
}
|
|
531
|
+
setLoading(true);
|
|
532
|
+
try {
|
|
533
|
+
const normalizedTokens = [];
|
|
534
|
+
for (const t of BASE_TOKENS) {
|
|
535
|
+
try {
|
|
536
|
+
const addr = getAddress(t.address);
|
|
537
|
+
normalizedTokens.push({ ...t, address: addr });
|
|
538
|
+
} catch {
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
const res = await client.multicall({
|
|
542
|
+
allowFailure: true,
|
|
543
|
+
contracts: normalizedTokens.map((t) => ({
|
|
544
|
+
address: t.address,
|
|
545
|
+
abi: erc20Abi2,
|
|
546
|
+
functionName: "balanceOf",
|
|
547
|
+
args: [address]
|
|
548
|
+
}))
|
|
549
|
+
});
|
|
550
|
+
const acc = [];
|
|
551
|
+
for (let idx = 0; idx < res.length; idx++) {
|
|
552
|
+
const r = res[idx];
|
|
553
|
+
const t = normalizedTokens[idx];
|
|
554
|
+
if (r.status === "success") {
|
|
555
|
+
const raw = r.result;
|
|
556
|
+
if (raw > 0n) acc.push({ ...t, raw, formatted: formatUnits(raw, t.decimals) });
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
if (!cancelled) setRows(acc);
|
|
560
|
+
} catch (e) {
|
|
561
|
+
if (!cancelled) {
|
|
562
|
+
const msg = e instanceof Error ? e.message : "Multicall failed";
|
|
563
|
+
setError(
|
|
564
|
+
baseClient ? msg : `Base client unavailable. Ensure Base (${BASE_CHAIN_ID}) is configured in Wagmi.`
|
|
565
|
+
);
|
|
566
|
+
}
|
|
567
|
+
} finally {
|
|
568
|
+
if (!cancelled) setLoading(false);
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
run();
|
|
572
|
+
return () => {
|
|
573
|
+
cancelled = true;
|
|
574
|
+
};
|
|
575
|
+
}, [address, baseClient, defaultClient]);
|
|
576
|
+
return { isLoading, error: error ?? null, erc20Balances: rows };
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
// src/hooks/useBaseNativeToken.ts
|
|
580
|
+
import { useAccount as useAccount2, useBalance } from "wagmi";
|
|
581
|
+
function useBaseNativeToken() {
|
|
582
|
+
const { address } = useAccount2();
|
|
583
|
+
const { data, isLoading, error } = useBalance({
|
|
584
|
+
address,
|
|
585
|
+
chainId: BASE_CHAIN_ID,
|
|
586
|
+
query: { enabled: !!address }
|
|
587
|
+
});
|
|
588
|
+
const nativeBalance = data ? { ...data, symbol: "ETH" /* ETH */, logoURI: "https://static.cdnlogo.com/logos/e/84/ethereum-eth_thumb.png" } : void 0;
|
|
589
|
+
return {
|
|
590
|
+
isLoadingNative: isLoading,
|
|
591
|
+
nativeError: error?.message ?? null,
|
|
592
|
+
nativeBalance
|
|
593
|
+
};
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
// src/hooks/useBaseTokens.ts
|
|
597
|
+
import useSWR from "swr";
|
|
598
|
+
var useBaseTokens = () => {
|
|
599
|
+
const { data: resData, isLoading } = useSWR(`/v1/base-transactions/tokens`);
|
|
600
|
+
return { tokens: resData?.data ?? [], tokensIsLoading: isLoading };
|
|
601
|
+
};
|
|
602
|
+
|
|
603
|
+
// src/modals/CryptoSelectModal.tsx
|
|
604
|
+
import { Fragment as Fragment2, jsx as jsx8, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
605
|
+
var CryptoSelectModal = NiceModal.create(() => {
|
|
606
|
+
const modal = useModal();
|
|
607
|
+
const { isLoading, error, erc20Balances } = useBaseERC20Token();
|
|
608
|
+
const { isLoadingNative, nativeError, nativeBalance } = useBaseNativeToken();
|
|
609
|
+
const { tokens, tokensIsLoading } = useBaseTokens();
|
|
610
|
+
const [search, setSearch] = useState2("");
|
|
611
|
+
const filteredCoins = useMemo(() => {
|
|
612
|
+
return tokens.filter(
|
|
613
|
+
(coin) => coin.name.toLowerCase().includes(search.toLowerCase()) || coin.symbol.toLowerCase().includes(search.toLowerCase())
|
|
614
|
+
);
|
|
615
|
+
}, [tokens, search]);
|
|
616
|
+
const { setSelectedPaymentMethod } = useSpreePaymentMethod();
|
|
617
|
+
const handleSelect = (coin) => {
|
|
618
|
+
modal.remove();
|
|
619
|
+
setSelectedPaymentMethod({ type: "CRYPTO" /* CRYPTO */, method: coin });
|
|
620
|
+
};
|
|
621
|
+
const userCoins = [nativeBalance, ...erc20Balances].filter(Boolean);
|
|
622
|
+
return /* @__PURE__ */ jsxs4(Dialog, { open: modal.visible, onOpenChange: modal.remove, children: [
|
|
623
|
+
/* @__PURE__ */ jsx8(DialogDescription, { className: "hidden", children: "Crypto Select Modal" }),
|
|
624
|
+
/* @__PURE__ */ jsxs4(DialogContent, { showCloseButton: false, className: "max-h-[90vh] gap-0 p-0", children: [
|
|
625
|
+
/* @__PURE__ */ jsxs4("div", { className: "flex flex-col gap-4 px-5 py-5 md:px-7", children: [
|
|
626
|
+
/* @__PURE__ */ jsxs4("div", { className: "flex items-center justify-between gap-4", children: [
|
|
627
|
+
/* @__PURE__ */ jsx8("button", { className: "rounded-md hover:bg-(--s-primary-hover)", onClick: modal.remove, children: /* @__PURE__ */ jsx8("svg", { xmlns: "http://www.w3.org/2000/svg", fill: "none", className: "size-6", viewBox: "0 0 24 25", children: /* @__PURE__ */ jsx8("path", { stroke: "currentColor", d: "m15 6.5-6 6 6 6" }) }) }),
|
|
628
|
+
/* @__PURE__ */ jsx8(DialogTitle, { className: "text-2xl font-medium text-(--brand-primary)", children: "Select a token" }),
|
|
629
|
+
/* @__PURE__ */ jsx8("button", { className: "rounded-md p-1 hover:bg-(--s-primary-hover)", onClick: modal.remove, children: /* @__PURE__ */ jsx8("svg", { xmlns: "http://www.w3.org/2000/svg", fill: "none", className: "size-4", viewBox: "0 0 16 17", children: /* @__PURE__ */ jsx8(
|
|
630
|
+
"path",
|
|
631
|
+
{
|
|
632
|
+
fill: "currentColor",
|
|
633
|
+
d: "M12.6 3.9c.2.2.2.52 0 .71L8.7 8.5l3.9 3.89a.5.5 0 1 1-.71.7L8 9.22 4.11 13.1a.5.5 0 1 1-.7-.71L7.28 8.5 3.4 4.61a.5.5 0 1 1 .71-.7L8 7.78l3.89-3.89c.2-.2.51-.2.7 0Z"
|
|
634
|
+
}
|
|
635
|
+
) }) })
|
|
636
|
+
] }),
|
|
637
|
+
/* @__PURE__ */ jsx8(Input, { onChange: (e) => setSearch(e.target.value), placeholder: "Search by token name", value: search })
|
|
638
|
+
] }),
|
|
639
|
+
/* @__PURE__ */ jsx8(Separator2, { className: "hidden md:block" }),
|
|
640
|
+
/* @__PURE__ */ jsxs4("div", { className: "flex flex-col gap-4 px-5 py-5 md:px-7", children: [
|
|
641
|
+
/* @__PURE__ */ jsx8("h3", { className: "text-md font-medium text-(--brand-primary)", children: "Tokens with wallet balance" }),
|
|
642
|
+
(error || nativeError) && /* @__PURE__ */ jsx8("p", { className: "text-center text-sm text-(--negative)", children: "Something wrong" }),
|
|
643
|
+
/* @__PURE__ */ jsxs4("div", { className: "flex w-full flex-col gap-1", children: [
|
|
644
|
+
isLoadingNative && /* @__PURE__ */ jsx8("div", { className: "h-11 animate-pulse rounded-md bg-(--s-primary)" }),
|
|
645
|
+
nativeBalance && /* @__PURE__ */ jsxs4(
|
|
646
|
+
"button",
|
|
647
|
+
{
|
|
648
|
+
className: "flex h-11 w-full items-center justify-between gap-4 rounded-sm px-1.5 text-(--brand-primary) hover:bg-(--s-primary-hover)",
|
|
649
|
+
onClick: () => handleSelect(nativeBalance),
|
|
650
|
+
children: [
|
|
651
|
+
/* @__PURE__ */ jsxs4("div", { className: "flex items-center gap-2", children: [
|
|
652
|
+
nativeBalance.logoURI && /* @__PURE__ */ jsx8("img", { className: "size-8 shrink-0", src: nativeBalance.logoURI, alt: `${nativeBalance.symbol} logo` }),
|
|
653
|
+
/* @__PURE__ */ jsx8("p", { className: "text-sm font-medium", children: nativeBalance.symbol })
|
|
654
|
+
] }),
|
|
655
|
+
/* @__PURE__ */ jsx8("p", { className: "text-sm font-medium", children: nativeBalance.formatted })
|
|
656
|
+
]
|
|
657
|
+
},
|
|
658
|
+
nativeBalance.symbol
|
|
659
|
+
),
|
|
660
|
+
isLoading && /* @__PURE__ */ jsxs4(Fragment2, { children: [
|
|
661
|
+
/* @__PURE__ */ jsx8("div", { className: "h-11 animate-pulse rounded-md bg-(--s-primary)" }),
|
|
662
|
+
/* @__PURE__ */ jsx8("div", { className: "h-11 animate-pulse rounded-md bg-(--s-primary)" }),
|
|
663
|
+
/* @__PURE__ */ jsx8("div", { className: "h-11 animate-pulse rounded-md bg-(--s-primary)" })
|
|
664
|
+
] }),
|
|
665
|
+
erc20Balances.map((coin) => {
|
|
666
|
+
const Icon = getSymbolLogo(coin.symbol);
|
|
667
|
+
return /* @__PURE__ */ jsxs4(
|
|
668
|
+
"button",
|
|
669
|
+
{
|
|
670
|
+
className: "flex h-11 w-full items-center justify-between gap-4 rounded-sm px-1.5 text-(--brand-primary) hover:bg-(--s-primary-hover) disabled:cursor-not-allowed disabled:opacity-50",
|
|
671
|
+
onClick: () => handleSelect(coin),
|
|
672
|
+
children: [
|
|
673
|
+
/* @__PURE__ */ jsxs4("div", { className: "flex items-center gap-2", children: [
|
|
674
|
+
Boolean(Icon) && Icon,
|
|
675
|
+
/* @__PURE__ */ jsx8("p", { className: "text-sm font-medium", children: coin.symbol })
|
|
676
|
+
] }),
|
|
677
|
+
/* @__PURE__ */ jsx8("p", { className: "text-sm font-medium", children: coin.formatted })
|
|
678
|
+
]
|
|
679
|
+
},
|
|
680
|
+
coin.symbol
|
|
681
|
+
);
|
|
682
|
+
})
|
|
683
|
+
] }),
|
|
684
|
+
/* @__PURE__ */ jsx8("h3", { className: "text-md font-medium text-(--brand-primary)", children: "All Tokens" }),
|
|
685
|
+
/* @__PURE__ */ jsxs4("div", { className: "flex max-h-[40vh] w-full flex-col gap-1 overflow-y-auto", children: [
|
|
686
|
+
tokensIsLoading && /* @__PURE__ */ jsxs4(Fragment2, { children: [
|
|
687
|
+
/* @__PURE__ */ jsx8("div", { className: "h-11 animate-pulse rounded-md bg-(--s-primary)" }),
|
|
688
|
+
/* @__PURE__ */ jsx8("div", { className: "h-11 animate-pulse rounded-md bg-(--s-primary)" }),
|
|
689
|
+
/* @__PURE__ */ jsx8("div", { className: "h-11 animate-pulse rounded-md bg-(--s-primary)" })
|
|
690
|
+
] }),
|
|
691
|
+
filteredCoins.map((token) => {
|
|
692
|
+
const userCoin = userCoins.find((c) => c.symbol === token.symbol);
|
|
693
|
+
return /* @__PURE__ */ jsx8(
|
|
694
|
+
"button",
|
|
695
|
+
{
|
|
696
|
+
disabled: !userCoin,
|
|
697
|
+
onClick: () => userCoin && handleSelect(userCoin),
|
|
698
|
+
className: "flex min-h-11 w-full items-center justify-between gap-4 rounded-sm px-1.5 text-(--brand-primary) hover:bg-(--s-primary-hover) disabled:cursor-not-allowed disabled:opacity-50",
|
|
699
|
+
children: /* @__PURE__ */ jsxs4("div", { className: "flex items-center gap-2", children: [
|
|
700
|
+
token.logoURI ? /* @__PURE__ */ jsx8("img", { className: "size-8 shrink-0", src: token.logoURI, alt: `${token.name} logo` }) : /* @__PURE__ */ jsx8("div", { className: "size-8 shrink-0 rounded-full bg-(--s-tertiary)" }),
|
|
701
|
+
/* @__PURE__ */ jsx8("p", { className: "text-sm font-medium", children: token.symbol })
|
|
702
|
+
] })
|
|
703
|
+
},
|
|
704
|
+
token.symbol
|
|
705
|
+
);
|
|
706
|
+
})
|
|
707
|
+
] })
|
|
708
|
+
] })
|
|
709
|
+
] })
|
|
710
|
+
] });
|
|
711
|
+
});
|
|
712
|
+
CryptoSelectModal.displayName = "CryptoSelectModal";
|
|
713
|
+
|
|
714
|
+
// src/components/CryptoTab/Crypto/SelectCoinButton.tsx
|
|
715
|
+
import { jsx as jsx9, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
716
|
+
var SelectCoinButton = () => {
|
|
717
|
+
const openModal = () => {
|
|
718
|
+
NiceModal2.show(CryptoSelectModal);
|
|
719
|
+
};
|
|
720
|
+
return /* @__PURE__ */ jsx9(
|
|
721
|
+
"button",
|
|
722
|
+
{
|
|
723
|
+
onClick: openModal,
|
|
724
|
+
className: "flex h-11 w-full overflow-hidden rounded-md bg-(--s-primary) hover:bg-(--s-primary-hover)",
|
|
725
|
+
children: /* @__PURE__ */ jsxs5("div", { className: "flex h-full w-full items-center justify-between px-3", children: [
|
|
726
|
+
/* @__PURE__ */ jsx9("div", { className: "flex items-center", children: /* @__PURE__ */ jsx9("p", { className: "font-medium text-(--secondary)", children: "Select a token" }) }),
|
|
727
|
+
/* @__PURE__ */ jsx9("svg", { xmlns: "http://www.w3.org/2000/svg", fill: "none", className: "size-4", viewBox: "0 0 16 16", children: /* @__PURE__ */ jsx9("path", { stroke: "currentColor", d: "m6 12.43 4-4-4-4" }) })
|
|
728
|
+
] })
|
|
729
|
+
}
|
|
730
|
+
);
|
|
731
|
+
};
|
|
732
|
+
|
|
733
|
+
// src/components/CryptoTab/Crypto/SelectedCoin.tsx
|
|
734
|
+
import { jsx as jsx10, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
735
|
+
var SelectedCoin = (props) => {
|
|
736
|
+
const { coin, balance, logoURI } = props;
|
|
737
|
+
const Icon = getSymbolLogo(coin);
|
|
738
|
+
return /* @__PURE__ */ jsxs6("div", { className: "flex h-12 w-full overflow-hidden rounded-md border-2 border-(--b-brand) bg-(--s-primary)", children: [
|
|
739
|
+
/* @__PURE__ */ jsx10("div", { className: "flex h-full w-11 shrink-0 items-center justify-center border-r border-(--b-brand) bg-(--s-primary)", children: /* @__PURE__ */ jsx10("div", { className: "flex size-5 items-center justify-center rounded-full border-2 border-(--brand-primary)", children: /* @__PURE__ */ jsx10("div", { className: "size-2 rounded-full bg-(--brand-primary)" }) }) }),
|
|
740
|
+
/* @__PURE__ */ jsxs6("div", { className: "flex h-full w-full items-center justify-between rounded-r-md px-3", children: [
|
|
741
|
+
/* @__PURE__ */ jsxs6("div", { className: "flex items-center gap-1", children: [
|
|
742
|
+
Icon,
|
|
743
|
+
!Icon && logoURI && /* @__PURE__ */ jsx10("img", { className: "mr-1 size-8 shrink-0", src: logoURI, alt: `${coin} logo` }),
|
|
744
|
+
/* @__PURE__ */ jsx10("p", { className: "font-semibold text-(--brand-primary)", children: coin }),
|
|
745
|
+
/* @__PURE__ */ jsx10("svg", { xmlns: "http://www.w3.org/2000/svg", className: "size-4", fill: "none", viewBox: "0 0 16 16", children: /* @__PURE__ */ jsx10("path", { stroke: "currentColor", d: "m6 12.434 4-4-4-4" }) })
|
|
746
|
+
] }),
|
|
747
|
+
/* @__PURE__ */ jsxs6("p", { className: "text-xs font-medium text-(--secondary)", children: [
|
|
748
|
+
"Wallet balance ",
|
|
749
|
+
/* @__PURE__ */ jsx10("span", { className: "text-(--brand-primary)", children: balance })
|
|
750
|
+
] })
|
|
751
|
+
] })
|
|
752
|
+
] });
|
|
753
|
+
};
|
|
754
|
+
|
|
755
|
+
// src/components/CryptoTab/Crypto/Crypto.tsx
|
|
756
|
+
import { jsx as jsx11, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
757
|
+
var Crypto = () => {
|
|
758
|
+
const { address } = useAccount3();
|
|
759
|
+
const { selectedPaymentMethod } = useSpreePaymentMethod();
|
|
760
|
+
const { cryptoPayment } = useCryptoPayment();
|
|
761
|
+
const { spreePayConfig } = useSpreePayConfig();
|
|
762
|
+
const isWalletConnected = Boolean(address);
|
|
763
|
+
const { register } = useSpreePayRegister();
|
|
764
|
+
const handlePay = useCallback(
|
|
765
|
+
async (data) => {
|
|
766
|
+
try {
|
|
767
|
+
const res = await cryptoPayment(data);
|
|
768
|
+
if (["AUTHORIZED" /* AUTHORIZED */, "CAPTURED" /* CAPTURED */].includes(res.status)) {
|
|
769
|
+
return Promise.resolve(res);
|
|
770
|
+
}
|
|
771
|
+
return Promise.reject(new PaymentError("Crypto payment failed", res.status));
|
|
772
|
+
} catch (_) {
|
|
773
|
+
return Promise.reject(new PaymentError("Payment failed", "FAILED" /* FAILED */));
|
|
774
|
+
}
|
|
775
|
+
},
|
|
776
|
+
[cryptoPayment]
|
|
777
|
+
);
|
|
778
|
+
useEffect2(() => {
|
|
779
|
+
register(handlePay);
|
|
780
|
+
}, [register, handlePay]);
|
|
781
|
+
return /* @__PURE__ */ jsxs7("div", { className: "flex flex-col items-baseline gap-4", children: [
|
|
782
|
+
/* @__PURE__ */ jsxs7("div", { className: "flex w-full items-center justify-between gap-4", children: [
|
|
783
|
+
/* @__PURE__ */ jsx11("h3", { className: "text-lg leading-7 font-medium text-(--brand-primary) md:text-[22px]", children: "Pay with Crypto" }),
|
|
784
|
+
/* @__PURE__ */ jsx11(ConnectButton, {})
|
|
785
|
+
] }),
|
|
786
|
+
!isWalletConnected && /* @__PURE__ */ jsx11(Logos, {}),
|
|
787
|
+
isWalletConnected && /* @__PURE__ */ jsxs7("div", { className: "flex w-full flex-col gap-4", children: [
|
|
788
|
+
selectedPaymentMethod.type === "CRYPTO" /* CRYPTO */ && selectedPaymentMethod.method && /* @__PURE__ */ jsx11(
|
|
789
|
+
SelectedCoin,
|
|
790
|
+
{
|
|
791
|
+
coin: selectedPaymentMethod.method.symbol,
|
|
792
|
+
balance: selectedPaymentMethod.method.formatted,
|
|
793
|
+
logoURI: selectedPaymentMethod.method.logoURI
|
|
794
|
+
}
|
|
795
|
+
),
|
|
796
|
+
/* @__PURE__ */ jsx11(SelectCoinButton, {})
|
|
797
|
+
] }),
|
|
798
|
+
spreePayConfig?.crypto.infoMessage && /* @__PURE__ */ jsx11(InfoBanner, { message: spreePayConfig.crypto.infoMessage })
|
|
799
|
+
] });
|
|
800
|
+
};
|
|
801
|
+
|
|
802
|
+
// src/components/CryptoTab/Crypto/CryptoWrapper.tsx
|
|
803
|
+
import { jsx as jsx12 } from "react/jsx-runtime";
|
|
804
|
+
var queryClient = new QueryClient();
|
|
805
|
+
var CHAINS = [base];
|
|
806
|
+
var wagmiConfigCache = /* @__PURE__ */ new Map();
|
|
807
|
+
function getCachedWagmiConfig(projectId, appName) {
|
|
808
|
+
const key = `${projectId}::${appName}`;
|
|
809
|
+
let cfg = wagmiConfigCache.get(key);
|
|
810
|
+
if (!cfg) {
|
|
811
|
+
cfg = getDefaultConfig({ appName, projectId, chains: CHAINS, ssr: true });
|
|
812
|
+
wagmiConfigCache.set(key, cfg);
|
|
813
|
+
}
|
|
814
|
+
return cfg;
|
|
815
|
+
}
|
|
816
|
+
var CryptoWrapper = () => {
|
|
817
|
+
const { spreePayConfig, configIsLoading } = useSpreePayConfig();
|
|
818
|
+
const wagmiConfig = useMemo2(() => {
|
|
819
|
+
if (!spreePayConfig) return null;
|
|
820
|
+
return getCachedWagmiConfig(spreePayConfig.rainbowProjectId, spreePayConfig.rainbowAppName);
|
|
821
|
+
}, [spreePayConfig]);
|
|
822
|
+
if (configIsLoading || !wagmiConfig) return null;
|
|
823
|
+
return /* @__PURE__ */ jsx12(WagmiProvider, { config: wagmiConfig, children: /* @__PURE__ */ jsx12(QueryClientProvider, { client: queryClient, children: /* @__PURE__ */ jsx12(RainbowKitProvider, { theme: lightTheme({ borderRadius: "large" }), children: /* @__PURE__ */ jsx12(NiceModal3.Provider, { children: /* @__PURE__ */ jsx12(Crypto, {}) }) }) }) });
|
|
824
|
+
};
|
|
825
|
+
|
|
826
|
+
// src/components/CryptoTab/CryptoTab.tsx
|
|
827
|
+
import { jsx as jsx13, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
828
|
+
var CryptoTab = ({ isLoggedIn }) => {
|
|
829
|
+
const { spreePayConfig } = useSpreePayConfig();
|
|
830
|
+
return /* @__PURE__ */ jsxs8("div", { children: [
|
|
831
|
+
/* @__PURE__ */ jsx13("div", { className: "border-b border-(--border-component-specific-card) px-5 py-5 md:px-7 md:py-6", children: /* @__PURE__ */ jsx13(CryptoWrapper, {}) }),
|
|
832
|
+
!spreePayConfig?.crypto.hidePoints && /* @__PURE__ */ jsx13("div", { className: "border-b border-(--border-component-specific-card) px-5 py-5 md:px-7 md:py-6", children: /* @__PURE__ */ jsx13(PointsSwitch, { disabled: true, message: spreePayConfig?.crypto.pointsInfoMessage }) }),
|
|
833
|
+
/* @__PURE__ */ jsx13(CheckoutButton, { isLoggedIn })
|
|
834
|
+
] });
|
|
835
|
+
};
|
|
836
|
+
export {
|
|
837
|
+
CryptoTab
|
|
838
|
+
};
|