Tatum
Search…
API Reference
How to withdraw assets from virtual accounts with gas pump wallets
This guide describes how to withdraw assets from virtual accounts connected to gas pump wallets. We will cover the whole flow of the user journey - gas pump wallet generation, virtual account creation, and withdrawal from the gas pump wallet to the blockchain address with correct virtual account balance updates.

The first step is to create a gas pump wallet for every user. Each wallet supports the native currency of the blockchain it's deployed on, as well as ERC-20, ERC-721, and ERC-1155 tokens by default.
In the following request, we will simply choose how many wallets we want to deploy:
JavaScript
cURL
import {Currency, generateCustodialWalletBatch } from '@tatumio/tatum';
/**
* Generate new custodial smart contract address on the blockchain.
* @param testnet - false for mainnet, true for testnet
* @param body - body of request - https://apidoc.tatum.io/tag/Gas-pump/#operation/GenerateCustodialWallet
* @returns transaction ID of wallet creation
*/
const body = {
chain: Currency.MATIC,
fromPrivateKey: "0x37b091fc4ce46a56da643f021254612551dbe0944679a6e09cb5724d3085c9ab",
batchCount: 200,
owner: "0x80d8bac9a6901698b3749fe336bbd1385c1f98f2"
}
const wallet = generateCustodialWalletBatch (false, body);
curl --location --request POST 'https://api-eu1.tatum.io/v3/blockchain/sc/custodial/batch' \
--header 'Content-Type: application/json' \
--header 'x-api-key: YOUR_API_KEY' \
--data-raw '{
"owner": "0x80d8bac9a6901698b3749fe336bbd1385c1f98f2",
"batchCount": 200,
"chain": "MATIC",
"fromPrivateKey": "0x37b091fc4ce46a56da643f021254612551dbe0944679a6e09cb5724d3085c9ab"
}'
The response is the transaction hash. From this hash, we can find the address of each contract we have deployed - our gas pump wallet addresses.
Response
{
"txId": "0x2979a3d30f812b0d9ebcc66d01b620d3779380c38e8770a82fa4ea4f2dfa8f69"
}

Now, for every user, we need to create a virtual account. Since we have decided to support MATIC and USDC for every user, we need to create 2 virtual accounts - MATIC and USDC. These accounts do not have xpubs because we will manually assign addresses to them later.
JavaScript
cURL
import { Account, Currency, CreateAccount, createAccount } from "@tatumio/tatum";
import { config } from "dotenv";
config();
const createNewAccount = async () => {
const createAccountData: CreateAccount = {
currency: Currency.MATIC
};
const accoun: Account = await createAccount(createAccountData);
console.log(accoun);
};
const createNewAccount = async () => {
const createAccountData: CreateAccount = {
currency: Currency.USDC_MATIC
};
const accoun: Account = await createAccount(createAccountData);
console.log(accoun);
};
createNewAccount();
curl --location --request POST 'https://api-eu1.tatum.io/v3/ledger/account' \
--header 'x-api-key: YOUR_API_KEY' \
--header 'Content-Type: application/json' \
--data-raw '{
"currency": "MATIC"
}'
curl --location --request POST 'https://api-eu1.tatum.io/v3/ledger/account' \
--header 'x-api-key: YOUR_API_KEY' \
--header 'Content-Type: application/json' \
--data-raw '{
"currency": "USDC_MATIC"
}'
The response will contain details about the two accounts we have just created.
Response
{
"currency": "MATIC",
"active": true,
"balance": {
"accountBalance": "0",
"availableBalance": "0"
},
"frozen": false,
"accountingCurrency": "EUR",
"id": "5fb7bdf6e96d9ab593e191a5"
}
{
"currency": "USDC_MATIC",
"active": true,
"balance": {
"accountBalance": "0",
"availableBalance": "0"
},
"frozen": false,
"accountingCurrency": "EUR",
"id": "5fb7bdf6e96d9ab593e191a6"
}
Once an account has been created, we need to assign a gas pump address to it. This process enables automatic incoming transaction synchronization for the specified address and currency.
JavaScript
cURL
import {assignDepositAddress} from '@tatumio/tatum';
/**
* Assign address for account
* @param id - account ID
* @param address - blockchain address to assign
*/
const assign = assignDepositAddress ("5fb7bdf6e96d9ab593e191a5","0x8Fca331d13E518059191Dde43765ea77C6C98336");
curl --location --request POST 'https://api-eu1.tatum.io/v3/offchain/account/5fb7bdf6e96d9ab593e191a5/address/0x8Fca331d13E518059191Dde43765ea77C6C98336' \
--header 'x-api-key: YOUR_API_KEY'
The response confirms the address has been assigned to the virtual account. Any incoming blockchain transaction to this address will be automatically detected and the virtual account balance will be updated.
Response
{
"address": "0x8Fca331d13E518059191Dde43765ea77C6C98336",
"currency": "MATIC"
}

The following 3 steps are necessary to withdraw assets from a virtual account and send them to the blockchain:
  1. 1.
    Create a withdrawal request to the virtual account - it will debit the withdrawn balance from the virtual account
  2. 2.
    Perform a transfer of the assets from the gas pump address - this will result in the blockchain transaction.
  3. 3.
    Mark the withdrawal from the 1st step as completed.
Let's withdraw 1 USDC from an account to the blockchain.

To make the virtual account balance in sync, you need to perform a withdrawal operation from that account.
JavaScript
cURL
import { offchainStoreWithdrawal } from '@tatumio/tatum';
/**
* Generate new custodial smart contract address on the blockchain.
* @param data - body of request - https://apidoc.tatum.io/tag/Withdrawal/#operation/storeWithdrawal
* @returns transaction reference, withdrawal id, withdrawal detail
*/
const body = {
senderAccountId: "5fb7bdf6e96d9ab593e191a6",
address: "0x42952e30fB119e0683607Ef6D5A7E8A97dBB7314",
amount: "1",
fee: "0.0005"
}
const wallet = offchainStoreWithdrawal (body);
curl --location --request POST 'https://api-eu1.tatum.io/v3/offchain/withdrawal' \
--header 'x-api-key: YOUR_API_KEY' \
--header 'Content-Type: application/json' \
--data-raw '{
"senderAccountId":"5fb7bdf6e96d9ab593e191a6",
"address":"0x42952e30fB119e0683607Ef6D5A7E8A97dBB7314",
"amount":"1",
"fee":"0.0005"
}'
The result is the virtual account transaction reference and the ID of the withdrawal. The account balance has been debited with 1 USDC and the transaction is visible in the list of the transactions for the virtual account. We still haven't done anything on the blockchain.
Response
{
"reference": "5e6be8e9e6aa436299950c41",
"id": "5e68c66581f2ee32bc354087"
}

Now it's time to move the assets to another address. Since we're using gas pump wallets, you don't have to send any MATIC there to pay for gas. You only need to specify which assets should be sent, how much of them, and where to send them, and the funds required to pay for the transaction will automatically be deducted from the owner address you specified when creating the gas pump wallets.
JavaScript
cURL
import { Currency, prepareTransferFromCustodialWallet } from '@tatumio/tatum';
/**
* Prepare signed transaction from the custodial SC wallet.
* @param testnet - false for mainnet, true for testnet
* @param data - body of request - https://apidoc.tatum.io/tag/Gas-pump/#operation/TransferCustodialWallet
* @param provider optional provider to enter. if not present, Tatum Web3 will be used.
* @returns {txId: string} Transaction ID of the operation, or signatureID in case of Tatum KMS
*/
const body = {
chain: Currency.MATIC,
custodialAddress: "0x8Fca331d13E518059191Dde43765ea77C6C98336",
tokenAddress: "0x2791bca1f2de4661ed88a30c99a7a9449aa84174",
contractType: 0,
recipient: "0x42952e30fB119e0683607Ef6D5A7E8A97dBB7314",
amount: "1",
tokenId: "100000",
fromPrivateKey: "0x37b091fc4ce46a56da643f021254612551dbe0944679a6e09cb5724d3085c9ab"
}
const transfer = prepareTransferFromCustodialWallet(false, body);
curl --location --request POST 'https://api-eu1.tatum.io/v3/blockchain/sc/custodial/transfer' \
--header 'Content-Type: application/json' \
--header 'x-api-key: YOUR_API_KEY' \
--data-raw '{
"chain": "MATIC",
"contractType": 0,
"amount": "1",
"tokenAddress": "0x2791bca1f2de4661ed88a30c99a7a9449aa84174",
"custodialAddress": "0x8Fca331d13E518059191Dde43765ea77C6C98336",
"recipient": "0x42952e30fB119e0683607Ef6D5A7E8A97dBB7314",
"fromPrivateKey": "0x37b091fc4ce46a56da643f021254612551dbe0944679a6e09cb5724d3085c9ab"
}'
The response is the transaction hash. As you can see, we have to enter the address of the token we want to transfer (tokenAddress 0x2791bca1f2de4661ed88a30c99a7a9449aa84174). The contractType attribute is very important, as it tells the wallet which kind of a token it is transferring - 0 for ERC-20 assets. For more on using gas pump wallets, please refer to our full guide.
Response
{
"txId": "0xa1bdc0008b6cce138cec38336a3cbbf1117cc56bcfe0004e14d6789a9d099b72"
}

Once the transaction is successful, you need to mark the withdrawal as completed. If the transaction fails and you want to refund the assets to the virtual account, you need to cancel the original withdrawal request.
That's it. 5 simple steps to connect your gas pump wallets to virtual accounts and keep them synced.
Copy link
On this page
Wallet generation
Virtual account creation
Withdraw assets from the account
Perform a withdrawal
Perform a blockchain transaction
Complete or cancel the withdrawal