Skip to main content

Case

Suppose you hold $GALA and want to put it to work earning yield in a lending protocol, specifically by converting it into interest bearing $aETHWETH in a single atomic transaction. With GlueX’s unified Router API and smart contract integrations, you can swap one token and deposit the proceeds into a lending vault atomically, without juggling approvals or multiple onchain calls. In this guide, we’ll walk through exactly how to:
  1. Build a swap quote to convert $GALA into $WETH
  2. Bundle that quote into a Zap that deposits directly into the Aave vault to receive $aETHWETH
  3. Submit the transaction and verify the resulting vault deposit
We’ll use a real Ethereum mainnet transaction as our reference example
Transaction hash: 0xd0c48d25ac3e0ab111b3ab401903d4a357baa22ee9c9efb832e0b80278ba5ba3

How It Works

  1. Transfer $GALA from the user’s wallet
  2. Swap $GALA for $WETH via Uniswap V3
  3. Receive $aETHWETH via Aave V3
  4. Start earning yield automatically

Why This Matters?

Moving from a regular token to a yield bearing asset usually requires multiple platforms, manual steps and higher fees. GlueX removes these barriers, bringing: ✅ Time efficiency - One transaction instead of many ✅ Cost savings - Lesser gas fees ✅ Simplicity - No manual interactions with multiple protocols This tutorial uses $GALA → $aETHWETH as an example, but GlueX allows you to swap any token to any yield generating token in a single transaction

Implementation

Prerequisites

Before running the script, ensure you have the following installed: Python:
  • Python 3.10
  • Web3 (pip install web3)
  • Requests (pip install requests)
TypeScript:
  • Node.js 18+
  • TypeScript (npm install -g typescript)
  • Web3 (npm install web3)
  • Axios (npm install axios)
JavaScript:
  • Node.js 18+
  • Web3 (npm install web3)
  • Axios (npm install axios)
Additionally, you need access to an Ethereum compatible blockchain node, such as an Infura or Tenderly RPC endpoint.

Setup and Configuration

from web3 import Web3
import requests
import json
import time

# Configuration
API_KEY = "your_api_key"
UNIQUE_PID = "your_unique_pid"
QUOTE_ENDPOINT = "https://router.gluex.xyz/v1/quote"
RPC_URL = "https://mainnet.gateway.tenderly.co/your_rpc_url"
PRIVATE_KEY = "your_private_key"

# Token Addresses
GALA_ADDRESS = "0xd1d2Eb1B1e90B638588728b4130137D262C87cae"
AETHWETH_ADDRESS = "0x4d5F47FA6A74757f35C14fD3a6Ef8E3C9BC514E8"

# Input amount (in decimals)
INPUT_AMOUNT = 2000000000000  # 2,000,000 GALA (including decimals)

# Initialize Web3
web3 = Web3(Web3.HTTPProvider(RPC_URL))
account = web3.eth.account.from_key(PRIVATE_KEY)
COMPUTATION_UNITS = 1000000
COMPUTATION_COST = web3.eth.gas_price

Fetching a Swap Quote

Before executing the transaction, fetch a quote from the GlueX Router
def fetch_quote():
    """Fetch a quote from the GlueX Router for $GALA to $aETHWETH swap"""
    headers = {"x-api-key": API_KEY}
    body = {
        "chainID": "ethereum",
        "userAddress": account.address,
        "outputReceiver": account.address,
        "uniquePID": UNIQUE_PID,
        "inputToken": GALA_ADDRESS,
        "outputToken": AETHWETH_ADDRESS,
        "inputAmount": INPUT_AMOUNT,
        "isPermit2": False
    }

    response = requests.post(QUOTE_ENDPOINT, json=body, headers=headers)

    return response.json()

Approving the Router Contract

Before executing the swap, the router contract needs permission to spend the user’s GALA tokens
def approve_spender(spender, amount, token_address):
    """Approve the router contract to spend $GALA"""
    signature = "0x095ea7b3"
    padded_spender = Web3.to_checksum_address(spender)[2:].zfill(64)
    calldata = f"{signature}{padded_spender}{int(amount):064x}"

    txn = {
        "from": account.address,
        "to": Web3.to_checksum_address(token_address),
        "data": calldata,
        "gas": COMPUTATION_UNITS,
        "gasPrice": web3.eth.gas_price,
        "nonce": web3.eth.get_transaction_count(account.address),
        "chainId": 1,  # Ethereum mainnet
    }

    signed_txn = web3.eth.account.sign_transaction(txn, account.key)
    txn_hash = web3.eth.send_raw_transaction(signed_txn.raw_transaction)
    print(f"Approval Transaction Hash: {web3.to_hex(txn_hash)}")

    return txn_hash

Executing the Transaction

After approval, we execute the transaction using the calldata from the quote
def execute_transaction(calldata, router_address):
    """Execute the swap transaction on GlueX Router"""
    txn = {
        "from": account.address,
        "to": Web3.to_checksum_address(router_address),
        "data": calldata,
        "gas": COMPUTATION_UNITS,
        "gasPrice": web3.eth.gas_price,
        "nonce": web3.eth.get_transaction_count(account.address),
        "chainId": 1,  # Ethereum mainnet
    }

    signed_txn = web3.eth.account.sign_transaction(txn, account.key)

    try:
        txn_hash = web3.eth.send_raw_transaction(signed_txn.raw_transaction)
    except Web3RPCError as err:
        time.sleep(5)
        txn["nonce"] = web3.eth.get_transaction_count(account.address)
        signed_txn = web3.eth.account.sign_transaction(txn, account.key)
        txn_hash = web3.eth.send_raw_transaction(signed_txn.raw_transaction)

    print(f"Transaction Hash: {web3.to_hex(txn_hash)}")

    return txn_hash

Putting Everything Together

def main():
    # Fetch the quote
    quote_data = fetch_quote()
    if quote_data.get('statusCode') != 200:
        print("Error fetching quote:", quote_data)
        return

    print("Quote received successfully:", quote_data)
    router_address = quote_data["result"]["router"]
    calldata = quote_data["result"]["calldata"]

    # Approve the router contract
    print("Approving router contract to spend GALA...")
    txn_hash = approve_spender(router_address, INPUT_AMOUNT, GALA_ADDRESS)
    print("Waiting for approval transaction confirmation...")
    approval_receipt = web3.eth.wait_for_transaction_receipt(txn_hash)
    if approval_receipt.status != 1:
        print("Approval failed. Aborting.")
        return

    # Execute the transaction
    print("Executing transaction...")
    execute_txn = execute_transaction(calldata, router_address)

    receipt = web3.eth.wait_for_transaction_receipt(execute_txn)
    print("Transaction confirmed. Receipt:")
    print(receipt)


if __name__ == "__main__":
    main()

Complete Code Implementation

from web3 import Web3
from web3.exceptions import Web3RPCError
import requests
import json
import time

# Configuration
API_KEY = "your_api_key"
UNIQUE_PID = "your_unique_pid"
QUOTE_ENDPOINT = "https://router.gluex.xyz/v1/quote"
RPC_URL = "https://mainnet.gateway.tenderly.co/your_rpc_url"
PRIVATE_KEY = "your_private_key"

# Token Addresses
GALA_ADDRESS = "0xd1d2Eb1B1e90B638588728b4130137D262C87cae"
AETHWETH_ADDRESS = "0x4d5F47FA6A74757f35C14fD3a6Ef8E3C9BC514E8"

# Input amount (in decimals)
INPUT_AMOUNT = 2000000000000  # 2,000,000 GALA (including decimals)

# Initialize Web3
web3 = Web3(Web3.HTTPProvider(RPC_URL))
account = web3.eth.account.from_key(PRIVATE_KEY)
COMPUTATION_UNITS = 1000000
COMPUTATION_COST = web3.eth.gas_price

def fetch_quote():
    """Fetch a quote from the GlueX Router for $GALA to $aETHWETH swap"""
    headers = {"x-api-key": API_KEY}
    body = {
        "chainID": "ethereum",
        "userAddress": account.address,
        "outputReceiver": account.address,
        "uniquePID": UNIQUE_PID,
        "inputToken": GALA_ADDRESS,
        "outputToken": AETHWETH_ADDRESS,
        "inputAmount": INPUT_AMOUNT,
        "isPermit2": False
    }

    response = requests.post(QUOTE_ENDPOINT, json=body, headers=headers)

    return response.json()

def approve_spender(spender, amount, token_address):
    """Approve the router contract to spend $GALA"""
    signature = "0x095ea7b3"
    padded_spender = Web3.to_checksum_address(spender)[2:].zfill(64)
    calldata = f"{signature}{padded_spender}{int(amount):064x}"

    txn = {
        "from": account.address,
        "to": Web3.to_checksum_address(token_address),
        "data": calldata,
        "gas": COMPUTATION_UNITS,
        "gasPrice": web3.eth.gas_price,
        "nonce": web3.eth.get_transaction_count(account.address),
        "chainId": 1,  # Ethereum mainnet
    }

    signed_txn = web3.eth.account.sign_transaction(txn, account.key)
    txn_hash = web3.eth.send_raw_transaction(signed_txn.raw_transaction)
    print(f"Approval Transaction Hash: {web3.to_hex(txn_hash)}")

    return txn_hash

def execute_transaction(calldata, router_address):
    """Execute the swap transaction on GlueX Router"""
    txn = {
        "from": account.address,
        "to": Web3.to_checksum_address(router_address),
        "data": calldata,
        "gas": COMPUTATION_UNITS,
        "gasPrice": web3.eth.gas_price,
        "nonce": web3.eth.get_transaction_count(account.address),
        "chainId": 1,  # Ethereum mainnet
    }

    signed_txn = web3.eth.account.sign_transaction(txn, account.key)

    try:
        txn_hash = web3.eth.send_raw_transaction(signed_txn.raw_transaction)
    except Web3RPCError as err:
        time.sleep(5)
        txn["nonce"] = web3.eth.get_transaction_count(account.address)
        signed_txn = web3.eth.account.sign_transaction(txn, account.key)
        txn_hash = web3.eth.send_raw_transaction(signed_txn.raw_transaction)

    print(f"Transaction Hash: {web3.to_hex(txn_hash)}")

    return txn_hash

def main():
    # Fetch the quote
    quote_data = fetch_quote()
    if quote_data.get('statusCode') != 200:
        print("Error fetching quote:", quote_data)
        return

    print("Quote received successfully:", quote_data)
    router_address = quote_data["result"]["router"]
    calldata = quote_data["result"]["calldata"]

    # Approve the router contract
    print("Approving router contract to spend GALA...")
    txn_hash = approve_spender(router_address, INPUT_AMOUNT, GALA_ADDRESS)
    print("Waiting for approval transaction confirmation...")
    approval_receipt = web3.eth.wait_for_transaction_receipt(txn_hash)
    if approval_receipt.status != 1:
        print("Approval failed. Aborting.")
        return

    # Execute the transaction
    print("Executing transaction...")
    execute_txn = execute_transaction(calldata, router_address)

    receipt = web3.eth.wait_for_transaction_receipt(execute_txn)
    print("Transaction confirmed. Receipt:")
    print(receipt)


if __name__ == "__main__":
    main()
import { Web3 } from 'web3';
import axios from 'axios';

// Configuration
const API_KEY = "your_api_key";
const UNIQUE_PID = "your_unique_pid";
const QUOTE_ENDPOINT = "https://router.gluex.xyz/v1/quote";
const RPC_URL = "https://mainnet.gateway.tenderly.co/your_rpc_url";
const PRIVATE_KEY = "your_private_key";

// Token Addresses
const GALA_ADDRESS = "0xd1d2Eb1B1e90B638588728b4130137D262C87cae";
const AETHWETH_ADDRESS = "0x4d5F47FA6A74757f35C14fD3a6Ef8E3C9BC514E8";

// Input amount (in decimals)
const INPUT_AMOUNT = BigInt("2000000000000"); // 2,000,000 GALA (including decimals)

// Initialize Web3
const web3 = new Web3(RPC_URL);
const account = web3.eth.accounts.privateKeyToAccount(PRIVATE_KEY);
const COMPUTATION_UNITS = 1000000;

// Types
interface QuoteBody {
    chainID: string;
    userAddress: string;
    outputReceiver: string;
    uniquePID: string;
    inputToken: string;
    outputToken: string;
    inputAmount: bigint;
    isPermit2: boolean;
}

interface QuoteRequestBody {
    chainID: string;
    userAddress: string;
    outputReceiver: string;
    uniquePID: string;
    inputToken: string;
    outputToken: string;
    inputAmount: string;
    isPermit2: boolean;
}

interface QuoteResponse {
    statusCode: number;
    result?: {
        router: string;
        calldata: string;
    };
}

const fetchQuote = async (body: QuoteRequestBody, headers: { [key: string]: string }): Promise<QuoteResponse> => {
    console.log("Fetch a quote from the GlueX Router for $GALA to $aETHWETH swap");
    const response = await axios.post(QUOTE_ENDPOINT, body, { headers });
    return response.data;
};

