gagliardetto / solana-go

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

send transaction success,but can not locate transaction on blockchain #212

Closed BillInUK closed 5 months ago

BillInUK commented 6 months ago

Hello, I have encountered a bug that occurs probabilistically. I want to send a transaction containing multiple instructions to the blockchain. Although I can successfully call

txSig, err := rpcClient.SendTransaction(context.Background(), tx)

and get a transaction ID, I sometimes find that the transaction is dropped by the Solana cluster when I search for it on the blockchain. What could be the reason?

The instructions in my transaction include:

Here is my code:

func TestMultiInstructionTransfer(t *testing.T) {
    rpcClient := rpc.New(RpcUrl)

    rewardPrivKey := solana.MustPrivateKeyFromBase58(MainPrivate)
    alicePrivateKey := solana.MustPrivateKeyFromBase58(AlicePrivate)
    rewardPubKey := rewardPrivKey.PublicKey()
    alicePubKey := solana.MustPublicKeyFromBase58(AliceNativePubKey)
    bobPubKey := solana.MustPublicKeyFromBase58(BobNativePubKey)
    dexPubKey, _ := solana.PublicKeyFromBase58(DexNativePubKey)

    // get token account
    tokenMintAccount := solana.MustPublicKeyFromBase58(TokenMintAddress)
    rewardTokenAccount, err := GetSPLTokenAccountByNative(rpcClient, rewardPubKey, tokenMintAccount)
    if err != nil {
        panic(err)
    }
    fmt.Printf("get reward token account %v \n", rewardTokenAccount)

    aliceTokenAccount, err := GetSPLTokenAccountByNative(rpcClient, alicePubKey, tokenMintAccount)
    if err != nil {
        panic(err)
    }
    fmt.Printf("get to token account of alice %v \n", aliceTokenAccount)

    bobTokenAccount, err := GetSPLTokenAccountByNative(rpcClient, bobPubKey, tokenMintAccount)
    if err != nil {
        panic(err)
    }
    fmt.Printf("get to token account of bob %v \n", bobTokenAccount)

    amount := uint64(1100000)
    rewardAmount := uint64(1100000) * 2
    // ComputeBudget111111111111111111111111111111
    computeBudgetPriceInst := computebudget.NewSetComputeUnitPriceInstructionBuilder().
        SetMicroLamports(100000).
        Build()

    // ComputeBudget111111111111111111111111111111
    computeBudgetLimitInst := computebudget.NewSetComputeUnitLimitInstructionBuilder().
        SetUnits(600000).
        Build()

    // 添加多条指令
    // 1.transfer token to bob
    transferInst := token.NewTransferCheckedInstructionBuilder().
        SetAmount(amount).
        SetDecimals(6).
        SetSourceAccount(*aliceTokenAccount).
        SetMintAccount(tokenMintAccount).
        SetDestinationAccount(*bobTokenAccount).
        SetOwnerAccount(alicePubKey).
        Build()

    // 2.reward token to alice
    rewardInst := token.NewTransferCheckedInstructionBuilder().
        SetAmount(rewardAmount).
        SetDecimals(6).
        SetSourceAccount(*rewardTokenAccount).
        SetMintAccount(tokenMintAccount).
        SetDestinationAccount(*aliceTokenAccount).
        SetOwnerAccount(rewardPubKey).
        Build()

    // 3.send sol to dex
    toDexInst := system.NewTransferInstructionBuilder().
        SetLamports(2250000).
        SetFundingAccount(alicePubKey).
        SetRecipientAccount(dexPubKey).
        Build()

    // get recent block hash
    recent, err := rpcClient.GetRecentBlockhash(context.Background(), rpc.CommitmentFinalized)
    if err != nil {
        panic(err)
    }
    fmt.Printf("recent block hash is %v\n", recent.Value.Blockhash)

    // construct a transaction to estimate gas fee
    tx0, err := solana.NewTransaction(
        []solana.Instruction{
            computeBudgetPriceInst,
            computeBudgetLimitInst,
            transferInst,
            rewardInst,
            toDexInst,
        },
        recent.Value.Blockhash,
        solana.TransactionPayer(alicePubKey),
    )
    if err != nil {
        panic(err)
    }

    messageContent0, err := tx0.Message.MarshalBinary()
    if err != nil {
        panic(err)
    }
    base64EncodeMsg := base64.StdEncoding.EncodeToString(messageContent0)

    feeResult, err := rpcClient.GetFeeForMessage(context.Background(), base64EncodeMsg, rpc.CommitmentConfirmed)
    if err != nil {
        panic(err)
    }
    fmt.Printf("get message fee %v\n", *feeResult.Value)

    // send gas fee & 2 to dex
    toDexInst = system.NewTransferInstructionBuilder().
        SetLamports(*feeResult.Value * 2).
        SetFundingAccount(alicePubKey).
        SetRecipientAccount(dexPubKey).
        Build()

    // construct the final transaction to send to blockchain
    tx, err := solana.NewTransaction(
        []solana.Instruction{
            computeBudgetPriceInst,
            computeBudgetLimitInst,
            transferInst,
            rewardInst,
            toDexInst,
        },
        recent.Value.Blockhash,
        solana.TransactionPayer(alicePubKey),
    )
    if err != nil {
        panic(err)
    }

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

    // get transaction sig and print it
    txSig, err := rpcClient.SendTransaction(context.Background(), tx)
    fmt.Printf("https://solscan.io/tx/%s\n", txSig)

}
BillInUK commented 6 months ago

txSig, err := rpcClient.SendTransaction(context.Background(), tx)

always return success,but somtimes,transaction can not located at blockchain.

metarsit commented 5 months ago

I personally doing this: https://docs.triton.one/chains/solana/sending-txs#tl-dr

works fine for me so far except fee calculation needs to be precise.

gagliardetto commented 5 months ago

When you send a transaction to an RPC, there are no guarantees that it will land on-chain.

There are many factors that influence your success chances.