# Quick start
In this article we will show you how to send bitcoin as cheaply as possible, to be confirmed by a date/time of your choosing, instead of choosing a fee. If you already have some bitcoin in hand, you can try it yourself. It takes less than 10 minutes - if you have any issues, feel free to get in touch.
The code in this tutorial can also be found in this github repository.
# Setting up your environment
If you haven't python installed, install it. We will use the user-friendly python library bit along with a wrapper for the bitpost interface, to make things easy. Install the dependencies with, pip install bit bitpost numpy
.
# Setting up your wallet
Let's start by creating a new private key and funding it.
import bit
key = bit.Key.from_bytes(b'REPLACE_WITH_YOUR_RANDOM_STRING')
print(key.segwit_address)
Tip: create a random key
You can print a cryptographically safe private key in python like this:
import os
import bit
random_bytes = os.urandom(16)
private_key_WIF = bit.Key.from_bytes(random_bytes).to_wif()
print(private_key_WIF)
You can then import the private key like this: key = bit.Key(private_key_WIF)
Send a small amount of bitcoin to it - 30,000 satoshis should be enough. On testnet, you can use this or that faucet. If can't get tBTC on neither faucet, asks us to send you. You don't need to wait for one confirmation to run the following code.
# Choose your payment
# Recipient, amount and maximum fee
Like any payment, you have to choose the recipient and the amount you want to pay. Then, instead of choosing the actual transaction fee, you simply have to choose a maximum you are willing to pay. This value represents the worst-case scenario and your transaction fee will likely be lower than that.
destination_address = '1BitcoinEaterAddressDontSendf59kuE'
sats_to_send = 566
max_dollar_fee = 3
# Scheduling your transaction
The main parameter that Bitpost uses to control the transaction fee of your transaction is a parameter we call target, which indicates a deadline for the transaction to be included in a block (first confirmation). We also provide the optional parameter delay, which allows you to delay the first broadcast to a time of your choosing.
This way, you can schedule, for example, the payment of your rent between the 25th and the 30th of the next month like shown below.
import datetime as dt
target = int(dt.datetime(2020, 7, 30, 0, 0).timestamp())
delay = int(dt.datetime(2020, 7, 25, 0, 0).timestamp())
# Preparing raw transactions
We will now sign several transactions that are identical except for the fee they pay. These transactions have to opt-in for replace-by-fee (RBF) for Bitpost to be able to adjust the fee.
# Choosing a set of feerates
We start by choosing the feerates of the transactions that will be relayed to Bitpost. You can opt for one of the two alternatives below after setting the user's maximum feerate. This step is discussed with more detail in this article.
Below is an approximate convertion between the user's choice for the maximum fee in fiat value and the maximum feerate. For a better approximation, use the results of your coin selection algorithm.
MAX_FEE_IN_SATS = bit.network.currency_to_satoshi(max_dollar_fee, 'usd')
HEURISTIC_TX_SIZE = 10 + 34*2 + 90 # 2 outputs and one P2SH-P2WKH input
USER_MAX_FEERATE = MAX_FEE_IN_SATS/HEURISTIC_TX_SIZE
# With the /feerateset endpoint (recommended)
This endpoint provides a set of feerates that can be used for the transactions relayed to bitpost. These feerates consider the current fee environment and use that information to adjust the spacing between the feerates. They are, therefore, not linearly space like in the alternative below.
from bitpost.interface_for_bit import BitpostInterfaceForBit
bitpost_interface = BitpostInterfaceForBit()
feerates = BitpostInterfaceForBit.get_feerates(USER_MAX_FEERATE, size=50, target=target)
# Without the /feerateset endpoint
You can create a linearly spaced array of feerates with the code below. This approach works well in most cases but may lead to a poor choice of feerates that limit our ability to adjust the fee efficiently.
import numpy as np
# Adjust maximum feerate to avoid unnecessarily high feerates
MAX_FEERATE = int(min(USER_MAX_FEERATE, max(20, bit.network.get_fee(fast=True) * 3)))
DEFAULT_NUMBER_TXS = 50 # create 50 transactions with different fees
feerates = [int(feerate) for feerate in np.arange(1, MAX_FEERATE + 0.001, step=max(1, (MAX_FEERATE - 1) / (DEFAULT_NUMBER_TXS - 1)))]
# Signing raw transactions
For each feerate that we chose before, we sign one transaction that pays sats_to_send
to destination_address
. These transactions spend from the same inputs and therefore only one can be mined. It's not unsafe to send these all these transactions to a third party - the worst thing that can happen is for the transaction with USER_MAX_FEERATE
to be mined or no transaction at all. The last scenario can easily be avoided by the user, if he wants to (see next section).
# select inputs
unspents = key.get_unspents()
selected_unspents, _ = bit.transaction.select_coins(sats_to_send, MAX_FEERATE, [34], min_change=566, unspents=unspents)
raw_signed_txs = []
for feerate in feerates:
tx = key.create_transaction([(destination_address, sats_to_send, 'satoshi')], feerate, combine=True, replace_by_fee=True, unspents=selected_unspents)
raw_signed_txs.append(tx)
# Controling the initial broadcast
By default, Bitpost will choose when and which transaction to broadcast first. If it's a low priority transaction and fees are high, it might take a long time for Bitpost to make the first broadcast. If you want to prevent this and force Bitpost to broadcast one transaction as soon as it receives the request, set delay=0
(the default being delay=1
). Alternatively, if you want Bitpost to (re)broadcast a specific transaction (which you might have broadcasted yourself), you can pass the txid of the broadcasted transaction to the parameter broadcast.
# Sending your request to Bitpost
You can send the request for the automatic fee adjustment of your transaction by calling our API directly or using an interface class if available for the language you are using.
Below we use the python interface:
request = bitpost_interface.create_bitpost_request(raw_signed_txs, target, delay=0)
request.send_request()
Our answer is structured in JSend.
{
"data": {
"id": "R9RJJBLz8Kd",
"url": "https://bitpost.co/explorer?query=R9RJJBLz8Kd",
"devurl": "https://api.bitpost.co/request?id=R9RJJBLz8Kd"
},
"status": "success"
}
# Tracking your request
If you open the URL contained in the answer, you can follow all the broadcasts and other events related to your fee adjustment request in our explorer. You can bump the fee yourself - although you don't need to - and it will show (with up to 5 min delay).
If you forget the URL of the request, you can query instead by a txid
that belongs to the request. It doesn't necessarily need to be a broadcasted transaction or the one currently present in the mempool.
# Further reading
- Check some common errors that might occur when you send your request to Bitpost.
- Check how to create child requests with Bitpost.