const approveSpender = async (
    spender: string,
    amount: bigint,
    tokenAddress: string
): Promise<string> => {
    const signature = "0x095ea7b3";
    const paddedSpender = web3.utils.toChecksumAddress(spender).slice(2).padStart(64, '0');
    const calldata = `${signature}${paddedSpender}${amount.toString(16).padStart(64, '0')}`;

    const txn = {
        from: account.address,
        to: web3.utils.toChecksumAddress(tokenAddress),
        data: calldata,
        gas: COMPUTATION_UNITS,
        gasPrice: await web3.eth.getGasPrice(),
        nonce: await web3.eth.getTransactionCount(account.address),
        chainId: 1, // Ethereum mainnet
    };

    const signedTxn = await account.signTransaction(txn);
    const receipt = await web3.eth.sendSignedTransaction(signedTxn.rawTransaction);
    const txHash = web3.utils.toHex(receipt.transactionHash);
    console.log(`Approval Transaction Hash: ${txHash}`);
    return txHash;
};

const executeTransaction = async (
    calldata: string,
    routerAddress: string
): Promise<string> => {
    const txn = {
        from: account.address,
        to: web3.utils.toChecksumAddress(routerAddress),
        data: calldata,
        gas: COMPUTATION_UNITS,
        gasPrice: await web3.eth.getGasPrice(),
        nonce: await web3.eth.getTransactionCount(account.address),
        chainId: 1, // Ethereum mainnet
    };

    try {
        const signedTxn = await account.signTransaction(txn);
        const receipt = await web3.eth.sendSignedTransaction(signedTxn.rawTransaction);
        const txHash = web3.utils.toHex(receipt.transactionHash);
        console.log(`Transaction Hash: ${txHash}`);
        return txHash;
    } catch (err) {
        await new Promise(resolve => setTimeout(resolve, 5000));
        txn.nonce = await web3.eth.getTransactionCount(account.address);
        const signedTxn = await account.signTransaction(txn);
        const receipt = await web3.eth.sendSignedTransaction(signedTxn.rawTransaction);
        const txHash = web3.utils.toHex(receipt.transactionHash);
        console.log(`Transaction Hash: ${txHash}`);
        return txHash;
    }
};

const main = async (): Promise<void> => {
    const headers = { "x-api-key": API_KEY };
    const body: QuoteBody = {
        chainID: "ethereum",
        userAddress: account.address,
        outputReceiver: account.address,
        uniquePID: UNIQUE_PID,
        inputToken: GALA_ADDRESS,
        outputToken: AETHWETH_ADDRESS,
        inputAmount: INPUT_AMOUNT,
        isPermit2: false
    };

    // Convert BigInt to string for axios serialization
    const requestBody = {
        ...body,
        inputAmount: body.inputAmount.toString()
    };

    console.log("Fetching swap quote...");
    const quoteData = await fetchQuote(requestBody, headers);

    if (quoteData.statusCode !== 200) {
        console.log("Error fetching quote:", quoteData);
        return;
    }

    console.log("Quote received successfully:", quoteData);
    const routerAddress = quoteData.result!.router;
    const calldata = quoteData.result!.calldata;

    // Approve the router contract
    console.log("Approving router contract to spend GALA...");
    const txnHash = await approveSpender(routerAddress, INPUT_AMOUNT, GALA_ADDRESS);
    console.log("Waiting for approval transaction confirmation...");
    const approvalReceipt = await web3.eth.getTransactionReceipt(txnHash);
    if (!approvalReceipt || !approvalReceipt.status) {
        console.log("Approval failed. Aborting.");
        return;
    }

    // Execute the transaction
    console.log("Executing transaction...");
    const executeTxn = await executeTransaction(calldata, routerAddress);

    const receipt = await web3.eth.getTransactionReceipt(executeTxn);
    console.log("Transaction confirmed. Receipt:");
    console.log(receipt);
};

// Run the main function
main().catch(console.error);
import { Web3 } from 'web3';
import axios from 'axios';

// Configuration
const API_KEY = "your_api_key";
const UNIQUE_PID = "your_unique_pid";
const QUOTE_ENDPOINT = "https://router.gluex.xyz/v1/quote";
const RPC_URL = "https://mainnet.gateway.tenderly.co/your_rpc_url";
const PRIVATE_KEY = "your_private_key";

// Token Addresses
const GALA_ADDRESS = "0xd1d2Eb1B1e90B638588728b4130137D262C87cae";
const AETHWETH_ADDRESS = "0x4d5F47FA6A74757f35C14fD3a6Ef8E3C9BC514E8";

// Input amount (in decimals)
const INPUT_AMOUNT = BigInt("2000000000000"); // 2,000,000 GALA (including decimals)

// Initialize Web3
const web3 = new Web3(RPC_URL);
const account = web3.eth.accounts.privateKeyToAccount(PRIVATE_KEY);
const COMPUTATION_UNITS = 1000000;

