Offload transaction fees to any Utila wallet
A sponsored transfer enables you to send tokens from a Utila wallet to any destination while another wallet, the sponsor, covers the blockchain fees (gas).
Sponsored transfers are supported across multiple blockchains through a single, streamlined experience. Utila supports two modes of sponsored transfers: native transfers and transfers based on token approval.
Learn more about Utila's sponsored transfers, including the use of EIP 7702 for EVM transactions, here
Native sponsored transfer
To create a sponsored transfer via API, call the initiateTransaction endpoint with an assetTransfer payload and specify the sponsor wallet.
Example payload
{
"details": {
"assetTransfer": {
"asset": "assets/native.polygon-mainnet",
"amount": "0.0001",
"source": "0x2A9E89409A3E283a7Be15BEA1Bee1CAE3fbfe1B3",
"destination": "0x71d8E161FE2d55C8BF6bAf92EdA849E65a454C8e",
"sponsor": "vaults/463871f49ca7/wallets/42771bd88677"
}
}
}Native sponsored transfer is supported on leading EVM chains, Solana, Sui and Aptos. For Sponsored transfers on Tron, refer to Sponsored transfer using token approvals
Sponsored transfer using token approvals
For standard ERC-20 and TRC-20 tokens, Utila also supports sponsorship with token approvals. Deposit Wallets must complete a one-time approval transaction, authorizing a Gas Wallet (which is also a Utila wallet) to move specific tokens. This setup requires an initial gas injection, but after that, the Deposit Wallet no longer needs additional gas. To streamline this, you can either send an initial amount for gas, right after wallet creation or use Utila’s batch transfer feature to fund multiple Deposit Wallets in a single transaction (via assetBatchTransfer in initiateTransaction).
Step 1 - One-time token approval for the Sponsor (Gas Wallet):

Step 2 - Performing a transfer:

Sponsor Wallet (gas wallet) - Will pay for the gas of the ERC-20 transfers, after the approval step.
Source Wallet - Performs the approval. After the approval, this wallet won't need to pay for gas.
Destination Wallet - Any address that will receive transfers.
Transaction Policy Enforcement
Sponsored transfers are safeguarded by Utila’s Transaction Policy framework. The EVM Token Approval policy rule allows users to whitelist specific Gas Wallets while restricting all other interactions. Importantly, the policy is enforced at the Deposit Wallet level, not the Gas Wallet, ensuring all transfers comply with organizational security rules. Make sure you set a rule to allow EVM APPROVAL in the Transaction Policy at Vault Settings.
Example on TRON Shasta
Step 1 - Setup Gas wallet Allowance
To initiate an TRC-20 approve transaction, replace the token, source wallet, gas wallet, bearer token, vault ID and run the following code:
from tronpy import Tron
from tronpy.keys import to_hex_address
from eth_abi.abi import encode as encode_abi
from eth_utils import keccak
import requests
# Tron Shasta network connection
client = Tron(network='shasta')
# Define relevant addresses
token_address = 'TG3XXyExBkPp9nzdajDZsozEu4BkaSJozs' # USDT on Shasta
source_wallet = 'TN2QSXwq4jvuvjXyWdGX1Kve3gaGUM7eEW' # Token holder
gas_wallet = 'TQQ52wvmP159cPR2PwUAncJzhh9icdhuRH' # Sponsor (spender)
# Encode function call: approve(address spender, uint256 amount)
amount = 1 * 10**6 # 1 USDT in smallest unitsPrepare the TRC-20 call data:
spender_hex = to_hex_address(gas_wallet)
spender_bytes = bytes.fromhex(spender_hex[2:])
selector = keccak(text="approve(address,uint256)")[:4].hex()
args_encoded = encode_abi(['address', 'uint256'], [spender_bytes, amount]).hex()
calldata = "0x" + selector + args_encodedInitiate a tronTriggerSmartContract transaction:
# Construct Utila-compatible payload
vault_id = "YOUR_VAULT_ID"
url = f"https://api.utila.io/v2/vaults/{vault_id}/transactions:initiate"
payload = {
"details": {
"tronTriggerSmartContract": {
"network": "networks/tron-testnet-shasta",
"ownerAddress": source_wallet,
"contractAddress": token_address,
"callValue": "0",
"data": calldata
}
},
"note": "Token Approval - Tron Shasta",
"includeReferencedResources": True
}
# Submit to Utila
headers = {
"Content-Type": "application/json",
"Authorization": "Bearer YOUR_BEARER_TOKEN"
}
response = requests.post(url, json=payload, headers=headers)
print(response.status_code)
print(response.json())After internal approval and signing, the transaction will appear in the console as:
Step 2 - Initiate transferFrom by the Gas wallet
Similarly, set a destination wallet address and initiate an ERC20 transferFrom transaction:
destination_wallet = "TE14iNUCWfGegXjEgMffEQVC1fGi3WJB3U"
amount = int(0.05 * (10 ** 6)) # Amount to approve (1 USDC = 1 * 10^6 because USDT has 6 decimals)
source_bytes = bytes.fromhex(to_hex_address(source_wallet)[2:])
destination_bytes = bytes.fromhex(to_hex_address(destination_wallet)[2:])
selector = keccak(text="transferFrom(address,address,uint256)")[:4].hex()
args_encoded = encode_abi(['address', 'address', 'uint256'], [source_bytes, destination_bytes, amount]).hex()
calldata = "0x" + selector + args_encoded
# Prepare payload
vault_id = "YOUR_VAULT_ID"
url = f"https://api.utila.io/v2/vaults/{vault_id}/transactions:initiate"
payload = {
"details": {
"tronTriggerSmartContract": {
"network": "networks/tron-testnet-shasta",
"ownerAddress": gas_wallet,
"contractAddress": token_address,
"callValue": "0",
"data": calldata
}
},
"note": "Token TransferFrom - Tron Shasta",
"includeReferencedResources": True
}
headers = {
"Content-Type": "application/json",
"Authorization": "Bearer YOUR_BEARER_TOKEN"
}
response = requests.post(url, json=payload, headers=headers)
print(response.status_code)
print(response.json())After internal approval and signing, the transaction will appear in the console as: