Libraries
Anchor Programs
Get Started
Utilities
Libraries
Anchor Programs
Convenient program clients and generated source based on Anchor IDLs.
GitHub Repository
Dependencies
- java.net.http
- systems.comodal.json_iterator
- org.bouncycastle.provider
- software.sava.core
- software.sava.rpc
- software.sava.solana_programs
- software.sava.anchor_src_gen
Usage Examples
Retrieve and Parse Drift placeOrders Transaction
Prints the following at the end:
Limit Long 0.1 @ 111 on SOL-PERP [reduceOnly=false] [postOnly=MustPostOnly]
try (var httpClient = HttpClient.newHttpClient()) {
var rpcClient = SolanaRpcClient.createClient(SolanaNetwork.MAIN_NET.getEndpoint(), httpClient);
// Fetch a Drift placeOrders Transaction
var txFuture = rpcClient.getTransaction(
"36Wnn99Y49mJ5GKiNiT3ja2q8gzSvMNrN5A3Bcn2YfCyrwY7kgQGVAu9VNzXqmWSbgzX76oUGxYNuPGM7tpPoJJS"
);
var tx = txFuture.join();
byte[] txData = tx.data();
var skeleton = TransactionSkeleton.deserializeSkeleton(txData);
Instruction[] instructions;
if (skeleton.isLegacy()) {
instructions = skeleton.parseLegacyInstructions();
} else {
// Fetch Lookup tables to allow parsing of versioned transactions.
int txVersion = skeleton.version();
if (txVersion == 0) {
var tableAccountInfos = rpcClient.getMultipleAccounts(
Arrays.asList(skeleton.lookupTableAccounts()),
AddressLookupTable.FACTORY
).join();
var lookupTables = tableAccountInfos.stream()
.map(AccountInfo::data)
.collect(Collectors.toUnmodifiableMap(AddressLookupTable::address, Function.identity()));
var accounts = skeleton.parseAccounts(lookupTables);
instructions = skeleton.parseInstructions(accounts);
} else {
throw new IllegalStateException("Unhandled tx version " + txVersion);
}
}
// instructions[0]; // Compute Budget Limit
// instructions[1]; // Compute Unit Price
// instructions[2]; // Drift Place Orders
var placeOrdersIxData = Arrays.stream(instructions)
.filter(DriftProgram.PLACE_ORDERS_DISCRIMINATOR)
.map(DriftProgram.PlaceOrdersIxData::read)
.findFirst()
.orElseThrow();
OrderParams[] orderParams = placeOrdersIxData.params();
OrderParams order = orderParams[0];
// Fetch token contexts to make use of convenient scaled value conversions.
var jupiterClient = JupiterClient.createClient(httpClient);
var verifiedTokens = jupiterClient.verifiedTokenMap().join();
// Create Drift Client to map market indexes from the order to its configuration.
var nativeProgramClient = NativeProgramClient.createClient();
var nativeProgramAccountClient = nativeProgramClient
.createAccountClient(AccountMeta.createFeePayer(PublicKey.NONE));
var driftClient = DriftProgramClient.createClient(nativeProgramAccountClient);
var driftAccounts = driftClient.accounts();
MarketConfig marketConfig;
TokenContext baseTokenContext;
if (order.marketType() == MarketType.Perp) {
var perpMarketConfig = driftAccounts.perpMarketConfig(order.marketIndex());
var spotConfig = driftClient.spotMarket(perpMarketConfig.baseAssetSymbol());
baseTokenContext = verifiedTokens.get(spotConfig.mint());
marketConfig = perpMarketConfig;
} else {
var spotConfig = driftAccounts.spotMarketConfig(order.marketIndex());
baseTokenContext = verifiedTokens.get(spotConfig.mint());
marketConfig = spotConfig;
}
// Assume all Drift markets are priced in USDC
var usdcTokenMint = driftClient.spotMarket(DriftAsset.USDC).mint();
var usdcTokenContext = verifiedTokens.get(usdcTokenMint);
// Limit Long 0.1 @ 111 on SOL-PERP [reduceOnly=false] [postOnly=MustPostOnly]
System.out.format("""
%s %s %s @ %s on %s [reduceOnly=%b] [postOnly=%s]
""",
order.orderType(),
order.direction(),
baseTokenContext.toDecimal(order.baseAssetAmount()).toPlainString(),
usdcTokenContext.toDecimal(order.price()).toPlainString(),
marketConfig.symbol(),
order.reduceOnly(),
order.postOnly()
);
}