@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.
- package/dist/cjs/anyspend/react/components/common/OrderDetails.js +152 -4
- package/dist/cjs/anyspend/react/components/common/OrderToken.js +1 -1
- package/dist/cjs/anyspend/react/components/common/OrderTokenAmount.js +1 -1
- package/dist/esm/anyspend/react/components/common/OrderDetails.js +152 -4
- package/dist/esm/anyspend/react/components/common/OrderToken.js +1 -1
- package/dist/esm/anyspend/react/components/common/OrderTokenAmount.js +1 -1
- package/package.json +8 -6
- package/src/anyspend/react/components/common/OrderDetails.tsx +201 -9
- package/src/anyspend/react/components/common/OrderToken.tsx +0 -1
- package/src/anyspend/react/components/common/OrderTokenAmount.tsx +0 -1
- package/src/anyspend/types/window.d.ts +15 -0
|
@@ -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
|
|
249
|
-
|
|
250
|
-
|
|
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.
|
|
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],
|
|
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],
|
|
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
|
|
243
|
-
|
|
244
|
-
|
|
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" })] }) }),
|
|
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],
|
|
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],
|
|
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.
|
|
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.
|
|
206
|
-
"@radix-ui/react-popover": "1.1.
|
|
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.
|
|
212
|
-
"@reservoir0x/relay-sdk": "
|
|
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
|
|
362
|
-
|
|
363
|
-
|
|
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
|
-
<
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
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
|
)}
|
|
@@ -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 {};
|