How to send a Bitcoin blockchain transaction from a virtual account

When you work with Tatum Virtual Accounts, you can perform instant transactions between virtual accounts which are not written to the underlying blockchain. But every once and a while, you need to synchronize some of the transactions to the blockchain. In this case, you have to perform a virtual account-to-blockchain transaction. As a prerequisite, you must have a virtual account with credited blockchain transactions available.

pageHow to automatically scan blockchain addresses and set up webhook notifications

This type of transaction consists of 3 steps:

  • Create a withdrawal transaction - this will perform a virtual account transaction from the source account. It will debit the amount from the source account.

  • Perform a blockchain transaction - in this step, the crypto assets are sent to the recipient's address. The source address of the blockchain transaction can be any blockchain address from your blockchain wallet.

  • Complete the withdrawal transaction - mark the withdrawal as successful and store the transaction ID of the blockchain transaction to the withdrawal operation. This step must be completed; otherwise, there will be inconsistencies within the virtual account state.

All of these actions can be performed as one API call for a specific blockchain. We will cover Bitcoin in this section, but it works similarly in others.

Blockchain transactions are signed using a private key via API, which is not a secure way of signing transactions. Your private keys and mnemonics should never leave your security perimeter. To correctly and securely sign a transaction, you can use Tatum CLI from the command line, a specific language library like Tatum JS, the local middleware API, or our complex key management system, Tatum KMS.

Sending Bitcoin from a virtual account to the blockchain

This operation will withdraw BTC from a virtual account to a blockchain address.

The required parameters are the virtual account's identifier, information about the blockchain wallet, recipient blockchain address, amount to be sent, and blockchain fee to be paid.

import {sendBitcoinOffchainTransaction} from '@tatumio/tatum';
/**
 * Send Bitcoin from Tatum account to address.
 * This will create Tatum internal withdrawal request with ID.
 * @param body - request body with data filter - https://apidoc.tatum.io/tag/Blockchain-operations/#operation/BtcTransfer
 * @param testnet - true if testnet, false if mainnet
 */
const body = {
  senderAccountId: "5fbaca3001421166273b3779",
  address: "mpTwPdF8up9kidgcAStriUPwRdnE9MRAg7",
  amount: "0.00195",
  fee: "0.00005",
  mnemonic: "behave season capable ridge repair creek seat rescue potato divide fox expose wrestle asthma luggage rack afford pistol ridge modify direct picnic magic cannon",
  xpub: "tpubDF1sYuDKCJr6mGietaVzqGmF2dqdKVBa1DtLJGBX8HXhtHZPv5UBz3WNWU22tiVAYSjqfvfFxMnDs3vM11iQrKej6dq33UCevhiPW9EQAS2"
  }
const tx = sendBitcoinOffchainTransaction(false, body);

The response will contain a transaction ID for the blockchain transaction, the account ID of the virtual account from which the funds were withdrawn, and whether or not the transaction was completed.

{
    "txId": "97bc1c3c23b179cba837e4060c0d07aa399f7ac7d34d91a7405cb5f801b93c8a",
    "id": "5fbc208c99a159b4e9120c30",
    "completed": true
}

Getting virtual account transactions

For a withdrawal, a virtual account transaction will be created for the source virtual account. To look up the details of this withdrawal transaction, use the find transactions for account endpoint:

import {getTransactionsByAccount} from '@tatumio/tatum';
/**
 * Finds transactions for the account identified by the given account ID.
 * @param filter - request body with data filter - https://apidoc.tatum.io/tag/Transaction/#operation/getTransactionsByAccountId
 * @param pageSize - max number of items per page is 50.
 * @param offset - optional Offset to obtain next page of the data.
 */
const filter = {
  id: "5fbc208c99a159b4e9120c30",
  }
const tx = getTransactionsByAccount(filter,50,0);

The response will contain the details of all transactions from the given account.

[
    {
        "amount": "-0.002",
        "operationType": "WITHDRAWAL",
        "transactionType": "DEBIT_WITHDRAWAL",
        "accountId": "5fbaca3001421166273b3779",
        "currency": "BTC",
        "reference": "c66a7a4e-d0f4-41c5-8b10-ef2b607aee6e",
        "attr": null,
        "anonymous": false,
        "senderNote": null,
        "counterAccountId": "mpTwPdF8up9kidgcAStriUPwRdnE9MRAg7",
        "paymentId": null,
        "marketValue": {
            "currency": "EUR",
            "source": "CoinGecko",
            "sourceDate": 1606164328453,
            "amount": "-30.99387999999999768043"
        },
        "created": 1606164620086,
        "txId": "97bc1c3c23b179cba837e4060c0d07aa399f7ac7d34d91a7405cb5f801b93c8a"
    },
    {
        "amount": "0.001",
        "operationType": "DEPOSIT",
        "currency": "BTC",
        "transactionType": "CREDIT_DEPOSIT",
        "accountId": "5fbaca3001421166273b3779",
        "anonymous": false,
        "reference": "c81a23dd-e162-4e0b-b0ff-e470c64f7b88",
        "txId": "cd63e729ecc513bc22e8632b69a433126d5621c5f11047f34a0cbe144ce9aaac",
        "address": "n22crsZTASULKtLqg3XzD1NwV1HnfrQpcd",
        "marketValue": {
            "currency": "EUR",
            "source": "CoinGecko",
            "sourceDate": 1606164328453,
            "amount": "15.49693999999999884022"
        },
        "created": 1606164532855
    },
    {
        "amount": "0.001",
        "operationType": "DEPOSIT",
        "currency": "BTC",
        "transactionType": "CREDIT_DEPOSIT",
        "accountId": "5fbaca3001421166273b3779",
        "anonymous": false,
        "reference": "be877b89-5398-4bf1-a621-0e7cabb4e41b",
        "txId": "aee79834efd1def0c6e0672e22524d98d0fa74192d69edfdbfddc54d1883035f",
        "address": "n22crsZTASULKtLqg3XzD1NwV1HnfrQpcd",
        "marketValue": {
            "currency": "EUR",
            "source": "CoinGecko",
            "sourceDate": 1606164328453,
            "amount": "15.49693999999999884022"
        },
        "created": 1606164532843
    }
]

Getting a Bitcoin transaction

Using the transaction ID of the withdrawal transaction, you can obtain the blockchain transaction details.

import { btcGetTransaction } from '@tatumio/tatum';
/**
 * @param hash - transaction hash
 * @returns - transaction detail
 */
const transaction = btcGetTransaction('97bc1c3c23b179cba837e4060c0d07aa399f7ac7d34d91a7405cb5f801b93c8a');

The response will contain the details of the blockchain transaction.

