mcpField
L
lulo

lulo

github.com/sendaifunその他130136 回インストール13 週間前に更新
リポジトリ

lulo について

lulo は、Solana アプリに DeFi 貯蓄機能を組み込む開発者向けのガイドです。API で入出金や口座データ取得、複数プロトコルへの配分を扱い、保護付きの預け入れ方式も選べます。

主な機能

  1. 01複数プロトコルへの預け入れ自動配分
  2. 02Protected、Boosted、カスタム預け入れ
  3. 03入出金トランザクションの生成
  4. 04残高、利息、APY の取得
  5. 05プールの金利と流動性の確認

活用シーン

  1. 01Solana アプリへの入出金機能の追加
  2. 02ユーザーのポジションと利息の確認
  3. 03プールの APY と容量の比較

インストール

コマンドをコピーしてターミナルで実行します。

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

インストール後、Claude Code で/luloを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

docs/
troubleshooting.md
examples/balance/
balance.ts
examples/deposit/
deposit.ts
examples/integration/
full-integration.ts
examples/withdraw/
withdraw.ts
resources/
api-reference.md
token-addresses.md
templates/
lulo-client.ts

詳細

Lulo 開発ガイド

Solana 唯一のレンディングアグリゲーターである Lulo をアプリケーションに統合するための包括的なガイドです。Lulo は、任意でスマートコントラクト保護を提供しながら、預け入れ資産を最高利回りの DeFi プロトコルへ自動的に振り分けます。

Lulo とは?

Lulo(旧称 FlexLend)は、Solana 上のステーブルコイン向け DeFi 貯蓄プラットフォームです。希望するリスクエクスポージャーを維持しながら、統合済みプロトコル間で預け入れ資産を自動的に配分し、利回りを最大化します。

主な機能
機能説明
利回りの集約預け入れ資産を最高利回りのプロトコルへ自動的に振り分けます
Lulo Protectスマートコントラクトのリスクに対する組み込み保護(Protected/Boosted 預け入れ)
カスタム預け入れプロトコルの配分とリスクパラメーターを完全に制御できます
即時引き出しロックアップ期間はありません(Boosted の 48 時間のクールダウンを除く)
マルチプロトコルKamino、Drift、MarginFi、Jupiter と統合しています
カストディなし資金は Lulo に保管されず、統合済みプロトコルへ直接送られます
Lulo を利用する理由
  • 自動リバランス:1 時間ごとにレートを確認し、より高い利回りが得られるよう資金を自動的に移動します
  • リスク管理:Protected(保険付き)、Boosted(高利回り)、または Custom の預け入れから選択できます
  • 管理手数料ゼロ:初回のみの初期化手数料 0.005 SOL がかかります
  • 複数の報酬を獲得:1 回の預け入れで複数のプロトコルから報酬を獲得できます
  • 累計預け入れユーザー数 9,400 人以上、振り分けられた流動性は 3,400 万ドル以上

概要

Lulo では、次の 3 種類の預け入れが利用できます。

  • Protected 預け入れ:プロトコル障害に対する自動補償を備えた安定した利回り
  • Boosted 預け入れ:Protected 預け入れに保険を提供することで、より高い利回りを得られます
  • Custom 預け入れ:資金を受け取るプロトコルを直接制御できます
統合済みプロトコル
プロトコル説明
Kamino Financeレンディングおよび流動性ボールト
Drift Protocol無期限先物およびレンディング
MarginFiレンディングおよび借り入れ
Jupiterレンディング/獲得機能
対応トークン
  • ステーブルコイン:USDC、USDT、USDS、PYUSD
  • ネイティブ:SOL
  • LST:bSOL、JitoSOL、mSOL
  • その他:BONK、JUP、ORCA、COPE、CASH

最低預け入れ額:ステーブルコインは 100 ドル、ネイティブトークンは 1 SOL


クイックスタート

API 認証

すべての API リクエストには、有効な API キーが必要です。Lulo Developer Dashboard からキーを取得してください。

const headers = {
  'Content-Type': 'application/json',
  'x-api-key': process.env.LULO_API_KEY,
};
ベース URL
環境URL
本番環境https://api.lulo.fi
Blinkshttps://blink.lulo.fi
開発者ポータルhttps://dev.lulo.fi

API リファレンス

預け入れトランザクションの生成

Lulo にトークンを預け入れるためのシリアライズ済みトランザクションを作成します。

エンドポイント:POST /v1/generate.transactions.deposit

クエリパラメーター:

パラメーター型説明
priorityFeenumberマイクロラムポート単位の優先手数料(例:500000)

リクエスト本文:

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

レスポンス:

{
  "transaction": "base64EncodedSerializedTransaction",
  "lastValidBlockHeight": 123456789
}
引き出しトランザクションの生成

Lulo からトークンを引き出すためのシリアライズ済みトランザクションを作成します。

エンドポイント:POST /v1/generate.transactions.withdraw

クエリパラメーター:

パラメーター型説明
priorityFeenumberマイクロラムポート単位の優先手数料

リクエスト本文:

{
  "owner": "YourWalletPublicKey",
  "mintAddress": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
  "withdrawType": "protected",
  "amount": 50000000
}
アカウントデータの取得

ユーザーの残高、獲得利息、APY 指標を取得します。

エンドポイント:GET /v1/account/{walletAddress}

レスポンス:

{
  "totalDeposited": 1000000000,
  "totalInterestEarned": 5000000,
  "currentApy": 8.5,
  "positions": [
    {
      "mint": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
      "depositType": "protected",
      "balance": 500000000,
      "interestEarned": 2500000,
      "apy": 7.2
    }
  ]
}
プールデータの取得

現在の APY レート、流動性額、容量指標を返します。

エンドポイント:GET /v1/pools

レスポンス:

{
  "pools": [
    {
      "mint": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
      "symbol": "USDC",
      "protectedApy": 6.5,
      "boostedApy": 9.2,
      "totalDeposited": 34000000000000,
      "availableCapacity": 10000000000000
    }
  ]
}
保留中の引き出しの取得

クールダウン期間中の有効な引き出しリクエストを一覧表示します。

エンドポイント:GET /v1/account/{walletAddress}/pending-withdrawals

リファラーの初期化

紹介手数料を獲得するためのリファラーアカウントを設定します。

エンドポイント:POST /v1/referrer/initialize

紹介報酬の請求

紹介手数料の請求を処理します。

エンドポイント:POST /v1/referrer/claim


預け入れタイプの説明

保護付き預け入れ

安定した利回りと自動補償を求める、リスクを避けたいユーザー向けに設計されています。

仕組み:

  1. 統合されたプロトコルでの貸し出しから利息を獲得します
  2. 利息の一部がブースト付き預け入れの利用者に分配されます(保護手数料)
  3. プロトコルに障害が発生した場合、ブースト付き預け入れが保護付き預け入れの損失を自動的に補填します

メリット:

  • 優先的な補償によりリスクが低減されます
  • 安定した予測可能な利回りが得られます
  • 請求手続きは不要です。保護は自動的に適用されます
  • 即時に引き出せます

補償対象:スマートコントラクトの悪用、オラクルの障害、不良債権の発生

補償対象外:Solana ネットワークの障害、USDC のペッグ解除、Lulo コントラクトの障害

ブースト付き預け入れ

保護付き預け入れの利用者に保険を提供する代わりに、より高い利回りを得られます。

仕組み:

  1. 統合されたプロトコルから貸し出し利回りを獲得します
  2. 保護付き預け入れの利息分配から追加の利回りを受け取ります
  3. プロトコルに障害が発生した場合、最初の損失を負担する層として機能します

メリット:

  • より高い APY(通常、保護付き預け入れより1~2%高い)
  • 2種類の収益源(貸し出し+保護手数料)

リスク:

  • プロトコル障害時に最初の損失を負担します
  • 48時間の引き出し待機期間があります
カスタム預け入れ

プロトコルの配分とリスクパラメーターを完全に管理できます。

機能:

  • 特定のプロトコルを選択できます(Kamino、Drift、MarginFi、Jupiter)
  • プロトコルごとに最大エクスポージャー上限を設定できます
  • 利回りが変化すると自動的に再配分されます
  • 保護の補償はありません(プロトコルに直接エクスポージャーを持ちます)

例:3つのプロトコルで最大エクスポージャーを50%に設定すると、単一のプロトコルに預け入れられる資金は全体の半分までになります。


統合例

TypeScript: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: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:アカウント残高の取得
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);
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との統合
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 Blinksの使用

Luloは、簡単に操作できるSolana Actions/Blinksにも対応しています。

// 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...

一般的なトークンアドレス

トークンMintアドレス
USDCEPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v
USDTEs9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB
USDSUSDSwr9ApdHk5bvJKMjzff41FfuX8bSxdKcR81vTwcA
PYUSD2b1kV6DkPAnxd5ixfnxCpjxmKwqjjaYmCZfHsFu24GXo
SOL(ラップ済み)So11111111111111111111111111111111111111112
mSOLmSoLzYCxHdYgdzU16g5QSh3i5K3z3KZK7ytfqcJm7So
JitoSOLJ1toso1uCk3RLmjorhTtrVwY9HJ7X8V9yYac6Y7kGCPn
bSOLbSo13r4TkiE4KumL71LsHTPpL2euBYLFx6h9HP3piy1

ベストプラクティス

セキュリティ
  1. APIキーを絶対に公開しない - 環境変数を使用します
  2. トランザクションを検証する - 署名する前にシミュレーションを実行します
  3. 適切なスリッページを設定する - レートの変動を考慮します
  4. ポジションを監視する - 残高と APY を定期的に確認します
パフォーマンス
  1. 優先手数料を使用する - 確認を速めるには500,000マイクロラムポート以上を設定します
  2. 操作をまとめる - 可能な場合は複数の操作を組み合わせます
  3. プールデータをキャッシュする - レートは1時間ごとに更新されるため、適切にキャッシュします
エラー処理
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;
  }
}
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);
}

手数料体系

手数料の種類金額発生時
初期化0.005 SOL初回の預け入れ時のみ
管理なし-
引き出しなし-
トランザクション変動トランザクションごと(Solanaの手数料)

リソース


スキルの構成

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

コメント