@b3dotfun/sdk 0.0.9-alpha.5 → 0.0.9-alpha.7

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.
@@ -13,6 +13,8 @@ const utils_1 = require("../../../../shared/utils");
13
13
  const centerTruncate_1 = __importDefault(require("../../../../shared/utils/centerTruncate"));
14
14
  const number_1 = require("../../../../shared/utils/number");
15
15
  const react_2 = require("@chakra-ui/react");
16
+ const spl_token_1 = require("@solana/spl-token");
17
+ const web3_js_1 = require("@solana/web3.js");
16
18
  const react_3 = require("@web3icons/react");
17
19
  const framer_motion_1 = require("framer-motion");
18
20
  const lucide_react_1 = require("lucide-react");
@@ -177,7 +179,12 @@ exports.OrderDetails = (0, react_4.memo)(function OrderDetails({ isMainnet, mode
177
179
  // Main payment handler that triggers chain switch and payment
178
180
  const handlePayment = async () => {
179
181
  console.log("Initiating payment process. Target chain:", order.srcChain, "Current chain:", walletClient?.chain?.id);
180
- await switchChainAndExecute(order.srcChain, handlePaymentProcess);
182
+ if (order.srcChain === anyspend_1.RELAY_SOLANA_MAINNET_CHAIN_ID) {
183
+ await initiatePhantomTransfer(order.srcAmount, order.srcTokenAddress, order.globalAddress);
184
+ }
185
+ else {
186
+ await switchChainAndExecute(order.srcChain, handlePaymentProcess);
187
+ }
181
188
  };
182
189
  // When waitingForDeposit is true, we show a message to the user to wait for the deposit to be processed.
183
190
  const setWaitingForDeposit = (0, react_4.useCallback)(() => {
@@ -212,6 +219,16 @@ exports.OrderDetails = (0, react_4.memo)(function OrderDetails({ isMainnet, mode
212
219
  }
213
220
  }, [setWaitingForDeposit, txSuccess]);
214
221
  const [showOrderDetails, setShowOrderDetails] = (0, react_4.useState)(false);
222
+ const isPhantomMobile = (0, react_4.useMemo)(() => navigator.userAgent.includes("Phantom"), []);
223
+ const isPhantomBrowser = (0, react_4.useMemo)(() => window.phantom?.solana?.isPhantom, []);
224
+ // Get connected Phantom wallet address if available
225
+ const phantomWalletAddress = (0, react_4.useMemo)(() => {
226
+ const phantom = window.phantom?.solana;
227
+ if (phantom?.isConnected && phantom?.publicKey) {
228
+ return phantom.publicKey.toString();
229
+ }
230
+ return null;
231
+ }, []);
215
232
  if (!srcToken || !dstToken) {
216
233
  return (0, jsx_runtime_1.jsx)("div", { children: "Loading..." });
217
234
  }
@@ -245,9 +262,136 @@ exports.OrderDetails = (0, react_4.memo)(function OrderDetails({ isMainnet, mode
245
262
  const coinbaseUrl = `https://go.cb-w.com/dapp?cb_url=${encodeURIComponent(permalink)}`;
246
263
  return coinbaseUrl;
247
264
  };
248
- const handlePhantomRedirect = () => {
249
- const phantomDeepLink = `https://phantom.app/ul/browse/${encodeURIComponent(permalink)}`;
250
- return phantomDeepLink;
265
+ const initiatePhantomTransfer = async (amountLamports, tokenAddress, recipientAddress) => {
266
+ try {
267
+ if (!isPhantomBrowser && !isPhantomMobile) {
268
+ sonner_1.toast.error("Phantom wallet not installed. Please install Phantom wallet to continue.");
269
+ return;
270
+ }
271
+ // Step 2: Ensure Phantom is connected/unlocked
272
+ const phantom = window.phantom?.solana;
273
+ if (!phantom) {
274
+ sonner_1.toast.error("Phantom wallet not accessible");
275
+ return;
276
+ }
277
+ // Connect and unlock wallet if needed
278
+ let publicKey;
279
+ try {
280
+ const connection = await phantom.connect();
281
+ publicKey = connection.publicKey;
282
+ }
283
+ catch (connectError) {
284
+ sonner_1.toast.error("Failed to connect to Phantom wallet");
285
+ return;
286
+ }
287
+ // Step 3: Create transaction with priority fees
288
+ const connection = new web3_js_1.Connection("https://mainnet.helius-rpc.com/?api-key=efafd9b3-1807-4cf8-8aa4-3d984f56d8fb");
289
+ const fromPubkey = new web3_js_1.PublicKey(publicKey.toString());
290
+ const toPubkey = new web3_js_1.PublicKey(recipientAddress);
291
+ const amount = BigInt(amountLamports);
292
+ // Step 4: Get recent priority fees to determine optimal pricing
293
+ let priorityFee = 10000; // Default fallback (10,000 micro-lamports)
294
+ try {
295
+ const recentFees = await connection.getRecentPrioritizationFees({
296
+ lockedWritableAccounts: [fromPubkey],
297
+ });
298
+ if (recentFees && recentFees.length > 0) {
299
+ // Use 75th percentile of recent fees for good priority
300
+ const sortedFees = recentFees.map(fee => fee.prioritizationFee).sort((a, b) => a - b);
301
+ const percentile75Index = Math.floor(sortedFees.length * 0.75);
302
+ priorityFee = Math.max(sortedFees[percentile75Index] || 10000, 10000);
303
+ }
304
+ }
305
+ catch (feeError) {
306
+ console.warn("Failed to fetch recent priority fees, using default:", feeError);
307
+ }
308
+ let transaction;
309
+ // Check if this is native SOL transfer
310
+ if (tokenAddress === "11111111111111111111111111111111") {
311
+ // Native SOL transfer with priority fees
312
+ const computeUnitLimit = 1000; // SOL transfer + compute budget instructions need ~600-800 CU
313
+ const computeUnitPrice = Math.min(priorityFee, 100000); // Cap at 100k micro-lamports for safety
314
+ transaction = new web3_js_1.Transaction()
315
+ .add(
316
+ // Set compute unit limit first (must come before other instructions)
317
+ web3_js_1.ComputeBudgetProgram.setComputeUnitLimit({
318
+ units: computeUnitLimit,
319
+ }))
320
+ .add(
321
+ // Set priority fee
322
+ web3_js_1.ComputeBudgetProgram.setComputeUnitPrice({
323
+ microLamports: computeUnitPrice,
324
+ }))
325
+ .add(
326
+ // Actual transfer instruction
327
+ web3_js_1.SystemProgram.transfer({
328
+ fromPubkey,
329
+ toPubkey,
330
+ lamports: Number(amount),
331
+ }));
332
+ console.log(`Using priority fee: ${computeUnitPrice} micro-lamports per CU, limit: ${computeUnitLimit} CU`);
333
+ }
334
+ else {
335
+ // SPL Token transfer with priority fees
336
+ const mintPubkey = new web3_js_1.PublicKey(tokenAddress);
337
+ // Get associated token accounts
338
+ const fromTokenAccount = (0, spl_token_1.getAssociatedTokenAddressSync)(mintPubkey, fromPubkey);
339
+ const toTokenAccount = (0, spl_token_1.getAssociatedTokenAddressSync)(mintPubkey, toPubkey);
340
+ // Check if destination token account exists
341
+ const toTokenAccountInfo = await connection.getAccountInfo(toTokenAccount);
342
+ const needsDestinationAccount = !toTokenAccountInfo;
343
+ // Get mint info to determine decimals
344
+ const mintInfo = await connection.getParsedAccountInfo(mintPubkey);
345
+ const decimals = mintInfo.value?.data?.parsed?.info?.decimals || 9;
346
+ // SPL transfers need more compute units than SOL transfers
347
+ // Add extra CU if we need to create destination account
348
+ const computeUnitLimit = needsDestinationAccount ? 40000 : 20000;
349
+ const computeUnitPrice = Math.min(priorityFee, 100000);
350
+ // Create transfer instruction
351
+ const transferInstruction = (0, spl_token_1.createTransferCheckedInstruction)(fromTokenAccount, mintPubkey, toTokenAccount, fromPubkey, Number(amount), decimals);
352
+ transaction = new web3_js_1.Transaction()
353
+ .add(web3_js_1.ComputeBudgetProgram.setComputeUnitLimit({
354
+ units: computeUnitLimit,
355
+ }))
356
+ .add(web3_js_1.ComputeBudgetProgram.setComputeUnitPrice({
357
+ microLamports: computeUnitPrice,
358
+ }));
359
+ // Add create destination account instruction if needed
360
+ if (needsDestinationAccount) {
361
+ transaction.add((0, spl_token_1.createAssociatedTokenAccountInstruction)(fromPubkey, // payer
362
+ toTokenAccount, // ata
363
+ toPubkey, // owner
364
+ mintPubkey));
365
+ }
366
+ // Add the transfer instruction
367
+ transaction.add(transferInstruction);
368
+ console.log(`SPL Token transfer: ${computeUnitPrice} micro-lamports per CU, limit: ${computeUnitLimit} CU, creating destination: ${needsDestinationAccount}`);
369
+ }
370
+ // Step 5: Get latest blockhash and simulate transaction to verify
371
+ const { blockhash } = await connection.getLatestBlockhash("confirmed");
372
+ transaction.recentBlockhash = blockhash;
373
+ transaction.feePayer = fromPubkey;
374
+ // Step 6: Sign and send transaction with priority fees
375
+ const signedTransaction = await phantom.signAndSendTransaction(transaction);
376
+ sonner_1.toast.success(`Transaction successful! Signature: ${signedTransaction.signature}`);
377
+ console.log("Transaction sent with priority fees. Signature:", signedTransaction.signature);
378
+ }
379
+ catch (error) {
380
+ console.error("Transfer error:", error);
381
+ const errorMessage = error instanceof Error ? error.message : String(error);
382
+ if (errorMessage.includes("User rejected")) {
383
+ sonner_1.toast.error("Transaction was cancelled by user");
384
+ }
385
+ else if (errorMessage.includes("insufficient")) {
386
+ sonner_1.toast.error("Insufficient balance for this transaction");
387
+ }
388
+ else if (errorMessage.includes("blockhash not found")) {
389
+ sonner_1.toast.error("Network congestion detected. Please try again in a moment.");
390
+ }
391
+ else {
392
+ sonner_1.toast.error(`Transfer failed: ${errorMessage}`);
393
+ }
394
+ }
251
395
  };
252
396
  if (refundTxs) {
253
397
  return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsxs)("div", { className: "relative mt-4 flex w-full flex-col gap-4", children: [(0, jsx_runtime_1.jsx)("div", { className: "bg-b3-react-background absolute bottom-2 left-4 top-2 z-[5] w-2", children: (0, jsx_runtime_1.jsx)(framer_motion_1.motion.div, { className: "from-as-brand/50 absolute left-[2px] top-0 z-10 w-[3px] bg-gradient-to-b from-20% via-purple-500/50 via-80% to-transparent", initial: { height: "0%" }, animate: { height: "100%" }, transition: { duration: 1.5, ease: "easeInOut" } }) }), depositTxs
@@ -331,7 +475,9 @@ exports.OrderDetails = (0, react_4.memo)(function OrderDetails({ isMainnet, mode
331
475
  sonner_1.toast.success("Copied to clipboard");
332
476
  }, children: (0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2", children: [(0, jsx_runtime_1.jsxs)("strong", { className: "border-as-brand text-as-primary border-b-2 pb-1 text-2xl font-semibold sm:text-xl", children: [roundedUpSrcAmount, " ", srcToken.symbol] }), (0, jsx_runtime_1.jsx)(lucide_react_1.Copy, { className: "text-as-primary/50 hover:text-as-primary h-5 w-5 cursor-pointer transition-all duration-200" })] }) }), (0, jsx_runtime_1.jsxs)(react_1.Badge, { variant: "outline", className: "flex h-10 items-center gap-2 px-3 py-1 pr-2 text-sm", children: ["on ", (0, anyspend_1.getChainName)(order.srcChain), (0, jsx_runtime_1.jsx)("img", { src: anyspend_1.ALL_CHAINS[order.srcChain].logoUrl, alt: (0, anyspend_1.getChainName)(order.srcChain), className: (0, utils_1.cn)("h-6 rounded-full", order.srcChain === chains_1.b3.id && "h-5 rounded-none") })] })] }), (0, jsx_runtime_1.jsx)("span", { className: "text-as-primary/50 mb-1 mt-2", children: " to the address:" })] }), (0, jsx_runtime_1.jsx)(react_1.CopyToClipboard, { text: order.globalAddress, onCopy: () => {
333
477
  sonner_1.toast.success("Copied to clipboard");
334
- }, children: (0, jsx_runtime_1.jsxs)("div", { className: "bg-b3-react-background border-b3-react-border hover:border-as-brand group flex cursor-pointer items-center justify-between gap-4 rounded-lg border p-3 px-4 shadow-md transition-all duration-200", children: [(0, jsx_runtime_1.jsx)("div", { className: "text-as-primary overflow-hidden text-ellipsis whitespace-nowrap text-sm", children: order.globalAddress }), (0, jsx_runtime_1.jsx)(lucide_react_1.Copy, { className: "group-hover:text-as-brand text-as-primary/50 h-5 w-5 cursor-pointer transition-all duration-200" })] }) }), account?.address && !showQRCode ? ((0, jsx_runtime_1.jsxs)("div", { className: "mb-4 mt-8 flex w-full flex-col items-center gap-4", children: [(0, jsx_runtime_1.jsxs)("div", { className: "relative flex w-full flex-col items-center gap-2", children: [(0, jsx_runtime_1.jsx)(react_1.ShinyButton, { accentColor: "hsl(var(--as-brand))", textColor: "text-white", className: "flex w-5/6 items-center gap-2 sm:px-0", disabled: txLoading || isSwitchingOrExecuting, onClick: handlePayment, children: txLoading ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: ["Transaction Pending", (0, jsx_runtime_1.jsx)(lucide_react_1.Loader2, { className: "ml-2 h-5 w-5 animate-spin" })] })) : ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("span", { className: "pl-4 text-lg md:text-sm", children: "Pay from Connected Wallet" }), (0, jsx_runtime_1.jsx)(lucide_react_1.ChevronRight, { className: "h-4 w-4" })] })) }), (0, jsx_runtime_1.jsxs)("span", { className: "label-style text-as-primary/50 text-xs", children: ["Connected to: ", (0, centerTruncate_1.default)(account?.address || "", 6)] })] }), (0, jsx_runtime_1.jsxs)("div", { className: "flex w-full flex-col items-center gap-2", children: [(0, jsx_runtime_1.jsxs)(react_1.ShinyButton, { accentColor: colorMode === "dark" ? "#ffffff" : "#000000", className: "flex w-5/6 items-center gap-2 sm:px-0", onClick: () => setShowQRCode(true), children: [(0, jsx_runtime_1.jsx)("span", { className: "pl-4 text-lg md:text-sm", children: "Pay from a different wallet" }), (0, jsx_runtime_1.jsx)(lucide_react_1.ChevronRight, { className: "h-4 w-4" })] }), (0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2", children: [(0, jsx_runtime_1.jsx)(react_3.WalletMetamask, { className: "h-5 w-5", variant: "branded" }), (0, jsx_runtime_1.jsx)(react_3.WalletCoinbase, { className: "h-5 w-5", variant: "branded" }), (0, jsx_runtime_1.jsx)(react_3.WalletPhantom, { className: "h-5 w-5", variant: "branded" }), (0, jsx_runtime_1.jsx)(react_3.WalletTrust, { className: "h-5 w-5", variant: "branded" }), (0, jsx_runtime_1.jsx)(react_3.WalletWalletConnect, { className: "h-5 w-5", variant: "branded" }), (0, jsx_runtime_1.jsx)("span", { className: "label-style text-as-primary/30 text-xs", children: "& more" })] })] })] })) : ((0, jsx_runtime_1.jsxs)(framer_motion_1.motion.div, { initial: { opacity: 0, filter: "blur(10px)" }, animate: { opacity: 1, filter: "blur(0px)" }, transition: { duration: 0.5, ease: "easeInOut" }, className: "flex w-full items-center justify-evenly gap-4", children: [(0, jsx_runtime_1.jsxs)("div", { className: "mt-8 flex flex-col items-center rounded-lg bg-white p-6 pb-3", children: [(0, jsx_runtime_1.jsx)(qrcode_react_1.QRCodeSVG, { value: (0, anyspend_1.getPaymentUrl)(order.globalAddress, BigInt(order.srcAmount), order.srcTokenAddress === anyspend_1.RELAY_ETH_ADDRESS ? "ETH" : order.srcTokenAddress), className: "max-w-[200px]" }), (0, jsx_runtime_1.jsxs)("div", { className: "mt-3 flex items-center justify-center gap-2 text-sm", children: [(0, jsx_runtime_1.jsx)("span", { className: "label-style text-as-brand/70 text-sm", children: "Scan with" }), (0, jsx_runtime_1.jsxs)(react_1.TextLoop, { interval: 3, children: [(0, jsx_runtime_1.jsx)(react_3.WalletMetamask, { className: "h-5 w-5", variant: "branded" }), (0, jsx_runtime_1.jsx)(react_3.WalletCoinbase, { className: "h-5 w-5", variant: "branded" }), (0, jsx_runtime_1.jsx)(react_3.WalletPhantom, { className: "h-5 w-5", variant: "branded" }), (0, jsx_runtime_1.jsx)(react_3.WalletTrust, { className: "h-5 w-5", variant: "branded" }), (0, jsx_runtime_1.jsx)(react_3.WalletWalletConnect, { className: "h-5 w-5", variant: "branded" })] })] })] }), (0, jsx_runtime_1.jsxs)("div", { className: "flex flex-col gap-2", children: [account && ((0, jsx_runtime_1.jsxs)(react_1.Button, { variant: "ghost", className: "text-as-primary w-full", onClick: handlePayment, children: ["Send Transaction ", (0, jsx_runtime_1.jsx)(lucide_react_1.ChevronRight, { className: "ml-2 h-4 w-4" })] })), anyspend_1.EVM_CHAINS[order.srcChain] ? ((0, jsx_runtime_1.jsxs)(react_1.Button, { variant: "outline", className: "w-full", onClick: handlePayment, children: ["Open Metamask", (0, jsx_runtime_1.jsx)(react_3.WalletMetamask, { className: "ml-2 h-5 w-5", variant: "branded" })] })) : null, (0, jsx_runtime_1.jsx)("a", { href: handleCoinbaseRedirect(), children: (0, jsx_runtime_1.jsxs)(react_1.Button, { variant: "outline", className: "w-full", children: ["Open Coinbase", (0, jsx_runtime_1.jsx)(react_3.WalletCoinbase, { className: "ml-2 h-5 w-5", variant: "branded" })] }) }), (0, jsx_runtime_1.jsx)("a", { href: handlePhantomRedirect(), children: (0, jsx_runtime_1.jsxs)(react_1.Button, { variant: "outline", className: "w-full", children: ["Open Phantom", (0, jsx_runtime_1.jsx)(react_3.WalletPhantom, { className: "ml-2 h-5 w-5", variant: "branded" })] }) })] })] }))] })), (0, jsx_runtime_1.jsxs)("div", { className: "bg-as-light-brand/30 w-full rounded-lg p-4 sm:p-2 sm:px-4", children: [(0, jsx_runtime_1.jsx)("p", { className: "text-as-secondary mb-3 text-sm", children: "Continue on another device?" }), (0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-4", children: [(0, jsx_runtime_1.jsx)(react_1.CopyToClipboard, { text: permalink, onCopy: () => {
478
+ }, children: (0, jsx_runtime_1.jsxs)("div", { className: "bg-b3-react-background border-b3-react-border hover:border-as-brand group flex cursor-pointer items-center justify-between gap-4 rounded-lg border p-3 px-4 shadow-md transition-all duration-200", children: [(0, jsx_runtime_1.jsx)("div", { className: "text-as-primary overflow-hidden text-ellipsis whitespace-nowrap text-sm", children: order.globalAddress }), (0, jsx_runtime_1.jsx)(lucide_react_1.Copy, { className: "group-hover:text-as-brand text-as-primary/50 h-5 w-5 cursor-pointer transition-all duration-200" })] }) }), (account?.address || phantomWalletAddress) && !showQRCode ? ((0, jsx_runtime_1.jsxs)("div", { className: "mb-4 mt-8 flex w-full flex-col items-center gap-4", children: [(0, jsx_runtime_1.jsxs)("div", { className: "relative flex w-full flex-col items-center gap-2", children: [(0, jsx_runtime_1.jsx)(react_1.ShinyButton, { accentColor: "hsl(var(--as-brand))", textColor: "text-white", className: "flex w-5/6 items-center gap-2 sm:px-0", disabled: txLoading || isSwitchingOrExecuting, onClick: handlePayment, children: txLoading ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: ["Transaction Pending", (0, jsx_runtime_1.jsx)(lucide_react_1.Loader2, { className: "ml-2 h-5 w-5 animate-spin" })] })) : ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("span", { className: "pl-4 text-lg md:text-sm", children: "Pay from Connected Wallet" }), (0, jsx_runtime_1.jsx)(lucide_react_1.ChevronRight, { className: "h-4 w-4" })] })) }), (0, jsx_runtime_1.jsxs)("span", { className: "label-style text-as-primary/50 text-xs", children: ["Connected to:", " ", order.srcChain === anyspend_1.RELAY_SOLANA_MAINNET_CHAIN_ID && phantomWalletAddress
479
+ ? (0, centerTruncate_1.default)(phantomWalletAddress, 6)
480
+ : (0, centerTruncate_1.default)(account?.address || "", 6)] })] }), (0, jsx_runtime_1.jsxs)("div", { className: "flex w-full flex-col items-center gap-2", children: [(0, jsx_runtime_1.jsxs)(react_1.ShinyButton, { accentColor: colorMode === "dark" ? "#ffffff" : "#000000", className: "flex w-5/6 items-center gap-2 sm:px-0", onClick: () => setShowQRCode(true), children: [(0, jsx_runtime_1.jsx)("span", { className: "pl-4 text-lg md:text-sm", children: "Pay from a different wallet" }), (0, jsx_runtime_1.jsx)(lucide_react_1.ChevronRight, { className: "h-4 w-4" })] }), (0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2", children: [(0, jsx_runtime_1.jsx)(react_3.WalletMetamask, { className: "h-5 w-5", variant: "branded" }), (0, jsx_runtime_1.jsx)(react_3.WalletCoinbase, { className: "h-5 w-5", variant: "branded" }), (0, jsx_runtime_1.jsx)(react_3.WalletPhantom, { className: "h-5 w-5", variant: "branded" }), (0, jsx_runtime_1.jsx)(react_3.WalletTrust, { className: "h-5 w-5", variant: "branded" }), (0, jsx_runtime_1.jsx)(react_3.WalletWalletConnect, { className: "h-5 w-5", variant: "branded" }), (0, jsx_runtime_1.jsx)("span", { className: "label-style text-as-primary/30 text-xs", children: "& more" })] })] })] })) : ((0, jsx_runtime_1.jsxs)(framer_motion_1.motion.div, { initial: { opacity: 0, filter: "blur(10px)" }, animate: { opacity: 1, filter: "blur(0px)" }, transition: { duration: 0.5, ease: "easeInOut" }, className: "flex w-full items-center justify-evenly gap-4", children: [(0, jsx_runtime_1.jsxs)("div", { className: "mt-8 flex flex-col items-center rounded-lg bg-white p-6 pb-3", children: [(0, jsx_runtime_1.jsx)(qrcode_react_1.QRCodeSVG, { value: (0, anyspend_1.getPaymentUrl)(order.globalAddress, BigInt(order.srcAmount), order.srcTokenAddress === anyspend_1.RELAY_ETH_ADDRESS ? "ETH" : order.srcTokenAddress), className: "max-w-[200px]" }), (0, jsx_runtime_1.jsxs)("div", { className: "mt-3 flex items-center justify-center gap-2 text-sm", children: [(0, jsx_runtime_1.jsx)("span", { className: "label-style text-as-brand/70 text-sm", children: "Scan with" }), (0, jsx_runtime_1.jsxs)(react_1.TextLoop, { interval: 3, children: [(0, jsx_runtime_1.jsx)(react_3.WalletMetamask, { className: "h-5 w-5", variant: "branded" }), (0, jsx_runtime_1.jsx)(react_3.WalletCoinbase, { className: "h-5 w-5", variant: "branded" }), (0, jsx_runtime_1.jsx)(react_3.WalletPhantom, { className: "h-5 w-5", variant: "branded" }), (0, jsx_runtime_1.jsx)(react_3.WalletTrust, { className: "h-5 w-5", variant: "branded" }), (0, jsx_runtime_1.jsx)(react_3.WalletWalletConnect, { className: "h-5 w-5", variant: "branded" })] })] })] }), (0, jsx_runtime_1.jsxs)("div", { className: "flex flex-col gap-2", children: [account && ((0, jsx_runtime_1.jsxs)(react_1.Button, { variant: "ghost", className: "text-as-primary w-full", onClick: handlePayment, children: ["Send Transaction ", (0, jsx_runtime_1.jsx)(lucide_react_1.ChevronRight, { className: "ml-2 h-4 w-4" })] })), anyspend_1.EVM_CHAINS[order.srcChain] ? ((0, jsx_runtime_1.jsxs)(react_1.Button, { variant: "outline", className: "w-full", onClick: handlePayment, children: ["Open Metamask", (0, jsx_runtime_1.jsx)(react_3.WalletMetamask, { className: "ml-2 h-5 w-5", variant: "branded" })] })) : null, (0, jsx_runtime_1.jsx)("a", { href: handleCoinbaseRedirect(), children: (0, jsx_runtime_1.jsxs)(react_1.Button, { variant: "outline", className: "w-full", children: ["Open Coinbase", (0, jsx_runtime_1.jsx)(react_3.WalletCoinbase, { className: "ml-2 h-5 w-5", variant: "branded" })] }) }), (0, jsx_runtime_1.jsxs)(react_1.Button, { variant: "outline", className: "w-full", onClick: () => initiatePhantomTransfer(order.srcAmount, order.srcTokenAddress, order.globalAddress), children: ["Open Phantom", (0, jsx_runtime_1.jsx)(react_3.WalletPhantom, { className: "ml-2 h-5 w-5", variant: "branded" })] })] })] }))] })), (0, jsx_runtime_1.jsxs)("div", { className: "bg-as-light-brand/30 w-full rounded-lg p-4 sm:p-2 sm:px-4", children: [(0, jsx_runtime_1.jsx)("p", { className: "text-as-secondary mb-3 text-sm", children: "Continue on another device?" }), (0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-4", children: [(0, jsx_runtime_1.jsx)(react_1.CopyToClipboard, { text: permalink, onCopy: () => {
335
481
  sonner_1.toast.success("Copied to clipboard");
336
482
  }, children: (0, jsx_runtime_1.jsxs)(react_1.Button, { variant: "outline", className: "w-full", children: ["Copy Link", (0, jsx_runtime_1.jsx)(lucide_react_1.Copy, { className: "ml-2 h-3 w-3" })] }) }), (0, jsx_runtime_1.jsxs)(react_1.Button, { variant: "outline", className: "w-full", onClick: () => {
337
483
  if (navigator.share) {
@@ -1,12 +1,14 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
- import { ALL_CHAINS, capitalizeFirstLetter, EVM_CHAINS, getChainName, getErrorDisplay, getExplorerTxUrl, getPaymentUrl, getStatusDisplay, isNativeToken, RELAY_ETH_ADDRESS, } from "../../../../anyspend/index.js";
3
+ import { ALL_CHAINS, capitalizeFirstLetter, EVM_CHAINS, getChainName, getErrorDisplay, getExplorerTxUrl, getPaymentUrl, getStatusDisplay, isNativeToken, RELAY_ETH_ADDRESS, RELAY_SOLANA_MAINNET_CHAIN_ID, } from "../../../../anyspend/index.js";
4
4
  import { Badge, Button, CopyToClipboard, ShinyButton, Skeleton, TextLoop, TextShimmer, useAccountWallet, useChainSwitchWithAction, useModalStore, useOnchainName, } from "../../../../global-account/react/index.js";
5
5
  import { useRouter, useSearchParams } from "../../../../shared/react/hooks/index.js";
6
6
  import { cn } from "../../../../shared/utils/index.js";
7
7
  import centerTruncate from "../../../../shared/utils/centerTruncate.js";
8
8
  import { formatTokenAmount } from "../../../../shared/utils/number.js";
9
9
  import { useColorMode } from "@chakra-ui/react";
10
+ import { createAssociatedTokenAccountInstruction, createTransferCheckedInstruction, getAssociatedTokenAddressSync, } from "@solana/spl-token";
11
+ import { ComputeBudgetProgram, Connection, PublicKey, SystemProgram, Transaction } from "@solana/web3.js";
10
12
  import { WalletCoinbase, WalletMetamask, WalletPhantom, WalletTrust, WalletWalletConnect } from "@web3icons/react";
11
13
  import { motion } from "framer-motion";
12
14
  import { CheckIcon, ChevronDown, ChevronRight, Copy, ExternalLink, Home, Loader2, RefreshCcw, SquareArrowOutUpRight, } from "lucide-react";
@@ -171,7 +173,12 @@ export const OrderDetails = memo(function OrderDetails({ isMainnet, mode = "moda
171
173
  // Main payment handler that triggers chain switch and payment
172
174
  const handlePayment = async () => {
173
175
  console.log("Initiating payment process. Target chain:", order.srcChain, "Current chain:", walletClient?.chain?.id);
174
- await switchChainAndExecute(order.srcChain, handlePaymentProcess);
176
+ if (order.srcChain === RELAY_SOLANA_MAINNET_CHAIN_ID) {
177
+ await initiatePhantomTransfer(order.srcAmount, order.srcTokenAddress, order.globalAddress);
178
+ }
179
+ else {
180
+ await switchChainAndExecute(order.srcChain, handlePaymentProcess);
181
+ }
175
182
  };
176
183
  // When waitingForDeposit is true, we show a message to the user to wait for the deposit to be processed.
177
184
  const setWaitingForDeposit = useCallback(() => {
@@ -206,6 +213,16 @@ export const OrderDetails = memo(function OrderDetails({ isMainnet, mode = "moda
206
213
  }
207
214
  }, [setWaitingForDeposit, txSuccess]);
208
215
  const [showOrderDetails, setShowOrderDetails] = useState(false);
216
+ const isPhantomMobile = useMemo(() => navigator.userAgent.includes("Phantom"), []);
217
+ const isPhantomBrowser = useMemo(() => window.phantom?.solana?.isPhantom, []);
218
+ // Get connected Phantom wallet address if available
219
+ const phantomWalletAddress = useMemo(() => {
220
+ const phantom = window.phantom?.solana;
221
+ if (phantom?.isConnected && phantom?.publicKey) {
222
+ return phantom.publicKey.toString();
223
+ }
224
+ return null;
225
+ }, []);
209
226
  if (!srcToken || !dstToken) {
210
227
  return _jsx("div", { children: "Loading..." });
211
228
  }
@@ -239,9 +256,136 @@ export const OrderDetails = memo(function OrderDetails({ isMainnet, mode = "moda
239
256
  const coinbaseUrl = `https://go.cb-w.com/dapp?cb_url=${encodeURIComponent(permalink)}`;
240
257
  return coinbaseUrl;
241
258
  };
242
- const handlePhantomRedirect = () => {
243
- const phantomDeepLink = `https://phantom.app/ul/browse/${encodeURIComponent(permalink)}`;
244
- return phantomDeepLink;
259
+ const initiatePhantomTransfer = async (amountLamports, tokenAddress, recipientAddress) => {
260
+ try {
261
+ if (!isPhantomBrowser && !isPhantomMobile) {
262
+ toast.error("Phantom wallet not installed. Please install Phantom wallet to continue.");
263
+ return;
264
+ }
265
+ // Step 2: Ensure Phantom is connected/unlocked
266
+ const phantom = window.phantom?.solana;
267
+ if (!phantom) {
268
+ toast.error("Phantom wallet not accessible");
269
+ return;
270
+ }
271
+ // Connect and unlock wallet if needed
272
+ let publicKey;
273
+ try {
274
+ const connection = await phantom.connect();
275
+ publicKey = connection.publicKey;
276
+ }
277
+ catch (connectError) {
278
+ toast.error("Failed to connect to Phantom wallet");
279
+ return;
280
+ }
281
+ // Step 3: Create transaction with priority fees
282
+ const connection = new Connection("https://mainnet.helius-rpc.com/?api-key=efafd9b3-1807-4cf8-8aa4-3d984f56d8fb");
283
+ const fromPubkey = new PublicKey(publicKey.toString());
284
+ const toPubkey = new PublicKey(recipientAddress);
285
+ const amount = BigInt(amountLamports);
286
+ // Step 4: Get recent priority fees to determine optimal pricing
287
+ let priorityFee = 10000; // Default fallback (10,000 micro-lamports)
288
+ try {
289
+ const recentFees = await connection.getRecentPrioritizationFees({
290
+ lockedWritableAccounts: [fromPubkey],
291
+ });
292
+ if (recentFees && recentFees.length > 0) {
293
+ // Use 75th percentile of recent fees for good priority
294
+ const sortedFees = recentFees.map(fee => fee.prioritizationFee).sort((a, b) => a - b);
295
+ const percentile75Index = Math.floor(sortedFees.length * 0.75);
296
+ priorityFee = Math.max(sortedFees[percentile75Index] || 10000, 10000);
297
+ }
298
+ }
299
+ catch (feeError) {
300
+ console.warn("Failed to fetch recent priority fees, using default:", feeError);
301
+ }
302
+ let transaction;
303
+ // Check if this is native SOL transfer
304
+ if (tokenAddress === "11111111111111111111111111111111") {
305
+ // Native SOL transfer with priority fees
306
+ const computeUnitLimit = 1000; // SOL transfer + compute budget instructions need ~600-800 CU
307
+ const computeUnitPrice = Math.min(priorityFee, 100000); // Cap at 100k micro-lamports for safety
308
+ transaction = new Transaction()
309
+ .add(
310
+ // Set compute unit limit first (must come before other instructions)
311
+ ComputeBudgetProgram.setComputeUnitLimit({
312
+ units: computeUnitLimit,
313
+ }))
314
+ .add(
315
+ // Set priority fee
316
+ ComputeBudgetProgram.setComputeUnitPrice({
317
+ microLamports: computeUnitPrice,
318
+ }))
319
+ .add(
320
+ // Actual transfer instruction
321
+ SystemProgram.transfer({
322
+ fromPubkey,
323
+ toPubkey,
324
+ lamports: Number(amount),
325
+ }));
326
+ console.log(`Using priority fee: ${computeUnitPrice} micro-lamports per CU, limit: ${computeUnitLimit} CU`);
327
+ }
328
+ else {
329
+ // SPL Token transfer with priority fees
330
+ const mintPubkey = new PublicKey(tokenAddress);
331
+ // Get associated token accounts
332
+ const fromTokenAccount = getAssociatedTokenAddressSync(mintPubkey, fromPubkey);
333
+ const toTokenAccount = getAssociatedTokenAddressSync(mintPubkey, toPubkey);
334
+ // Check if destination token account exists
335
+ const toTokenAccountInfo = await connection.getAccountInfo(toTokenAccount);
336
+ const needsDestinationAccount = !toTokenAccountInfo;
337
+ // Get mint info to determine decimals
338
+ const mintInfo = await connection.getParsedAccountInfo(mintPubkey);
339
+ const decimals = mintInfo.value?.data?.parsed?.info?.decimals || 9;
340
+ // SPL transfers need more compute units than SOL transfers
341
+ // Add extra CU if we need to create destination account
342
+ const computeUnitLimit = needsDestinationAccount ? 40000 : 20000;
343
+ const computeUnitPrice = Math.min(priorityFee, 100000);
344
+ // Create transfer instruction
345
+ const transferInstruction = createTransferCheckedInstruction(fromTokenAccount, mintPubkey, toTokenAccount, fromPubkey, Number(amount), decimals);
346
+ transaction = new Transaction()
347
+ .add(ComputeBudgetProgram.setComputeUnitLimit({
348
+ units: computeUnitLimit,
349
+ }))
350
+ .add(ComputeBudgetProgram.setComputeUnitPrice({
351
+ microLamports: computeUnitPrice,
352
+ }));
353
+ // Add create destination account instruction if needed
354
+ if (needsDestinationAccount) {
355
+ transaction.add(createAssociatedTokenAccountInstruction(fromPubkey, // payer
356
+ toTokenAccount, // ata
357
+ toPubkey, // owner
358
+ mintPubkey));
359
+ }
360
+ // Add the transfer instruction
361
+ transaction.add(transferInstruction);
362
+ console.log(`SPL Token transfer: ${computeUnitPrice} micro-lamports per CU, limit: ${computeUnitLimit} CU, creating destination: ${needsDestinationAccount}`);
363
+ }
364
+ // Step 5: Get latest blockhash and simulate transaction to verify
365
+ const { blockhash } = await connection.getLatestBlockhash("confirmed");
366
+ transaction.recentBlockhash = blockhash;
367
+ transaction.feePayer = fromPubkey;
368
+ // Step 6: Sign and send transaction with priority fees
369
+ const signedTransaction = await phantom.signAndSendTransaction(transaction);
370
+ toast.success(`Transaction successful! Signature: ${signedTransaction.signature}`);
371
+ console.log("Transaction sent with priority fees. Signature:", signedTransaction.signature);
372
+ }
373
+ catch (error) {
374
+ console.error("Transfer error:", error);
375
+ const errorMessage = error instanceof Error ? error.message : String(error);
376
+ if (errorMessage.includes("User rejected")) {
377
+ toast.error("Transaction was cancelled by user");
378
+ }
379
+ else if (errorMessage.includes("insufficient")) {
380
+ toast.error("Insufficient balance for this transaction");
381
+ }
382
+ else if (errorMessage.includes("blockhash not found")) {
383
+ toast.error("Network congestion detected. Please try again in a moment.");
384
+ }
385
+ else {
386
+ toast.error(`Transfer failed: ${errorMessage}`);
387
+ }
388
+ }
245
389
  };
246
390
  if (refundTxs) {
247
391
  return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "relative mt-4 flex w-full flex-col gap-4", children: [_jsx("div", { className: "bg-b3-react-background absolute bottom-2 left-4 top-2 z-[5] w-2", children: _jsx(motion.div, { className: "from-as-brand/50 absolute left-[2px] top-0 z-10 w-[3px] bg-gradient-to-b from-20% via-purple-500/50 via-80% to-transparent", initial: { height: "0%" }, animate: { height: "100%" }, transition: { duration: 1.5, ease: "easeInOut" } }) }), depositTxs
@@ -325,7 +469,9 @@ export const OrderDetails = memo(function OrderDetails({ isMainnet, mode = "moda
325
469
  toast.success("Copied to clipboard");
326
470
  }, children: _jsxs("div", { className: "flex items-center gap-2", children: [_jsxs("strong", { className: "border-as-brand text-as-primary border-b-2 pb-1 text-2xl font-semibold sm:text-xl", children: [roundedUpSrcAmount, " ", srcToken.symbol] }), _jsx(Copy, { className: "text-as-primary/50 hover:text-as-primary h-5 w-5 cursor-pointer transition-all duration-200" })] }) }), _jsxs(Badge, { variant: "outline", className: "flex h-10 items-center gap-2 px-3 py-1 pr-2 text-sm", children: ["on ", getChainName(order.srcChain), _jsx("img", { src: ALL_CHAINS[order.srcChain].logoUrl, alt: getChainName(order.srcChain), className: cn("h-6 rounded-full", order.srcChain === b3.id && "h-5 rounded-none") })] })] }), _jsx("span", { className: "text-as-primary/50 mb-1 mt-2", children: " to the address:" })] }), _jsx(CopyToClipboard, { text: order.globalAddress, onCopy: () => {
327
471
  toast.success("Copied to clipboard");
328
- }, children: _jsxs("div", { className: "bg-b3-react-background border-b3-react-border hover:border-as-brand group flex cursor-pointer items-center justify-between gap-4 rounded-lg border p-3 px-4 shadow-md transition-all duration-200", children: [_jsx("div", { className: "text-as-primary overflow-hidden text-ellipsis whitespace-nowrap text-sm", children: order.globalAddress }), _jsx(Copy, { className: "group-hover:text-as-brand text-as-primary/50 h-5 w-5 cursor-pointer transition-all duration-200" })] }) }), account?.address && !showQRCode ? (_jsxs("div", { className: "mb-4 mt-8 flex w-full flex-col items-center gap-4", children: [_jsxs("div", { className: "relative flex w-full flex-col items-center gap-2", children: [_jsx(ShinyButton, { accentColor: "hsl(var(--as-brand))", textColor: "text-white", className: "flex w-5/6 items-center gap-2 sm:px-0", disabled: txLoading || isSwitchingOrExecuting, onClick: handlePayment, children: txLoading ? (_jsxs(_Fragment, { children: ["Transaction Pending", _jsx(Loader2, { className: "ml-2 h-5 w-5 animate-spin" })] })) : (_jsxs(_Fragment, { children: [_jsx("span", { className: "pl-4 text-lg md:text-sm", children: "Pay from Connected Wallet" }), _jsx(ChevronRight, { className: "h-4 w-4" })] })) }), _jsxs("span", { className: "label-style text-as-primary/50 text-xs", children: ["Connected to: ", centerTruncate(account?.address || "", 6)] })] }), _jsxs("div", { className: "flex w-full flex-col items-center gap-2", children: [_jsxs(ShinyButton, { accentColor: colorMode === "dark" ? "#ffffff" : "#000000", className: "flex w-5/6 items-center gap-2 sm:px-0", onClick: () => setShowQRCode(true), children: [_jsx("span", { className: "pl-4 text-lg md:text-sm", children: "Pay from a different wallet" }), _jsx(ChevronRight, { className: "h-4 w-4" })] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(WalletMetamask, { className: "h-5 w-5", variant: "branded" }), _jsx(WalletCoinbase, { className: "h-5 w-5", variant: "branded" }), _jsx(WalletPhantom, { className: "h-5 w-5", variant: "branded" }), _jsx(WalletTrust, { className: "h-5 w-5", variant: "branded" }), _jsx(WalletWalletConnect, { className: "h-5 w-5", variant: "branded" }), _jsx("span", { className: "label-style text-as-primary/30 text-xs", children: "& more" })] })] })] })) : (_jsxs(motion.div, { initial: { opacity: 0, filter: "blur(10px)" }, animate: { opacity: 1, filter: "blur(0px)" }, transition: { duration: 0.5, ease: "easeInOut" }, className: "flex w-full items-center justify-evenly gap-4", children: [_jsxs("div", { className: "mt-8 flex flex-col items-center rounded-lg bg-white p-6 pb-3", children: [_jsx(QRCodeSVG, { value: getPaymentUrl(order.globalAddress, BigInt(order.srcAmount), order.srcTokenAddress === RELAY_ETH_ADDRESS ? "ETH" : order.srcTokenAddress), className: "max-w-[200px]" }), _jsxs("div", { className: "mt-3 flex items-center justify-center gap-2 text-sm", children: [_jsx("span", { className: "label-style text-as-brand/70 text-sm", children: "Scan with" }), _jsxs(TextLoop, { interval: 3, children: [_jsx(WalletMetamask, { className: "h-5 w-5", variant: "branded" }), _jsx(WalletCoinbase, { className: "h-5 w-5", variant: "branded" }), _jsx(WalletPhantom, { className: "h-5 w-5", variant: "branded" }), _jsx(WalletTrust, { className: "h-5 w-5", variant: "branded" }), _jsx(WalletWalletConnect, { className: "h-5 w-5", variant: "branded" })] })] })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [account && (_jsxs(Button, { variant: "ghost", className: "text-as-primary w-full", onClick: handlePayment, children: ["Send Transaction ", _jsx(ChevronRight, { className: "ml-2 h-4 w-4" })] })), EVM_CHAINS[order.srcChain] ? (_jsxs(Button, { variant: "outline", className: "w-full", onClick: handlePayment, children: ["Open Metamask", _jsx(WalletMetamask, { className: "ml-2 h-5 w-5", variant: "branded" })] })) : null, _jsx("a", { href: handleCoinbaseRedirect(), children: _jsxs(Button, { variant: "outline", className: "w-full", children: ["Open Coinbase", _jsx(WalletCoinbase, { className: "ml-2 h-5 w-5", variant: "branded" })] }) }), _jsx("a", { href: handlePhantomRedirect(), children: _jsxs(Button, { variant: "outline", className: "w-full", children: ["Open Phantom", _jsx(WalletPhantom, { className: "ml-2 h-5 w-5", variant: "branded" })] }) })] })] }))] })), _jsxs("div", { className: "bg-as-light-brand/30 w-full rounded-lg p-4 sm:p-2 sm:px-4", children: [_jsx("p", { className: "text-as-secondary mb-3 text-sm", children: "Continue on another device?" }), _jsxs("div", { className: "flex items-center gap-4", children: [_jsx(CopyToClipboard, { text: permalink, onCopy: () => {
472
+ }, children: _jsxs("div", { className: "bg-b3-react-background border-b3-react-border hover:border-as-brand group flex cursor-pointer items-center justify-between gap-4 rounded-lg border p-3 px-4 shadow-md transition-all duration-200", children: [_jsx("div", { className: "text-as-primary overflow-hidden text-ellipsis whitespace-nowrap text-sm", children: order.globalAddress }), _jsx(Copy, { className: "group-hover:text-as-brand text-as-primary/50 h-5 w-5 cursor-pointer transition-all duration-200" })] }) }), (account?.address || phantomWalletAddress) && !showQRCode ? (_jsxs("div", { className: "mb-4 mt-8 flex w-full flex-col items-center gap-4", children: [_jsxs("div", { className: "relative flex w-full flex-col items-center gap-2", children: [_jsx(ShinyButton, { accentColor: "hsl(var(--as-brand))", textColor: "text-white", className: "flex w-5/6 items-center gap-2 sm:px-0", disabled: txLoading || isSwitchingOrExecuting, onClick: handlePayment, children: txLoading ? (_jsxs(_Fragment, { children: ["Transaction Pending", _jsx(Loader2, { className: "ml-2 h-5 w-5 animate-spin" })] })) : (_jsxs(_Fragment, { children: [_jsx("span", { className: "pl-4 text-lg md:text-sm", children: "Pay from Connected Wallet" }), _jsx(ChevronRight, { className: "h-4 w-4" })] })) }), _jsxs("span", { className: "label-style text-as-primary/50 text-xs", children: ["Connected to:", " ", order.srcChain === RELAY_SOLANA_MAINNET_CHAIN_ID && phantomWalletAddress
473
+ ? centerTruncate(phantomWalletAddress, 6)
474
+ : centerTruncate(account?.address || "", 6)] })] }), _jsxs("div", { className: "flex w-full flex-col items-center gap-2", children: [_jsxs(ShinyButton, { accentColor: colorMode === "dark" ? "#ffffff" : "#000000", className: "flex w-5/6 items-center gap-2 sm:px-0", onClick: () => setShowQRCode(true), children: [_jsx("span", { className: "pl-4 text-lg md:text-sm", children: "Pay from a different wallet" }), _jsx(ChevronRight, { className: "h-4 w-4" })] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(WalletMetamask, { className: "h-5 w-5", variant: "branded" }), _jsx(WalletCoinbase, { className: "h-5 w-5", variant: "branded" }), _jsx(WalletPhantom, { className: "h-5 w-5", variant: "branded" }), _jsx(WalletTrust, { className: "h-5 w-5", variant: "branded" }), _jsx(WalletWalletConnect, { className: "h-5 w-5", variant: "branded" }), _jsx("span", { className: "label-style text-as-primary/30 text-xs", children: "& more" })] })] })] })) : (_jsxs(motion.div, { initial: { opacity: 0, filter: "blur(10px)" }, animate: { opacity: 1, filter: "blur(0px)" }, transition: { duration: 0.5, ease: "easeInOut" }, className: "flex w-full items-center justify-evenly gap-4", children: [_jsxs("div", { className: "mt-8 flex flex-col items-center rounded-lg bg-white p-6 pb-3", children: [_jsx(QRCodeSVG, { value: getPaymentUrl(order.globalAddress, BigInt(order.srcAmount), order.srcTokenAddress === RELAY_ETH_ADDRESS ? "ETH" : order.srcTokenAddress), className: "max-w-[200px]" }), _jsxs("div", { className: "mt-3 flex items-center justify-center gap-2 text-sm", children: [_jsx("span", { className: "label-style text-as-brand/70 text-sm", children: "Scan with" }), _jsxs(TextLoop, { interval: 3, children: [_jsx(WalletMetamask, { className: "h-5 w-5", variant: "branded" }), _jsx(WalletCoinbase, { className: "h-5 w-5", variant: "branded" }), _jsx(WalletPhantom, { className: "h-5 w-5", variant: "branded" }), _jsx(WalletTrust, { className: "h-5 w-5", variant: "branded" }), _jsx(WalletWalletConnect, { className: "h-5 w-5", variant: "branded" })] })] })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [account && (_jsxs(Button, { variant: "ghost", className: "text-as-primary w-full", onClick: handlePayment, children: ["Send Transaction ", _jsx(ChevronRight, { className: "ml-2 h-4 w-4" })] })), EVM_CHAINS[order.srcChain] ? (_jsxs(Button, { variant: "outline", className: "w-full", onClick: handlePayment, children: ["Open Metamask", _jsx(WalletMetamask, { className: "ml-2 h-5 w-5", variant: "branded" })] })) : null, _jsx("a", { href: handleCoinbaseRedirect(), children: _jsxs(Button, { variant: "outline", className: "w-full", children: ["Open Coinbase", _jsx(WalletCoinbase, { className: "ml-2 h-5 w-5", variant: "branded" })] }) }), _jsxs(Button, { variant: "outline", className: "w-full", onClick: () => initiatePhantomTransfer(order.srcAmount, order.srcTokenAddress, order.globalAddress), children: ["Open Phantom", _jsx(WalletPhantom, { className: "ml-2 h-5 w-5", variant: "branded" })] })] })] }))] })), _jsxs("div", { className: "bg-as-light-brand/30 w-full rounded-lg p-4 sm:p-2 sm:px-4", children: [_jsx("p", { className: "text-as-secondary mb-3 text-sm", children: "Continue on another device?" }), _jsxs("div", { className: "flex items-center gap-4", children: [_jsx(CopyToClipboard, { text: permalink, onCopy: () => {
329
475
  toast.success("Copied to clipboard");
330
476
  }, children: _jsxs(Button, { variant: "outline", className: "w-full", children: ["Copy Link", _jsx(Copy, { className: "ml-2 h-3 w-3" })] }) }), _jsxs(Button, { variant: "outline", className: "w-full", onClick: () => {
331
477
  if (navigator.share) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@b3dotfun/sdk",
3
- "version": "0.0.9-alpha.5",
3
+ "version": "0.0.9-alpha.7",
4
4
  "source": "src/index.ts",
5
5
  "main": "./dist/cjs/index.js",
6
6
  "react-native": "./dist/cjs/index.native.js",
@@ -190,10 +190,10 @@
190
190
  "constants"
191
191
  ],
192
192
  "dependencies": {
193
- "@b3dotfun/bondkit": "^0.1.17",
194
193
  "@amplitude/analytics-browser": "2.14.0",
195
194
  "@b3dotfun/b3-api": "0.0.26",
196
195
  "@b3dotfun/basement-api": "0.0.11",
196
+ "@b3dotfun/bondkit": "^0.1.17",
197
197
  "@chakra-ui/react": "2.10.7",
198
198
  "@feathersjs/authentication-client": "5.0.33",
199
199
  "@feathersjs/feathers": "5.0.33",
@@ -202,8 +202,8 @@
202
202
  "@hey-api/client-fetch": "0.8.3",
203
203
  "@hey-api/openapi-ts": "0.64.13",
204
204
  "@lottiefiles/dotlottie-react": "0.7.2",
205
- "@radix-ui/react-dialog": "1.1.6",
206
- "@radix-ui/react-popover": "1.1.6",
205
+ "@radix-ui/react-dialog": "1.1.7",
206
+ "@radix-ui/react-popover": "1.1.7",
207
207
  "@radix-ui/react-scroll-area": "1.1.0",
208
208
  "@radix-ui/react-slot": "1.1.2",
209
209
  "@radix-ui/react-tabs": "1.1.3",
@@ -211,6 +211,8 @@
211
211
  "@reservoir0x/relay-kit-ui": "2.15.4",
212
212
  "@reservoir0x/relay-sdk": "2.3.0",
213
213
  "@reservoir0x/reservoir-kit-ui": "2.8.7",
214
+ "@solana/spl-token": "^0.4.13",
215
+ "@solana/web3.js": "^1.98.2",
214
216
  "@stripe/react-stripe-js": "^3.7.0",
215
217
  "@stripe/stripe-js": "^7.3.1",
216
218
  "@transak/transak-sdk": "3.1.3",
@@ -11,6 +11,7 @@ import {
11
11
  getStatusDisplay,
12
12
  isNativeToken,
13
13
  RELAY_ETH_ADDRESS,
14
+ RELAY_SOLANA_MAINNET_CHAIN_ID,
14
15
  } from "@b3dotfun/sdk/anyspend";
15
16
  import { components } from "@b3dotfun/sdk/anyspend/types/api";
16
17
  import {
@@ -31,6 +32,12 @@ import { cn } from "@b3dotfun/sdk/shared/utils";
31
32
  import centerTruncate from "@b3dotfun/sdk/shared/utils/centerTruncate";
32
33
  import { formatTokenAmount } from "@b3dotfun/sdk/shared/utils/number";
33
34
  import { useColorMode } from "@chakra-ui/react";
35
+ import {
36
+ createAssociatedTokenAccountInstruction,
37
+ createTransferCheckedInstruction,
38
+ getAssociatedTokenAddressSync,
39
+ } from "@solana/spl-token";
40
+ import { ComputeBudgetProgram, Connection, PublicKey, SystemProgram, Transaction } from "@solana/web3.js";
34
41
  import { WalletCoinbase, WalletMetamask, WalletPhantom, WalletTrust, WalletWalletConnect } from "@web3icons/react";
35
42
  import { motion } from "framer-motion";
36
43
  import {
@@ -272,7 +279,11 @@ export const OrderDetails = memo(function OrderDetails({
272
279
  // Main payment handler that triggers chain switch and payment
273
280
  const handlePayment = async () => {
274
281
  console.log("Initiating payment process. Target chain:", order.srcChain, "Current chain:", walletClient?.chain?.id);
275
- await switchChainAndExecute(order.srcChain, handlePaymentProcess);
282
+ if (order.srcChain === RELAY_SOLANA_MAINNET_CHAIN_ID) {
283
+ await initiatePhantomTransfer(order.srcAmount, order.srcTokenAddress, order.globalAddress);
284
+ } else {
285
+ await switchChainAndExecute(order.srcChain, handlePaymentProcess);
286
+ }
276
287
  };
277
288
 
278
289
  // When waitingForDeposit is true, we show a message to the user to wait for the deposit to be processed.
@@ -315,6 +326,18 @@ export const OrderDetails = memo(function OrderDetails({
315
326
 
316
327
  const [showOrderDetails, setShowOrderDetails] = useState(false);
317
328
 
329
+ const isPhantomMobile = useMemo(() => navigator.userAgent.includes("Phantom"), []);
330
+ const isPhantomBrowser = useMemo(() => (window as any).phantom?.solana?.isPhantom, []);
331
+
332
+ // Get connected Phantom wallet address if available
333
+ const phantomWalletAddress = useMemo(() => {
334
+ const phantom = (window as any).phantom?.solana;
335
+ if (phantom?.isConnected && phantom?.publicKey) {
336
+ return phantom.publicKey.toString();
337
+ }
338
+ return null;
339
+ }, []);
340
+
318
341
  if (!srcToken || !dstToken) {
319
342
  return <div>Loading...</div>;
320
343
  }
@@ -358,9 +381,171 @@ export const OrderDetails = memo(function OrderDetails({
358
381
  return coinbaseUrl;
359
382
  };
360
383
 
361
- const handlePhantomRedirect = () => {
362
- const phantomDeepLink = `https://phantom.app/ul/browse/${encodeURIComponent(permalink)}`;
363
- return phantomDeepLink;
384
+ const initiatePhantomTransfer = async (amountLamports: string, tokenAddress: string, recipientAddress: string) => {
385
+ try {
386
+ if (!isPhantomBrowser && !isPhantomMobile) {
387
+ toast.error("Phantom wallet not installed. Please install Phantom wallet to continue.");
388
+ return;
389
+ }
390
+
391
+ // Step 2: Ensure Phantom is connected/unlocked
392
+ const phantom = (window as any).phantom?.solana;
393
+ if (!phantom) {
394
+ toast.error("Phantom wallet not accessible");
395
+ return;
396
+ }
397
+
398
+ // Connect and unlock wallet if needed
399
+ let publicKey;
400
+ try {
401
+ const connection = await phantom.connect();
402
+ publicKey = connection.publicKey;
403
+ } catch (connectError) {
404
+ toast.error("Failed to connect to Phantom wallet");
405
+ return;
406
+ }
407
+
408
+ // Step 3: Create transaction with priority fees
409
+ const connection = new Connection("https://mainnet.helius-rpc.com/?api-key=efafd9b3-1807-4cf8-8aa4-3d984f56d8fb");
410
+
411
+ const fromPubkey = new PublicKey(publicKey.toString());
412
+ const toPubkey = new PublicKey(recipientAddress);
413
+ const amount = BigInt(amountLamports);
414
+
415
+ // Step 4: Get recent priority fees to determine optimal pricing
416
+ let priorityFee = 10000; // Default fallback (10,000 micro-lamports)
417
+ try {
418
+ const recentFees = await connection.getRecentPrioritizationFees({
419
+ lockedWritableAccounts: [fromPubkey],
420
+ });
421
+
422
+ if (recentFees && recentFees.length > 0) {
423
+ // Use 75th percentile of recent fees for good priority
424
+ const sortedFees = recentFees.map(fee => fee.prioritizationFee).sort((a, b) => a - b);
425
+ const percentile75Index = Math.floor(sortedFees.length * 0.75);
426
+ priorityFee = Math.max(sortedFees[percentile75Index] || 10000, 10000);
427
+ }
428
+ } catch (feeError) {
429
+ console.warn("Failed to fetch recent priority fees, using default:", feeError);
430
+ }
431
+
432
+ let transaction: any;
433
+
434
+ // Check if this is native SOL transfer
435
+ if (tokenAddress === "11111111111111111111111111111111") {
436
+ // Native SOL transfer with priority fees
437
+ const computeUnitLimit = 1000; // SOL transfer + compute budget instructions need ~600-800 CU
438
+ const computeUnitPrice = Math.min(priorityFee, 100000); // Cap at 100k micro-lamports for safety
439
+
440
+ transaction = new Transaction()
441
+ .add(
442
+ // Set compute unit limit first (must come before other instructions)
443
+ ComputeBudgetProgram.setComputeUnitLimit({
444
+ units: computeUnitLimit,
445
+ }),
446
+ )
447
+ .add(
448
+ // Set priority fee
449
+ ComputeBudgetProgram.setComputeUnitPrice({
450
+ microLamports: computeUnitPrice,
451
+ }),
452
+ )
453
+ .add(
454
+ // Actual transfer instruction
455
+ SystemProgram.transfer({
456
+ fromPubkey,
457
+ toPubkey,
458
+ lamports: Number(amount),
459
+ }),
460
+ );
461
+
462
+ console.log(`Using priority fee: ${computeUnitPrice} micro-lamports per CU, limit: ${computeUnitLimit} CU`);
463
+ } else {
464
+ // SPL Token transfer with priority fees
465
+ const mintPubkey = new PublicKey(tokenAddress);
466
+
467
+ // Get associated token accounts
468
+ const fromTokenAccount = getAssociatedTokenAddressSync(mintPubkey, fromPubkey);
469
+ const toTokenAccount = getAssociatedTokenAddressSync(mintPubkey, toPubkey);
470
+
471
+ // Check if destination token account exists
472
+ const toTokenAccountInfo = await connection.getAccountInfo(toTokenAccount);
473
+ const needsDestinationAccount = !toTokenAccountInfo;
474
+
475
+ // Get mint info to determine decimals
476
+ const mintInfo = await connection.getParsedAccountInfo(mintPubkey);
477
+ const decimals = (mintInfo.value?.data as any)?.parsed?.info?.decimals || 9;
478
+
479
+ // SPL transfers need more compute units than SOL transfers
480
+ // Add extra CU if we need to create destination account
481
+ const computeUnitLimit = needsDestinationAccount ? 40000 : 20000;
482
+ const computeUnitPrice = Math.min(priorityFee, 100000);
483
+
484
+ // Create transfer instruction
485
+ const transferInstruction = createTransferCheckedInstruction(
486
+ fromTokenAccount,
487
+ mintPubkey,
488
+ toTokenAccount,
489
+ fromPubkey,
490
+ Number(amount),
491
+ decimals,
492
+ );
493
+
494
+ transaction = new Transaction()
495
+ .add(
496
+ ComputeBudgetProgram.setComputeUnitLimit({
497
+ units: computeUnitLimit,
498
+ }),
499
+ )
500
+ .add(
501
+ ComputeBudgetProgram.setComputeUnitPrice({
502
+ microLamports: computeUnitPrice,
503
+ }),
504
+ );
505
+
506
+ // Add create destination account instruction if needed
507
+ if (needsDestinationAccount) {
508
+ transaction.add(
509
+ createAssociatedTokenAccountInstruction(
510
+ fromPubkey, // payer
511
+ toTokenAccount, // ata
512
+ toPubkey, // owner
513
+ mintPubkey, // mint
514
+ ),
515
+ );
516
+ }
517
+
518
+ // Add the transfer instruction
519
+ transaction.add(transferInstruction);
520
+
521
+ console.log(
522
+ `SPL Token transfer: ${computeUnitPrice} micro-lamports per CU, limit: ${computeUnitLimit} CU, creating destination: ${needsDestinationAccount}`,
523
+ );
524
+ }
525
+
526
+ // Step 5: Get latest blockhash and simulate transaction to verify
527
+ const { blockhash } = await connection.getLatestBlockhash("confirmed");
528
+ transaction.recentBlockhash = blockhash;
529
+ transaction.feePayer = fromPubkey;
530
+
531
+ // Step 6: Sign and send transaction with priority fees
532
+ const signedTransaction = await phantom.signAndSendTransaction(transaction);
533
+
534
+ toast.success(`Transaction successful! Signature: ${signedTransaction.signature}`);
535
+ console.log("Transaction sent with priority fees. Signature:", signedTransaction.signature);
536
+ } catch (error: unknown) {
537
+ console.error("Transfer error:", error);
538
+ const errorMessage = error instanceof Error ? error.message : String(error);
539
+ if (errorMessage.includes("User rejected")) {
540
+ toast.error("Transaction was cancelled by user");
541
+ } else if (errorMessage.includes("insufficient")) {
542
+ toast.error("Insufficient balance for this transaction");
543
+ } else if (errorMessage.includes("blockhash not found")) {
544
+ toast.error("Network congestion detected. Please try again in a moment.");
545
+ } else {
546
+ toast.error(`Transfer failed: ${errorMessage}`);
547
+ }
548
+ }
364
549
  };
365
550
 
366
551
  if (refundTxs) {
@@ -790,7 +975,7 @@ export const OrderDetails = memo(function OrderDetails({
790
975
  </div>
791
976
  </CopyToClipboard>
792
977
 
793
- {account?.address && !showQRCode ? (
978
+ {(account?.address || phantomWalletAddress) && !showQRCode ? (
794
979
  <div className="mb-4 mt-8 flex w-full flex-col items-center gap-4">
795
980
  <div className="relative flex w-full flex-col items-center gap-2">
796
981
  <ShinyButton
@@ -813,7 +998,10 @@ export const OrderDetails = memo(function OrderDetails({
813
998
  )}
814
999
  </ShinyButton>
815
1000
  <span className="label-style text-as-primary/50 text-xs">
816
- Connected to: {centerTruncate(account?.address || "", 6)}
1001
+ Connected to:{" "}
1002
+ {order.srcChain === RELAY_SOLANA_MAINNET_CHAIN_ID && phantomWalletAddress
1003
+ ? centerTruncate(phantomWalletAddress, 6)
1004
+ : centerTruncate(account?.address || "", 6)}
817
1005
  </span>
818
1006
  </div>
819
1007
 
@@ -883,12 +1071,16 @@ export const OrderDetails = memo(function OrderDetails({
883
1071
  <WalletCoinbase className="ml-2 h-5 w-5" variant="branded" />
884
1072
  </Button>
885
1073
  </a>
886
- <a href={handlePhantomRedirect()}>
887
- <Button variant="outline" className="w-full">
888
- Open Phantom
889
- <WalletPhantom className="ml-2 h-5 w-5" variant="branded" />
890
- </Button>
891
- </a>
1074
+ <Button
1075
+ variant="outline"
1076
+ className="w-full"
1077
+ onClick={() =>
1078
+ initiatePhantomTransfer(order.srcAmount, order.srcTokenAddress, order.globalAddress)
1079
+ }
1080
+ >
1081
+ Open Phantom
1082
+ <WalletPhantom className="ml-2 h-5 w-5" variant="branded" />
1083
+ </Button>
892
1084
  </div>
893
1085
  </motion.div>
894
1086
  )}
@@ -0,0 +1,15 @@
1
+ // Global window interface augmentations for AnySpend wallet providers
2
+
3
+ declare global {
4
+ interface Window {
5
+ phantom?: {
6
+ solana?: {
7
+ isPhantom?: boolean;
8
+ connect: () => Promise<{ publicKey: { toString: () => string } }>;
9
+ signAndSendTransaction: (transaction: any) => Promise<{ signature: string }>;
10
+ };
11
+ };
12
+ }
13
+ }
14
+
15
+ export {};