home / skills / sendaifun / skills / lulo
This skill helps you integrate Lulo to optimize stablecoin yields across Solana protocols with automated risk protection and flexible deposits.
npx playbooks add skill sendaifun/skills --skill luloReview the files below or copy the command above to add this skill to your agents.
---
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](https://dev.lulo.fi).
```typescript
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**:
```json
{
"owner": "YourWalletPublicKey",
"mintAddress": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
"depositType": "protected",
"amount": 100000000
}
```
**Response**:
```json
{
"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**:
```json
{
"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**:
```json
{
"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**:
```json
{
"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
```typescript
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
```typescript
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
```typescript
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
```typescript
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
```python
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}")
```
### Using Lulo Blinks
Lulo also supports Solana Actions/Blinks for simplified interactions:
```typescript
// 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
```typescript
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
```typescript
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
- [Lulo App](https://lulo.fi)
- [Documentation](https://docs.lulo.fi)
- [Developer Portal](https://dev.lulo.fi)
- [API Documentation](https://docs.lulo.fi/api-docs)
- [Lulo Labs GitHub](https://github.com/lulo-labs)
- [Lulo CPI Example](https://github.com/gabrielkoerich/lulo-cpi-example)
- [Discord](https://discord.gg/lulo)
- [Telegram](https://t.me/uselulo)
- [Twitter/X](https://twitter.com/uselulo)
---
## 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
```
This skill is a complete integration guide for Lulo, Solana's lending aggregator that automates yield optimization across Kamino, Drift, MarginFi, and Jupiter. It explains API flows for deposits, withdrawals, balance queries, and special deposit types (Protected, Boosted, Custom). The guide includes TypeScript and Python examples and practical notes for production usage.
The skill inspects Lulo API endpoints to generate serialized Solana transactions for deposits and withdrawals, and to fetch account and pool data. It shows how to sign and submit VersionedTransactions on-chain after receiving base64 serialized transactions from Lulo. It also documents deposit behaviors: automatic routing, hourly rebalancing, and protection mechanics between Protected and Boosted deposits.
What deposit types does Lulo support?
Protected, Boosted, and Custom deposits are supported. Protected offers coverage, Boosted provides higher yield in exchange for first-loss exposure, and Custom lets you pick protocol allocations.
Do funds get custodyed by Lulo?
No. Lulo routes funds directly to integrated protocols; it does not custody user funds.