> ## Content Index
> Fetch the complete content index at: https://sudosecurity.org/llms.txt
> Use this file to discover other available public pages before exploring further.

# How to Make a Bitcoin Transaction in Go Lang
- URL: https://sudosecurity.org/bitcoin-transaction-in-go/
- Published: 2023-04-10T19:01:00.000Z
- Updated: 2026-07-17T10:53:14.000Z
- Author: Traven
- Tags: Software Development

**Bitcoin** is all the rage in recent years. So does **Golang** \- a programming language developed by Google. Today I am going to instruct you how to make a simple Bitcoin transaction with **Golang**. Hopefully, you will be able to transfer Bitcoin without using any **DApps** after this tutorial.

The tutorial requires you to have some basic understanding of **Bitcoin** as well as **Golang**. I recommend you should visit these two sites below if you are unfamiliar with the aforementioned topics:

 \+ **Bitcoin**: [https://developer.bitcoin.org/reference/](https://developer.bitcoin.org/reference/?ref=co.vpsnode.org)

 \+ **Golang**: [https://go.dev/doc/tutorial/getting-started](https://go.dev/doc/tutorial/getting-started?ref=co.vpsnode.org)

To begin with, you need to install two libraries for working with Bitcoin in Golang.

 \+ **btcutil**: [https://github.com/btcsuite/btcutil](https://github.com/btcsuite/btcutil?ref=co.vpsnode.org)

 \+ **btcd**: [https://github.com/btcsuite/btcd](https://github.com/btcsuite/btcd?ref=co.vpsnode.org)

 You can simply get these libraries by executing the following commands:

```
go get github.com/btcsuite/btcutil
go get github.com/btcsuite/btcd

```

**Step 1**: You need to initialize required variables for your bitcoin transaction inputs. Here's the example inputs:

```
var fromAddress string = "XXXXXXXXXXXXXXXXXXXX"
var fromAddressPrivateKey string = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
var toAddress string = "XXXXXXXXXXXXXXXXXXXXXX"
var sentAmount int64 = 10000000 // in Satoshi unit 

```

In addition, you need to specify the Bitcoin network you will broadcast your transaction. There are two types of Bitcoin network: **mainnet** and **testnet**. In this tutorial, I will set it to the **testnet** network since Bitcoin doesn't hold any value in this environment.

```
var network *chaincfg.Params = &chaincfg.TestNet3Params

```

As for the fee rate, you can refer to various sites to get this data. For this tutorial, I will get the fee rate from [**mempool**](https://mempool.space/testnet?ref=co.vpsnode.org). Make sure that you get the rate in the correct network and convert it into Satoshi unit.

```
var feeRate int = 100000 // in Satoshi unit

```

**Step 2**: You will create a list of transaction input from your **UTXO** (unspent transaction output) from previous transactions. To get the **UTXO**, you can use this endpoint from the [mempool](https://mempool.space/docs/api/rest?ref=co.vpsnode.org#get-address-utxo). Here's the example:

```
https://mempool.space/api/address/1KFHE7w8BhaENAswwryaoccDb6qcT6DbYY/utxo

```

After you get the response from **mempool**, you will convert it into the following struct:

```
type UTXO struct {
 TxID   string `json:"txid"`
 Vout   uint32 `json:"vout"`
 Status bool   `json:"status"`
 Value  int64  `json:"value"` // value is in Satoshi unit
}

```

Here's the example array of UTXO fetched from **mempool**.

```
var UTXO = []UTXO{
		{
			TxID:   "XXXXXXXXXXXXXXX",
			Vout:   300,
			Status: true,
		},
		{
			TxID:   "XXXXXXXXXXXXXXX",
			Vout:   100,
			Status: false,
		},
}

```

Now, you will create a variable - **txMsg** that contains the tx inputs and outputs for your transaction. 

```
var txMsg *wire.MsgTx = wire.NewMsgTx(wire.TxVersion)

```

Now, you will loop through the **UTXO** array to add the required tx inputs into **txMsg.** You will need to create a variable - **requiredTxInAmount** to keep track of the total input amount. The iteration will stop when **requiredTxInAmount** is greater than **sentAmount**. Besides, you will need to skip the item in **UTXO** whose status is false.

```
var requiredTxInAmount int64

for index, v := range UTXO {
		if !v.Status || requiredTxInAmount > sentAmount {
			continue
		}

		chainHash, _ := chainhash.NewHashFromStr(v.TxID)
		outPoint := wire.NewOutPoint(chainHash, uint32(index))
		txIn := wire.NewTxIn(outPoint, nil, nil)
		txMsg.AddTxIn(txIn)
		requiredTxInAmount += v.Value
}

```

**Step 3**: This is a tricky step. The way Bitcoin calculates the fee is based on two factors: fee rate, the size of transaction message. Regarding the latter, you will need the length of the tx inputs and outputs. In this tutorial, we will set the tx output length to 2 by default. Here's the formula to calculate the transaction size:

```
txSize = txInputNo*180 + txOutputNo*34 + 10

```

Once the tx size is calculated, we will determine the transaction fee. If the **fee** and the **sentAmount** is greater than **requiredTxInAmount**, you will need to add more item from the **UTXO** array.

```
var fee = txInputSize * feeRate

```

**Step 4**: As mentioned earlier, you will need to set two transaction outputs. One is for the **toAddress** and the other is for **fromAddress**. I guess this might get you feel confused. The reason why we need to create an output for the sender's address is because we need to transfer the remaining amount from **requiredTxInAmount** back to the sender's. Otherwise, this amount will be considered as transaction fee.

```
// For recipient
toAddressScript, _ := GetPayToAddrScript(toAddress, network)
txMsg.AddTxOut(wire.NewTxOut(sentAmount, toAddressScript))

// For sender
remainingUTX0 := requiredTxInAmount - int64(fee) - sentAmount
fromAddrScript, _ := GetPayToAddrScript(fromAddress, network)
txMsg.AddTxOut(wire.NewTxOut(remainingUTX0, fromAddrScript))

```

**Step 5**: You are going to validate the **txMsg** by signing it with your private key in this step. You will need to convert the string-typed private key into the required format due to the library's requirement.

```
// Process private key
privateKeyBytes, err := hex.DecodeString(fromAddressPrivateKey)
	if err != nil {
		return
	}
privateKey, _ := btcec.PrivKeyFromBytes(btcec.S256(), privateKeyBytes)

for i, v := range txMsg.TxIn {
		sigScript, _ := txscript.SignatureScript(
			txMsg,               // The tx to be signed.
			i,                   // The index of the txin the signature is for.
			fromAddrScript,      // The other half of the script from the PubKeyHash.
			txscript.SigHashAll, // The signature flags that indicate what the sig covers.
			privateKey,          // The key to generate the signature with.
			false)               // The compress sig flag. This saves space on the blockchain.

		v.SignatureScript = sigScript
}

```

**Step 6**: The **txMsg** will be executed by the installed Bitcoin library. If succeed, you will convert it into the raw transaction string. To broadcast the transaction into Bitcoin network, you will need a third-party site or a Bitcoin node. In this tutorial, we will use [mempool ](https://mempool.space/docs/api/rest?ref=co.vpsnode.org#post-transaction)again to do so. 

```
flags := txscript.StandardVerifyFlags
vm, err := txscript.NewEngine(fromAddrScript, txMsg, len(txMsg.TxIn), flags, nil, nil, requiredTxInAmount)
if err != nil {
	return
}
vm.Execute()

// Convert raw transaction into hex string
buf := bytes.NewBuffer(make([]byte, 0, txMsg.SerializeSize()))
txMsg.Serialize(buf)
rawTxHex := hex.EncodeToString(buf.Bytes())
fmt.Println(rawTxHex)

```

Once the transaction is broadcast, you can check its details by using either the **fromAddress** or **toAddress** on any block explorer site. Bear in mind that you need to check the transaction details in the Bitcoin network you broadcast the transaction.

In summary, I hope you can succeed in making a transaction in Golang with your own computer. By doing so, you can feel assured that your transaction will not be corrupted like in DApps.