简体   繁体   中英

How to properly transfer Solana SOL using web3js via Phantom

Am working with Solana Blockchain. Am trying to transfer Solana SOL via Phantom. To this effect I used the code below which was leveraged in stackoverflow. source link

I already have Phantom installed in my chrome browser .

when I run the code, It displays error

Uncaught (in promise) TypeError: Cannot read properties of null (reading 'toString')

I think its this line of code that is causing the error above

  console.log("Public key of the emitter: ",provider.publicKey.toString());

Here is the Code

import * as web3 from '@solana/web3.js';
  import * as splToken from '@solana/spl-token';
  
  const getProvider = async () => {
    if ("solana" in window) {
      const provider = window.solana;
      if (provider.isPhantom) {
        console.log("Is Phantom installed?  ", provider.isPhantom);
        return provider;
      }
    } else {
      window.open("https://www.phantom.app/", "_blank");
    }
  };


  async function transferSOL() {
    // Detecing and storing the phantom wallet of the user (creator in this case)
    var provider = await getProvider();
    console.log("Public key of the emitter: ",provider.publicKey.toString());

    // Establishing connection
    var connection = new web3.Connection(
      web3.clusterApiUrl('devnet'),
    );

    // I have hardcoded my secondary wallet address here. You can take this address either from user input or your DB or wherever
    var recieverWallet = new web3.PublicKey("CkiKLEa9eSEoG6CoTSuaahsF2WqNgArnvoCSbNZjJ7BQ");

    // Airdrop some SOL to the sender's wallet, so that it can handle the txn fee
    var airdropSignature = await connection.requestAirdrop(
      provider.publicKey,
      web3.LAMPORTS_PER_SOL,
    );

    // Confirming that the airdrop went through
    await connection.confirmTransaction(airdropSignature);
    console.log("Airdropped");

    var transaction = new web3.Transaction().add(
      web3.SystemProgram.transfer({
        fromPubkey: provider.publicKey,
        toPubkey: recieverWallet,
        lamports: web3.LAMPORTS_PER_SOL //Investing 1 SOL. Remember 1 Lamport = 10^-9 SOL.
      }),
    );

    // Setting the variables for the transaction
    transaction.feePayer = await provider.publicKey;
    let blockhashObj = await connection.getRecentBlockhash();
    transaction.recentBlockhash = await blockhashObj.blockhash;

    // Transaction constructor initialized successfully
    if(transaction) {
      console.log("Txn created successfully");
    }
    
    // Request creator to sign the transaction (allow the transaction)
    let signed = await provider.signTransaction(transaction);
    // The signature is generated
    let signature = await connection.sendRawTransaction(signed.serialize());
    // Confirm whether the transaction went through or not
    await connection.confirmTransaction(signature);

    //Signature chhap diya idhar
    console.log("Signature: ", signature);
  }

