sendaifun

lulo

38
6
# Install this skill:
npx skills add sendaifun/skills --skill "lulo"

Install specific skill from multi-skill repository

# Description

Complete guide for Lulo - Solana's premier lending aggregator. Covers API integration for deposits, withdrawals, balance queries, Protected/Boosted deposits, Custom deposits, and automated yield optimization across Kamino, Drift, MarginFi, and Jupiter.

# SKILL.md


name: lulo
description: Complete guide for Lulo - Solana's premier lending aggregator. Covers API integration for deposits, withdrawals, balance queries, Protected/Boosted deposits, Custom deposits, and automated yield optimization across Kamino, Drift, MarginFi, and Jupiter.


Lulo Development Guide

A comprehensive guide for integrating Lulo, Solana's only lending aggregator, into your applications. Lulo automatically routes deposits to the highest-yielding DeFi protocols while providing optional smart contract protection.

What is Lulo?

Lulo (formerly FlexLend) is a DeFi savings platform for stablecoins on Solana. It automatically allocates deposits across integrated protocols to maximize yields while maintaining desired risk exposure.

Key Features

Feature Description
Yield Aggregation Automatically routes deposits to highest-yielding protocols
Lulo Protect Built-in smart contract risk protection (Protected/Boosted deposits)
Custom Deposits Full control over protocol allocation and risk parameters
Instant Withdrawals No lock-up periods (except 48h cooldown for Boosted)
Multi-Protocol Integrates Kamino, Drift, MarginFi, Jupiter
No Custody Funds flow directly to integrated protocols, not held by Lulo

Why Use Lulo?

  • Automated Rebalancing: Checks rates hourly, automatically moves funds to better yields
  • Risk Management: Choose between Protected (insured), Boosted (higher yield), or Custom deposits
  • Zero Management Fees: Only 0.005 SOL one-time initialization fee
  • Multi-Reward Accrual: Earn rewards from multiple protocols from a single deposit
  • 9,400+ Lifetime Depositors with $34M+ in directed liquidity

Overview

Lulo provides three deposit types:

  • Protected Deposits: Stable yields with automatic coverage against protocol failures
  • Boosted Deposits: Higher yields by providing insurance for Protected deposits
  • Custom Deposits: Direct control over which protocols receive your funds

Integrated Protocols

Protocol Description
Kamino Finance Lending and liquidity vaults
Drift Protocol Perpetuals and lending
MarginFi Lending and borrowing
Jupiter Lending/earn features

Supported Tokens

  • Stablecoins: USDC, USDT, USDS, PYUSD
  • Native: SOL
  • LSTs: bSOL, JitoSOL, mSOL
  • Other: BONK, JUP, ORCA, COPE, CASH

Minimum Deposits: $100 for stablecoins, 1 SOL for native token


Quick Start

API Authentication

All API requests require a valid API key. Get your key from the Lulo Developer Dashboard.

const headers = {
  'Content-Type': 'application/json',
  'x-api-key': process.env.LULO_API_KEY,
};

Base URLs

Environment URL
Production https://api.lulo.fi
Staging https://staging.lulo.fi
Blinks https://blink.lulo.fi
Developer Portal https://dev.lulo.fi

API Reference

Generate Deposit Transaction

Creates a serialized transaction for depositing tokens into Lulo.

Endpoint: POST /v1/generate.transactions.deposit

Query Parameters:
| Parameter | Type | Description |
|-----------|------|-------------|
| priorityFee | number | Priority fee in microlamports (e.g., 500000) |

Request Body:

{
  "owner": "YourWalletPublicKey",
  "mintAddress": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
  "depositType": "protected",
  "amount": 100000000
}

Response:

{
  "transaction": "base64EncodedSerializedTransaction",
  "lastValidBlockHeight": 123456789
}

Generate Withdrawal Transaction

Creates a serialized transaction for withdrawing tokens from Lulo.

Endpoint: POST /v1/generate.transactions.withdraw

Query Parameters:
| Parameter | Type | Description |
|-----------|------|-------------|
| priorityFee | number | Priority fee in microlamports |

Request Body:

{
  "owner": "YourWalletPublicKey",
  "mintAddress": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
  "withdrawType": "protected",
  "amount": 50000000
}

Get Account Data

Retrieves user balances, interest earned, and APY metrics.

Endpoint: GET /v1/account/{walletAddress}

Response:

{
  "totalDeposited": 1000000000,
  "totalInterestEarned": 5000000,
  "currentApy": 8.5,
  "positions": [
    {
      "mint": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
      "depositType": "protected",
      "balance": 500000000,
      "interestEarned": 2500000,
      "apy": 7.2
    }
  ]
}

Get Pool Data

Returns current APY rates, liquidity amounts, and capacity metrics.

Endpoint: GET /v1/pools

Response:

{
  "pools": [
    {
      "mint": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
      "symbol": "USDC",
      "protectedApy": 6.5,
      "boostedApy": 9.2,
      "totalDeposited": 34000000000000,
      "availableCapacity": 10000000000000
    }
  ]
}

Get Pending Withdrawals

Lists active withdrawal requests with cooldown periods.

Endpoint: GET /v1/account/{walletAddress}/pending-withdrawals

Initialize Referrer

Sets up a referrer account for earning referral fees.

Endpoint: POST /v1/referrer/initialize

Claim Referral Rewards

Processes referral fee claims.

Endpoint: POST /v1/referrer/claim


Deposit Types Explained

Protected Deposits

Designed for risk-averse users seeking stable yields with automatic coverage.

How it works:
1. Deposits earn interest from lending across integrated protocols
2. A portion of interest is shared with Boosted depositors (protection fee)
3. If a protocol fails, Boosted deposits cover Protected losses automatically

Benefits:
- Lower risk with priority coverage
- Stable, predictable yields
- No claims to file - protection is automatic
- Instant withdrawals

Coverage includes: Smart contract exploits, oracle failures, bad debt events

Not covered: Solana network failures, USDC depegging, Lulo contract failures

Boosted Deposits

Higher yields in exchange for providing insurance to Protected depositors.

How it works:
1. Earn lending yields from integrated protocols
2. Receive additional yield from Protected deposit interest sharing
3. Act as first-loss layer if a protocol fails

Benefits:
- Higher APY (typically 1-2% more than Protected)
- Dual income streams (lending + protection fees)

Risks:
- First-loss position in case of protocol failures
- 48-hour withdrawal cooldown

Custom Deposits

Full control over protocol allocation and risk parameters.

Features:
- Select specific protocols (Kamino, Drift, MarginFi, Jupiter)
- Set maximum exposure caps per protocol
- Automatic reallocation when yields change
- No protection coverage (direct protocol exposure)

Example: 50% max exposure with 3 protocols means no more than half your funds in any single protocol.


Integration Examples

TypeScript: Deposit to Lulo

import { Connection, Transaction, VersionedTransaction, Keypair } from '@solana/web3.js';

const LULO_API_URL = 'https://api.lulo.fi';

interface DepositParams {
  owner: string;
  mintAddress: string;
  amount: number;
  depositType: 'protected' | 'boosted' | 'regular';
  priorityFee?: number;
}

async function generateDepositTransaction(params: DepositParams): Promise<string> {
  const { owner, mintAddress, amount, depositType, priorityFee = 500000 } = params;

  const response = await fetch(
    `${LULO_API_URL}/v1/generate.transactions.deposit?priorityFee=${priorityFee}`,
    {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'x-api-key': process.env.LULO_API_KEY!,
      },
      body: JSON.stringify({
        owner,
        mintAddress,
        depositType,
        amount,
      }),
    }
  );

  if (!response.ok) {
    throw new Error(`Deposit failed: ${response.statusText}`);
  }

  const data = await response.json();
  return data.transaction;
}

async function deposit(
  connection: Connection,
  wallet: Keypair,
  mintAddress: string,
  amount: number,
  depositType: 'protected' | 'boosted' | 'regular' = 'protected'
): Promise<string> {
  // Generate deposit transaction
  const serializedTx = await generateDepositTransaction({
    owner: wallet.publicKey.toBase58(),
    mintAddress,
    amount,
    depositType,
  });

  // Deserialize and sign
  const txBuffer = Buffer.from(serializedTx, 'base64');
  const transaction = VersionedTransaction.deserialize(txBuffer);
  transaction.sign([wallet]);

  // Send and confirm
  const signature = await connection.sendTransaction(transaction);
  await connection.confirmTransaction(signature, 'confirmed');

  console.log('Deposit successful:', signature);
  return signature;
}

// Usage
const connection = new Connection('https://api.mainnet-beta.solana.com');
const USDC_MINT = 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v';

await deposit(
  connection,
  wallet,
  USDC_MINT,
  100_000_000, // 100 USDC (6 decimals)
  'protected'
);

TypeScript: Withdraw from Lulo

interface WithdrawParams {
  owner: string;
  mintAddress: string;
  amount: number;
  withdrawType: 'protected' | 'boosted' | 'regular';
  priorityFee?: number;
}

