gagliardetto / solana-go

Go SDK library and RPC client for the Solana Blockchain
Apache License 2.0
943 stars 267 forks source link

How do I transfer tokens #71

Open scottywm opened 2 years ago

scottywm commented 2 years ago

Is there a way that I can use this library to send tokens from one wallet to another?

If so please tell me where.

jxad commented 1 year ago

Old question but could be useful to someone

import (
    "context"
    "fmt"
    "math"
    "strconv"

    "github.com/gagliardetto/solana-go"
    associatedtokenaccount "github.com/gagliardetto/solana-go/programs/associated-token-account"
    "github.com/gagliardetto/solana-go/programs/token"
    "github.com/gagliardetto/solana-go/rpc"
)

func tokenTransfer(sender solana.PrivateKey, receiver solana.PublicKey, tokenMint solana.PublicKey) (solana.Signature, error) {
    httpClient := rpc.New(rpc.DevNet_RPC)
    ixs := []solana.Instruction{}
    tokenAta := solana.PublicKey{}

    //get list of ata associated to the token mint address
    atas, err := httpClient.GetTokenAccountsByOwner(
        context.TODO(),
        receiver,
        &rpc.GetTokenAccountsConfig{
            Mint: &tokenMint,
        },
        &rpc.GetTokenAccountsOpts{})

    //create new ata if none found
    if err != nil || len(atas.Value) == 0 {
        if err != nil {
            panic(err)
        }

        ix, err := associatedtokenaccount.NewCreateInstruction(
            sender.PublicKey(),
            receiver,
            tokenMint).ValidateAndBuild()
        if err != nil {
            panic(err)
        }
        ixs = append(ixs, ix)
        tokenAta = ix.Accounts()[1].PublicKey
    } else {
        //if token atas found i just get the first one
        tokenAta = atas.Value[0].Pubkey
    }

    //transfer instruction
    amount := uint64(10 * math.Pow10(9)) //Sending n10 tokens of a token that has 9 decimals
    fromAccountAtas, err := httpClient.GetTokenAccountsByOwner(
        context.TODO(),
        sender.PublicKey(),
        &rpc.GetTokenAccountsConfig{
            Mint: &tokenMint,
        },
        &rpc.GetTokenAccountsOpts{})
    if err != nil {
        panic(err)
    }
    if len(fromAccountAtas.Value) == 0 {
        panic(fmt.Errorf("sender does not have any tokens"))
    }

    //get sender wallet token ata + check if sender have enough tokens
    fromAccountAta := solana.PublicKey{}
    hasEnoughTokens := true
    for _, ata := range fromAccountAtas.Value {
        hasEnoughTokens = true
        balance, err := httpClient.GetTokenAccountBalance(context.TODO(), ata.Pubkey, rpc.CommitmentFinalized)
        if err != nil {
            continue
        }
        a, err := strconv.ParseUint(balance.Value.Amount, 10, 64)
        if err != nil {
            continue
        }
        if a < amount {
            hasEnoughTokens = false
            continue
        }
        fromAccountAta = ata.Pubkey
        break
    }

    if fromAccountAta == solana.SystemProgramID {
        if !hasEnoughTokens {
            err = fmt.Errorf("sender does not have enough tokens")
        }
        panic(err)
    }

    //transfer instruction
    ix := token.NewTransferInstruction(
        amount,
        fromAccountAta,
        tokenAta,
        sender.PublicKey(),
        []solana.PublicKey{}).Build()
    ixs = append(ixs, ix)

    recentBlockHash, err := httpClient.GetRecentBlockhash(context.TODO(), rpc.CommitmentFinalized)
    if err != nil {
        panic(err)
    }
    tx, err := solana.NewTransaction(
        ixs,
        recentBlockHash.Value.Blockhash,
        solana.TransactionPayer(sender.PublicKey()),
    )
    if err != nil {
        panic(err)
    }

    //sign transaction
    _, err = tx.Sign(
        func(key solana.PublicKey) *solana.PrivateKey {
            if sender.PublicKey().Equals(key) {
                return &sender
            }
            return nil
        },
    )
    if err != nil {
        panic(err)
    }

    //send transaction
    sig, err := httpClient.SendTransaction(
        context.TODO(),
        tx,
    )
    if err != nil {
        panic(err)
    }

    // send transaction and wait for confirmation
    // import confirm "github.com/gagliardetto/solana-go/rpc/sendAndConfirmTransaction"
    // sig, err := confirm.SendAndConfirmTransaction(
    //  context.TODO(),
    //  httpClient,
    //  wsClient,
    //  tx,
    //   )
    return sig, nil
}
A-Lazar-A commented 7 months ago

doesnt work for me i have empty transaction idk why 2AtiEsBtknVgEhNL4Hv8a3hq6Z7GiL4x4hmx64ZVg7CZmkZnXpQPtNX68amYnvhrCkcWwvCDhj4kMnnTyjGUb2Y6