Guide de Développement Lulo
Un guide complet pour intégrer Lulo, l'unique agrégateur de prêt de Solana, dans vos applications. Lulo achemine automatiquement les dépôts vers les protocoles DeFi offrant les meilleurs rendements tout en fournissant une protection facultative par contrat intelligent.
Qu'est-ce que Lulo ?
Lulo (anciennement FlexLend) est une plateforme d'épargne DeFi pour stablecoins sur Solana. Elle alloue automatiquement les dépôts entre les protocoles intégrés pour maximiser les rendements tout en maintenant l'exposition au risque souhaitée.
Fonctionnalités clés
| Fonctionnalité | Description |
|---|---|
| Agrégation de rendements | Achemine automatiquement les dépôts vers les protocoles offrant les meilleurs rendements |
| Lulo Protect | Protection intégrée contre les risques des contrats intelligents (dépôts protégés/boostés) |
| Dépôts personnalisés | Contrôle total sur l'allocation des protocoles et les paramètres de risque |
| Retraits instantanés | Aucune période de verrouillage (sauf délai de 48h pour les dépôts boostés) |
| Multi-protocole | Intègre Kamino, Drift, MarginFi, Jupiter |
| Pas de garde | Les fonds circulent directement vers les protocoles intégrés, pas conservés par Lulo |
Pourquoi utiliser Lulo ?
- Rééquilibrage automatisé : Vérifie les taux toutes les heures, déplace automatiquement les fonds vers les meilleurs rendements
- Gestion des risques : Choisissez entre les dépôts protégés (assurés), boostés (rendement plus élevé) ou personnalisés
- Aucuns frais de gestion : Seulement 0,005 SOL comme frais d'initialisation uniques
- Accumulation multi-récompenses : Gagnez des récompenses de plusieurs protocoles à partir d'un seul dépôt
- 9 400+ déposants à vie avec plus de 34 millions de dollars en liquidités dirigées
Aperçu
Lulo propose trois types de dépôts :
- Dépôts protégés : Rendements stables avec couverture automatique contre les défaillances de protocoles
- Dépôts boostés : Rendements plus élevés en fournissant une assurance pour les dépôts protégés
- Dépôts personnalisés : Contrôle direct sur les protocoles qui reçoivent vos fonds
Protocoles intégrés
| Protocole | Description |
|---|---|
| Kamino Finance | Prêt et coffres de liquidité |
| Drift Protocol | Contrats perpétuels et prêt |
| MarginFi | Prêt et emprunt |
| Jupiter | Fonctionnalités de prêt/gain |
Tokens supportés
- Stablecoins : USDC, USDT, USDS, PYUSD
- Natif : SOL
- LSTs : bSOL, JitoSOL, mSOL
- Autres : BONK, JUP, ORCA, COPE, CASH
Dépôts minimums : 100 $ pour les stablecoins, 1 SOL pour le token natif
Démarrage rapide
Authentification API
Toutes les requêtes API nécessitent une clé API valide. Obtenez votre clé depuis le Tableau de bord des développeurs Lulo.
const headers = {
'Content-Type': 'application/json',
'x-api-key': process.env.LULO_API_KEY,
};
URLs de base
| Environnement | URL |
|---|---|
| Production | https://api.lulo.fi |
| Staging | https://staging.lulo.fi |
| Blinks | https://blink.lulo.fi |
| Portail développeur | https://dev.lulo.fi |
Référence API
Générer une transaction de dépôt
Crée une transaction sérialisée pour déposer des tokens dans Lulo.
Point de terminaison : POST /v1/generate.transactions.deposit
Paramètres de requête :
| Paramètre | Type | Description |
|-----------|------|-------------|
| priorityFee | nombre | Frais prioritaires en microlamports (ex. 500000) |
Corps de la requête :
{
"owner": "YourWalletPublicKey",
"mintAddress": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
"depositType": "protected",
"amount": 100000000
}
Réponse :
{
"transaction": "base64EncodedSerializedTransaction",
"lastValidBlockHeight": 123456789
}
Générer une transaction de retrait
Crée une transaction sérialisée pour retirer des tokens de Lulo.
Point de terminaison : POST /v1/generate.transactions.withdraw
Paramètres de requête :
| Paramètre | Type | Description |
|-----------|------|-------------|
| priorityFee | nombre | Frais prioritaires en microlamports |
Corps de la requête :
{
"owner": "YourWalletPublicKey",
"mintAddress": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
"withdrawType": "protected",
"amount": 50000000
}
Obtenir les données du compte
Récupère les soldes de l'utilisateur, les intérêts gagnés et les métriques APY.
Point de terminaison : GET /v1/account/{walletAddress}
Réponse :
{
"totalDeposited": 1000000000,
"totalInterestEarned": 5000000,
"currentApy": 8.5,
"positions": [
{
"mint": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
"depositType": "protected",
"balance": 500000000,
"interestEarned": 2500000,
"apy": 7.2
}
]
}
Obtenir les données des pools
Retourne les taux APY actuels, les montants de liquidité et les métriques de capacité.
Point de terminaison : GET /v1/pools
Réponse :
{
"pools": [
{
"mint": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
"symbol": "USDC",
"protectedApy": 6.5,
"boostedApy": 9.2,
"totalDeposited": 34000000000000,
"availableCapacity": 10000000000000
}
]
}
Obtenir les retraits en attente
Liste les demandes de retrait actives avec les périodes de refroidissement.
Point de terminaison : GET /v1/account/{walletAddress}/pending-withdrawals
Initialiser un parrain
Configure un compte parrain pour gagner des frais de parrainage.
Point de terminaison : POST /v1/referrer/initialize
Réclamer les récompenses de parrainage
Traite les réclamations de frais de parrainage.
Point de terminaison : POST /v1/referrer/claim
Types de dépôts expliqués
Dépôts protégés
Conçus pour les utilisateurs averses au risque cherchant des rendements stables avec couverture automatique.
Fonctionnement :
- Les dépôts génèrent des intérêts sur les prêts entre protocoles intégrés
- Une partie des intérêts est partagée avec les déposants boostés (frais de protection)
- Si un protocole échoue, les dépôts boostés couvrent automatiquement les pertes protégées
Avantages :
- Risque plus faible avec couverture prioritaire
- Rendements stables et prévisibles
- Aucune réclamation à déposer - la protection est automatique
- Retraits instantanés
Couverture inclut : Exploitations de contrats intelligents, défaillances d'oracle, événements de mauvaise dette
Non couvert : Défaillances du réseau Solana, dépégage d'USDC, défaillances du contrat Lulo
Dépôts boostés
Rendements plus élevés en échange de la fourniture d'assurance aux déposants protégés.
Fonctionnement :
- Gagnez des rendements de prêt des protocoles intégrés
- Recevez un rendement supplémentaire du partage d'intérêts de dépôt protégé
- Agissez comme couche de première perte en cas de défaillance de protocole
Avantages :
- APY plus élevé (généralement 1-2% de plus que protégé)
- Flux de revenus doubles (prêt + frais de protection)
Risques :
- Position de première perte en cas de défaillance de protocole
- Délai de refroidissement de retrait de 48 heures
Dépôts personnalisés
Contrôle total sur l'allocation des protocoles et les paramètres de risque.
Fonctionnalités :
- Sélectionnez des protocoles spécifiques (Kamino, Drift, MarginFi, Jupiter)
- Définissez des plafonds d'exposition maximale par protocole
- Réallocation automatique lors de changements de rendements
- Aucune couverture de protection (exposition directe au protocole)
Exemple : Une exposition maximale de 50% avec 3 protocoles signifie pas plus de la moitié de vos fonds dans un protocole unique.
Exemples d'intégration
TypeScript : Dépôt sur 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 : Retrait de 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 : Obtenir le solde du compte
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);
Utilisation de Solana Agent Kit
import { SolanaAgentKit } from 'solana-agent-kit';
const agent = new SolanaAgentKit(
privateKey,
rpcUrl,
openAiApiKey
);
// Prêter USDC en utilisant Lulo (obtient le meilleur APR)
const signature = await agent.methods.lendAssets(
agent,
100 // montant d'USDC à prêter
);
console.log('Lending transaction:', signature);
Python : Intégration Lulo
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}")
Utilisation des Blinks Lulo
Lulo supporte également les Actions Solana/Blinks pour les interactions simplifiées :
// Endpoint Blink pour les prêts
const blinkUrl = `https://blink.lulo.fi/actions?amount=${amount}&symbol=USDC`;
// Récupérer les métadonnées blink
const response = await fetch(blinkUrl);
const blinkData = await response.json();
// Exécuter l'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...
Adresses de tokens courantes
| Token | Adresse Mint |
|---|---|
| USDC | EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v |
| USDT | Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB |
| USDS | USDSwr9ApdHk5bvJKMjzff41FfuX8bSxdKcR81vTwcA |
| PYUSD | 2b1kV6DkPAnxd5ixfnxCpjxmKwqjjaYmCZfHsFu24GXo |
| SOL (Wrapped) | So11111111111111111111111111111111111111112 |
| mSOL | mSoLzYCxHdYgdzU16g5QSh3i5K3z3KZK7ytfqcJm7So |
| JitoSOL | J1toso1uCk3RLmjorhTtrVwY9HJ7X8V9yYac6Y7kGCPn |
| bSOL | bSo13r4TkiE4KumL71LsHTPpL2euBYLFx6h9HP3piy1 |
Bonnes pratiques
Sécurité
- Ne jamais exposer les clés API - Utilisez les variables d'environnement
- Valider les transactions - Simulez avant de signer
- Définir le slippage approprié - Tenez compte des changements de taux
- Surveiller les positions - Vérifiez régulièrement les soldes et l'APY
Performance
- Utiliser des frais prioritaires - 500 000+ microlamports pour une confirmation plus rapide
- Regrouper les opérations - Combinez plusieurs opérations si possible
- Mettre en cache les données des pools - Les taux se mettent à jour toutes les heures, mettez en cache en conséquence
Gestion des erreurs
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;
}
}
Surveiller l'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);
}
Structure des frais
| Type de frais | Montant | Quand |
|---|---|---|
| Initialisation | 0,005 SOL | Premier dépôt uniquement |
| Gestion | Aucun | - |
| Retrait | Aucun | - |
| Transaction | Variable | Par transaction (frais Solana) |
Ressources
- Application Lulo
- Documentation
- Portail développeur
- Documentation API
- GitHub Lulo Labs
- Exemple Lulo CPI
- Discord
- Telegram
- Twitter/X
Structure de skill
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
Vérifier
- Un vrai appel RPC/SDK a été émis (mainnet, devnet, ou validateur local) et le payload de réponse est capturé dans la transcription, pas seulement paraphrasé
- Chaque transaction a été simulée (
simulateTransactionou équivalent) avant toute étape de signature/envoi ; les logs de simulation sont joints - Pour toute transaction signée/envoyée, la signature obtenue est enregistrée et confirmée sur chaîne (statut retourné par
getSignatureStatusesou URL explorateur) - Slippage, frais prioritaires et limites d'unités de calcul ont été définis explicitement avec des valeurs numériques concrètes, pas laissés à défaut de bibliothèque
- Les adresses de compte, les adresses mint et les ID de programme utilisés dans l'exécution correspondent aux adresses documentées lulo-yield pour le cluster ciblé (pas de mélange mainnet/devnet)
- Le chemin d'erreur a été exercé au moins une fois (solde insuffisant, oracle obsolète, blockhash expiré, etc.) et la gestion d'erreur de l'agent a produit un message lisible par l'utilisateur