1

I want to list all user accounts for a given SPL mint token. I have been following the docs over here

I can manage to execute the RPC call from the CLI using curl (notice I am using my own pre-deployed SPL token)

curl http://api.devnet.solana.com -X POST -H "Content-Type: application/json" -d '
  {
    "jsonrpc": "2.0",
    "id": 1,
    "method": "getProgramAccounts",
    "params": [
      "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
      {
        "encoding": "jsonParsed",
        "filters": [
          {
            "dataSize": 165
          },
          {
            "memcmp": {
              "offset": 0,
              "bytes": "tmyjPipMS9jHkmhibbepduYAWpdrv5qkEqGz5fm4PQN"
            }
          }
        ]
      }
    ]
  }
'

Expected output

{"jsonrpc":"2.0","result":[{"account":{"data":{"parsed":{"info":{"isNative":false,"mint":"tmyjPipMS9jHkmhibbepduYAWpdrv5qkEqGz5fm4PQN","owner":"5Jja5kAvCFkixQSQhKpKWgWRx8FX6eh3FXH7oqJ98x7","state":"initialized","tokenAmount":{"amount":"100000000000","decimals":9,"uiAmount":100.0,"uiAmountString":"100"}},"type":"account"},"program":"spl-token","space":165},"executable":false,"lamports":2039280,"owner":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA","rentEpoch":361},"pubkey":"2qWFXQyFBLuzUGveSaEajojPVjUtLAjRM8QCWu34NUe2"}],"id":1}

But I am having trouble translating this to Rust code:

#[cfg(test)]
mod test {
    use std::str::FromStr;

    use solana_client::{
        rpc_config::RpcProgramAccountsConfig,
        rpc_filter::{Memcmp, MemcmpEncodedBytes, RpcFilterType},
    };

    use super::*;

    #[tokio::test]
    async fn testing() {
        // Test code snippet
        let rpc = RpcClient::new("https://api.devnet.solana.com".to_string());
        // Step 1 Assume that I have the data here.
        let token_account =
            Pubkey::from_str("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA").unwrap();
        let mint_account = Pubkey::from_str("tmyjPipMS9jHkmhibbepduYAWpdrv5qkEqGz5fm4PQN").unwrap();

        let filters = vec![
            RpcFilterType::DataSize(165),
            RpcFilterType::Memcmp(Memcmp::new_base58_encoded(0, &mint_account.to_bytes())),
        ];
        let filters = RpcProgramAccountsConfig {
            filters: Some(filters),
            ..Default::default()
        };
        let all_user_accounts = rpc
            .get_program_accounts_with_config(&token_account, filters)
            .await;
        println!("{all_user_accounts:?}");
// Err(ClientError { request: Some(GetProgramAccounts), kind: RpcError(RpcResponseError { code: -32600, message: "Encoded binary (base 58) data should be less than 128 bytes, please use Base64 encoding.", data: Empty }) })
    }
}

Changing the filters to use Base64 encoding results in a different error:

        let filters = vec![
            RpcFilterType::DataSize(165),
            RpcFilterType::Memcmp(Memcmp {
                offset: 0,
                bytes: MemcmpEncodedBytes::Base64(mint_account.to_string()),
                encoding: None,
            }),
        ];
   // Err(ClientError { request: None, kind: RpcError(RpcRequestError("RPC node on old version does not support base64 encoding for memcmp filters")) })

0

2 Answers 2

1

The only way how I managed to retrieve the data I wanted, was to bypass the get_program_accounts helper on the SDK client, as it was unable to properly parse the returned data. Here is the code that returns the expected result.


#[cfg(test)]
mod test {
    use std::str::FromStr;

    use serde_json::json;
    use solana_account_decoder::UiAccountEncoding;
    use solana_client::{
        rpc_client::RpcClient,
        rpc_config::{RpcAccountInfoConfig, RpcProgramAccountsConfig},
        rpc_filter::{Memcmp, MemcmpEncodedBytes, RpcFilterType},
        rpc_request::RpcRequest, rpc_response::RpcKeyedAccount,
    };
    use solana_sdk::{commitment_config::{CommitmentLevel, CommitmentConfig}, pubkey::Pubkey};

    #[tokio::test(flavor = "multi_thread")]
    async fn testing() {
        let rpc = RpcClient::new("https://api.devnet.solana.com".to_string());
        let token_account =
            Pubkey::from_str("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA").unwrap();
        let mint_account = Pubkey::from_str("tmyjPipMS9jHkmhibbepduYAWpdrv5qkEqGz5fm4PQN").unwrap();

        let filters = vec![
            RpcFilterType::DataSize(165),
            RpcFilterType::Memcmp(Memcmp::new_raw_bytes(0, mint_account.to_bytes().to_vec())),
        ];
        let filters = RpcProgramAccountsConfig {
            filters: Some(filters),
            account_config: RpcAccountInfoConfig {
                encoding: Some(UiAccountEncoding::JsonParsed),
                data_slice: None,
                commitment: Some(CommitmentConfig {
                    commitment: CommitmentLevel::Finalized,
                }),
                min_context_slot: None,
            },
            ..Default::default()
        };
        let all_user_accounts = rpc.send::<Vec<RpcKeyedAccount>>(
            RpcRequest::GetProgramAccounts,
            json!([token_account.to_string(), filters]),
        );
        println!("{all_user_accounts:?}");
    }
}
1

If you read the first error message, it complains about the returned data being too large for base58.

// Err(ClientError { request: Some(GetProgramAccounts), kind: RpcError(RpcResponseError { code: -32600, message: "Encoded binary (base 58) data should be less than 128 bytes, please use Base64 encoding.", data: Empty }) })

To make your first snippet work you need to specify your encoding.

    let config = RpcProgramAccountsConfig {
        filters: Some(filters),
        account_config: RpcAccountInfoConfig {
            encoding: Some(UiAccountEncoding::JsonParsed), // This is necessary to avoid 
            data_slice: None,
            commitment: Some(CommitmentConfig {
                commitment: CommitmentLevel::Finalized,
            }),
            min_context_slot: None,
        },
        ..Default::default()
    };

This is what your solution had except that you don't need to do all the work manually you can use the original rpc.get_program_accounts_with_config

An example in a get_account call where it isn't using the default encoding https://github.com/solana-labs/solana/blob/9fb7ec77c8aa444544c4dd20497d92c9f378ea4f/client/src/nonblocking/rpc_client.rs#L3911

1
  • The request does get executed, but there's another error, which originates from the parse_keyed_accounts function: Err(ClientError { request: Some(GetProgramAccounts), kind: RpcError(ParseError("Account from rpc")) }) So it seems that even though the request gets executed and the data gets returned, the Solana client instance cannot properly parse the returned data. Commented Aug 25, 2022 at 15:14

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.