I'm not sure if this is the best solution going ahead, but yours is a problem of the persistence of the phantom wallet after the user signs in. You'll have to go for front-end heavy solution for this. One of which is:

  1. Assuming you are using React, use context APIs to persist the data about the wallet. Here is a rough guideline on how to do that by creating a file under the context folder in your project root:

 import React, { createContext, useState} from "react"; export const WalletDataContext=createContext(); export const WalletDataContextProvider=(props)=>{ const [publicKey,setPublicKey]=useState(null); const [wallet,setWallet]=useState(null); return ( <WalletDataContext.Provider value={{publicKey,setPublicKey,wallet,setWallet}} > {props.children} </WalletDataContext.Provider> ) }

  1. Create a connectWallet function, something like this:

     //import {WalletDataContext} //import other stuff: const {setPublicKey,setWallet}=useContext(WalletDataContext) const connectWallet = async() { const provider = await getProvider(); if(provider) { await provider.connect(); let publicKey = ""; provider.on("connect", async () => { setWallet(provider); publicKey = provider.pubicKey.toString(); setPublicKey(publicKey); /* // more things that you would like to do here */ }); } }

  2. Make the following changes in your transferSOL function:

 async function transferSOL() { //Changes are only here, in the beginning const phantomProvider = wallet; if(!phantomProvider){ //Urge the user to sign in(connect) again } const pubKey = await phantomProvider.publicKey; console.log("Public Key: ", pubKey); // Establishing connection var connection = new web3.Connection( web3.clusterApiUrl('devnet'), ); // I have hardcoded my secondary wallet address here. You can take this address either from user input or your DB or wherever var recieverWallet = new web3.PublicKey("CkiKLEa9eSEoG6CoTSuaahsF2WqNgArnvoCSbNZjJ7BQ"); // Airdrop some SOL to the sender's wallet, so that it can handle the txn fee var airdropSignature = await connection.requestAirdrop( provider.publicKey, web3.LAMPORTS_PER_SOL, ); // Confirming that the airdrop went through await connection.confirmTransaction(airdropSignature); console.log("Airdropped"); var transaction = new web3.Transaction().add( web3.SystemProgram.transfer({ fromPubkey: provider.publicKey, toPubkey: recieverWallet, lamports: web3.LAMPORTS_PER_SOL //Investing 1 SOL. Remember 1 Lamport = 10^-9 SOL. }), ); // Setting the variables for the transaction transaction.feePayer = await provider.publicKey; let blockhashObj = await connection.getRecentBlockhash(); transaction.recentBlockhash = await blockhashObj.blockhash; // Transaction constructor initialized successfully if(transaction) { console.log("Txn created successfully"); } // Request creator to sign the transaction (allow the transaction) let signed = await provider.signTransaction(transaction); // The signature is generated let signature = await connection.sendRawTransaction(signed.serialize()); // Confirm whether the transaction went through or not await connection.confirmTransaction(signature); //Signature or the txn hash console.log("Signature: ", signature); }

You need to connect to the wallet. That part is missing

  const getProvider = async () => {
    if ("solana" in window) {

      // opens wallet to connect to
      await window.solana.connect(); 

      const provider = window.solana;
      if (provider.isPhantom) {
        console.log("Is Phantom installed?  ", provider.isPhantom);
        return provider;
      }
    } else {
      window.open("https://www.phantom.app/", "_blank");
    }
  };

The phantom team has an example of transferring sol from the user's wallet to another address: https://codesandbox.io/s/github/phantom-labs/sandbox?file=/src/App.tsx

Note:

  1. Check which wallet is active in phantom and understand where you are sending from to whom
  2. Airdrop yourself some sol on devnet
  3. Make sure you are using PublicKey objects not just a string

TLDR:

import { Connection, PublicKey, clusterApiUrl } from "@solana/web3.js";


//3
const createTransaction = async(instructions) => {
    const anyTransaction = new web3.Transaction().add(instructions);
    anyTransaction.feePayer = getProvider().publicKey;
    console.log("Getting Recent Blockhash");
    anyTransaction.recentBlockhash = (
      await getConnectionProvider().connection.getRecentBlockhash()
    ).blockhash;
    return anyTransaction;
  }

//2
  const createTransferTransaction = async (from, to, amount) => {
    return createTransaction(
      web3.SystemProgram.transfer({
        fromPubkey: from,
        toPubkey: to,
        lamports: 100000 * amount,
    }));
  }

//1 The interesting part
//from and to are pubkey objects (not strings!)
  const sendTransaction = async(from, to, amount) => {
    try {
      console.log(`sending ${amount} from: ${from}, to: ${to}`);
      let { signature } = await 
      getProvider().signAndSendTransaction(await createTransferTransaction(from, to, amount));
      console.log("Submitted transaction " + signature + ", awaiting confirmation");
      await connection.confirmTransaction(signature);
      console.log("Transaction " + signature + " confirmed");
    } catch (err) {
      console.warn(err);
      console.error("Error: " + JSON.stringify(err));
    }
  }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM