Tatum
Search…
How to pay fees for token transfers from another address
A gas pump for your native assets, ERC-20, ERC-721 and ERC-1155 tokens.
If you’ve built or want to build a custodial exchange like Binance or Kraken, a custodial wallet service, or a custodial NFT marketplace, there’s one major headache that you’re bound to encounter: how do you pay the gas fees for sending tokens from your users’ accounts? Astoundingly, there really hasn’t been any simple way to do this until now. As an exchange owner, every time you want to send tokens from one of your user’s accounts, you have to calculate the gas fees, send that amount to their account (a transaction that also incurs gas fees), and send the tokens from their account.
Two transactions, both of which incur gas fees, plus can result in minuscule differences of crypto “dust” remaining in their accounts. If you have thousands or millions of addresses, this method creates an enormous amount of work and fees for you as a custodial provider.
In short, a big headache. So we decided to fix that.

Gas pump: a smart contract as a user address

Our new gas pump uses a master address that generates user sub-accounts as smart contracts.
Smart contract implementation is available here.
You simply top up the master address with a balance of crypto from which you’ll pay all of your gas fees, and every time you send tokens from a user address, the gas fees are deducted from the master address. As with everything else in Tatum, it’s super easy to implement.
We've created a Postman collection with all of the API calls we'll be using in this guide:
Custodial wallet.postman_collection.json
28KB
Binary
Postman collection

Generate new gas pump smart contracts

The first step is to create gas pump smart contracts. Each smart contract functions as a wallet/user address that can accept ERC-20, ERC-721, ERC-721, and native assets on the blockchain you deploy it on.
Any combination of tokens can be transferred in a batch transaction. This is significantly more efficient and saves enormously on gas fees compared to transferring assets one by one.
Enough of the talk, let's generate some gas pump wallets on the Polygon Mumbai testnet. In this call, we must designate the number of wallets we want to create in the "batchCount" field.
Request
Response
1
curl --location --request POST 'https://api-eu1.tatum.io/v3/blockchain/sc/custodial/batch' \
2
--header 'Content-Type: application/json' \
3
--header 'x-api-key: YOUR_API_KEY' \
4
--data-raw '{
5
"owner": "0x80d8bac9a6901698b3749fe336bbd1385c1f98f2",
6
"batchCount": 200,
7
"chain": "MATIC",
8
"fromPrivateKey": "0x37b091fc4ce46a56da643f021254612551dbe0944679a6e09cb5724d3085c9ab"
9
}'
Copied!
1
{
2
"txId": "0x6b9ecce4bf716a06c8dfad19feea13692b99737dc042ceaa1ca4204fdc1556a5"
3
}
Copied!
As a response, we obtain transaction hash. From this hash, we can find the addresses of the contracts - the addresses of our gas pump wallets.
Request
Response
1
curl --location --request GET 'https://api-eu1.tatum.io/v3/blockchain/sc/custodial/MATIC/0x6b9ecce4bf716a06c8dfad19feea13692b99737dc042ceaa1ca4204fdc1556a5' \
2
--header 'Content-Type: application/json' \
3
--header 'x-api-key: YOUR_API_KEY'
Copied!
1
[
2
"0xc83779f2537fd40082c031fcef91bd6557ee2a13",
3
"0xb67f45ea6c7466e25635f4154c671955df130977",
4
"0x6dcc090c52e6427938c29a5dcf03274e5bdf0630",
5
"0x29696548784515d2884fdd09ad7b4e689c56ed3f",
6
"0xf12294780cedfa51dee310cba7f3a0968d881246",
7
"0xe7b002b13a86d533abb74bbc7d5b6a16af2e6b13",
8
"0x88cf6afd1dd665abaa03cac06e6bde554e1fffd5",
9
"0xff79ff532723d56eb78c7547b53611e8dc73321e",
10
"0x17b862cf61212013602290a2a7a1ee7775b51ff6",
11
"0xba33fa91e471780db0e71771fd8af63aba6a1fb2",
12
"0xbb5747dc66e35825b9a9da800de15d6743b66b55",
13
.
14
.
15
.
16
]
Copied!
You can assign these addresses to your users. They are capable of accepting MATIC and any ERC-* tokens out of the box.

