Skip to content

BuildAndSendInvokeTxn

Builds, signs, estimates fees, and sends an invoke transaction to execute contract functions on Starknet. This method handles the complete transaction lifecycle including nonce management, fee estimation, and transaction signing automatically.

Method Signature

func (account *Account) BuildAndSendInvokeTxn(
	ctx context.Context,
	functionCalls []rpc.InvokeFunctionCall,
	opts *TxnOptions,
) (rpc.AddInvokeTransactionResponse, error)

Source: transaction.go

Parameters

  • ctx (context.Context): Context for request cancellation and timeout
  • functionCalls ([]rpc.InvokeFunctionCall): Slice of function calls to execute on target contracts
  • opts (*TxnOptions): Transaction options for fee estimation and execution. Pass nil to use default values

Returns

  • rpc.AddInvokeTransactionResponse: Response containing the transaction hash
  • error: Error if transaction fails

AddInvokeTransactionResponse Structure

Source: types_transaction_response.go

The returned response contains:

type AddInvokeTransactionResponse struct {
	Hash *felt.Felt `json:"transaction_hash"`
}
Fields:
  • Hash (*felt.Felt): Transaction hash of the invoke transaction

Usage Example

package main
 
import (
	"context"
	"fmt"
	"log"
	"math/big"
	"os"
	"time"
 
	"github.com/NethermindEth/juno/core/felt"
	"github.com/NethermindEth/starknet.go/account"
	"github.com/NethermindEth/starknet.go/rpc"
	"github.com/NethermindEth/starknet.go/utils"
	"github.com/joho/godotenv"
)
 
func main() {
	ctx := context.Background()
 
	if err := godotenv.Load(); err != nil {
		log.Fatal("Failed to load .env file:", err)
	}
 
	rpcURL := os.Getenv("STARKNET_RPC_URL")
	if rpcURL == "" {
		log.Fatal("STARKNET_RPC_URL not set in .env file")
	}
 
	provider, err := rpc.NewProvider(ctx, rpcURL)
	if err != nil {
		log.Fatal("Failed to create provider:", err)
	}
 
	accountAddress := os.Getenv("ACCOUNT_ADDRESS")
	publicKey := os.Getenv("ACCOUNT_PUBLIC_KEY")
	privateKey := os.Getenv("ACCOUNT_PRIVATE_KEY")
 
	if accountAddress == "" || publicKey == "" || privateKey == "" {
		log.Fatal("ACCOUNT_ADDRESS, ACCOUNT_PUBLIC_KEY, or ACCOUNT_PRIVATE_KEY not set in .env")
	}
 
	ks := account.NewMemKeystore()
	privKeyBI, ok := new(big.Int).SetString(privateKey, 0)
	if !ok {
		log.Fatal("Failed to parse private key")
	}
	ks.Put(publicKey, privKeyBI)
 
	accountAddressFelt, err := utils.HexToFelt(accountAddress)
	if err != nil {
		log.Fatal("Failed to parse account address:", err)
	}
 
	accnt, err := account.NewAccount(
		provider,
		accountAddressFelt,
		publicKey,
		ks,
		account.CairoV2,
	)
	if err != nil {
		log.Fatal("Failed to create account:", err)
	}
 
	strkContractAddress, err := utils.HexToFelt("0x04718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d")
	if err != nil {
		log.Fatal("Failed to parse STRK contract address:", err)
	}
 
	recipient := accountAddressFelt
 
	amount := new(felt.Felt).SetUint64(1000000000000000)
 
	u256Amount, err := utils.HexToU256Felt(amount.String())
	if err != nil {
		log.Fatal("Failed to convert amount to U256:", err)
	}
 
	transferCall := rpc.InvokeFunctionCall{
		ContractAddress: strkContractAddress,
		FunctionName:    "transfer",
		CallData:        append([]*felt.Felt{recipient}, u256Amount...),
	}
 
	opts := &account.TxnOptions{
		FeeMultiplier: 1.5,
		TipMultiplier: 1.0,
	}
 
	response, err := accnt.BuildAndSendInvokeTxn(
		ctx,
		[]rpc.InvokeFunctionCall{transferCall},
		opts,
	)
	if err != nil {
		log.Fatal("Failed to send invoke transaction:", err)
	}
 
	fmt.Printf("Invoke Transaction Successful:\n")
	fmt.Printf("Transaction Hash: %s\n", response.Hash.String())
 
	txReceipt, err := accnt.WaitForTransactionReceipt(
		ctx,
		response.Hash,
		3*time.Second,
	)
	if err != nil {
		log.Fatal("Failed to get transaction receipt:", err)
	}
 
	fmt.Printf("Block Number:     %d\n", txReceipt.BlockNumber)
	fmt.Printf("Execution Status: %s\n", txReceipt.ExecutionStatus)
	fmt.Printf("Finality Status:  %s\n", txReceipt.FinalityStatus)
}

Error Handling

response, err := acc.BuildAndSendInvokeTxn(ctx, functionCalls, opts)
if err != nil {
	switch {
	case errors.Is(err, rpc.ErrInsufficientAccountBalance):
		log.Println("Insufficient balance to pay transaction fees")
	case errors.Is(err, rpc.ErrContractNotFound):
		log.Println("Target contract not found")
	case errors.Is(err, rpc.ErrInvalidCallData):
		log.Println("Invalid function call data")
	default:
		log.Printf("Failed to send invoke transaction: %v", err)
	}
	return
}
 
fmt.Printf("Transaction sent successfully: %s\n", response.Hash.String())

Common Use Cases

  • Execute contract functions like token transfers, approvals, or swaps
  • Interact with DeFi protocols (staking, lending, trading)
  • Update contract state by calling state-changing functions
  • Batch multiple function calls into a single transaction for atomic execution
  • Automatically handles nonce management, fee estimation, and signing in one method call