@b3dotfun/sdk 0.0.9-alpha.4 → 0.0.9-alpha.6

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");
@@ -245,9 +247,155 @@ exports.OrderDetails = (0, react_4.memo)(function OrderDetails({ isMainnet, mode
245
247
  const coinbaseUrl = `https://go.cb-w.com/dapp?cb_url=${encodeURIComponent(permalink)}`;
246
248
  return coinbaseUrl;
247
249
  };
248
- const handlePhantomRedirect = () => {
249
- const phantomDeepLink = `https://phantom.app/ul/browse/${encodeURIComponent(permalink)}`;
250
- return phantomDeepLink;
250
+ const initiatePhantomTransfer = async (amountLamports, tokenAddress, recipientAddress) => {
251
+ try {
252
+ // Step 1: Check if Phantom is available
253
+ const isPhantomMobile = navigator.userAgent.includes("Phantom");
254
+ const isPhantomBrowser = window.phantom?.solana?.isPhantom;
255
+ if (!isPhantomBrowser && !isPhantomMobile) {
256
+ sonner_1.toast.error("Phantom wallet not installed. Please install Phantom wallet to continue.");
257
+ return;
258
+ }
259
+ // Step 2: Ensure Phantom is connected/unlocked
260
+ const phantom = window.phantom?.solana;
261
+ if (!phantom) {
262
+ sonner_1.toast.error("Phantom wallet not accessible");
263
+ return;
264
+ }
265
+ // Connect and unlock wallet if needed
266
+ let publicKey;
267
+ try {
268
+ const connection = await phantom.connect();
269
+ publicKey = connection.publicKey;
270
+ }
271
+ catch (connectError) {
272
+ sonner_1.toast.error("Failed to connect to Phantom wallet");
273
+ return;
274
+ }
275
+ // Step 3: Create transaction with priority fees
276
+ const connection = new web3_js_1.Connection("https://mainnet.helius-rpc.com/?api-key=efafd9b3-1807-4cf8-8aa4-3d984f56d8fb");
277
+ const fromPubkey = new web3_js_1.PublicKey(publicKey.toString());
278
+ const toPubkey = new web3_js_1.PublicKey(recipientAddress);
279
+ const amount = BigInt(amountLamports);
280
+ // Step 4: Get recent priority fees to determine optimal pricing
281
+ let priorityFee = 10000; // Default fallback (10,000 micro-lamports)
282
+ try {
283
+ const recentFees = await connection.getRecentPrioritizationFees({
284
+ lockedWritableAccounts: [fromPubkey],
285
+ });
286
+ if (recentFees && recentFees.length > 0) {
287
+ // Use 75th percentile of recent fees for good priority
288
+ const sortedFees = recentFees.map(fee => fee.prioritizationFee).sort((a, b) => a - b);
289
+ const percentile75Index = Math.floor(sortedFees.length * 0.75);
290
+ priorityFee = Math.max(sortedFees[percentile75Index] || 10000, 10000);
291
+ }
292
+ }
293
+ catch (feeError) {
294
+ console.warn("Failed to fetch recent priority fees, using default:", feeError);
295
+ }
296
+ let transaction;
297
+ // Check if this is native SOL transfer
298
+ if (tokenAddress === "11111111111111111111111111111111") {
299
+ // Native SOL transfer with priority fees
300
+ const computeUnitLimit = 1000; // SOL transfer + compute budget instructions need ~600-800 CU
301
+ const computeUnitPrice = Math.min(priorityFee, 100000); // Cap at 100k micro-lamports for safety
302
+ transaction = new web3_js_1.Transaction()
303
+ .add(
304
+ // Set compute unit limit first (must come before other instructions)
305
+ web3_js_1.ComputeBudgetProgram.setComputeUnitLimit({
306
+ units: computeUnitLimit,
307
+ }))
308
+ .add(
309
+ // Set priority fee
310
+ web3_js_1.ComputeBudgetProgram.setComputeUnitPrice({
311
+ microLamports: computeUnitPrice,
312
+ }))
313
+ .add(
314
+ // Actual transfer instruction
315
+ web3_js_1.SystemProgram.transfer({
316
+ fromPubkey,
317
+ toPubkey,
318
+ lamports: Number(amount),
319
+ }));
320
+ console.log(`Using priority fee: ${computeUnitPrice} micro-lamports per CU, limit: ${computeUnitLimit} CU`);
321
+ }
322
+ else {
323
+ // SPL Token transfer with priority fees
324
+ const mintPubkey = new web3_js_1.PublicKey(tokenAddress);
325
+ // Get associated token accounts
326
+ const fromTokenAccount = (0, spl_token_1.getAssociatedTokenAddressSync)(mintPubkey, fromPubkey);
327
+ const toTokenAccount = (0, spl_token_1.getAssociatedTokenAddressSync)(mintPubkey, toPubkey);
328
+ // Check if destination token account exists
329
+ const toTokenAccountInfo = await connection.getAccountInfo(toTokenAccount);
330
+ const needsDestinationAccount = !toTokenAccountInfo;
331
+ // Get mint info to determine decimals
332
+ const mintInfo = await connection.getParsedAccountInfo(mintPubkey);
333
+ const decimals = mintInfo.value?.data?.parsed?.info?.decimals || 9;
334
+ // SPL transfers need more compute units than SOL transfers
335
+ // Add extra CU if we need to create destination account
336
+ const computeUnitLimit = needsDestinationAccount ? 40000 : 20000;
337
+ const computeUnitPrice = Math.min(priorityFee, 100000);
338
+ // Create transfer instruction
339
+ const transferInstruction = (0, spl_token_1.createTransferCheckedInstruction)(fromTokenAccount, mintPubkey, toTokenAccount, fromPubkey, Number(amount), decimals);
340
+ transaction = new web3_js_1.Transaction()
341
+ .add(web3_js_1.ComputeBudgetProgram.setComputeUnitLimit({
342
+ units: computeUnitLimit,
343
+ }))
344
+ .add(web3_js_1.ComputeBudgetProgram.setComputeUnitPrice({
345
+ microLamports: computeUnitPrice,
346
+ }));
347
+ // Add create destination account instruction if needed
348
+ if (needsDestinationAccount) {
349
+ transaction.add((0, spl_token_1.createAssociatedTokenAccountInstruction)(fromPubkey, // payer
350
+ toTokenAccount, // ata
351
+ toPubkey, // owner
352
+ mintPubkey));
353
+ }
354
+ // Add the transfer instruction
355
+ transaction.add(transferInstruction);
356
+ console.log(`SPL Token transfer: ${computeUnitPrice} micro-lamports per CU, limit: ${computeUnitLimit} CU, creating destination: ${needsDestinationAccount}`);
357
+ }
358
+ // Step 5: Get latest blockhash and simulate transaction to verify
359
+ const { blockhash } = await connection.getLatestBlockhash("confirmed");
360
+ transaction.recentBlockhash = blockhash;
361
+ transaction.feePayer = fromPubkey;
362
+ // Optional: Simulate transaction to catch issues early
363
+ try {
364
+ const simulation = await connection.simulateTransaction(transaction, {
365
+ sigVerify: false,
366
+ replaceRecentBlockhash: true,
367
+ });
368
+ if (simulation.value.err) {
369
+ console.error("Transaction simulation failed:", simulation.value.err);
370
+ sonner_1.toast.error("Transaction simulation failed. Please check your balance and try again.");
371
+ return;
372
+ }
373
+ console.log("Transaction simulation successful. Compute units used:", simulation.value.unitsConsumed);
374
+ }
375
+ catch (simError) {
376
+ console.warn("Transaction simulation failed, proceeding anyway:", simError);
377
+ }
378
+ // Step 6: Sign and send transaction with priority fees
379
+ const signedTransaction = await phantom.signAndSendTransaction(transaction);
380
+ sonner_1.toast.success(`Transaction successful! Signature: ${signedTransaction.signature}`);
381
+ console.log("Transaction sent with priority fees. Signature:", signedTransaction.signature);
382
+ }
383
+ catch (error) {
384
+ console.error("Transfer error:", error);
385
+ const errorMessage = error instanceof Error ? error.message : String(error);
386
+ if (errorMessage.includes("User rejected")) {
387
+ sonner_1.toast.error("Transaction was cancelled by user");
388
+ }
389
+ else if (errorMessage.includes("insufficient")) {
390
+ sonner_1.toast.error("Insufficient balance for this transaction");
391
+ }
392
+ else if (errorMessage.includes("blockhash not found")) {
393
+ sonner_1.toast.error("Network congestion detected. Please try again in a moment.");
394
+ }
395
+ else {
396
+ sonner_1.toast.error(`Transfer failed: ${errorMessage}`);
397
+ }
398
+ }
251
399
  };
252
400
  if (refundTxs) {
253
401
  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 +479,7 @@ exports.OrderDetails = (0, react_4.memo)(function OrderDetails({ isMainnet, mode
331
479
  sonner_1.toast.success("Copied to clipboard");
332
480
  }, 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
481
  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: () => {
482
+ }, 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.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
483
  sonner_1.toast.success("Copied to clipboard");
336
484
  }, 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
485
  if (navigator.share) {
@@ -43,7 +43,7 @@ function OrderToken({ context, address, chainId, setChainId, token, setToken, re
43
43
  hasEnoughBalance,
44
44
  };
45
45
  }, [chainId, fungibleTokens, nativeTokens, requiredAmount, token, wallet?.address]);
46
- return ((0, jsx_runtime_1.jsx)(relay_kit_ui_1.TokenSelector, { address: address, chainIdsFilter: Object.values(anyspend_1.ALL_CHAINS).map(chain => chain.id), context: context, fromChainWalletVMSupported: true, isValidAddress: true, lockedChainIds: Object.values(anyspend_1.ALL_CHAINS).map(chain => chain.id), multiWalletSupportEnabled: true, onAnalyticEvent: undefined, popularChainIds: [1, 8453, anyspend_1.RELAY_SOLANA_MAINNET_CHAIN_ID], restrictedToken: undefined, setToken: token => {
46
+ return ((0, jsx_runtime_1.jsx)(relay_kit_ui_1.TokenSelector, { address: address, chainIdsFilter: Object.values(anyspend_1.ALL_CHAINS).map(chain => chain.id), context: context, fromChainWalletVMSupported: true, isValidAddress: true, lockedChainIds: Object.values(anyspend_1.ALL_CHAINS).map(chain => chain.id), multiWalletSupportEnabled: true, onAnalyticEvent: undefined, popularChainIds: [1, 8453, anyspend_1.RELAY_SOLANA_MAINNET_CHAIN_ID], setToken: token => {
47
47
  setChainId(token.chainId);
48
48
  setToken({
49
49
  address: token.address,
@@ -46,5 +46,5 @@ function OrderTokenAmount({ disabled, inputValue, onChangeInput, context, addres
46
46
  onChangeInput("0.01");
47
47
  }
48
48
  };
49
- return ((0, jsx_runtime_1.jsx)("div", { className: (0, utils_1.cn)("border-as-stroke flex w-full flex-col gap-2 rounded-xl", className), children: (0, jsx_runtime_1.jsxs)("div", { className: (0, utils_1.cn)("flex items-center justify-between gap-3", innerClassName), children: [!canEditAmount ? ((0, jsx_runtime_1.jsx)("h2", { className: (0, utils_1.cn)("text-3xl font-medium text-white", amountClassName), children: inputValue || "--" })) : ((0, jsx_runtime_1.jsx)(react_number_format_1.NumericFormat, { decimalSeparator: ".", allowedDecimalSeparators: [","], thousandSeparator: true, inputMode: "decimal", autoComplete: "off", autoCorrect: "off", type: "text", placeholder: "0.00", minLength: 1, maxLength: 30, spellCheck: "false", className: (0, utils_1.cn)("placeholder:text-as-primary/70 disabled:text-as-primary/70 text-as-primary w-full bg-transparent text-4xl font-semibold leading-[42px] outline-none sm:text-[30px]", amountClassName), pattern: "^[0-9]*[.,]?[0-9]*$", disabled: disabled, value: inputValue, allowNegative: false, onChange: e => onChangeInput(e.currentTarget.value) }, `input-${token.address}-${chainId}`)), !hideTokenSelect && ((0, jsx_runtime_1.jsx)(relay_kit_ui_1.TokenSelector, { address: address, chainIdsFilter: Object.values(anyspend_1.ALL_CHAINS).map(chain => chain.id), context: context, fromChainWalletVMSupported: true, isValidAddress: true, lockedChainIds: Object.values(anyspend_1.ALL_CHAINS).map(chain => chain.id), multiWalletSupportEnabled: true, onAnalyticEvent: undefined, popularChainIds: [1, 8453, anyspend_1.RELAY_SOLANA_MAINNET_CHAIN_ID], restrictedToken: undefined, setToken: handleTokenSelect, supportedWalletVMs: ["evm", "svm"], token: undefined, trigger: (0, jsx_runtime_1.jsxs)(react_2.Button, { variant: "outline", role: "combobox", className: (0, utils_1.cn)("bg-b3-react-background border-as-stroke flex h-auto w-fit shrink-0 items-center justify-center gap-2 rounded-xl border-2 px-2 py-1 pr-2 text-center", tokenSelectClassName), children: [token.metadata.logoURI ? ((0, jsx_runtime_1.jsx)(ChainTokenIcon_1.ChainTokenIcon, { chainUrl: anyspend_1.ALL_CHAINS[chainId].logoUrl, tokenUrl: token.metadata.logoURI, className: "h-8 min-h-8 w-8 min-w-8" })) : ((0, jsx_runtime_1.jsx)("div", { className: "h-8 w-8 rounded-full bg-gray-700" })), (0, jsx_runtime_1.jsxs)("div", { className: "flex flex-col items-start gap-0", children: [(0, jsx_runtime_1.jsx)("div", { className: "text-as-primary font-semibold", children: token.symbol }), (0, jsx_runtime_1.jsx)("div", { className: "text-as-primary/70 text-xs", children: anyspend_1.ALL_CHAINS[chainId].name })] }), (0, jsx_runtime_1.jsx)(lucide_react_1.ChevronsUpDown, { className: "h-4 w-4 shrink-0 opacity-70" })] }) }, `selector-${context}-${token.address}-${chainId}`))] }) }, `${context}-${token.address}-${chainId}`));
49
+ return ((0, jsx_runtime_1.jsx)("div", { className: (0, utils_1.cn)("border-as-stroke flex w-full flex-col gap-2 rounded-xl", className), children: (0, jsx_runtime_1.jsxs)("div", { className: (0, utils_1.cn)("flex items-center justify-between gap-3", innerClassName), children: [!canEditAmount ? ((0, jsx_runtime_1.jsx)("h2", { className: (0, utils_1.cn)("text-3xl font-medium text-white", amountClassName), children: inputValue || "--" })) : ((0, jsx_runtime_1.jsx)(react_number_format_1.NumericFormat, { decimalSeparator: ".", allowedDecimalSeparators: [","], thousandSeparator: true, inputMode: "decimal", autoComplete: "off", autoCorrect: "off", type: "text", placeholder: "0.00", minLength: 1, maxLength: 30, spellCheck: "false", className: (0, utils_1.cn)("placeholder:text-as-primary/70 disabled:text-as-primary/70 text-as-primary w-full bg-transparent text-4xl font-semibold leading-[42px] outline-none sm:text-[30px]", amountClassName), pattern: "^[0-9]*[.,]?[0-9]*$", disabled: disabled, value: inputValue, allowNegative: false, onChange: e => onChangeInput(e.currentTarget.value) }, `input-${token.address}-${chainId}`)), !hideTokenSelect && ((0, jsx_runtime_1.jsx)(relay_kit_ui_1.TokenSelector, { address: address, chainIdsFilter: Object.values(anyspend_1.ALL_CHAINS).map(chain => chain.id), context: context, fromChainWalletVMSupported: true, isValidAddress: true, lockedChainIds: Object.values(anyspend_1.ALL_CHAINS).map(chain => chain.id), multiWalletSupportEnabled: true, onAnalyticEvent: undefined, popularChainIds: [1, 8453, anyspend_1.RELAY_SOLANA_MAINNET_CHAIN_ID], setToken: handleTokenSelect, supportedWalletVMs: ["evm", "svm"], token: undefined, trigger: (0, jsx_runtime_1.jsxs)(react_2.Button, { variant: "outline", role: "combobox", className: (0, utils_1.cn)("bg-b3-react-background border-as-stroke flex h-auto w-fit shrink-0 items-center justify-center gap-2 rounded-xl border-2 px-2 py-1 pr-2 text-center", tokenSelectClassName), children: [token.metadata.logoURI ? ((0, jsx_runtime_1.jsx)(ChainTokenIcon_1.ChainTokenIcon, { chainUrl: anyspend_1.ALL_CHAINS[chainId].logoUrl, tokenUrl: token.metadata.logoURI, className: "h-8 min-h-8 w-8 min-w-8" })) : ((0, jsx_runtime_1.jsx)("div", { className: "h-8 w-8 rounded-full bg-gray-700" })), (0, jsx_runtime_1.jsxs)("div", { className: "flex flex-col items-start gap-0", children: [(0, jsx_runtime_1.jsx)("div", { className: "text-as-primary font-semibold", children: token.symbol }), (0, jsx_runtime_1.jsx)("div", { className: "text-as-primary/70 text-xs", children: anyspend_1.ALL_CHAINS[chainId].name })] }), (0, jsx_runtime_1.jsx)(lucide_react_1.ChevronsUpDown, { className: "h-4 w-4 shrink-0 opacity-70" })] }) }, `selector-${context}-${token.address}-${chainId}`))] }) }, `${context}-${token.address}-${chainId}`));
50
50
  }
@@ -7,6 +7,8 @@ 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";
@@ -239,9 +241,155 @@ export const OrderDetails = memo(function OrderDetails({ isMainnet, mode = "moda
239
241
  const coinbaseUrl = `https://go.cb-w.com/dapp?cb_url=${encodeURIComponent(permalink)}`;
240
242
  return coinbaseUrl;
241
243
  };
242
- const handlePhantomRedirect = () => {
243
- const phantomDeepLink = `https://phantom.app/ul/browse/${encodeURIComponent(permalink)}`;
244
- return phantomDeepLink;
244
+ const initiatePhantomTransfer = async (amountLamports, tokenAddress, recipientAddress) => {
245
+ try {
246
+ // Step 1: Check if Phantom is available
247
+ const isPhantomMobile = navigator.userAgent.includes("Phantom");
248
+ const isPhantomBrowser = window.phantom?.solana?.isPhantom;
249
+ if (!isPhantomBrowser && !isPhantomMobile) {
250
+ toast.error("Phantom wallet not installed. Please install Phantom wallet to continue.");
251
+ return;
252
+ }
253
+ // Step 2: Ensure Phantom is connected/unlocked
254
+ const phantom = window.phantom?.solana;
255
+ if (!phantom) {
256
+ toast.error("Phantom wallet not accessible");
257
+ return;
258
+ }
259
+ // Connect and unlock wallet if needed
260
+ let publicKey;
261
+ try {
262
+ const connection = await phantom.connect();
263
+ publicKey = connection.publicKey;
264
+ }
265
+ catch (connectError) {
266
+ toast.error("Failed to connect to Phantom wallet");
267
+ return;
268
+ }
269
+ // Step 3: Create transaction with priority fees
270
+ const connection = new Connection("https://mainnet.helius-rpc.com/?api-key=efafd9b3-1807-4cf8-8aa4-3d984f56d8fb");
271
+ const fromPubkey = new PublicKey(publicKey.toString());
272
+ const toPubkey = new PublicKey(recipientAddress);
273
+ const amount = BigInt(amountLamports);
274
+ // Step 4: Get recent priority fees to determine optimal pricing
275
+ let priorityFee = 10000; // Default fallback (10,000 micro-lamports)
276
+ try {
277
+ const recentFees = await connection.getRecentPrioritizationFees({
278
+ lockedWritableAccounts: [fromPubkey],
279
+ });
280
+ if (recentFees && recentFees.length > 0) {
281
+ // Use 75th percentile of recent fees for good priority
282
+ const sortedFees = recentFees.map(fee => fee.prioritizationFee).sort((a, b) => a - b);
283
+ const percentile75Index = Math.floor(sortedFees.length * 0.75);
284
+ priorityFee = Math.max(sortedFees[percentile75Index] || 10000, 10000);
285
+ }
286
+ }
287
+ catch (feeError) {
288
+ console.warn("Failed to fetch recent priority fees, using default:", feeError);
289
+ }
290
+ let transaction;
291
+ // Check if this is native SOL transfer
292
+ if (tokenAddress === "11111111111111111111111111111111") {
293
+ // Native SOL transfer with priority fees
294
+ const computeUnitLimit = 1000; // SOL transfer + compute budget instructions need ~600-800 CU
295
+ const computeUnitPrice = Math.min(priorityFee, 100000); // Cap at 100k micro-lamports for safety
296
+ transaction = new Transaction()
297
+ .add(
298
+ // Set compute unit limit first (must come before other instructions)
299
+ ComputeBudgetProgram.setComputeUnitLimit({
300
+ units: computeUnitLimit,
301
+ }))
302
+ .add(
303
+ // Set priority fee
304
+ ComputeBudgetProgram.setComputeUnitPrice({
305
+ microLamports: computeUnitPrice,
306
+ }))
307
+ .add(
308
+ // Actual transfer instruction
309
+ SystemProgram.transfer({
310
+ fromPubkey,
311
+ toPubkey,
312
+ lamports: Number(amount),
313
+ }));
314
+ console.log(`Using priority fee: ${computeUnitPrice} micro-lamports per CU, limit: ${computeUnitLimit} CU`);
315
+ }
316
+ else {
317
+ // SPL Token transfer with priority fees
318
+ const mintPubkey = new PublicKey(tokenAddress);
319
+ // Get associated token accounts
320
+ const fromTokenAccount = getAssociatedTokenAddressSync(mintPubkey, fromPubkey);
321
+ const toTokenAccount = getAssociatedTokenAddressSync(mintPubkey, toPubkey);
322
+ // Check if destination token account exists
323
+ const toTokenAccountInfo = await connection.getAccountInfo(toTokenAccount);
324
+ const needsDestinationAccount = !toTokenAccountInfo;
325
+ // Get mint info to determine decimals
326
+ const mintInfo = await connection.getParsedAccountInfo(mintPubkey);
327
+ const decimals = mintInfo.value?.data?.parsed?.info?.decimals || 9;
328
+ // SPL transfers need more compute units than SOL transfers
329
+ // Add extra CU if we need to create destination account
330
+ const computeUnitLimit = needsDestinationAccount ? 40000 : 20000;
331
+ const computeUnitPrice = Math.min(priorityFee, 100000);
332
+ // Create transfer instruction
333
+ const transferInstruction = createTransferCheckedInstruction(fromTokenAccount, mintPubkey, toTokenAccount, fromPubkey, Number(amount), decimals);
334
+ transaction = new Transaction()
335
+ .add(ComputeBudgetProgram.setComputeUnitLimit({
336
+ units: computeUnitLimit,
337
+ }))
338
+ .add(ComputeBudgetProgram.setComputeUnitPrice({
339
+ microLamports: computeUnitPrice,
340
+ }));
341
+ // Add create destination account instruction if needed
342
+ if (needsDestinationAccount) {
343
+ transaction.add(createAssociatedTokenAccountInstruction(fromPubkey, // payer
344
+ toTokenAccount, // ata
345
+ toPubkey, // owner
346
+ mintPubkey));
347
+ }
348
+ // Add the transfer instruction
349
+ transaction.add(transferInstruction);
350
+ console.log(`SPL Token transfer: ${computeUnitPrice} micro-lamports per CU, limit: ${computeUnitLimit} CU, creating destination: ${needsDestinationAccount}`);
351
+ }
352
+ // Step 5: Get latest blockhash and simulate transaction to verify
353
+ const { blockhash } = await connection.getLatestBlockhash("confirmed");
354
+ transaction.recentBlockhash = blockhash;
355
+ transaction.feePayer = fromPubkey;
356
+ // Optional: Simulate transaction to catch issues early
357
+ try {
358
+ const simulation = await connection.simulateTransaction(transaction, {
359
+ sigVerify: false,
360
+ replaceRecentBlockhash: true,
361
+ });
362
+ if (simulation.value.err) {
363
+ console.error("Transaction simulation failed:", simulation.value.err);
364
+ toast.error("Transaction simulation failed. Please check your balance and try again.");
365
+ return;
366
+ }
367
+ console.log("Transaction simulation successful. Compute units used:", simulation.value.unitsConsumed);
368
+ }
369
+ catch (simError) {
370
+ console.warn("Transaction simulation failed, proceeding anyway:", simError);
371
+ }
372
+ // Step 6: Sign and send transaction with priority fees
373
+ const signedTransaction = await phantom.signAndSendTransaction(transaction);
374
+ toast.success(`Transaction successful! Signature: ${signedTransaction.signature}`);
375
+ console.log("Transaction sent with priority fees. Signature:", signedTransaction.signature);
376
+ }
377
+ catch (error) {
378
+ console.error("Transfer error:", error);
379
+ const errorMessage = error instanceof Error ? error.message : String(error);
380
+ if (errorMessage.includes("User rejected")) {
381
+ toast.error("Transaction was cancelled by user");
382
+ }
383
+ else if (errorMessage.includes("insufficient")) {
384
+ toast.error("Insufficient balance for this transaction");
385
+ }
386
+ else if (errorMessage.includes("blockhash not found")) {
387
+ toast.error("Network congestion detected. Please try again in a moment.");
388
+ }
389
+ else {
390
+ toast.error(`Transfer failed: ${errorMessage}`);
391
+ }
392
+ }
245
393
  };
246
394
  if (refundTxs) {
247
395
  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 +473,7 @@ export const OrderDetails = memo(function OrderDetails({ isMainnet, mode = "moda
325
473
  toast.success("Copied to clipboard");
326
474
  }, 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
475
  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: () => {
476
+ }, 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" })] }) }), _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
477
  toast.success("Copied to clipboard");
330
478
  }, 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
479
  if (navigator.share) {
@@ -40,7 +40,7 @@ export function OrderToken({ context, address, chainId, setChainId, token, setTo
40
40
  hasEnoughBalance,
41
41
  };
42
42
  }, [chainId, fungibleTokens, nativeTokens, requiredAmount, token, wallet?.address]);
43
- return (_jsx(TokenSelector, { address: address, chainIdsFilter: Object.values(ALL_CHAINS).map(chain => chain.id), context: context, fromChainWalletVMSupported: true, isValidAddress: true, lockedChainIds: Object.values(ALL_CHAINS).map(chain => chain.id), multiWalletSupportEnabled: true, onAnalyticEvent: undefined, popularChainIds: [1, 8453, RELAY_SOLANA_MAINNET_CHAIN_ID], restrictedToken: undefined, setToken: token => {
43
+ return (_jsx(TokenSelector, { address: address, chainIdsFilter: Object.values(ALL_CHAINS).map(chain => chain.id), context: context, fromChainWalletVMSupported: true, isValidAddress: true, lockedChainIds: Object.values(ALL_CHAINS).map(chain => chain.id), multiWalletSupportEnabled: true, onAnalyticEvent: undefined, popularChainIds: [1, 8453, RELAY_SOLANA_MAINNET_CHAIN_ID], setToken: token => {
44
44
  setChainId(token.chainId);
45
45
  setToken({
46
46
  address: token.address,
@@ -43,5 +43,5 @@ export function OrderTokenAmount({ disabled, inputValue, onChangeInput, context,
43
43
  onChangeInput("0.01");
44
44
  }
45
45
  };
46
- return (_jsx("div", { className: cn("border-as-stroke flex w-full flex-col gap-2 rounded-xl", className), children: _jsxs("div", { className: cn("flex items-center justify-between gap-3", innerClassName), children: [!canEditAmount ? (_jsx("h2", { className: cn("text-3xl font-medium text-white", amountClassName), children: inputValue || "--" })) : (_jsx(NumericFormat, { decimalSeparator: ".", allowedDecimalSeparators: [","], thousandSeparator: true, inputMode: "decimal", autoComplete: "off", autoCorrect: "off", type: "text", placeholder: "0.00", minLength: 1, maxLength: 30, spellCheck: "false", className: cn("placeholder:text-as-primary/70 disabled:text-as-primary/70 text-as-primary w-full bg-transparent text-4xl font-semibold leading-[42px] outline-none sm:text-[30px]", amountClassName), pattern: "^[0-9]*[.,]?[0-9]*$", disabled: disabled, value: inputValue, allowNegative: false, onChange: e => onChangeInput(e.currentTarget.value) }, `input-${token.address}-${chainId}`)), !hideTokenSelect && (_jsx(TokenSelector, { address: address, chainIdsFilter: Object.values(ALL_CHAINS).map(chain => chain.id), context: context, fromChainWalletVMSupported: true, isValidAddress: true, lockedChainIds: Object.values(ALL_CHAINS).map(chain => chain.id), multiWalletSupportEnabled: true, onAnalyticEvent: undefined, popularChainIds: [1, 8453, RELAY_SOLANA_MAINNET_CHAIN_ID], restrictedToken: undefined, setToken: handleTokenSelect, supportedWalletVMs: ["evm", "svm"], token: undefined, trigger: _jsxs(Button, { variant: "outline", role: "combobox", className: cn("bg-b3-react-background border-as-stroke flex h-auto w-fit shrink-0 items-center justify-center gap-2 rounded-xl border-2 px-2 py-1 pr-2 text-center", tokenSelectClassName), children: [token.metadata.logoURI ? (_jsx(ChainTokenIcon, { chainUrl: ALL_CHAINS[chainId].logoUrl, tokenUrl: token.metadata.logoURI, className: "h-8 min-h-8 w-8 min-w-8" })) : (_jsx("div", { className: "h-8 w-8 rounded-full bg-gray-700" })), _jsxs("div", { className: "flex flex-col items-start gap-0", children: [_jsx("div", { className: "text-as-primary font-semibold", children: token.symbol }), _jsx("div", { className: "text-as-primary/70 text-xs", children: ALL_CHAINS[chainId].name })] }), _jsx(ChevronsUpDown, { className: "h-4 w-4 shrink-0 opacity-70" })] }) }, `selector-${context}-${token.address}-${chainId}`))] }) }, `${context}-${token.address}-${chainId}`));
46
+ return (_jsx("div", { className: cn("border-as-stroke flex w-full flex-col gap-2 rounded-xl", className), children: _jsxs("div", { className: cn("flex items-center justify-between gap-3", innerClassName), children: [!canEditAmount ? (_jsx("h2", { className: cn("text-3xl font-medium text-white", amountClassName), children: inputValue || "--" })) : (_jsx(NumericFormat, { decimalSeparator: ".", allowedDecimalSeparators: [","], thousandSeparator: true, inputMode: "decimal", autoComplete: "off", autoCorrect: "off", type: "text", placeholder: "0.00", minLength: 1, maxLength: 30, spellCheck: "false", className: cn("placeholder:text-as-primary/70 disabled:text-as-primary/70 text-as-primary w-full bg-transparent text-4xl font-semibold leading-[42px] outline-none sm:text-[30px]", amountClassName), pattern: "^[0-9]*[.,]?[0-9]*$", disabled: disabled, value: inputValue, allowNegative: false, onChange: e => onChangeInput(e.currentTarget.value) }, `input-${token.address}-${chainId}`)), !hideTokenSelect && (_jsx(TokenSelector, { address: address, chainIdsFilter: Object.values(ALL_CHAINS).map(chain => chain.id), context: context, fromChainWalletVMSupported: true, isValidAddress: true, lockedChainIds: Object.values(ALL_CHAINS).map(chain => chain.id), multiWalletSupportEnabled: true, onAnalyticEvent: undefined, popularChainIds: [1, 8453, RELAY_SOLANA_MAINNET_CHAIN_ID], setToken: handleTokenSelect, supportedWalletVMs: ["evm", "svm"], token: undefined, trigger: _jsxs(Button, { variant: "outline", role: "combobox", className: cn("bg-b3-react-background border-as-stroke flex h-auto w-fit shrink-0 items-center justify-center gap-2 rounded-xl border-2 px-2 py-1 pr-2 text-center", tokenSelectClassName), children: [token.metadata.logoURI ? (_jsx(ChainTokenIcon, { chainUrl: ALL_CHAINS[chainId].logoUrl, tokenUrl: token.metadata.logoURI, className: "h-8 min-h-8 w-8 min-w-8" })) : (_jsx("div", { className: "h-8 w-8 rounded-full bg-gray-700" })), _jsxs("div", { className: "flex flex-col items-start gap-0", children: [_jsx("div", { className: "text-as-primary font-semibold", children: token.symbol }), _jsx("div", { className: "text-as-primary/70 text-xs", children: ALL_CHAINS[chainId].name })] }), _jsx(ChevronsUpDown, { className: "h-4 w-4 shrink-0 opacity-70" })] }) }, `selector-${context}-${token.address}-${chainId}`))] }) }, `${context}-${token.address}-${chainId}`));
47
47
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@b3dotfun/sdk",
3
- "version": "0.0.9-alpha.4",
3
+ "version": "0.0.9-alpha.6",
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,15 +202,17 @@
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",
210
210
  "@radix-ui/react-tooltip": "1.1.8",
211
- "@reservoir0x/relay-kit-ui": "2.9.13",
212
- "@reservoir0x/relay-sdk": "1.7.3",
211
+ "@reservoir0x/relay-kit-ui": "2.15.4",
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",
@@ -31,6 +31,12 @@ import { cn } from "@b3dotfun/sdk/shared/utils";
31
31
  import centerTruncate from "@b3dotfun/sdk/shared/utils/centerTruncate";
32
32
  import { formatTokenAmount } from "@b3dotfun/sdk/shared/utils/number";
33
33
  import { useColorMode } from "@chakra-ui/react";
34
+ import {
35
+ createAssociatedTokenAccountInstruction,
36
+ createTransferCheckedInstruction,
37
+ getAssociatedTokenAddressSync,
38
+ } from "@solana/spl-token";
39
+ import { ComputeBudgetProgram, Connection, PublicKey, SystemProgram, Transaction } from "@solana/web3.js";
34
40
  import { WalletCoinbase, WalletMetamask, WalletPhantom, WalletTrust, WalletWalletConnect } from "@web3icons/react";
35
41
  import { motion } from "framer-motion";
36
42
  import {
@@ -358,9 +364,191 @@ export const OrderDetails = memo(function OrderDetails({
358
364
  return coinbaseUrl;
359
365
  };
360
366
 
361
- const handlePhantomRedirect = () => {
362
- const phantomDeepLink = `https://phantom.app/ul/browse/${encodeURIComponent(permalink)}`;
363
- return phantomDeepLink;
367
+ const initiatePhantomTransfer = async (amountLamports: string, tokenAddress: string, recipientAddress: string) => {
368
+ try {
369
+ // Step 1: Check if Phantom is available
370
+ const isPhantomMobile = navigator.userAgent.includes("Phantom");
371
+ const isPhantomBrowser = (window as any).phantom?.solana?.isPhantom;
372
+
373
+ if (!isPhantomBrowser && !isPhantomMobile) {
374
+ toast.error("Phantom wallet not installed. Please install Phantom wallet to continue.");
375
+ return;
376
+ }
377
+
378
+ // Step 2: Ensure Phantom is connected/unlocked
379
+ const phantom = (window as any).phantom?.solana;
380
+ if (!phantom) {
381
+ toast.error("Phantom wallet not accessible");
382
+ return;
383
+ }
384
+
385
+ // Connect and unlock wallet if needed
386
+ let publicKey;
387
+ try {
388
+ const connection = await phantom.connect();
389
+ publicKey = connection.publicKey;
390
+ } catch (connectError) {
391
+ toast.error("Failed to connect to Phantom wallet");
392
+ return;
393
+ }
394
+
395
+ // Step 3: Create transaction with priority fees
396
+ const connection = new Connection("https://mainnet.helius-rpc.com/?api-key=efafd9b3-1807-4cf8-8aa4-3d984f56d8fb");
397
+
398
+ const fromPubkey = new PublicKey(publicKey.toString());
399
+ const toPubkey = new PublicKey(recipientAddress);
400
+ const amount = BigInt(amountLamports);
401
+
402
+ // Step 4: Get recent priority fees to determine optimal pricing
403
+ let priorityFee = 10000; // Default fallback (10,000 micro-lamports)
404
+ try {
405
+ const recentFees = await connection.getRecentPrioritizationFees({
406
+ lockedWritableAccounts: [fromPubkey],
407
+ });
408
+
409
+ if (recentFees && recentFees.length > 0) {
410
+ // Use 75th percentile of recent fees for good priority
411
+ const sortedFees = recentFees.map(fee => fee.prioritizationFee).sort((a, b) => a - b);
412
+ const percentile75Index = Math.floor(sortedFees.length * 0.75);
413
+ priorityFee = Math.max(sortedFees[percentile75Index] || 10000, 10000);
414
+ }
415
+ } catch (feeError) {
416
+ console.warn("Failed to fetch recent priority fees, using default:", feeError);
417
+ }
418
+
419
+ let transaction: any;
420
+
421
+ // Check if this is native SOL transfer
422
+ if (tokenAddress === "11111111111111111111111111111111") {
423
+ // Native SOL transfer with priority fees
424
+ const computeUnitLimit = 1000; // SOL transfer + compute budget instructions need ~600-800 CU
425
+ const computeUnitPrice = Math.min(priorityFee, 100000); // Cap at 100k micro-lamports for safety
426
+
427
+ transaction = new Transaction()
428
+ .add(
429
+ // Set compute unit limit first (must come before other instructions)
430
+ ComputeBudgetProgram.setComputeUnitLimit({
431
+ units: computeUnitLimit,
432
+ }),
433
+ )
434
+ .add(
435
+ // Set priority fee
436
+ ComputeBudgetProgram.setComputeUnitPrice({
437
+ microLamports: computeUnitPrice,
438
+ }),
439
+ )
440
+ .add(
441
+ // Actual transfer instruction
442
+ SystemProgram.transfer({
443
+ fromPubkey,
444
+ toPubkey,
445
+ lamports: Number(amount),
446
+ }),
447
+ );
448
+
449
+ console.log(`Using priority fee: ${computeUnitPrice} micro-lamports per CU, limit: ${computeUnitLimit} CU`);
450
+ } else {
451
+ // SPL Token transfer with priority fees
452
+ const mintPubkey = new PublicKey(tokenAddress);
453
+
454
+ // Get associated token accounts
455
+ const fromTokenAccount = getAssociatedTokenAddressSync(mintPubkey, fromPubkey);
456
+ const toTokenAccount = getAssociatedTokenAddressSync(mintPubkey, toPubkey);
457
+
458
+ // Check if destination token account exists
459
+ const toTokenAccountInfo = await connection.getAccountInfo(toTokenAccount);
460
+ const needsDestinationAccount = !toTokenAccountInfo;
461
+
462
+ // Get mint info to determine decimals
463
+ const mintInfo = await connection.getParsedAccountInfo(mintPubkey);
464
+ const decimals = (mintInfo.value?.data as any)?.parsed?.info?.decimals || 9;
465
+
466
+ // SPL transfers need more compute units than SOL transfers
467
+ // Add extra CU if we need to create destination account
468
+ const computeUnitLimit = needsDestinationAccount ? 40000 : 20000;
469
+ const computeUnitPrice = Math.min(priorityFee, 100000);
470
+
471
+ // Create transfer instruction
472
+ const transferInstruction = createTransferCheckedInstruction(
473
+ fromTokenAccount,
474
+ mintPubkey,
475
+ toTokenAccount,
476
+ fromPubkey,
477
+ Number(amount),
478
+ decimals,
479
+ );
480
+
481
+ transaction = new Transaction()
482
+ .add(
483
+ ComputeBudgetProgram.setComputeUnitLimit({
484
+ units: computeUnitLimit,
485
+ }),
486
+ )
487
+ .add(
488
+ ComputeBudgetProgram.setComputeUnitPrice({
489
+ microLamports: computeUnitPrice,
490
+ }),
491
+ );
492
+
493
+ // Add create destination account instruction if needed
494
+ if (needsDestinationAccount) {
495
+ transaction.add(
496
+ createAssociatedTokenAccountInstruction(
497
+ fromPubkey, // payer
498
+ toTokenAccount, // ata
499
+ toPubkey, // owner
500
+ mintPubkey, // mint
501
+ ),
502
+ );
503
+ }
504
+
505
+ // Add the transfer instruction
506
+ transaction.add(transferInstruction);
507
+
508
+ console.log(
509
+ `SPL Token transfer: ${computeUnitPrice} micro-lamports per CU, limit: ${computeUnitLimit} CU, creating destination: ${needsDestinationAccount}`,
510
+ );
511
+ }
512
+
513
+ // Step 5: Get latest blockhash and simulate transaction to verify
514
+ const { blockhash } = await connection.getLatestBlockhash("confirmed");
515
+ transaction.recentBlockhash = blockhash;
516
+ transaction.feePayer = fromPubkey;
517
+
518
+ // Optional: Simulate transaction to catch issues early
519
+ try {
520
+ const simulation = await connection.simulateTransaction(transaction, {
521
+ sigVerify: false,
522
+ replaceRecentBlockhash: true,
523
+ });
524
+ if (simulation.value.err) {
525
+ console.error("Transaction simulation failed:", simulation.value.err);
526
+ toast.error("Transaction simulation failed. Please check your balance and try again.");
527
+ return;
528
+ }
529
+ console.log("Transaction simulation successful. Compute units used:", simulation.value.unitsConsumed);
530
+ } catch (simError) {
531
+ console.warn("Transaction simulation failed, proceeding anyway:", simError);
532
+ }
533
+
534
+ // Step 6: Sign and send transaction with priority fees
535
+ const signedTransaction = await phantom.signAndSendTransaction(transaction);
536
+
537
+ toast.success(`Transaction successful! Signature: ${signedTransaction.signature}`);
538
+ console.log("Transaction sent with priority fees. Signature:", signedTransaction.signature);
539
+ } catch (error: unknown) {
540
+ console.error("Transfer error:", error);
541
+ const errorMessage = error instanceof Error ? error.message : String(error);
542
+ if (errorMessage.includes("User rejected")) {
543
+ toast.error("Transaction was cancelled by user");
544
+ } else if (errorMessage.includes("insufficient")) {
545
+ toast.error("Insufficient balance for this transaction");
546
+ } else if (errorMessage.includes("blockhash not found")) {
547
+ toast.error("Network congestion detected. Please try again in a moment.");
548
+ } else {
549
+ toast.error(`Transfer failed: ${errorMessage}`);
550
+ }
551
+ }
364
552
  };
365
553
 
366
554
  if (refundTxs) {
@@ -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
  )}
@@ -78,7 +78,6 @@ export function OrderToken({
78
78
  multiWalletSupportEnabled={true}
79
79
  onAnalyticEvent={undefined}
80
80
  popularChainIds={[1, 8453, RELAY_SOLANA_MAINNET_CHAIN_ID]}
81
- restrictedToken={undefined}
82
81
  setToken={token => {
83
82
  setChainId(token.chainId);
84
83
  setToken({
@@ -132,7 +132,6 @@ export function OrderTokenAmount({
132
132
  multiWalletSupportEnabled={true}
133
133
  onAnalyticEvent={undefined}
134
134
  popularChainIds={[1, 8453, RELAY_SOLANA_MAINNET_CHAIN_ID]}
135
- restrictedToken={undefined}
136
135
  setToken={handleTokenSelect}
137
136
  supportedWalletVMs={["evm", "svm"]}
138
137
  token={undefined}
@@ -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 {};