Transfer asset from a gas pump address

Your user deposited some ERC-20 or ERC-721 tokens into his account. Now it's time to move assets to another address. You don't have to send any MATIC there to pay for gas, just say which assets should be sent, how much of them, and where. Let's send an ERC-721 token with a tokenId of 100 from the address.
Request
Response
1
curl --location --request POST 'https://api-eu1.tatum.io/v3/blockchain/sc/custodial/transfer' \
2
--header 'Content-Type: application/json' \
3
--header 'x-api-key: YOUR_API_KEY' \
4
--data-raw '{
5
"chain": "MATIC",
6
"contractType": 1,
7
"tokenId": "100",
8
"tokenAddress": "0x6d8eae641416B8b79e0fB3a92b17448CfFf02b11",
9
"custodialAddress": "0xA09Eee86868ECE1A44B57b1EEBEe4f40Df2Ed43D",
10
"recipient": "0x8cb76aEd9C5e336ef961265c6079C14e9cD3D2eA",
11
"fromPrivateKey": "0x37b091fc4ce46a56da643f021254612551dbe0944679a6e09cb5724d3085c9ab"
12
}'
Copied!
1
{
2
"txId": "0xa1bdc0008b6cce138cec38336a3cbbf1117cc56bcfe0004e14d6789a9d099b72"
3
}
Copied!
As a result, we obtain a transaction hash. As you can see, we have to enter the address of the token we want to transfer, and for an ERC-721, a tokenId as well.
The contractType attribute is important, as it tells the wallet which kind of token it is transferring:
  • contractType 0 - ERC-20 token
  • contractType 1 - ERC-721 token
  • contractType 2 - ERC-1155 token
  • contractType 3 - native asset - MATIC
Since the transfer endpoint is universal and neither ERC-20 tokens nor native assets have tokenIds, this field can be omitted with transfers of such tokens. Similarly, for amount, ERC-721 tokens are always one-of-a-kind, so the amount is omitted. Native assets don't have contract addresses, so this field is omitted when they are transferred.

Transfer assets from a custodial address in a batch call

Simple transfers are great, but batch transfers are much more powerful. In this operation, you can basically transfer all of the assets from an address in 1 transaction. You can define different recipients for different assets, you can split the balance of the ERC-20 token into 2 different recipients and many more.
In this example, we are transferring all types of assets we have in the gas pump wallet in 1 call:
Request
Response
1
curl --location --request POST 'https://api-eu1.tatum.io/v3/blockchain/sc/custodial/transfer/batch' \
2
--header 'Content-Type: application/json' \
3
--header 'x-api-key: YOUR_API_KEY' \
4
--data-raw '{
5
"chain": "MATIC",
6
"contractType": [0,1,2,3],
7
"tokenId": ["0","100","1","0"],
8
"amount": ["0.001","0","1","0.009"],
9
"tokenAddress": [
10
"0x2d7882bedcbfddce29ba99965dd3cdf7fcb10a1e",
11
"0x6d8eae641416B8b79e0fB3a92b17448CfFf02b11",
12
"0x664F97470654e8f00E42433CFFC0d08a5f4f7BC7",
13
"0"
14
],
15
"custodialAddress": "0x4eC40a4A0dA042d46cC4529f918080957003b531",
16
"recipient": [
17
"0x8cb76aEd9C5e336ef961265c6079C14e9cD3D2eA",
18
"0x8cb76aEd9C5e336ef961265c6079C14e9cD3D2eA",
19
"0x8cb76aEd9C5e336ef961265c6079C14e9cD3D2eA",
20
"0x8cb76aEd9C5e336ef961265c6079C14e9cD3D2eA"
21
],
22
"fromPrivateKey": "0x37b091fc4ce46a56da643f021254612551dbe0944679a6e09cb5724d3085c9ab"
23
}'
Copied!
1
{
2
"txId": "0xc1e1b725d68a5e35d36121307d2365678ad9179411c1f59a2deac8a7080afffa"
3
}
Copied!
As a result, we again obtain transaction hash as a response.
When the asset you want to transfer does not have tokenAddress or amount or tokenId, use 0 instead. We will take care of the rest for you.
To find out more about the API calls we have just used, please refer to our API Reference.
Last modified 21d ago