blocto / solana-go-sdk

Solana Golang SDK
https://blocto.github.io/solana-go-sdk/
MIT License
349 stars 86 forks source link

Add new Instructions to already existing transactions #175

Open Moses-Alero opened 1 week ago

Moses-Alero commented 1 week ago

I would like to know if it is possible to add new instructions to pre-existing transactions similar to transaction.add() in the @solana/web3.js package.

I tried to do something to make it work using the example below

var feePayer, _ = types.AccountFromBase58("4TMFNY9ntAn3CHzguSAvDNLPRoQTaK3sWbQQXdDXaE6KWRBLufGL6PJdsD2koiEe3gGmMdRK3aAw7sikGNksHJrN")

// 9aE476sH92Vz7DMPyq5WLPkrKWivxeuTKEFKd2sZZcde
var alice, _ = types.AccountFromBase58("4voSPg3tYuWbKzimpQK9EbXHmuyy5fUrtXvpLDMLkmY6TRncaTHAKGD8jUg3maB5Jbrd9CkQg4qjJMyN6sQvnEF2")

func main() {
    c := client.NewClient(rpc.DevnetRPCEndpoint)

    // to fetch recent blockhash
    recentBlockhashResponse, err := c.GetLatestBlockhash(context.Background())
    if err != nil {
        log.Fatalf("failed to get recent blockhash, err: %v", err)
    }

    // create a transfer tx
    tx, err := types.NewTransaction(types.NewTransactionParam{
        Signers: []types.Account{feePayer, alice},
        Message: types.NewMessage(types.NewMessageParam{
            FeePayer:        feePayer.PublicKey,
            RecentBlockhash: recentBlockhashResponse.Blockhash,
            Instructions: []types.Instruction{
                system.Transfer(system.TransferParam{
                    From:   alice.PublicKey,
                    To:     common.PublicKeyFromString("2xNweLHLqrbx4zo1waDvgWJHgsUpPj8Y8icbAFeR4a8i"),
                    Amount: 1e8, // 0.1 SOL
                }),
            },
        }),
    })
    if err != nil {
        log.Fatalf("failed to new a transaction, err: %v", err)
    }
    //new instruction
    newInstruction := system.Transfer(system.TransferParam{
        From:   alice.PublicKey,
        To:     common.PublicKeyFromString("2xNweLHLqrbx4zo1waDvgWJHgsUpPj8Y8icbAFeR4a8i"),
        Amount: 1e8, // 0.1 SOL
    })

    //add new instruction to pre-existing Transaction
    msg := types.NewMessage(types.NewMessageParam{
        Instructions: []types.Instruction{newInstruction},
    })
    msgByte, _ := msg.Serialize()

    newMsg, _ := types.MessageDeserialize(msgByte)

    tx.Message.Instructions = append(tx.Message.Instructions, newMsg.Instructions...)

        //get transaction hash
    txhash, err := c.SendTransaction(context.Background(), tx)
    if err != nil {
        log.Fatalf("failed to send tx, err: %v", err)
    }

    log.Println("txhash:", txhash)
}

I tried to make it work using the example above. It returns tx hash but the second instruction isn't added to the transaction when viewed on the explorar Is there a way to do this?

yihau commented 1 week ago

your example might not work. tx.Message.Instructions is compiled instruction. you need the same account list to generate a correct compiled instruction. we can do something similar to transaction.add() by this SDK but it's a bit tricky atm. could you provide more details about your scenario? does it help if you pass the instructions instead then compose them into a transaction in the finial step?

Moses-Alero commented 1 week ago

@yihau my current use case requires the transaction to be passed in as an argument to a function then an additional instruction which is the new instruction is then added to the transaction before it is then used. Here's a simple example

func CreatePostRes(tx types.Transaction, ref common.PublicKey){
     //do simple check
     if len(tx.Message.Instruction) < 1 {
        return
     }
     instruction := fetchNewInstruction(ref)

     //append instruction to tx

     tx = AddIxToTransaction(tx, instruction) //we do the tricky stuff here

   ...
}

Something like the above.

If only there was a way to decompile instructions then compile them back with the data intact. I think that might be helpful too. you said that doing something similar to transaction.add() is possible but tricky. I'm pretty sure I can handle tricky :)