Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
156 changes: 103 additions & 53 deletions assets/js/webapp/swapUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,19 @@
const handleCancel = (err) => {
const msg = err?.message || "";
if (msg.includes("rejected") || msg.includes("cancelled") || msg.includes("user declined")) {
console.log("[Ultra] Swap cancelled");
console.log("[Ultra] Swap cancelled by user");
throw new Error("USER_REJECTED");
}
return false;
};

// ==================== ULTRA SWAP (Primary) ====================
async function performUltraSwap(inputMint, outputMint, rawAmount, provider, connectedWallet) {
if (rawAmount < 100000) {
console.warn("[Ultra] Amount too small, skipping Ultra quote");
throw new Error("Amount too small — try a larger swap");
}

try {
console.log(`[Ultra] Requesting quote: ${rawAmount} from ${inputMint} to ${outputMint}`);
const ultraRes = await fetch(
Expand All @@ -29,71 +34,105 @@ async function performUltraSwap(inputMint, outputMint, rawAmount, provider, conn
}
}

// ==================== SMART SIGNING ====================
async function executeUltraTransaction(quote, provider) {
try {
const connectionMethod = window.connectionMethod || localStorage.getItem('connection_method');

// ==================== GOOGLE / APPLE (Embedded Wallet) ====================
if (connectionMethod === 'google' || connectionMethod === 'apple') {
console.log("[Ultra] Embedded wallet detected → Using v1 proxy + SDK signing");
console.log(`[Ultra] Connected wallet: ${window.connectedWallet}`);

const proxyRes = await fetch('https://giddy-key-swaps-production.up.railway.app/api/swap/v1', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
inputMint: quote.inputMint || "So11111111111111111111111111111111111111112",
outputMint: quote.outputMint,
rawAmount: quote.inAmount,
connectedWallet: window.connectedWallet
})
});

const data = await proxyRes.json();
if (!data.success) throw new Error(data.error || "v1 proxy failed");

const tx = solanaWeb3.VersionedTransaction.deserialize(Buffer.from(data.swapTransaction, "base64"));

// ==================== SMART SIGNING WITH CLEVER DETECTION ====================
async function executeUltraTransaction(quote, providerParam) {
const connectionMethod = window.connectionMethod || localStorage.getItem('connection_method') || 'injected';
console.log(`[Ultra] Detected connection method: ${connectionMethod}`);

let signer = null;
let isEmbedded = false;

if (connectionMethod === 'google' || connectionMethod === 'apple') {
// === EMBEDDED WALLETS (Google / Apple) ===
isEmbedded = true;
console.log("[Ultra] Embedded wallet detected — forcing SDK signer only");
const sdk = await window.getPhantomSDK?.();
if (sdk?.solana) {
signer = sdk.solana;
console.log("✅ Using Phantom SDK signer (Google/Apple)");
}
} else {
// === INJECTED WALLETS (Phantom extension / app) ===
console.log("[Ultra] Injected wallet detected — prioritizing window.solana");

if (providerParam && typeof providerParam.signTransaction === 'function') {
signer = providerParam;
console.log("✅ Using legacy provider (injected)");
}
else if (window.solana?.isPhantom) {
signer = window.solana;
console.log("✅ Using direct window.solana (injected)");
}
else {
// Last resort fallback for injected
const sdk = await window.getPhantomSDK?.();
if (!sdk?.solana) throw new Error("Phantom SDK not available");

console.log("[Ultra] Requesting SDK signAndSendTransaction...");
// Removed presignTransaction hook to prevent 403 Forbidden on /prepare
const result = await sdk.solana.signAndSendTransaction(tx);

console.log("[Ultra] SDK Success, TXID:", result.signature);
return { success: true, txid: result.signature, router: "v1-proxy" };
if (sdk?.solana) {
signer = sdk.solana;
console.log("✅ Using SDK signer as fallback for injected");
}
}
}

// ==================== INJECTED PHANTOM ====================
console.log("[Ultra] Signing with Injected Phantom...");
if (!provider || typeof provider.signTransaction !== 'function') {
throw new Error("No valid injected provider");
}
if (!signer) {
throw new Error(`No valid signer found for connection type: ${connectionMethod}`);
}

// For embedded wallets we prefer the stable v1 proxy path
if (isEmbedded) {
console.log("[Ultra] Embedded wallet → using v1 proxy for reliable signing");
return await performV1Swap(
quote.inputMint || "So11111111111111111111111111111111111111112",
quote.outputMint,
quote.inAmount || quote.amount,
providerParam,
window.connectedWallet
);
}

// Injected path → try direct Jupiter Ultra execute first
try {
console.log("[Ultra] Injected → attempting direct Jupiter Ultra execute");
const vtx = solanaWeb3.VersionedTransaction.deserialize(Buffer.from(quote.transaction, 'base64'));
const signed = await provider.signTransaction(vtx);
const signed = await signer.signTransaction(vtx);

const signedTxBase64 = btoa(String.fromCharCode(...new Uint8Array(signed.serialize())));

const executeRes = await fetch('https://lite-api.jup.ag/ultra/v1/execute', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ signedTransaction: signedTxBase64, requestId: quote.requestId })
body: JSON.stringify({
signedTransaction: signedTxBase64,
requestId: quote.requestId
})
});

if (!executeRes.ok) throw new Error(`Ultra Execute Failed: ${executeRes.status}`);
if (!executeRes.ok) throw new Error(`Ultra Execute HTTP ${executeRes.status}`);
const result = await executeRes.json();
return { success: true, txid: result.signature, router: quote.router || 'OKX/DFlow' };
} catch (error) {
if (!handleCancel(error)) console.error("[Ultra] Execute failed:", error);
throw error;

if (!result.signature) throw new Error("Ultra execute did not return signature");

console.log(`✅ SUCCESS (Ultra direct)! TX: ${result.signature}`);
return { success: true, txid: result.signature, router: "Ultra" };

} catch (ultraErr) {
console.warn("[Ultra] Direct Ultra execute failed, falling back to v1 proxy...", ultraErr.message);
if (!handleCancel(ultraErr)) {
return await performV1Swap(
quote.inputMint || "So11111111111111111111111111111111111111112",
quote.outputMint,
quote.inAmount || quote.amount,
providerParam,
window.connectedWallet
);
}
throw ultraErr;
}
}

// ==================== V1 FALLBACK ====================
// ==================== V1 PROXY FALLBACK ====================
async function performV1Swap(inputMint, outputMint, rawAmount, provider, connectedWallet) {
try {
console.log("[V1 Proxy] Using fallback proxy");
const proxyRes = await fetch('https://giddy-key-swaps-production.up.railway.app/api/swap/v1', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
Expand All @@ -104,7 +143,21 @@ async function performV1Swap(inputMint, outputMint, rawAmount, provider, connect
if (!data.success) throw new Error(data.error || "v1 proxy failed");

const tx = solanaWeb3.VersionedTransaction.deserialize(Buffer.from(data.swapTransaction, "base64"));
const result = await provider.signAndSendTransaction(tx);

// Re-detect signer for v1 path (respects Google/Apple vs injected)
const connectionMethod = window.connectionMethod || localStorage.getItem('connection_method') || 'injected';
let signer = null;

if (connectionMethod === 'google' || connectionMethod === 'apple') {
const sdk = await window.getPhantomSDK?.();
signer = sdk?.solana || null;
} else {
signer = provider || window.solana || (await window.getPhantomSDK?.())?.solana;
}

if (!signer) throw new Error("No signer available for v1 fallback");

const result = await signer.signAndSendTransaction(tx);
return { success: true, txid: result.signature, version: "v1" };
} catch (error) {
if (!handleCancel(error)) console.error("[V1 Proxy] Failed:", error.message);
Expand All @@ -116,7 +169,4 @@ async function performV1Swap(inputMint, outputMint, rawAmount, provider, connect
window.performUltraSwap = performUltraSwap;
window.performV1Swap = performV1Swap;

// Dispatch ready event
window.dispatchEvent(new CustomEvent('swap-engine-ready'));

console.log("✅ swapUtils.js loaded and READY");
console.log("✅ swapUtils.js loaded — Smart detection active (Google/Apple → SDK only | Injected → window.solana priority)");
27 changes: 13 additions & 14 deletions assets/js/webapp/wallet.js
Original file line number Diff line number Diff line change
Expand Up @@ -608,44 +608,43 @@ function initShareWalletButton() {
if (!shareBtn) return;

shareBtn.addEventListener('click', async () => {
// 1. Get the dynamic address from storage
const address = localStorage.getItem('wallet_address');
const address = localStorage.getItem('wallet_address') || window.connectedWallet;
if (!address) {
alert("No wallet address found.");
return;
}

// 2. Updated: share only the address
const shareText = address;
const shareText = `My Giddy Key Wallet Address:\n${address}`;

// 3. Try native share
// Try native share first
if (navigator.share) {
try {
await navigator.share({
title: 'My Giddy Key Wallet',
text: shareText
// Removed 'url' so only the address is shared
});
return;
} catch (err) {
console.log("Share cancelled or failed");
console.log("Native share cancelled → clipboard fallback");
}
}

// 4. Fallback: Copy address to clipboard
// Silent clipboard fallback
try {
await navigator.clipboard.writeText(address);

// Visual feedback
const originalIcon = shareBtn.innerHTML;
shareBtn.innerHTML = `<i class="fas fa-check text-emerald-400"></i>`;
// Visual feedback - same size as share icon
const originalHTML = shareBtn.innerHTML;
shareBtn.innerHTML = `<i class="fas fa-check text-sm text-emerald-400"></i>`;

setTimeout(() => {
shareBtn.innerHTML = originalIcon;
}, 1500);
shareBtn.innerHTML = originalHTML;
}, 1600);

console.log("✅ Address copied to clipboard");
} catch (err) {
prompt("Copy your wallet address:", address);
console.error("Clipboard failed:", err);
alert(`Wallet address copied!\n\n${address}`);
}
});
}
Expand Down
Loading
Loading