lulo

Par elophanto · elophanto

Guide complet pour Lulo - le principal agrégateur de prêts sur Solana. Couvre l'intégration API pour les dépôts, les retraits, les requêtes de solde, les dépôts Protected/Boosted, les dépôts personnalisés, et l'optimisation automatisée du rendement sur Kamino, Drift, MarginFi et Jupiter.

npx skills add https://github.com/elophanto/elophanto --skill lulo

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 :

  1. Les dépôts génèrent des intérêts sur les prêts entre protocoles intégrés
  2. Une partie des intérêts est partagée avec les déposants boostés (frais de protection)
  3. 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 :

  1. Gagnez des rendements de prêt des protocoles intégrés
  2. Recevez un rendement supplémentaire du partage d'intérêts de dépôt protégé
  3. 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é

  1. Ne jamais exposer les clés API - Utilisez les variables d'environnement
  2. Valider les transactions - Simulez avant de signer
  3. Définir le slippage approprié - Tenez compte des changements de taux
  4. Surveiller les positions - Vérifiez régulièrement les soldes et l'APY

Performance

  1. Utiliser des frais prioritaires - 500 000+ microlamports pour une confirmation plus rapide
  2. Regrouper les opérations - Combinez plusieurs opérations si possible
  3. 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


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 (simulateTransaction ou é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 getSignatureStatuses ou 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

Skills similaires