sol-chainsaw 0.0.2

Deserializing Solana accounts using their progam IDL
Documentation
# chainsaw [![Build+Test]https://github.com/ironforge-cloud/chainsaw/actions/workflows/build+test.yml/badge.svg?branch=master]https://github.com/ironforge-cloud/chainsaw/actions/workflows/build+test.yml

Deserializing Solana accounts using their progam IDL.

<!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
**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

<!-- END doctoc generated TOC please keep comment here to allow auto update -->

## 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