Ethereum (Sepolia)

Step 1 - Setup Gas wallet Allowance

To initiate an ERC20 approve transaction, replace the token, source wallet, gas wallet, bearer token, vault ID and run the following code:

from web3 import Web3
import requests

bearer_token = "YOUR_BEARER_TOKEN" # Replace with an updated token
vault_id = "YOUR_VAULT_ID"  # Replace with the actual vault ID
token_address = "0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238" # approval token smart contract address (USDC Sepolia)
source_wallet_address = "0xB70e07bAc842d9547C1d56AB7eF5e27021024f23" # Source Wallet
gas_wallet_address = "0x968284717EcdB5696c03e842A5aFa3d1F0333bBd" # Gas Wallet
amount = 1 * (10 ** 6) # Amount to approve (1 USDC = 1 * 10^6 because USDC has 6 decimals)

Prepare the ERC20 call data:

# ABI for approve function
approve_abi =[{
    "constant": False,
    "inputs": [
        {"name": "spender", "type": "address"},
        {"name": "amount", "type": "uint256"}
    ],
    "name": "approve",
    "outputs": [{"name": "", "type": "bool"}],
    "stateMutability": "nonpayable",
    "type": "function"
}]
# Connect to the Sepolia network
rpc_url = "https://ethereum-sepolia-rpc.publicnode.com"  # Replace with a valid Polygon RPC URL if needed
web3 = Web3(Web3.HTTPProvider(rpc_url))
# Encode function call data
contract = web3.eth.contract(address=token_address, abi=approve_abi)
calldata = contract.functions.approve(gas_wallet_address, amount).build_transaction({"from": source_wallet_address})["data"]

Initiate an EVM Transaction:

url = f"https://api.utila.io/v2/vaults/{vault_id}/transactions:initiate" 
approve_payload = {
    "details": {
        "evmTransaction": {
            "network": "networks/ethereum-testnet-sepolia",
            "fromAddress": "{0}".format(source_wallet_address),
            "toAddress": "{0}".format(token_address),
            "data": "{0}".format(calldata),  # Replace with actual calldata
        },
    },
    "note": "Token Approval",
    "includeReferencedResources": True
}
headers = {
    "Content-Type": "application/json",
    "Authorization": "Bearer {0}".format(bearer_token)  # If authentication is required
}
response = requests.post(url, json=approve_payload, headers=headers)
print(response.status_code)
print(response.json())  # If the response is in JSON format

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_address = "0x9e21BF0F214cD33b2DC3Bfd49301E86d62b564Dc" 
amount = int(0.05 * (10 ** 6)) # Amount to approve (1 USDC = 1 * 10^6 because USDC has 6 decimals)

transfer_from_abi = [{
    "constant": False,
    "inputs": [
        {"name": "from", "type": "address"},
        {"name": "to", "type": "address"},
        {"name": "value", "type": "uint256"}
    ],
    "name": "transferFrom",
    "outputs": [{"name": "", "type": "bool"}],
    "stateMutability": "nonpayable",
    "type": "function"
}]
# Connect to the Sepolia network
rpc_url = "https://ethereum-sepolia-rpc.publicnode.com"  # Replace with a valid Polygon RPC URL if needed
web3 = Web3(Web3.HTTPProvider(rpc_url))
# Encode function call data
contract = web3.eth.contract(address=token_address, abi=transfer_from_abi)
calldata = contract.functions.transferFrom(
    source_wallet_address,  # The owner of the USDC
    destination_address,  # The recipient of USDC
    amount  # Amount to transfer
).build_transaction({
    "from": gas_wallet_address,  # The approved spender (gas wallet)
    "gas": 100000,
    "gasPrice": web3.to_wei("50", "gwei"),
    "nonce": web3.eth.get_transaction_count(gas_wallet_address),
})["data"]

url = f"https://api.utila.io/v1alpha2/vaults/{vault_id}/transactions:initiate"  # Replace with the correct API endpoint
transferFrom_payload = {
    "details": {
        "evmTransaction": {
            "network": "networks/ethereum-testnet-sepolia".format(token_address),
            "fromAddress": "{0}".format(gas_wallet_address),
            "toAddress": "{0}".format(token_address),
            "data": "{0}".format(calldata),  # Replace with actual calldata
        },
    },
    "note": "Transfer USDC using transferFrom",
    "includeReferencedResources": True
}
headers = {
    "Content-Type": "application/json",
    "Authorization": "Bearer {0}".format(bearer_token)  # If authentication is required
}
response = requests.post(url, json=transferFrom_payload, headers=headers)
print(response.status_code)
print(response.json())  # If the response is in JSON format

After internal approval and signing, the transaction will appear in the console as: