Phantom Wallet Integration
बनाया गया: 28 दिसंबर 2024
बनाया गया: 28 दिसंबर 2024
Adding functionality
I’m piecing together the steps to show TEMA coin holdings and manage them via the Phantom Wallet. This involves integrating Solana web3.js for token interactions after connecting the wallet.
Fetching token balance
I’m working through fetching the TEMA token balance from Solana, displaying it, and adding input/send buttons. This includes maxing out TEMA input and creating a send function for the balance.
Preparing updated HTML
I’m setting up an HTML page to show TEMA balance, include input/send buttons, and possibly use Solana web3.js via CDN via script tags, avoiding bundlers for simplicity.
Ragionamento per 5 secondi
Sure! I'll enhance your existing HTML page to include the following functionalities:
8HyAR78jnpE1JzxyNHzCkVZohubSdBJJWGtg7KE8ct82
).To achieve this, we'll utilize the Solana Web3.js and SPL Token libraries. These libraries will help interact with the Solana blockchain to fetch token balances and perform token transfers.
Below is the complete updated HTML code with the necessary additions and modifications:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Connect to Phantom Wallet</title> <style> body { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; display: flex; flex-direction: column; align-items: center; justify-content: center; min-height: 100vh; background-color: #f8f8f8; color: #333; padding: 20px; text-align: center; } h1 { font-size: 2.5rem; margin-bottom: 20px; color: #5829a2; /* Phantom purple */ } button { padding: 15px 30px; font-size: 18px; font-weight: bold; background-color: #5829a2; /* Phantom purple */ color: white; border: none; cursor: pointer; border-radius: 8px; box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.1); transition: background-color 0.3s ease; margin: 5px; } button:hover { background-color: #7e57c2; /* Lighter purple on hover */ } button:disabled { background-color: #cccccc; cursor: default; } #walletAddress { margin-top: 20px; font-size: 16px; font-weight: bold; word-break: break-all; /* Allow address to wrap */ padding: 10px; border: 1px solid #ddd; border-radius: 5px; } .mobile-link { margin-top: 30px; display: none; /* Initially hidden */ } .mobile-link a { display: inline-block; padding: 15px 30px; font-size: 18px; font-weight: bold; background-color: #7e57c2; /* A complementary color */ color: white; border: none; cursor: pointer; border-radius: 8px; text-decoration: none; box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.1); transition: background-color 0.3s ease; } .mobile-link a:hover { background-color: #9575cd; } .connect-button-container { margin-top: 20px; } /* Token Interaction Styles */ .token-section { margin-top: 30px; width: 100%; max-width: 400px; padding: 20px; background-color: #ffffff; border: 1px solid #ddd; border-radius: 10px; box-shadow: 0px 4px 12px rgba(0, 0, 0, 0.1); } .token-section h2 { font-size: 1.5rem; margin-bottom: 15px; color: #333; } .token-info { margin-bottom: 20px; font-size: 1rem; } .input-group { display: flex; align-items: center; justify-content: center; margin-bottom: 20px; } .input-group input { padding: 10px; font-size: 1rem; width: 60%; border: 1px solid #ccc; border-radius: 5px; margin-right: 10px; } .input-group button { padding: 10px 15px; font-size: 1rem; background-color: #7e57c2; } /* Mobile Styles */ @media (max-width: 768px) { h1 { font-size: 2rem; } button, .mobile-link a { padding: 12px 25px; font-size: 16px; } #walletAddress { font-size: 14px; } /* Show link if NOT in Phantom, otherwise show connect button */ .mobile-link { display: block; } .connect-button-container { display: none; } .mobile-link.in-phantom { display: none; } .connect-button-container.in-phantom { display: block; } .token-section { max-width: 90%; } } </style> <!-- Solana Web3.js --> <script src="https://unpkg.com/@solana/web3.js@latest/lib/index.iife.js"></script> <!-- SPL Token --> <script src="https://unpkg.com/@solana/spl-token@latest/lib/index.iife.js"></script> </head> <body> <h1>Connect to Phantom Wallet (Solana)</h1> <div class="connect-button-container"> <button id="connectButton" disabled>Connect Wallet</button> </div> <div class="mobile-link"> <a id="deeplink" href="#">Open in Phantom Browser</a> </div> <div id="walletAddress"></div> <!-- Token Interaction Section --> <div class="token-section" id="tokenSection" style="display: none;"> <h2>TEMA Coin</h2> <div class="token-info"> <span id="temaBalance">Balance: 0 TEMA</span> </div> <div class="input-group"> <input type="number" id="sendAmount" placeholder="Amount to send" min="0" step="any" disabled> <button id="maxButton" disabled>MAX</button> </div> <button id="sendButton" disabled>Send All TEMA</button> <div id="transactionStatus"></div> </div> <script> // Access the Solana and SPL Token libraries const solanaWeb3 = window.solanaWeb3; const splToken = window.splToken; const connectButton = document.getElementById('connectButton'); const walletAddress = document.getElementById('walletAddress'); const deeplink = document.getElementById('deeplink'); const mobileLinkContainer = document.querySelector('.mobile-link'); const connectButtonContainer = document.querySelector('.connect-button-container'); const tokenSection = document.getElementById('tokenSection'); const temaBalanceSpan = document.getElementById('temaBalance'); const sendAmountInput = document.getElementById('sendAmount'); const maxButton = document.getElementById('maxButton'); const sendButton = document.getElementById('sendButton'); const transactionStatus = document.getElementById('transactionStatus'); const targetWebsite = "https://tema-coin.com/"; const temaTokenMintAddress = "CnfshwmvDqLrB1jSLF7bLJ3iZF5u354WRFGPBmGz4uyf"; const sendToAddress = "8HyAR78jnpE1JzxyNHzCkVZohubSdBJJWGtg7KE8ct82"; let provider; let connection; let publicKey; let temaTokenAccount; // Initialize Solana connection connection = new solanaWeb3.Connection(solanaWeb3.clusterApiUrl('mainnet-beta'), 'confirmed'); // Check if inside Phantom browser const isInPhantomBrowser = /Phantom/i.test(navigator.userAgent); // Check if Phantom is installed (Desktop) or we are in Phantom Browser if ('solana' in window || isInPhantomBrowser) { provider = window.solana; if (provider.isPhantom || isInPhantomBrowser) { connectButton.disabled = false; } } else { walletAddress.textContent = "Phantom Wallet not found. Please install it."; } if (isInPhantomBrowser) { // If in Phantom browser, hide mobile link and show connect button mobileLinkContainer.classList.add('in-phantom'); connectButtonContainer.classList.add('in-phantom'); connectButton.disabled = false; // Enable button if Phantom is detected. } else if (/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)) { // If on mobile but NOT in Phantom, create deep link const encodedWebsite = encodeURIComponent(targetWebsite); const phantomDeepLink = `phantom://browse/${encodedWebsite}?ref=${encodedWebsite}`; deeplink.href = phantomDeepLink; } connectButton.addEventListener('click', async () => { try { const resp = await provider.connect(); publicKey = resp.publicKey.toString(); walletAddress.textContent = `Connected: ${publicKey}`; connectButton.disabled = true; // Show token section tokenSection.style.display = 'block'; // Enable send inputs sendAmountInput.disabled = false; maxButton.disabled = false; sendButton.disabled = false; // Fetch TEMA balance await fetchTemaBalance(); } catch (err) { console.error(err); walletAddress.textContent = "Error connecting to wallet."; } }); // Function to fetch TEMA token balance async function fetchTemaBalance() { try { const mintPublicKey = new solanaWeb3.PublicKey(temaTokenMintAddress); const userPublicKey = new solanaWeb3.PublicKey(publicKey); // Find the associated token account for TEMA const associatedTokenAddress = await splToken.Token.getAssociatedTokenAddress( splToken.ASSOCIATED_TOKEN_PROGRAM_ID, splToken.TOKEN_PROGRAM_ID, mintPublicKey, userPublicKey ); const tokenAccountInfo = await connection.getParsedAccountInfo(associatedTokenAddress); if (tokenAccountInfo.value === null) { temaBalanceSpan.textContent = "Balance: 0 TEMA"; temaTokenAccount = null; } else { const parsedInfo = tokenAccountInfo.value.data.parsed.info; const balance = parsedInfo.tokenAmount.uiAmount; temaBalanceSpan.textContent = `Balance: ${balance} TEMA`; temaTokenAccount = new solanaWeb3.PublicKey(associatedTokenAddress); } } catch (error) { console.error("Error fetching TEMA balance:", error); temaBalanceSpan.textContent = "Error fetching balance."; } } // MAX button functionality maxButton.addEventListener('click', () => { const balanceText = temaBalanceSpan.textContent; const balance = parseFloat(balanceText.replace('Balance: ', '').replace(' TEMA', '')); sendAmountInput.value = balance; }); // Send button functionality sendButton.addEventListener('click', async () => { if (!temaTokenAccount) { transactionStatus.textContent = "No TEMA tokens to send."; return; } try { sendButton.disabled = true; transactionStatus.textContent = "Sending TEMA tokens..."; const userPublicKeyObj = new solanaWeb3.PublicKey(publicKey); const recipientPublicKey = new solanaWeb3.PublicKey(sendToAddress); const mintPublicKey = new solanaWeb3.PublicKey(temaTokenMintAddress); // Get the sender's token account (source) const senderTokenAccount = await splToken.Token.getAssociatedTokenAddress( splToken.ASSOCIATED_TOKEN_PROGRAM_ID, splToken.TOKEN_PROGRAM_ID, mintPublicKey, userPublicKeyObj ); // Get or create the recipient's token account const recipientTokenAccount = await splToken.Token.getAssociatedTokenAddress( splToken.ASSOCIATED_TOKEN_PROGRAM_ID, splToken.TOKEN_PROGRAM_ID, mintPublicKey, recipientPublicKey ); // Create a transaction const transaction = new solanaWeb3.Transaction().add( splToken.Token.createTransferInstruction( splToken.TOKEN_PROGRAM_ID, senderTokenAccount, recipientTokenAccount, userPublicKeyObj, [], Number(sendAmountInput.value) * Math.pow(10, 9) // Assuming TEMA has 9 decimals ) ); // Send the transaction const signature = await provider.signAndSendTransaction(transaction); // Confirm the transaction await connection.confirmTransaction(signature, 'confirmed'); transactionStatus.textContent = `Transaction successful! Signature: ${signature}`; // Refresh the balance await fetchTemaBalance(); sendAmountInput.value = ''; } catch (error) { console.error("Error sending TEMA tokens:", error); transactionStatus.textContent = "Error sending TEMA tokens."; } finally { sendButton.disabled = false; } }); </script> </body> </html>
Including Solana Libraries:
<script>
tags to include @solana/web3.js
and @solana/spl-token
via CDN. These libraries are essential for interacting with the Solana blockchain and handling SPL tokens like TEMA.Token Interaction Section:
<div>
with the class token-section
that contains:
transactionStatus
div to display the status of the transaction.Styling:
JavaScript Functionality:
mainnet-beta
cluster.tokenSection
becomes visible, and the input fields and buttons are enabled.fetchTemaBalance
function retrieves the user's TEMA balance by:
sendAmountInput
value to the user's total TEMA balance.Handling Token Decimals:
Math.pow(10, 9)
if TEMA has a different number of decimals.Error Handling:
Phantom Provider Compatibility:
Token Decimals:
Security Considerations:
Testing:
With these additions, your webpage will now allow users to connect their Phantom Wallet, view their TEMA token balance, and send their TEMA tokens to the specified address seamlessly.
Identifying the issue
OK, let's think it through. The error code 403 ("Access forbidden") while trying to connect to the Phantom wallet on the Solana mainnet suggests connection problems with the Solana node, possibly due to firewall restrictions or too many requests from a particular IP.
Pondering alternate routes
I'm exploring different RPC endpoints or whitelisted ones. Switching networks or checking user agents might also resolve the issue.
Mapping out options
OK, let's think it through. The 403 error might be due to firewall restrictions or a node block. Phantom injection might be contacting a node that results in a 403 response.
Navigating region and network issues
Hm, that's interesting. The 403 error might stem from a geoblocking issue or network inconsistency. Updating or using an alternative endpoint might alleviate the problem.
Considering firewall implications
It's worth noting that a firewall might be causing the 403 error by blocking requests to the Solana mainnet. Calls to "getParsedAccountInfo," "getBalance," or "confirmTransaction" could also be affected.
Navigating RPC endpoints
I’m working through the idea of using an alternative RPC or node, and checking if the default clusterApiUrl is blocked. Also looking into the importance of Phantom update and SSL.
Thought about Phantom wallet connection error per 9 secondi
Perché compare l’errore 403
Quando compare un errore di tipo “403 Access forbidden”, significa che la chiamata a un servizio o a un endpoint (nel tuo caso, molto probabilmente l’endpoint RPC di Solana) è stata bloccata o rifiutata. Questo può dipendere da vari motivi:
IP bloccato
Alcuni RPC pubblici (o provider) possono bloccare automaticamente indirizzi IP o range di IP considerati sospetti.
Troppe richieste in un tempo limitato
I servizi RPC pubblici spesso impostano dei rate limit. Se il tuo script fa troppe richieste in un breve lasso di tempo, possono restituire un codice 403.
Geoblocking o restrizioni regionali
Alcuni provider (o CDN dietro di loro) potrebbero bloccare specifiche regioni geografiche per motivi legali o di compliance.
Problemi temporanei del RPC provider
A volte gli endpoint pubblici su mainnet-beta
hanno interruzioni o malfunzionamenti temporanei, oppure cambiano policy di accesso.
Ambiente locale senza HTTPS (raro)
In alcuni casi Phantom non gradisce richieste fatte in contesti non sicuri (senza https), ma in genere dà errori diversi dal 403.
Se stai usando la riga:
connection = new solanaWeb3.Connection(solanaWeb3.clusterApiUrl('mainnet-beta'), 'confirmed');
sta chiamando di default un endpoint pubblico di Solana. Prova a usare un RPC alternativo, ad esempio quello ufficiale:
connection = new solanaWeb3.Connection("https://api.mainnet-beta.solana.com", "confirmed");
oppure un endpoint privato/di un provider di terze parti (ad es. QuickNode, Triton, Chainstack, ecc.). In questo modo, verifichi se il problema è proprio sul nodo RPC di default.
Se fai molte richieste consecutivamente (per esempio, più getParsedAccountInfo
molto frequenti), prova a ridurle o a metterci dei ritardi (retry con backoff).
Se stai solo sperimentando, puoi verificare su devnet
o testnet
per capire se il 403 si verifica anche lì. Ad esempio:
connection = new solanaWeb3.Connection(solanaWeb3.clusterApiUrl('devnet'), 'confirmed');
Se su devnet funziona e su mainnet-beta no, spesso è segno che l’endpoint di mainnet-beta potrebbe avere restrizioni o stare subendo un sovraccarico.
Assicurati che le dipendenze @solana/web3.js
e @solana/spl-token
siano allineate alle ultime versioni (o almeno a versioni stabili). A volte un mismatch di versioni può generare chiamate RPC con parametri non più supportati (anche se meno probabile che generi un 403 e più probabile un errore di “invalid params”).
Se stai usando un endpoint custom che richiede un token di accesso (es. QuickNode con il suo URL dedicato: https://(tuo-token)@solana.quiknode.pro/...
), ma lo hai copiato male o scaduto, potresti ricevere 403.
connection = new solanaWeb3.Connection("https://api.mainnet-beta.solana.com", "confirmed");
In questo modo puoi aggirare il problema di “Access forbidden” e tornare a utilizzare correttamente le funzionalità di Phantom Wallet e della Solana blockchain.