Skip to content

Ellipsis-Labs/reverse-idl-parser

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

6 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Atlas IDL Schema

This repo contains a library to transform JSON IDL files for Solana programs into normal Rust objects.

How It Works

The Solana Program IDL pipeline is extremely convoluted. One might even argue that it creates a programming anti-pattern. However, given the prevalence of IDLs for SVM programs, developers should adapt to the situation and develop tooling to make it easier to work with what is available

flowchart TD
    A[SVM Program] -->|*Generates via* IDL Parser| B[IDL JSON File]
    B  --> |*Feeds into*| C[Reverse IDL Parser]
    E[Account Data] -.-> |*Queries with* Raw Account Data| D 
    G[Instruction Data] -.-> |*Queries with* Raw Instruction Data| D
    C -->|*Generates via* Schema Parser| D[Schema Nodes] -.-> |*Generates via* Value Parser| F[Value Nodes] -.-> |*Derives*| H[Formatted JSON Data]  
Loading

This library can convert a JSON IDL string into an intermediate Rust SchemaNode type. The SchemaNode is a recursively defined type that takes in an opaque byte object as input and decodes it into a ValueNode (another recursively defined type). Serializing the ValueNode will yield the parsed byte object as human-readable JSON.

Example

Here's an example of how to read a Drift V2 State account from Solana Mainnet given the Drift IDL file.

use atlas_idl_schema::parse_idl;
use solana_client::rpc_client::RpcClient;
use std::str::FromStr;

fn main() -> anyhow::Result<()> {
    let drift_idl = parse_idl::parse_idl_file("tests/idls/drift.json")
        .map_err(|e| anyhow::anyhow!("Failed to parse IDL file: {}", e))?;

    let client = RpcClient::new("https://api.mainnet-beta.solana.com".to_string());

    // State
    let account_data = client.get_account_data(
        &solana_sdk::pubkey::Pubkey::from_str("5zpq7DvB6UdFFvpmBPspGPNfUGoBRRCE2HHg5u3gxcsN")
            .unwrap(),
    )?;
    let parsed_account = drift_idl.get_parsed_account(account_data, true)?;
    println!("{}\n", serde_json::to_string(&parsed_account).unwrap());
}

Output:

{"name":"State","schema":{"admin":"pubkey","whitelistMint":"pubkey","discountMint":"pubkey","signer":"pubkey","srmVault":"pubkey","perpFeeStructure":{"feeTiers":{"feeNumerator":"u32","feeDenominator":"u32","makerRebateNumerator":"u32","makerRebateDenominator":"u32","referrerRewardNumerator":"u32","referrerRewardDenominator":"u32","refereeFeeNumerator":"u32","refereeFeeDenominator":"u32"},"fillerRewardStructure":{"rewardNumerator":"u32","rewardDenominator":"u32","timeBasedRewardLowerBound":"u128"},"referrerRewardEpochUpperBound":"u64","flatFillerFee":"u64"},"spotFeeStructure":{"feeTiers":{"feeNumerator":"u32","feeDenominator":"u32","makerRebateNumerator":"u32","makerRebateDenominator":"u32","referrerRewardNumerator":"u32","referrerRewardDenominator":"u32","refereeFeeNumerator":"u32","refereeFeeDenominator":"u32"},"fillerRewardStructure":{"rewardNumerator":"u32","rewardDenominator":"u32","timeBasedRewardLowerBound":"u128"},"referrerRewardEpochUpperBound":"u64","flatFillerFee":"u64"},"oracleGuardRails":{"priceDivergence":{"markOraclePercentDivergence":"u64","oracleTwap5minPercentDivergence":"u64"},"validity":{"slotsBeforeStaleForAmm":"i64","slotsBeforeStaleForMargin":"i64","confidenceIntervalMaxSize":"u64","tooVolatileRatio":"i64"}},"numberOfAuthorities":"u64","numberOfSubAccounts":"u64","lpCooldownTime":"u64","liquidationMarginBufferRatio":"u32","settlementDuration":"u16","numberOfMarkets":"u16","numberOfSpotMarkets":"u16","signerNonce":"u8","minPerpAuctionDuration":"u8","defaultMarketOrderTimeInForce":"u8","defaultSpotAuctionDuration":"u8","exchangeStatus":"u8","liquidationDuration":"u8","initialPctToLiquidate":"u16","maxNumberOfSubAccounts":"u16","maxInitializeUserFee":"u16","padding":{"size":10,"type":"u8"}},"value":{"admin":"E1admb4tW2Y6bpbnpE5jYZsc4TE2NArG7siZqDsafnob","whitelistMint":"11111111111111111111111111111111","discountMint":"11111111111111111111111111111111","signer":"JCNCMFXo5M5qwUPg2Utu1u6YWp3MbygxqBsBeXXJfrw","srmVault":"11111111111111111111111111111111","perpFeeStructure":{"feeTiers":{"feeNumerator":100,"feeDenominator":100000,"makerRebateNumerator":10,"makerRebateDenominator":100000,"referrerRewardNumerator":15,"referrerRewardDenominator":100,"refereeFeeNumerator":5,"refereeFeeDenominator":100},"fillerRewardStructure":{"rewardNumerator":90,"rewardDenominator":100000,"timeBasedRewardLowerBound":"7922816251703135349956767907850"},"referrerRewardEpochUpperBound":"429496729605","flatFillerFee":"429496729600080"},"spotFeeStructure":{"feeTiers":{"feeNumerator":10,"feeDenominator":100000,"makerRebateNumerator":15,"makerRebateDenominator":100,"referrerRewardNumerator":5,"referrerRewardDenominator":100,"refereeFeeNumerator":70,"refereeFeeDenominator":100000},"fillerRewardStructure":{"rewardNumerator":10,"rewardDenominator":100000,"timeBasedRewardLowerBound":"7922816251518667480152439521295"},"referrerRewardEpochUpperBound":"429496729600060","flatFillerFee":"429496729600010"},"oracleGuardRails":{"priceDivergence":{"markOraclePercentDivergence":"429496729615","oracleTwap5minPercentDivergence":"429496729605"},"validity":{"slotsBeforeStaleForAmm":"429496729600030","slotsBeforeStaleForMargin":"429496729600010","confidenceIntervalMaxSize":"429496729615","tooVolatileRatio":"429496729605"}},"numberOfAuthorities":"429496729600000","numberOfSubAccounts":"429496729600000","lpCooldownTime":"429496729600","liquidationMarginBufferRatio":0,"settlementDuration":100,"numberOfMarkets":0,"numberOfSpotMarkets":0,"signerNonce":0,"minPerpAuctionDuration":0,"defaultMarketOrderTimeInForce":160,"defaultSpotAuctionDuration":134,"exchangeStatus":1,"liquidationDuration":0,"initialPctToLiquidate":0,"maxNumberOfSubAccounts":0,"maxInitializeUserFee":34464,"padding":[1,0,0,0,0,0,100,0,0,0]}}

About

Recursive Rust types for parsing IDL JSON files

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages