【问题标题】:How to properly transfer Solana SOL using web3js via Phantom如何通过 Phantom 使用 web3js 正确传输 Solana SOL
【发布时间】:2021-12-01 10:19:20
【问题描述】:

我正在使用 Solana 区块链。我正在尝试通过 Phantom 转移 Solana SOL。为此,我使用了下面在 stackoverflow 中使用的代码。 source link

我已经在我的 chrome 浏览器中安装了 Phantom。

当我运行代码时,它显示错误

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

我认为是这行代码导致了上面的错误

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

这是代码

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);
  }

【问题讨论】:

  • 不幸的是,该示例不包括连接到外部钱包,例如 Phantom。我建议查看 solana 钱包适配器库,以便轻松连接到各种钱包。它包含一些您在集成应用程序时可以遵循的示例:github.com/solana-labs/wallet-adapter

标签: javascript web3 solana


【解决方案1】:

您需要连接到钱包。那部分不见了

  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");
    }
  };

【讨论】:

    【解决方案2】:

    我不确定这是否是最好的解决方案,但您的问题是用户登录后虚拟钱包的持久性问题。您必须为此寻求前端重型解决方案。其中之一是:

    1. 假设您正在使用 React,请使用上下文 API 来保存有关钱包的数据。以下是关于如何通过在项目根目录的 context 文件夹下创建文件的粗略指南:

    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. 创建一个 connectWallet 函数,如下所示:

        
        //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. 在您的 transferSOL 函数中进行以下更改:

    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);
      }

    【讨论】:

      【解决方案3】:

      幻影团队有一个将sol从用户钱包转移到另一个地址的例子:https://codesandbox.io/s/github/phantom-labs/sandbox?file=/src/App.tsx

      注意:

      1. 检查哪个钱包在虚拟中处于活动状态,并了解您从哪里发送给谁
      2. 在 devnet 上给自己空投一些 sol
      3. 确保您使用的 PublicKey 对象不仅仅是一个字符串

      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));
          }
        }
      

      【讨论】:

        猜你喜欢
        • 2021-09-10
        • 2021-12-05
        • 2022-07-28
        • 1970-01-01
        • 2022-06-15
        • 2021-09-15
        • 1970-01-01
        • 2021-12-03
        • 2021-12-14
        相关资源
        最近更新 更多