Guide de développement du SDK Drift Protocol
Un guide complet pour créer des applications Solana avec le SDK Drift Protocol - le protocole de trading de futures perpétuels et de spot leading sur Solana.
Vue d'ensemble
Drift Protocol est un exchange décentralisé sur Solana proposant :
- Futures perpétuels : Jusqu'à 20x de levier sur les crypto-actifs
- Trading de spot : Emprunt/prêt et trading sur marge
- Cross-collateral : Utiliser plusieurs actifs comme collateral
- Vaults : Pools de trading délégué
- Intégration Jupiter : Swaps de spot directs
Démarrage rapide
Installation
npm install @drift-labs/sdk @solana/web3.js @coral-xyz/anchor
Pour Python :
pip install driftpy
Configuration de base (TypeScript)
import { Connection, Keypair } from '@solana/web3.js';
import { Wallet } from '@coral-xyz/anchor';
import {
DriftClient,
initialize,
DriftEnv,
BulkAccountLoader
} from '@drift-labs/sdk';
// 1. Configurer la connexion et le wallet
const connection = new Connection('https://api.mainnet-beta.solana.com');
const keypair = Keypair.fromSecretKey(/* votre clé secrète */);
const wallet = new Wallet(keypair);
// 2. Initialiser le SDK
const sdkConfig = initialize({ env: 'mainnet-beta' as DriftEnv });
// 3. Créer DriftClient
const driftClient = new DriftClient({
connection,
wallet,
env: 'mainnet-beta',
accountSubscription: {
type: 'polling',
accountLoader: new BulkAccountLoader(connection, 'confirmed', 1000),
},
});
// 4. S'abonner aux mises à jour
await driftClient.subscribe();
// 5. Vérifier si le compte utilisateur existe
const user = driftClient.getUser();
const userExists = await user.exists();
if (!userExists) {
// Initialiser le compte utilisateur (coûte ~0,035 SOL de rent)
await driftClient.initializeUserAccount();
}
Configuration de base (Python)
import asyncio
from solana.rpc.async_api import AsyncClient
from solders.keypair import Keypair
from driftpy.drift_client import DriftClient
from driftpy.account_subscription_config import AccountSubscriptionConfig
from anchorpy import Wallet
async def main():
connection = AsyncClient("https://api.mainnet-beta.solana.com")
keypair = Keypair.from_bytes(secret_key_bytes)
wallet = Wallet(keypair)
drift_client = DriftClient(
connection,
wallet,
"mainnet",
account_subscription=AccountSubscriptionConfig("polling"),
)
await drift_client.subscribe()
user = drift_client.get_user()
if not await user.exists():
await drift_client.initialize_user_account()
asyncio.run(main())
Concepts fondamentaux
1. Précision et BN (BigNumber)
Drift utilise BN.js pour toutes les valeurs numériques car la précision des tokens dépasse les limites des floats JavaScript. Tous les montants sont des entiers avec des niveaux de précision désignés.
Constantes de précision clés :
| Constante | Valeur | Cas d'usage |
|---|---|---|
QUOTE_PRECISION |
10^6 | Montants USDC |
BASE_PRECISION |
10^9 | Montants d'actif de base perp |
PRICE_PRECISION |
10^6 | Prix |
SPOT_MARKET_BALANCE_PRECISION |
10^9 | Soldes des tokens de spot |
FUNDING_RATE_PRECISION |
10^9 | Taux de financement |
MARGIN_PRECISION |
10 000 | Ratios de marge |
PEG_PRECISION |
10^6 | Peg AMM |
AMM_RESERVE_PRECISION |
10^9 | Réserves AMM |
Convertisseur d'aide :
import { convertToNumber } from '@drift-labs/sdk';
// La division BN retourne le plancher - utiliser l'aide pour une division précise
const result = convertToNumber(new BN(10500), new BN(1000)); // = 10.5
// Conversion de montants
const perpAmount = driftClient.convertToPerpPrecision(100); // 100 unités de base
const spotAmount = driftClient.convertToSpotPrecision(0, 100); // 100 USDC
const price = driftClient.convertToPricePrecision(21.23); // $21.23
2. Types de marché
Marchés perpétuels (MarketType.PERP) :
- Dérivés sans expiration
- Positions suivies via
baseAssetAmount - Positif = Long, Négatif = Short
Marchés de spot (MarketType.SPOT) :
- Dépôts/emprunts de tokens réels
- Positions suivies via
scaledBalance SpotBalanceType.DEPOSITouSpotBalanceType.BORROW
Index de marché courants :
0- USDC (actif de quote)1- SOL- Les index de marché varient - interroger
getPerpMarketAccounts()/getSpotMarketAccounts()
3. Types d'ordres
import { OrderType, PositionDirection, OrderTriggerCondition } from '@drift-labs/sdk';
// Types d'ordres disponibles
OrderType.MARKET // Exécution immédiate
OrderType.LIMIT // Ordres spécifiques au prix
OrderType.TRIGGER_MARKET // Stop-loss/prise de profit au marché
OrderType.TRIGGER_LIMIT // Stop-loss/prise de profit limité
OrderType.ORACLE // Pricing basé sur oracle
// Directions de position
PositionDirection.LONG // Acheter/enchérir
PositionDirection.SHORT // Vendre/demander
// Conditions de déclenchement (pour les ordres de stop)
OrderTriggerCondition.ABOVE // Déclencher quand prix > seuil
OrderTriggerCondition.BELOW // Déclencher quand prix < seuil
4. Paramètres Post-Only
import { PostOnlyParams } from '@drift-labs/sdk';
PostOnlyParams.NONE // Pas d'application (peut être preneur)
PostOnlyParams.MUST_POST_ONLY // Échouer si l'ordre croiserait le spread
PostOnlyParams.TRY_POST_ONLY // Ignorer l'ordre s'il croise le spread
PostOnlyParams.SLIDE // Ajuster le prix pour être post-only
Opérations de trading
Placer des ordres perpétuels
// Ordre au marché
await driftClient.placePerpOrder({
orderType: OrderType.MARKET,
marketIndex: 0, // SOL-PERP
direction: PositionDirection.LONG,
baseAssetAmount: driftClient.convertToPerpPrecision(1), // 1 SOL
});
// Ordre limité
await driftClient.placePerpOrder({
orderType: OrderType.LIMIT,
marketIndex: 0,
direction: PositionDirection.LONG,
baseAssetAmount: driftClient.convertToPerpPrecision(1),
price: driftClient.convertToPricePrecision(100), // $100
postOnly: PostOnlyParams.MUST_POST_ONLY,
});
// Ordre stop-loss
await driftClient.placePerpOrder({
orderType: OrderType.TRIGGER_MARKET,
marketIndex: 0,
direction: PositionDirection.SHORT, // Fermer long
baseAssetAmount: driftClient.convertToPerpPrecision(1),
triggerPrice: driftClient.convertToPricePrecision(90),
triggerCondition: OrderTriggerCondition.BELOW,
reduceOnly: true,
});
// Ordre de prise de profit
await driftClient.placePerpOrder({
orderType: OrderType.TRIGGER_LIMIT,
marketIndex: 0,
direction: PositionDirection.SHORT, // Fermer long
baseAssetAmount: driftClient.convertToPerpPrecision(1),
price: driftClient.convertToPricePrecision(120),
triggerPrice: driftClient.convertToPricePrecision(120),
triggerCondition: OrderTriggerCondition.ABOVE,
reduceOnly: true,
});
// Ordre Oracle (prix relatif à l'oracle)
await driftClient.placePerpOrder({
orderType: OrderType.ORACLE,
marketIndex: 0,
direction: PositionDirection.LONG,
baseAssetAmount: driftClient.convertToPerpPrecision(1),
oraclePriceOffset: -100000, // $0,10 en dessous de l'oracle (PRICE_PRECISION)
auctionDuration: 10,
auctionStartPrice: new BN(-200000), // Commencer $0,20 en dessous
auctionEndPrice: new BN(-50000), // Finir $0,05 en dessous
});
Placer des ordres de spot
// Ordre de spot au marché
await driftClient.placeSpotOrder({
orderType: OrderType.MARKET,
marketIndex: 1, // SOL
direction: PositionDirection.LONG, // Acheter
baseAssetAmount: driftClient.convertToSpotPrecision(1, 1), // 1 SOL
});
// Ordre limité de spot
await driftClient.placeSpotOrder({
orderType: OrderType.LIMIT,
marketIndex: 1,
direction: PositionDirection.LONG,
baseAssetAmount: driftClient.convertToSpotPrecision(1, 1),
price: driftClient.convertToPricePrecision(100),
});
Ordres multiples
// Placer plusieurs ordres atomiquement
await driftClient.placeOrders([
{
orderType: OrderType.LIMIT,
marketType: MarketType.PERP,
marketIndex: 0,
direction: PositionDirection.LONG,
baseAssetAmount: driftClient.convertToPerpPrecision(1),
price: driftClient.convertToPricePrecision(99),
},
{
orderType: OrderType.LIMIT,
marketType: MarketType.PERP,
marketIndex: 0,
direction: PositionDirection.SHORT,
baseAssetAmount: driftClient.convertToPerpPrecision(1),
price: driftClient.convertToPricePrecision(101),
},
]);
Gestion des ordres
// Annuler par ID d'ordre
await driftClient.cancelOrder(orderId);
// Annuler par ID d'ordre utilisateur
await driftClient.cancelOrderByUserOrderId(userOrderId);
// Annuler tous les ordres pour un marché
await driftClient.cancelOrders(MarketType.PERP, 0); // Tous les ordres SOL-PERP
// Annuler tous les ordres
await driftClient.cancelOrders();
// Modifier l'ordre existant
await driftClient.modifyOrder(orderId, {
price: driftClient.convertToPricePrecision(102),
});
// Annuler et placer atomiquement
await driftClient.cancelAndPlaceOrders({
cancelOrderParams: { orderId: existingOrderId },
placeOrderParams: [{
orderType: OrderType.LIMIT,
marketType: MarketType.PERP,
marketIndex: 0,
direction: PositionDirection.LONG,
baseAssetAmount: driftClient.convertToPerpPrecision(1),
price: driftClient.convertToPricePrecision(100),
}],
});
Obtenir les ordres
// Obtenir un ordre spécifique
const order = driftClient.getOrder(orderId);
// Obtenir l'ordre par ID utilisateur
const order = driftClient.getOrderByUserId(userOrderId);
// Obtenir tous les ordres ouverts
const user = driftClient.getUser();
const openOrders = user.getOpenOrders();
Dépôts et retraits
Dépôts
// Obtenir le compte de token associé
const associatedTokenAccount = await driftClient.getAssociatedTokenAccount(0); // USDC
// Déposer USDC
const amount = driftClient.convertToSpotPrecision(0, 100); // 100 USDC
await driftClient.deposit(amount, 0, associatedTokenAccount);
// Déposer SOL
const solAccount = await driftClient.getAssociatedTokenAccount(1);
const solAmount = driftClient.convertToSpotPrecision(1, 1); // 1 SOL
await driftClient.deposit(solAmount, 1, solAccount);
// Initialiser l'utilisateur et déposer le collateral dans une seule transaction
const [txSig, userPubkey] = await driftClient.initializeUserAccountAndDepositCollateral(
driftClient.convertToSpotPrecision(0, 100),
await driftClient.getAssociatedTokenAccount(0),
0, // market index
0, // sub account ID
'MyAccount', // name
);
Retraits
const associatedTokenAccount = await driftClient.getAssociatedTokenAccount(0);
const amount = driftClient.convertToSpotPrecision(0, 50); // 50 USDC
// Retirer (peut créer un emprunt en cas de dépôts insuffisants)
await driftClient.withdraw(amount, 0, associatedTokenAccount);
// Retirer avec reduce-only (empêche de créer un emprunt)
await driftClient.withdraw(amount, 0, associatedTokenAccount, undefined, true);
Transferts entre sous-comptes
// Transférer les dépôts
await driftClient.transferDeposit(
driftClient.convertToSpotPrecision(0, 100),
0, // market index
0, // from sub-account
1, // to sub-account
);
// Transférer les positions perp
await driftClient.transferPerpPosition({
fromSubAccountId: 0,
toSubAccountId: 1,
marketIndex: 0,
amount: driftClient.convertToPerpPrecision(1),
});
Gestion des positions
Lire les positions
const user = driftClient.getUser();
// Position perpétuelle
const perpPosition = user.getPerpPosition(0); // SOL-PERP
if (perpPosition) {
const baseAmount = perpPosition.baseAssetAmount;
const isLong = baseAmount.gt(new BN(0));
const isShort = baseAmount.lt(new BN(0));
console.log('Position size:', convertToNumber(baseAmount, BASE_PRECISION));
}
// Toutes les positions perp actives
const activePerpPositions = user.getActivePerpPositions();
// Position de spot
const spotPosition = user.getSpotPosition(0); // USDC
const tokenAmount = user.getTokenAmount(0);
const isDeposit = tokenAmount.gt(new BN(0));
const isBorrow = tokenAmount.lt(new BN(0));
// Toutes les positions de spot actives
const activeSpotPositions = user.getActiveSpotPositions();
Collateral et marge
const user = driftClient.getUser();
// Valeur totale du collateral
const totalCollateral = user.getTotalCollateral();
// Collateral libre (disponible pour les nouvelles positions)
const freeCollateral = user.getFreeCollateral();
// Exigences de marge
const initialMargin = user.getInitialMarginRequirement();
const maintenanceMargin = user.getMaintenanceMarginRequirement();
// Levier actuel
const leverage = user.getLeverage();
// Santé du compte (0-100, liquidation à 0)
const health = user.getHealth();
// Levier max pour un marché
const maxLeverage = user.getMaxLeverageForPerp(0); // SOL-PERP
// Pouvoir d'achat
const buyingPower = user.getPerpBuyingPower(0);
Calculs PnL
const user = driftClient.getUser();
// PnL non réalisé (toutes les positions)
const unrealizedPnl = user.getUnrealizedPNL();
// PnL non réalisé avec financement
const unrealizedPnlWithFunding = user.getUnrealizedPNL(true);
// PnL pour un marché spécifique
const marketPnl = user.getUnrealizedPNL(false, 0);
// PnL de financement non réalisé
const fundingPnl = user.getUnrealizedFundingPNL();
// Régler le PnL
await driftClient.settlePNL(
user.getUserAccountPublicKey(),
user.getUserAccount(),
0 // market index
);
Prix de liquidation
const user = driftClient.getUser();
// Obtenir le prix de liquidation pour la position perp
const liqPrice = user.liquidationPrice(0); // SOL-PERP
// Vérifier si peut être liquidé
const canBeLiquidated = user.canBeLiquidated();
Données de marché
Comptes de marché
// Marché perpétuel
const perpMarket = driftClient.getPerpMarketAccount(0);
console.log('Market index:', perpMarket.marketIndex);
console.log('AMM base reserves:', perpMarket.amm.baseAssetReserve.toString());
// Tous les marchés perp
const allPerpMarkets = driftClient.getPerpMarketAccounts();
// Marché de spot
const spotMarket = driftClient.getSpotMarketAccount(0);
console.log('Decimals:', spotMarket.decimals);
// Tous les marchés de spot
const allSpotMarkets = driftClient.getSpotMarketAccounts();
Données Oracle
// Obtenir le prix oracle pour le marché perp
const oracleData = driftClient.getOracleDataForPerpMarket(0);
const price = oracleData.price; // BN en PRICE_PRECISION
console.log('Oracle price:', convertToNumber(price, PRICE_PRECISION));
// Obtenir l'oracle pour le marché de spot
const spotOracleData = driftClient.getOracleDataForSpotMarket(1);
Calculer les prix depuis AMM
import { calculateBidAskPrice } from '@drift-labs/sdk';
const perpMarket = driftClient.getPerpMarketAccount(0);
const oracleData = driftClient.getOracleDataForPerpMarket(0);
const [bidPrice, askPrice] = calculateBidAskPrice(
perpMarket.amm,
oracleData
);
Événements
Abonné aux événements
import { EventSubscriber } from '@drift-labs/sdk';
const eventSubscriber = new EventSubscriber(connection, driftClient.program, {
eventTypes: [
'DepositRecord',
'FundingPaymentRecord',
'LiquidationRecord',
'OrderRecord',
'OrderActionRecord',
'FundingRateRecord',
'SettlePnlRecord',
'LPRecord',
'InsuranceFundRecord',
'SpotInterestRecord',
],
maxTx: 4096,
maxEventsPerType: 4096,
commitment: 'confirmed',
logProviderConfig: { type: 'websocket' },
});
await eventSubscriber.subscribe();
// Écouter les événements
eventSubscriber.eventEmitter.on('newEvent', (event) => {
console.log('Event type:', event.eventType);
console.log('Event data:', event);
});
// Obtenir les événements par type
const depositEvents = eventSubscriber.getEventsReceived()
.filter(e => e.eventType === 'DepositRecord');
// Désabonner
await eventSubscriber.unsubscribe();
Swaps Jupiter
import { JupiterClient } from '@drift-labs/sdk';
// Initialiser le client Jupiter
const jupiterClient = new JupiterClient({ connection });
// Obtenir l'aperçu du devis
const quote = await jupiterClient.getQuote({
inputMint: /* USDC mint */,
outputMint: /* SOL mint */,
amount: driftClient.convertToSpotPrecision(0, 100),
slippageBps: 50,
});
// Exécuter le swap via Drift
const txSig = await driftClient.swap({
jupiterClient,
inMarketIndex: 0, // USDC
outMarketIndex: 1, // SOL
amount: driftClient.convertToSpotPrecision(0, 100),
slippageBps: 50,
onlyDirectRoutes: false,
});
Sous-comptes
// Créer un nouveau sous-compte
const [txSig, userPubkey] = await driftClient.initializeUserAccount(
1, // sub-account ID
'SubAccount1' // name
);
// Basculer le sous-compte actif
await driftClient.switchActiveUser(1);
// Obtenir l'utilisateur pour un sous-compte spécifique
const user = driftClient.getUser(1);
// Supprimer le sous-compte (doit n'avoir aucune position)
await driftClient.deleteUser(1);
// Mettre à jour le délégué (permettre à une autre clé de trader)
await driftClient.updateUserDelegate(delegatePublicKey, 0);
Avancé : Protocole Swift (Trades sans ordres)
Swift permet la signature d'ordres off-chain sans transactions Solana :
// Signer le message d'ordre
const orderMessage = {
marketIndex: 0,
marketType: MarketType.PERP,
direction: PositionDirection.LONG,
baseAssetAmount: driftClient.convertToPerpPrecision(1),
price: driftClient.convertToPricePrecision(100),
};
const signature = driftClient.signSignedMsgOrderParamsMessage(orderMessage);
// Soumettre au serveur Swift
await axios.post('https://swift.drift.trade/orders', {
market_index: 0,
message: signature.message,
signature: signature.signature,
taker_authority: wallet.publicKey.toString(),
});
Avancé : Builder Codes (DBC)
Pour les plateformes routant les trades via Drift pour gagner des frais :
// Initialiser en tant que builder
await driftClient.initializeRevenueShare(builderAuthority);
// Initialiser l'escrow de l'utilisateur
await driftClient.initializeRevenueShareEscrow(userAuthority, numOrders);
// Approuver le builder
await driftClient.changeApprovedBuilder(
builderAuthority,
maxFeeTenthBps, // max fee en dixièmes de points de base
true // ajouter (false pour supprimer)
);
Gestion des erreurs
try {
await driftClient.placePerpOrder(orderParams);
} catch (error) {
if (error.message.includes('InsufficientCollateral')) {
console.error('Pas assez de collateral pour ce trade');
} else if (error.message.includes('MaxLeverageExceeded')) {
console.error('Dépasserait le levier maximum');
} else if (error.message.includes('OrderWouldCrossMaker')) {
console.error('L\'ordre post-only croiserait le spread');
} else {
throw error;
}
}
Ressources
- Documentation Drift Protocol
- Documentation SDK
- SDK TypeScript
- SDK Python (DriftPy)
- Exemples Keeper Bots
Structure de skill
drift-protocol/
├── SKILL.md # Ce fichier
├── resources/
│ ├── precision-constants.md # Toutes les constantes de précision
│ ├── types-reference.md # Types et enums TypeScript
│ ├── drift-client-api.md # Référence des méthodes DriftClient
│ └── user-api.md # Référence des méthodes de la classe User
├── examples/
│ ├── basic-setup/ # Initialisation du client
│ ├── orders/ # Exemples de placement d'ordres
│ ├── deposits-withdrawals/ # Gestion des collaterals
│ ├── positions/ # Requêtes de positions
│ ├── jupiter-swaps/ # Intégration de swaps
│ ├── vaults/ # Gestion des vaults
│ └── events/ # Abonnement aux événements
├── docs/
│ ├── vaults.md # Documentation des vaults
│ ├── market-making.md # Guide du market making
│ └── troubleshooting.md # Problèmes courants
└── templates/
└── trading-bot-template.ts # Starter copy-paste
Vérifier
- Un appel RPC/SDK réel a été émis (mainnet, devnet ou validateur local) et la charge utile de réponse est capturée dans le transcript, non juste paraphrasée
- Chaque transaction a été simulée (
simulateTransactionou équivalent) avant toute étape de signature/envoi ; les logs de simulation sont attachés - Pour toute transaction signée/envoyée, la signature résultante est enregistrée et confirmée on-chain (statut retourné par
getSignatureStatusesou une URL d'explorateur) - Le slippage, les frais de priorité et les limites d'unités de calcul ont été définis explicitement avec des valeurs numériques concrètes, non laissés aux valeurs par défaut de la bibliothèque
- Les adresses de compte, les mints et les IDs de programme utilisés dans l'exécution correspondent aux adresses drift-trading documentées pour le cluster ciblé (pas de mélange mainnet/devnet)
- Le chemin d'échec a été exercé au moins une fois (solde insuffisant, oracle obsolète, blockhash expiré, etc.) et la gestion des erreurs de l'agent a produit un message lisible par un humain