{
    "txid": "97bc1c3c23b179cba837e4060c0d07aa399f7ac7d34d91a7405cb5f801b93c8a",
    "hash": "97bc1c3c23b179cba837e4060c0d07aa399f7ac7d34d91a7405cb5f801b93c8a",
    "size": 374,
    "vsize": 374,
    "version": 2,
    "locktime": 0,
    "vin": [
        {
            "txid": "aee79834efd1def0c6e0672e22524d98d0fa74192d69edfdbfddc54d1883035f",
            "scriptSig": {
                "asm": "30450221009080c34c2d256fd69675d808c8f8b98cf005d99d0b94fabd13dba314638133d402202e4b70a2ea9c89f16a887838b348fd404177515aea2df238990f1fa075c15fbe01 02f3ec43983975622cb97af7a55b895e25536b1355c34d681d5926638dfe2f16f9",
                "hex": "4830450221009080c34c2d256fd69675d808c8f8b98cf005d99d0b94fabd13dba314638133d402202e4b70a2ea9c89f16a887838b348fd404177515aea2df238990f1fa075c15fbe012102f3ec43983975622cb97af7a55b895e25536b1355c34d681d5926638dfe2f16f9"
            },
            "sequence": 4294967295,
            "vout": 0
        },
        {
            "txid": "cd63e729ecc513bc22e8632b69a433126d5621c5f11047f34a0cbe144ce9aaac",
            "scriptSig": {
                "asm": "3045022100f5867400c4d4c7d475a59498b17d24cb03c564cfa813033f56a13c0720e84fb9022077c9cb331e1eb39595caea0a745f0b79137b4a6cb47c01a45c0f0bc5baa4ab7201 02f3ec43983975622cb97af7a55b895e25536b1355c34d681d5926638dfe2f16f9",
                "hex": "483045022100f5867400c4d4c7d475a59498b17d24cb03c564cfa813033f56a13c0720e84fb9022077c9cb331e1eb39595caea0a745f0b79137b4a6cb47c01a45c0f0bc5baa4ab72012102f3ec43983975622cb97af7a55b895e25536b1355c34d681d5926638dfe2f16f9"
            },
            "sequence": 4294967295,
            "vout": 1
        }
    ],
    "vout": [
        {
            "value": 0.00195,
            "n": 0,
            "scriptPubKey": {
                "asm": "OP_DUP OP_HASH160 6227212d7fada54f0d0b450d4f2ad7918168eacb OP_EQUALVERIFY OP_CHECKSIG",
                "hex": "76a9146227212d7fada54f0d0b450d4f2ad7918168eacb88ac",
                "type": "PUBKEYHASH",
                "reqSigs": 1,
                "addresses": [
                    "mpTwPdF8up9kidgcAStriUPwRdnE9MRAg7"
                ]
            }
        },
        {
            "value": 0,
            "n": 1,
            "scriptPubKey": {
                "asm": "OP_DUP OP_HASH160 ac6dfdfe0653b5f58a6938672ef20adef502b791 OP_EQUALVERIFY OP_CHECKSIG",
                "hex": "76a914ac6dfdfe0653b5f58a6938672ef20adef502b79188ac",
                "type": "PUBKEYHASH",
                "reqSigs": 1,
                "addresses": [
                    "mwEgHSMSQCMehcT7zPF5oYFVUJiF9572Le"
                ]
            }
        }
    ],
    "blockhash": "00000000000000205a4941c1afd9481ba417533393fd8ad94468b991d668643e",
    "confirmations": 1,
    "time": 1606164810,
    "blocktime": 1606164732,
    "hex": "02000000025f0383184dc5ddbffded692d1974fad0984d52222e67e0c6f0ded1ef3498e7ae000000006b4830450221009080c34c2d256fd69675d808c8f8b98cf005d99d0b94fabd13dba314638133d402202e4b70a2ea9c89f16a887838b348fd404177515aea2df238990f1fa075c15fbe012102f3ec43983975622cb97af7a55b895e25536b1355c34d681d5926638dfe2f16f9ffffffffacaae94c14be0c4af34710f1c521566d1233a4692b63e822bc13c5ec29e763cd010000006b483045022100f5867400c4d4c7d475a59498b17d24cb03c564cfa813033f56a13c0720e84fb9022077c9cb331e1eb39595caea0a745f0b79137b4a6cb47c01a45c0f0bc5baa4ab72012102f3ec43983975622cb97af7a55b895e25536b1355c34d681d5926638dfe2f16f9ffffffff02b8f90200000000001976a9146227212d7fada54f0d0b450d4f2ad7918168eacb88ac00000000000000001976a914ac6dfdfe0653b5f58a6938672ef20adef502b79188ac00000000"
}

Let's take a look at this blockchain transaction. You can see that the transaction consumed two deposit transactions - vin array - these are the two blockchain transactions credited to the account. As an output of the transaction - vout array - there are also two recipients. The first one is the recipient address you entered in the request. The second one is the address from your blockchain wallet with index 0. By default, Tatum uses address 0 of the blockchain wallet as an internal system address, where all the leftovers from the transactions are being acquired. They are used again in the next transaction.

In blockchain transactions, unspent deposits from every blockchain address in the same blockchain wallet are automatically collected at the address 0. This is necessary to fully utilize Tatum's off-chain features.

The logic is the same for the Litecoin and Bitcoin Cash blockchains. Ethereum is based on different principles and will be described in a separate article.

Last updated