const fetchQuote = async (body, headers) => {
    console.log("Fetch a quote from the GlueX Router for $GALA to $aETHWETH swap");
    const response = await axios.post(QUOTE_ENDPOINT, body, { headers });
    return response.data;
};

const approveSpender = async (spender, amount, tokenAddress) => {
    const signature = "0x095ea7b3";
    const paddedSpender = web3.utils.toChecksumAddress(spender).slice(2).padStart(64, '0');
    const calldata = `${signature}${paddedSpender}${amount.toString(16).padStart(64, '0')}`;

    const txn = {
        from: account.address,
        to: web3.utils.toChecksumAddress(tokenAddress),
        data: calldata,
        gas: COMPUTATION_UNITS,
        gasPrice: await web3.eth.getGasPrice(),
        nonce: await web3.eth.getTransactionCount(account.address),
        chainId: 1, // Ethereum mainnet
    };

    const signedTxn = await account.signTransaction(txn);
    const receipt = await web3.eth.sendSignedTransaction(signedTxn.rawTransaction);
    const txHash = web3.utils.toHex(receipt.transactionHash);
    console.log(`Approval Transaction Hash: ${txHash}`);
    return txHash;
};

const executeTransaction = async (calldata, routerAddress) => {
    const txn = {
        from: account.address,
        to: web3.utils.toChecksumAddress(routerAddress),
        data: calldata,
        gas: COMPUTATION_UNITS,
        gasPrice: await web3.eth.getGasPrice(),
        nonce: await web3.eth.getTransactionCount(account.address),
        chainId: 1, // Ethereum mainnet
    };

    try {
        const signedTxn = await account.signTransaction(txn);
        const receipt = await web3.eth.sendSignedTransaction(signedTxn.rawTransaction);
        const txHash = web3.utils.toHex(receipt.transactionHash);
        console.log(`Transaction Hash: ${txHash}`);
        return txHash;
    } catch (err) {
        await new Promise(resolve => setTimeout(resolve, 5000));
        txn.nonce = await web3.eth.getTransactionCount(account.address);
        const signedTxn = await account.signTransaction(txn);
        const receipt = await web3.eth.sendSignedTransaction(signedTxn.rawTransaction);
        const txHash = web3.utils.toHex(receipt.transactionHash);
        console.log(`Transaction Hash: ${txHash}`);
        return txHash;
    }
};

const main = async () => {
    const headers = { "x-api-key": API_KEY };
    const body = {
        chainID: "ethereum",
        userAddress: account.address,
        outputReceiver: account.address,
        uniquePID: UNIQUE_PID,
        inputToken: GALA_ADDRESS,
        outputToken: AETHWETH_ADDRESS,
        inputAmount: INPUT_AMOUNT,
        isPermit2: false
    };

    // Convert BigInt to string for axios serialization
    const requestBody = {
        ...body,
        inputAmount: body.inputAmount.toString()
    };

    console.log("Fetching swap quote...");
    const quoteData = await fetchQuote(requestBody, headers);

    if (quoteData.statusCode !== 200) {
        console.log("Error fetching quote:", quoteData);
        return;
    }

    console.log("Quote received successfully:", quoteData);
    const routerAddress = quoteData.result.router;
    const calldata = quoteData.result.calldata;

    // Approve the router contract
    console.log("Approving router contract to spend GALA...");
    const txnHash = await approveSpender(routerAddress, INPUT_AMOUNT, GALA_ADDRESS);
    console.log("Waiting for approval transaction confirmation...");
    const approvalReceipt = await web3.eth.getTransactionReceipt(txnHash);
    if (!approvalReceipt || !approvalReceipt.status) {
        console.log("Approval failed. Aborting.");
        return;
    }

    // Execute the transaction
    console.log("Executing transaction...");
    const executeTxn = await executeTransaction(calldata, routerAddress);

    const receipt = await web3.eth.getTransactionReceipt(executeTxn);
    console.log("Transaction confirmed. Receipt:");
    console.log(receipt);
};

// Run the main function
main().catch(console.error);

Running the Script

To execute the deposit, run the script as follows:
python deposit_to_lending_vault.py

Conclusion

With GlueX, moving from $GALA to a yield generating asset like $aETHWETH takes just one transaction. This tutorial demonstrates:
  • Fetching a swap quote
  • Approving the router contract to spend $GALA
  • Executing the transaction to swap and deposit smoothly

Opportunities

🔹 Instant access to yield without manual interventions 🔹 Seamless diversification into yield generating assets 🔹 Cost savings by reducing gas fees across multiple steps With GlueX, DeFi becomes as easy as sending an email 🚀