# chainsaw [](https://github.com/ironforge-cloud/chainsaw/actions/workflows/build+test.yml)
Deserializing Solana accounts using their progam IDL.
**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)*
- [Installation](#installation)
- [Example](#example)
- [Network Feature](#network-feature)
- [Development](#development)
- [Test Tasks](#test-tasks)
- [Updating IDLs](#updating-idls)
- [Fetching Test Data from Chain](#fetching-test-data-from-chain)
- [Triaging Account Deserialization](#triaging-account-deserialization)
- [LICENSE](#license)
## Installation
```
cargo add sol-chainsaw
```
## Example
```rs
let opts = SerializationOpts {
pubkey_as_base58: true,
n64_as_string: false,
n128_as_string: true,
};
let mut chainsaw = ChainsawDeserializer::new(&opts);
// 1. Add IDLS
// Candy Machine IDL
let cndy_program_id = "cndy3Z4yapfJBmL3ShUp5exZKqR3z33thTzeNMm2gRZ";
{
let idl_json = read_idl_json(&cndy_program_id);
chainsaw
.add_idl_json(cndy_program_id.to_string(), &idl_json, IdlProvider::Anchor)
.expect("failed adding IDL JSON");
}
// Staking Program IDL
let stake_program_id = "StakeSSzfxn391k3LvdKbZP5WVwWd6AsY1DNiXHjQfK";
{
let idl_json = read_idl_json(&stake_program_id);
chainsaw
.add_idl_json(stake_program_id.to_string(), &idl_json, IdlProvider::Anchor)
.expect("failed adding IDL JSON");
}
// 2. Read Accounts Data
// Stake Account
let stake_acc_data = read_account(
&stake_program_id,
"EscrowHistory",
"5AEHnKRonYWeXWQTCqbfaEY6jHy38ifutWsriVsxsgbL",
);
// Candy Machine Account
let cndy_acc_data = read_account(
&cndy_program_id,
"CollectionPDA",
"4gt6YPtgZp2MYJUP7cAH8E3UiL6mUruYaPprEiyJytQ4",
);
// 3. Deserialize Accounts
// Stake Account
{
let mut acc_json = String::new();
chainsaw
.deserialize_account(
&stake_program_id,
&mut stake_acc_data.as_slice(),
&mut acc_json,
)
.expect("failed to deserialize account");
assert!(acc_json.contains("{\"escrow\":\"4uj6fRJzqoNRPktmYqGX1nBkjAJBsimJ4ug77S3Tzj7y\""));
}
// Candy Machine Account
{
let mut acc_json = String::new();
chainsaw
.deserialize_account(
&cndy_program_id,
&mut cndy_acc_data.as_slice(),
&mut acc_json,
)
.expect("failed to deserialize account");
assert_eq!(acc_json, "{\"mint\":\"BrqNo3sQFTaq9JevoWYhgagJEjE3MmTgYonfaHV5Mf3E\",\"candyMachine\":\"DpBwktkJsEPTtsRpD8kCFGwEUjwTkXARSGSTQ7MJr4kE\"}");
}
```
See [./examples/multiple_idls_and_accounts.rs](./examples/multiple_idls_and_accounts.rs) for
the full example.
Run it via: `cargo run --example multiple_idls_and_accounts`
## Network Feature
To make retrieving IDLs from chain easier an idl client implementation is included which is
only enabled when the `network` feature is enabled.
```rs
let idl_client = IdlClient::for_anchor_on_mainnet();
let program_idl = idl_client.fetch(program_id)?;
```
_more detailed example inside [`tests/task_update_idls.rs`](tests/task_update_idls.rs).
## Development
### Test Tasks
Various tasks related to tests were included via the [`./Makefile`](./Makefile). They are part of the `tests` folder but gated
behind features. Thus to activate them specific features need to be enabled.
In order to use a custom RPC cluster when running those tasks please provide it via the
`RPC_URL` env var, i.e.:
```sh
export RPC_URL="https://solana-mainnet.g.alchemy.com/v2/<mykey>"
```
#### Updating IDLs
Inside [`./tests/data/programs_with_idl.json`](./tests/data/programs_with_idl.json) are program
ids which have IDLs uploaded on chain.
Those IDLs are stored inside [`./fixtures/idls`](./fixtures/idls) to be used by integration tests.
- **idl_update**: update IDLs to what's currently on chain
#### Fetching Test Data from Chain
- **fetch_account**: fetches a specific account from chain (requires `ADDRESS` env var) and
stores it in a tmp file
- **fetch_accounts_for_program**: Updates accounts for specific program from chain (requires `PROGRAM_ID` env var)
- **fetch_accounts_for_all_programs**: Updates accounts for all known program from chain (requires
COUNT env var to indicate how many accounts per account type to fetch)
#### Triaging Account Deserialization
In order to isolate deserialization issues for specific accounts use on of the following tasks
and provide the required env vars.
- **deserialize_account_type_address_for_program**: deserializes an account of program
`PROGRAM_ID` of account type `ACCOUNT_TYPE` at address `ADDRESS`
- **deserialize_account_type_for_program**: deserializes all accounts of program
`PROGRAM_ID` and account type `ACCOUNT_TYPE`
- **deserialize_accounts_for_program**: deserializes all accounts of program
`PROGRAM_ID`
**Examples**:
```sh
export PROGRAM_ID=1USDCmv8QmvZ9JaL7bmevGsNHn7ez8TNahJzCN551sb
export ACCOUNT_TYPE=DepositReceipt
make deserialize_account_type_address_for_program ADDRESS=AyMxDEJPvJv6XQaFK9ZZ9XxDxHuVB8d7NUxP8TfLX45K
```
```sh
export PROGRAM_ID=1USDCmv8QmvZ9JaL7bmevGsNHn7ez8TNahJzCN551sb
make deserialize_account_type_for_program ACCOUNT_TYPE=Realm
```
```sh
export PROGRAM_ID=1USDCmv8QmvZ9JaL7bmevGsNHn7ez8TNahJzCN551sb
make deserialize_accounts_for_program
```
## LICENSE
MIT