async function generateWithdrawTransaction(params: WithdrawParams): Promise<string> {
  const { owner, mintAddress, amount, withdrawType, priorityFee = 500000 } = params;

  const response = await fetch(
    `${LULO_API_URL}/v1/generate.transactions.withdraw?priorityFee=${priorityFee}`,
    {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'x-api-key': process.env.LULO_API_KEY!,
      },
      body: JSON.stringify({
        owner,
        mintAddress,
        withdrawType,
        amount,
      }),
    }
  );

  if (!response.ok) {
    throw new Error(`Withdrawal failed: ${response.statusText}`);
  }

  const data = await response.json();
  return data.transaction;
}

async function withdraw(
  connection: Connection,
  wallet: Keypair,
  mintAddress: string,
  amount: number,
  withdrawType: 'protected' | 'boosted' | 'regular' = 'protected'
): Promise<string> {
  const serializedTx = await generateWithdrawTransaction({
    owner: wallet.publicKey.toBase58(),
    mintAddress,
    amount,
    withdrawType,
  });

  const txBuffer = Buffer.from(serializedTx, 'base64');
  const transaction = VersionedTransaction.deserialize(txBuffer);
  transaction.sign([wallet]);

  const signature = await connection.sendTransaction(transaction);
  await connection.confirmTransaction(signature, 'confirmed');

  console.log('Withdrawal successful:', signature);
  return signature;
}

TypeScript: Get Account Balance

interface LuloPosition {
  mint: string;
  depositType: string;
  balance: number;
  interestEarned: number;
  apy: number;
}

interface LuloAccount {
  totalDeposited: number;
  totalInterestEarned: number;
  currentApy: number;
  positions: LuloPosition[];
}

async function getAccountData(walletAddress: string): Promise<LuloAccount> {
  const response = await fetch(
    `${LULO_API_URL}/v1/account/${walletAddress}`,
    {
      headers: {
        'x-api-key': process.env.LULO_API_KEY!,
      },
    }
  );

  if (!response.ok) {
    throw new Error(`Failed to fetch account: ${response.statusText}`);
  }

  return response.json();
}

// Usage
const account = await getAccountData(wallet.publicKey.toBase58());
console.log('Total Deposited:', account.totalDeposited);
console.log('Interest Earned:', account.totalInterestEarned);
console.log('Current APY:', account.currentApy);

Using Solana Agent Kit

import { SolanaAgentKit } from 'solana-agent-kit';

const agent = new SolanaAgentKit(
  privateKey,
  rpcUrl,
  openAiApiKey
);

// Lend USDC using Lulo (gets best APR)
const signature = await agent.methods.lendAssets(
  agent,
  100 // amount of USDC to lend
);

console.log('Lending transaction:', signature);

Python: Lulo Integration

import aiohttp
import os
from solders.keypair import Keypair
from solders.transaction import VersionedTransaction
from solana.rpc.async_api import AsyncClient
from solana.rpc.commitment import Confirmed
from solana.rpc.types import TxOpts
import base64

LULO_API_URL = "https://api.lulo.fi"
LULO_API_KEY = os.environ.get("LULO_API_KEY")

async def lulo_deposit(
    client: AsyncClient,
    wallet: Keypair,
    mint_address: str,
    amount: int,
    deposit_type: str = "protected"
) -> str:
    """Deposit tokens to Lulo for yield optimization."""

    async with aiohttp.ClientSession() as session:
        # Generate deposit transaction
        async with session.post(
            f"{LULO_API_URL}/v1/generate.transactions.deposit?priorityFee=500000",
            headers={
                "Content-Type": "application/json",
                "x-api-key": LULO_API_KEY,
            },
            json={
                "owner": str(wallet.pubkey()),
                "mintAddress": mint_address,
                "depositType": deposit_type,
                "amount": amount,
            }
        ) as response:
            if response.status != 200:
                raise Exception(f"Deposit failed: {await response.text()}")

            data = await response.json()
            tx_data = base64.b64decode(data["transaction"])

    # Deserialize, sign, and send
    transaction = VersionedTransaction.from_bytes(tx_data)
    transaction.sign([wallet])

    signature = await client.send_transaction(
        transaction,
        opts=TxOpts(preflight_commitment=Confirmed)
    )

    await client.confirm_transaction(signature.value, commitment="confirmed")

    return str(signature.value)

async def lulo_withdraw(
    client: AsyncClient,
    wallet: Keypair,
    mint_address: str,
    amount: int,
    withdraw_type: str = "protected"
) -> str:
    """Withdraw tokens from Lulo."""

    async with aiohttp.ClientSession() as session:
        async with session.post(
            f"{LULO_API_URL}/v1/generate.transactions.withdraw?priorityFee=500000",
            headers={
                "Content-Type": "application/json",
                "x-api-key": LULO_API_KEY,
            },
            json={
                "owner": str(wallet.pubkey()),
                "mintAddress": mint_address,
                "withdrawType": withdraw_type,
                "amount": amount,
            }
        ) as response:
            if response.status != 200:
                raise Exception(f"Withdrawal failed: {await response.text()}")

            data = await response.json()
            tx_data = base64.b64decode(data["transaction"])

    transaction = VersionedTransaction.from_bytes(tx_data)
    transaction.sign([wallet])

    signature = await client.send_transaction(
        transaction,
        opts=TxOpts(preflight_commitment=Confirmed)
    )

    await client.confirm_transaction(signature.value, commitment="confirmed")

    return str(signature.value)

# Usage
async def main():
    client = AsyncClient("https://api.mainnet-beta.solana.com")
    wallet = Keypair.from_base58_string("your-private-key")

    USDC_MINT = "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"

    # Deposit 100 USDC
    signature = await lulo_deposit(
        client,
        wallet,
        USDC_MINT,
        100_000_000,  # 100 USDC
        "protected"
    )
    print(f"Deposit: {signature}")

Lulo also supports Solana Actions/Blinks for simplified interactions:

// Blink endpoint for lending
const blinkUrl = `https://blink.lulo.fi/actions?amount=${amount}&symbol=USDC`;

// Fetch blink metadata
const response = await fetch(blinkUrl);
const blinkData = await response.json();

// Execute the action
const postResponse = await fetch(blinkUrl, {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ account: walletAddress }),
});

const { transaction } = await postResponse.json();
// Sign and send transaction...

Common Token Addresses

Token Mint Address
USDC EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v
USDT Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB
USDS USDSwr9ApdHk5bvJKMjzff41FfuX8bSxdKcR81vTwcA
PYUSD 2b1kV6DkPAnxd5ixfnxCpjxmKwqjjaYmCZfHsFu24GXo
SOL (Wrapped) So11111111111111111111111111111111111111112
mSOL mSoLzYCxHdYgdzU16g5QSh3i5K3z3KZK7ytfqcJm7So
JitoSOL J1toso1uCk3RLmjorhTtrVwY9HJ7X8V9yYac6Y7kGCPn
bSOL bSo13r4TkiE4KumL71LsHTPpL2euBYLFx6h9HP3piy1

Best Practices

Security

  1. Never expose API keys - Use environment variables
  2. Validate transactions - Simulate before signing
  3. Set appropriate slippage - Account for rate changes
  4. Monitor positions - Regularly check balances and APY

Performance

  1. Use priority fees - 500,000+ microlamports for faster confirmation
  2. Batch operations - Combine multiple operations when possible
  3. Cache pool data - Rates update hourly, cache appropriately

Error Handling

try {
  const signature = await deposit(connection, wallet, USDC_MINT, amount, 'protected');
} catch (error) {
  if (error.message.includes('BAD_REQUEST')) {
    console.error('Invalid parameters');
  } else if (error.message.includes('UNAUTHORIZED')) {
    console.error('Invalid API key');
  } else if (error.message.includes('NOT_FOUND')) {
    console.error('Account or pool not found');
  } else {
    throw error;
  }
}

Monitoring APY

async function monitorRates(interval: number = 3600000) { // 1 hour
  setInterval(async () => {
    const pools = await fetch(`${LULO_API_URL}/v1/pools`, {
      headers: { 'x-api-key': process.env.LULO_API_KEY! },
    }).then(r => r.json());

    pools.pools.forEach(pool => {
      console.log(`${pool.symbol}: Protected ${pool.protectedApy}% | Boosted ${pool.boostedApy}%`);
    });
  }, interval);
}

Fee Structure

Fee Type Amount When
Initialization 0.005 SOL First deposit only
Management None -
Withdrawal None -
Transaction Variable Per transaction (Solana fees)

Resources


Skill Structure

lulo/
├── SKILL.md                      # This file
├── resources/
│   ├── api-reference.md          # Complete API documentation
│   └── token-addresses.md        # Supported token addresses
├── examples/
│   ├── deposit/
│   │   └── deposit.ts            # Deposit examples
│   ├── withdraw/
│   │   └── withdraw.ts           # Withdrawal examples
│   ├── balance/
│   │   └── balance.ts            # Balance query examples
│   └── integration/
│       └── full-integration.ts   # Complete integration example
├── templates/
│   └── lulo-client.ts            # Ready-to-use client template
└── docs/
    └── troubleshooting.md        # Common issues and solutions

# Supported AI Coding Agents

This skill is compatible with the SKILL.md standard and works with all major AI coding agents:

Learn more about the SKILL.md standard and how to use these skills with your preferred AI coding agent.