lulo について
lulo は、Solana アプリに DeFi 貯蓄機能を組み込む開発者向けのガイドです。API で入出金や口座データ取得、複数プロトコルへの配分を扱い、保護付きの預け入れ方式も選べます。
主な機能
- 01複数プロトコルへの預け入れ自動配分
- 02Protected、Boosted、カスタム預け入れ
- 03入出金トランザクションの生成
- 04残高、利息、APY の取得
- 05プールの金利と流動性の確認
活用シーン
- 01Solana アプリへの入出金機能の追加
- 02ユーザーのポジションと利息の確認
- 03プールの APY と容量の比較
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ sendaifun/ skills --skill luloインストール後、Claude Code で/luloを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
docs/troubleshooting.mdexamples/balance/balance.tsexamples/deposit/deposit.tsexamples/integration/full-integration.tsexamples/withdraw/withdraw.tsresources/api-reference.mdtoken-addresses.mdtemplates/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 |
| Blinks | https://blink.lulo.fi |
| 開発者ポータル | https://dev.lulo.fi |
API リファレンス
預け入れトランザクションの生成
Lulo にトークンを預け入れるためのシリアライズ済みトランザクションを作成します。
エンドポイント:POST /v1/generate.transactions.deposit
クエリパラメーター:
| パラメーター | 型 | 説明 |
|---|---|---|
priorityFee | number | マイクロラムポート単位の優先手数料(例:500000) |
リクエスト本文:
{
"owner": "YourWalletPublicKey",
"mintAddress": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
"depositType": "protected",
"amount": 100000000
}
レスポンス:
{
"transaction": "base64EncodedSerializedTransaction",
"lastValidBlockHeight": 123456789
}
引き出しトランザクションの生成
Lulo からトークンを引き出すためのシリアライズ済みトランザクションを作成します。
エンドポイント:POST /v1/generate.transactions.withdraw
クエリパラメーター:
| パラメーター | 型 | 説明 |
|---|---|---|
priorityFee | number | マイクロラムポート単位の優先手数料 |
リクエスト本文:
{
"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
預け入れタイプの説明
保護付き預け入れ
安定した利回りと自動補償を求める、リスクを避けたいユーザー向けに設計されています。
仕組み:
- 統合されたプロトコルでの貸し出しから利息を獲得します
- 利息の一部がブースト付き預け入れの利用者に分配されます(保護手数料)
- プロトコルに障害が発生した場合、ブースト付き預け入れが保護付き預け入れの損失を自動的に補填します
メリット:
- 優先的な補償によりリスクが低減されます
- 安定した予測可能な利回りが得られます
- 請求手続きは不要です。保護は自動的に適用されます
- 即時に引き出せます
補償対象:スマートコントラクトの悪用、オラクルの障害、不良債権の発生
補償対象外:Solana ネットワークの障害、USDC のペッグ解除、Lulo コントラクトの障害
ブースト付き預け入れ
保護付き預け入れの利用者に保険を提供する代わりに、より高い利回りを得られます。
仕組み:
- 統合されたプロトコルから貸し出し利回りを獲得します
- 保護付き預け入れの利息分配から追加の利回りを受け取ります
- プロトコルに障害が発生した場合、最初の損失を負担する層として機能します
メリット:
- より高い 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アドレス |
|---|---|
| USDC | EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v |
| USDT | Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB |
| USDS | USDSwr9ApdHk5bvJKMjzff41FfuX8bSxdKcR81vTwcA |
| PYUSD | 2b1kV6DkPAnxd5ixfnxCpjxmKwqjjaYmCZfHsFu24GXo |
| SOL(ラップ済み) | So11111111111111111111111111111111111111112 |
| mSOL | mSoLzYCxHdYgdzU16g5QSh3i5K3z3KZK7ytfqcJm7So |
| JitoSOL | J1toso1uCk3RLmjorhTtrVwY9HJ7X8V9yYac6Y7kGCPn |
| bSOL | bSo13r4TkiE4KumL71LsHTPpL2euBYLFx6h9HP3piy1 |
ベストプラクティス
セキュリティ
- APIキーを絶対に公開しない - 環境変数を使用します
- トランザクションを検証する - 署名する前にシミュレーションを実行します
- 適切なスリッページを設定する - レートの変動を考慮します
- ポジションを監視する - 残高と APY を定期的に確認します
パフォーマンス
- 優先手数料を使用する - 確認を速めるには500,000マイクロラムポート以上を設定します
- 操作をまとめる - 可能な場合は複数の操作を組み合わせます
- プールデータをキャッシュする - レートは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