Utila enables transaction sponsorship on Solana through the initiateTransaction → solanaSerializedTransaction API flow. Each sponsored transaction requires two signatures: one from the source wallet and another from the designated fee payer.
When the source wallet transfers SPL tokens, the transaction is evaluated by the transaction policy as a standard token transfer, ensuring that your organizational governance applies seamlessly to sponsored transfers - just as it does on EVM and TRON.
By default, Solana multi-signed transactions must be signed by the co-signer. To enable signing such transactions with the mobile app, contact Utila support
The following Python script demonstrates how to transfer 1.0 USDT from a source wallet, with the transaction fees sponsored by a separate fee payer.
from solders.transaction import VersionedTransaction
from spl.token.instructions import transfer, get_associated_token_address, TransferParams, create_associated_token_account
from solders.message import Message
from spl.token.constants import TOKEN_PROGRAM_ID
from solders.pubkey import Pubkey
from solders import null_signer
import base64
import requests
# Utila wallet address, for example AAsGWqT2q3tF6D1hDXwkeB92KjhMq4YnSL8ydHgtuNLK
fee_payer_address_str = "<fee payer address>"
# Utila wallet address, for example gtD3B7eLi6hFk5vzHdWnuUbHdiAPazd5c6JFbwhu78W
sender_address_str = "<USDT sender address>"
# for example: 3jLPC331SdoJKvsrJuQwqa6XqqcErrY2Qxe4dtPx2Roq
recipient_address_str = "<USDT recepient address>"
# for example: 76ce849bc0f8
vault = "<vault id>"
# for example: [email protected]
designated_signer = "<designated signer id>"
amount = 1.0
# USDT mint address
usdt_mint = Pubkey.from_string("Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB")
# Derive Associated Token Accounts (ATAs)
sender_data = get_associated_token_address(sender_address, usdt_mint)
recipient_data = get_associated_token_address(recipient_address, usdt_mint)
transfer_instruction = transfer(
TransferParams(
amount=int(amount * (10 ** 6)), # USDT typically uses 6 decimals
dest=recipient_data,
owner=sender_address,
program_id=TOKEN_PROGRAM_ID,
source=sender_data,
)
)
token_account_creation_instruction = create_associated_token_account(
payer=fee_payer_address,
owner=recipient_address,
mint=usdt_mint,
token_program_id=TOKEN_PROGRAM_ID
)
message = Message(payer=sender_address, instructions=[token_account_creation_instruction, transfer_instruction])
# Build the transaction
tx = VersionedTransaction(keypairs=[null_signer.NullSigner(fee_payer_address), null_signer.NullSigner(sender_address)], message=message)
# Serialize and convert to base64
b64 = base64.b64encode(bytes(tx)).decode('utf-8')
print("Base64 Transaction:", b64)
# Construct the JSON payload
payload = {
"parent": "vaults/" + vault,
"details": {
"solanaSerializedTransaction": {
"publish": True,
"validateOnly": True,
"rawTransaction": b64,
"network": "networks/solana-mainnet",
"replaceBlockhash": True
}
},
"designatedSigners": ["users/" + designated_signer]
}
access_token = "<access token, see https://docs.utila.io/reference/authentication#/>"
# Set the headers, including the access token for authorization
headers = {
"Authorization": f"Bearer {access_token}",
"Content-Type": "application/json"
}
# Send the POST request
response = requests.post(
"https://api.utila.io/v1alpha2/vaults/" + vault + "/transactions:initiate",
json=payload,
headers=headers
)