Add config param to specify offset/length for single and program account info (#11515)

* Add config param to specify dataSlice for account info and program accounts

* Use match instead of if
This commit is contained in:
Tyera Eulberg
2020-08-10 16:35:29 -06:00
committed by GitHub
parent da210ddd51
commit 88ca04dbdb
12 changed files with 210 additions and 26 deletions

1
Cargo.lock generated
View File

@ -3509,6 +3509,7 @@ dependencies = [
name = "solana-core" name = "solana-core"
version = "1.4.0" version = "1.4.0"
dependencies = [ dependencies = [
"base64 0.12.3",
"bincode", "bincode",
"bs58", "bs58",
"bv", "bv",

View File

@ -51,19 +51,23 @@ impl UiAccount {
account: Account, account: Account,
encoding: UiAccountEncoding, encoding: UiAccountEncoding,
additional_data: Option<AccountAdditionalData>, additional_data: Option<AccountAdditionalData>,
data_slice_config: Option<UiDataSliceConfig>,
) -> Self { ) -> Self {
let data = match encoding { let data = match encoding {
UiAccountEncoding::Binary => { UiAccountEncoding::Binary => UiAccountData::Binary(
UiAccountData::Binary(bs58::encode(account.data).into_string()) bs58::encode(slice_data(&account.data, data_slice_config)).into_string(),
} ),
UiAccountEncoding::Binary64 => UiAccountData::Binary64(base64::encode(account.data)), UiAccountEncoding::Binary64 => UiAccountData::Binary64(base64::encode(slice_data(
&account.data,
data_slice_config,
))),
UiAccountEncoding::JsonParsed => { UiAccountEncoding::JsonParsed => {
if let Ok(parsed_data) = if let Ok(parsed_data) =
parse_account_data(pubkey, &account.owner, &account.data, additional_data) parse_account_data(pubkey, &account.owner, &account.data, additional_data)
{ {
UiAccountData::Json(parsed_data) UiAccountData::Json(parsed_data)
} else { } else {
UiAccountData::Binary64(base64::encode(account.data)) UiAccountData::Binary64(base64::encode(&account.data))
} }
} }
}; };
@ -113,3 +117,57 @@ impl Default for UiFeeCalculator {
} }
} }
} }
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct UiDataSliceConfig {
pub offset: usize,
pub length: usize,
}
fn slice_data(data: &[u8], data_slice_config: Option<UiDataSliceConfig>) -> &[u8] {
if let Some(UiDataSliceConfig { offset, length }) = data_slice_config {
if offset >= data.len() {
&[]
} else if length > data.len() - offset {
&data[offset..]
} else {
&data[offset..offset + length]
}
} else {
data
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_slice_data() {
let data = vec![1, 2, 3, 4, 5];
let slice_config = Some(UiDataSliceConfig {
offset: 0,
length: 5,
});
assert_eq!(slice_data(&data, slice_config), &data[..]);
let slice_config = Some(UiDataSliceConfig {
offset: 0,
length: 10,
});
assert_eq!(slice_data(&data, slice_config), &data[..]);
let slice_config = Some(UiDataSliceConfig {
offset: 1,
length: 2,
});
assert_eq!(slice_data(&data, slice_config), &data[1..3]);
let slice_config = Some(UiDataSliceConfig {
offset: 10,
length: 2,
});
assert_eq!(slice_data(&data, slice_config), &[] as &[u8]);
}
}

View File

@ -1120,7 +1120,13 @@ fn process_show_account(
let cli_account = CliAccount { let cli_account = CliAccount {
keyed_account: RpcKeyedAccount { keyed_account: RpcKeyedAccount {
pubkey: account_pubkey.to_string(), pubkey: account_pubkey.to_string(),
account: UiAccount::encode(account_pubkey, account, UiAccountEncoding::Binary64, None), account: UiAccount::encode(
account_pubkey,
account,
UiAccountEncoding::Binary64,
None,
None,
),
}, },
use_lamports_unit, use_lamports_unit,
}; };

View File

@ -355,6 +355,7 @@ mod tests {
nonce_account, nonce_account,
UiAccountEncoding::Binary64, UiAccountEncoding::Binary64,
None, None,
None,
); );
let get_account_response = json!(Response { let get_account_response = json!(Response {
context: RpcResponseContext { slot: 1 }, context: RpcResponseContext { slot: 1 },

View File

@ -472,6 +472,7 @@ impl RpcClient {
let config = RpcAccountInfoConfig { let config = RpcAccountInfoConfig {
encoding: Some(UiAccountEncoding::Binary64), encoding: Some(UiAccountEncoding::Binary64),
commitment: Some(commitment_config), commitment: Some(commitment_config),
data_slice: None,
}; };
let response = self.sender.send( let response = self.sender.send(
RpcRequest::GetAccountInfo, RpcRequest::GetAccountInfo,

View File

@ -1,5 +1,5 @@
use crate::rpc_filter::RpcFilterType; use crate::rpc_filter::RpcFilterType;
use solana_account_decoder::UiAccountEncoding; use solana_account_decoder::{UiAccountEncoding, UiDataSliceConfig};
use solana_sdk::{clock::Epoch, commitment_config::CommitmentConfig}; use solana_sdk::{clock::Epoch, commitment_config::CommitmentConfig};
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
@ -47,6 +47,7 @@ pub struct RpcStakeConfig {
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct RpcAccountInfoConfig { pub struct RpcAccountInfoConfig {
pub encoding: Option<UiAccountEncoding>, pub encoding: Option<UiAccountEncoding>,
pub data_slice: Option<UiDataSliceConfig>,
#[serde(flatten)] #[serde(flatten)]
pub commitment: Option<CommitmentConfig>, pub commitment: Option<CommitmentConfig>,
} }

View File

@ -79,6 +79,7 @@ solana-rayon-threadlimit = { path = "../rayon-threadlimit", version = "1.4.0" }
trees = "0.2.1" trees = "0.2.1"
[dev-dependencies] [dev-dependencies]
base64 = "0.12.3"
matches = "0.1.6" matches = "0.1.6"
reqwest = { version = "0.10.6", default-features = false, features = ["blocking", "rustls-tls", "json"] } reqwest = { version = "0.10.6", default-features = false, features = ["blocking", "rustls-tls", "json"] }
serial_test = "0.4.0" serial_test = "0.4.0"

View File

@ -244,6 +244,7 @@ impl JsonRpcRequestProcessor {
let config = config.unwrap_or_default(); let config = config.unwrap_or_default();
let bank = self.bank(config.commitment); let bank = self.bank(config.commitment);
let encoding = config.encoding.unwrap_or(UiAccountEncoding::Binary); let encoding = config.encoding.unwrap_or(UiAccountEncoding::Binary);
check_slice_and_encoding(&encoding, config.data_slice.is_some())?;
let mut response = None; let mut response = None;
if let Some(account) = bank.get_account(pubkey) { if let Some(account) = bank.get_account(pubkey) {
if account.owner == spl_token_id_v1_0() && encoding == UiAccountEncoding::JsonParsed { if account.owner == spl_token_id_v1_0() && encoding == UiAccountEncoding::JsonParsed {
@ -256,7 +257,13 @@ impl JsonRpcRequestProcessor {
data: None, data: None,
}); });
} else { } else {
response = Some(UiAccount::encode(pubkey, account, encoding, None)); response = Some(UiAccount::encode(
pubkey,
account,
encoding,
None,
config.data_slice,
));
} }
} }
@ -277,21 +284,31 @@ impl JsonRpcRequestProcessor {
program_id: &Pubkey, program_id: &Pubkey,
config: Option<RpcAccountInfoConfig>, config: Option<RpcAccountInfoConfig>,
filters: Vec<RpcFilterType>, filters: Vec<RpcFilterType>,
) -> Vec<RpcKeyedAccount> { ) -> Result<Vec<RpcKeyedAccount>> {
let config = config.unwrap_or_default(); let config = config.unwrap_or_default();
let bank = self.bank(config.commitment); let bank = self.bank(config.commitment);
let encoding = config.encoding.unwrap_or(UiAccountEncoding::Binary); let encoding = config.encoding.unwrap_or(UiAccountEncoding::Binary);
let data_slice_config = config.data_slice;
check_slice_and_encoding(&encoding, data_slice_config.is_some())?;
let keyed_accounts = get_filtered_program_accounts(&bank, program_id, filters); let keyed_accounts = get_filtered_program_accounts(&bank, program_id, filters);
if program_id == &spl_token_id_v1_0() && encoding == UiAccountEncoding::JsonParsed { let result =
get_parsed_token_accounts(bank, keyed_accounts).collect() if program_id == &spl_token_id_v1_0() && encoding == UiAccountEncoding::JsonParsed {
} else { get_parsed_token_accounts(bank, keyed_accounts).collect()
keyed_accounts } else {
.map(|(pubkey, account)| RpcKeyedAccount { keyed_accounts
pubkey: pubkey.to_string(), .map(|(pubkey, account)| RpcKeyedAccount {
account: UiAccount::encode(&pubkey, account, encoding.clone(), None), pubkey: pubkey.to_string(),
}) account: UiAccount::encode(
.collect() &pubkey,
} account,
encoding.clone(),
None,
data_slice_config,
),
})
.collect()
};
Ok(result)
} }
pub fn get_inflation_governor( pub fn get_inflation_governor(
@ -1107,6 +1124,8 @@ impl JsonRpcRequestProcessor {
let config = config.unwrap_or_default(); let config = config.unwrap_or_default();
let bank = self.bank(config.commitment); let bank = self.bank(config.commitment);
let encoding = config.encoding.unwrap_or(UiAccountEncoding::Binary); let encoding = config.encoding.unwrap_or(UiAccountEncoding::Binary);
let data_slice_config = config.data_slice;
check_slice_and_encoding(&encoding, data_slice_config.is_some())?;
let (token_program_id, mint) = get_token_program_id_and_mint(&bank, token_account_filter)?; let (token_program_id, mint) = get_token_program_id_and_mint(&bank, token_account_filter)?;
let mut filters = vec![ let mut filters = vec![
@ -1134,7 +1153,13 @@ impl JsonRpcRequestProcessor {
keyed_accounts keyed_accounts
.map(|(pubkey, account)| RpcKeyedAccount { .map(|(pubkey, account)| RpcKeyedAccount {
pubkey: pubkey.to_string(), pubkey: pubkey.to_string(),
account: UiAccount::encode(&pubkey, account, encoding.clone(), None), account: UiAccount::encode(
&pubkey,
account,
encoding.clone(),
None,
data_slice_config,
),
}) })
.collect() .collect()
}; };
@ -1150,6 +1175,8 @@ impl JsonRpcRequestProcessor {
let config = config.unwrap_or_default(); let config = config.unwrap_or_default();
let bank = self.bank(config.commitment); let bank = self.bank(config.commitment);
let encoding = config.encoding.unwrap_or(UiAccountEncoding::Binary); let encoding = config.encoding.unwrap_or(UiAccountEncoding::Binary);
let data_slice_config = config.data_slice;
check_slice_and_encoding(&encoding, data_slice_config.is_some())?;
let (token_program_id, mint) = get_token_program_id_and_mint(&bank, token_account_filter)?; let (token_program_id, mint) = get_token_program_id_and_mint(&bank, token_account_filter)?;
let mut filters = vec![ let mut filters = vec![
@ -1185,7 +1212,13 @@ impl JsonRpcRequestProcessor {
keyed_accounts keyed_accounts
.map(|(pubkey, account)| RpcKeyedAccount { .map(|(pubkey, account)| RpcKeyedAccount {
pubkey: pubkey.to_string(), pubkey: pubkey.to_string(),
account: UiAccount::encode(&pubkey, account, encoding.clone(), None), account: UiAccount::encode(
&pubkey,
account,
encoding.clone(),
None,
data_slice_config,
),
}) })
.collect() .collect()
}; };
@ -1226,6 +1259,26 @@ fn verify_token_account_filter(
} }
} }
fn check_slice_and_encoding(encoding: &UiAccountEncoding, data_slice_is_some: bool) -> Result<()> {
match encoding {
UiAccountEncoding::JsonParsed => {
if data_slice_is_some {
let message =
"Sliced account data can only be encoded using binary (base 58) or binary64 encoding."
.to_string();
Err(error::Error {
code: error::ErrorCode::InvalidRequest,
message,
data: None,
})
} else {
Ok(())
}
}
UiAccountEncoding::Binary | UiAccountEncoding::Binary64 => Ok(()),
}
}
/// Use a set of filters to get an iterator of keyed program accounts from a bank /// Use a set of filters to get an iterator of keyed program accounts from a bank
fn get_filtered_program_accounts( fn get_filtered_program_accounts(
bank: &Arc<Bank>, bank: &Arc<Bank>,
@ -1258,6 +1311,7 @@ pub(crate) fn get_parsed_token_account(
account, account,
UiAccountEncoding::JsonParsed, UiAccountEncoding::JsonParsed,
additional_data, additional_data,
None,
) )
} }
@ -1286,6 +1340,7 @@ where
account, account,
UiAccountEncoding::JsonParsed, UiAccountEncoding::JsonParsed,
additional_data, additional_data,
None,
), ),
} }
}) })
@ -1753,7 +1808,7 @@ impl RpcSol for RpcSolImpl {
for filter in &filters { for filter in &filters {
verify_filter(filter)?; verify_filter(filter)?;
} }
Ok(meta.get_program_accounts(&program_id, config, filters)) meta.get_program_accounts(&program_id, config, filters)
} }
fn get_inflation_governor( fn get_inflation_governor(
@ -3028,13 +3083,13 @@ pub mod tests {
#[test] #[test]
fn test_rpc_get_account_info() { fn test_rpc_get_account_info() {
let bob_pubkey = Pubkey::new_rand(); let bob_pubkey = Pubkey::new_rand();
let RpcHandler { io, meta, .. } = start_rpc_handler_with_tx(&bob_pubkey); let RpcHandler { io, meta, bank, .. } = start_rpc_handler_with_tx(&bob_pubkey);
let req = format!( let req = format!(
r#"{{"jsonrpc":"2.0","id":1,"method":"getAccountInfo","params":["{}"]}}"#, r#"{{"jsonrpc":"2.0","id":1,"method":"getAccountInfo","params":["{}"]}}"#,
bob_pubkey bob_pubkey
); );
let res = io.handle_request_sync(&req, meta); let res = io.handle_request_sync(&req, meta.clone());
let expected = json!({ let expected = json!({
"jsonrpc": "2.0", "jsonrpc": "2.0",
"result": { "result": {
@ -3054,6 +3109,54 @@ pub mod tests {
let result: Response = serde_json::from_str(&res.expect("actual response")) let result: Response = serde_json::from_str(&res.expect("actual response"))
.expect("actual response deserialization"); .expect("actual response deserialization");
assert_eq!(expected, result); assert_eq!(expected, result);
let address = Pubkey::new_rand();
let data = vec![1, 2, 3, 4, 5];
let mut account = Account::new(42, 5, &Pubkey::default());
account.data = data.clone();
bank.store_account(&address, &account);
let req = format!(
r#"{{"jsonrpc":"2.0","id":1,"method":"getAccountInfo","params":["{}", {{"encoding":"binary64"}}]}}"#,
address
);
let res = io.handle_request_sync(&req, meta.clone());
let result: Value = serde_json::from_str(&res.expect("actual response"))
.expect("actual response deserialization");
assert_eq!(result["result"]["value"]["data"], base64::encode(&data));
let req = format!(
r#"{{"jsonrpc":"2.0","id":1,"method":"getAccountInfo","params":["{}", {{"encoding":"binary64", "dataSlice": {{"length": 2, "offset": 1}}}}]}}"#,
address
);
let res = io.handle_request_sync(&req, meta.clone());
let result: Value = serde_json::from_str(&res.expect("actual response"))
.expect("actual response deserialization");
assert_eq!(
result["result"]["value"]["data"],
base64::encode(&data[1..3]),
);
let req = format!(
r#"{{"jsonrpc":"2.0","id":1,"method":"getAccountInfo","params":["{}", {{"encoding":"binary", "dataSlice": {{"length": 2, "offset": 1}}}}]}}"#,
address
);
let res = io.handle_request_sync(&req, meta.clone());
let result: Value = serde_json::from_str(&res.expect("actual response"))
.expect("actual response deserialization");
assert_eq!(
result["result"]["value"]["data"],
bs58::encode(&data[1..3]).into_string(),
);
let req = format!(
r#"{{"jsonrpc":"2.0","id":1,"method":"getAccountInfo","params":["{}", {{"encoding":"jsonParsed", "dataSlice": {{"length": 2, "offset": 1}}}}]}}"#,
address
);
let res = io.handle_request_sync(&req, meta);
let result: Value = serde_json::from_str(&res.expect("actual response"))
.expect("actual response deserialization");
result["error"].as_object().unwrap();
} }
#[test] #[test]

View File

@ -542,6 +542,7 @@ mod tests {
Some(RpcAccountInfoConfig { Some(RpcAccountInfoConfig {
commitment: Some(CommitmentConfig::recent()), commitment: Some(CommitmentConfig::recent()),
encoding: None, encoding: None,
data_slice: None,
}), }),
); );
@ -649,6 +650,7 @@ mod tests {
Some(RpcAccountInfoConfig { Some(RpcAccountInfoConfig {
commitment: Some(CommitmentConfig::recent()), commitment: Some(CommitmentConfig::recent()),
encoding: Some(UiAccountEncoding::JsonParsed), encoding: Some(UiAccountEncoding::JsonParsed),
data_slice: None,
}), }),
); );
@ -769,6 +771,7 @@ mod tests {
Some(RpcAccountInfoConfig { Some(RpcAccountInfoConfig {
commitment: Some(CommitmentConfig::root()), commitment: Some(CommitmentConfig::root()),
encoding: None, encoding: None,
data_slice: None,
}), }),
); );
@ -818,6 +821,7 @@ mod tests {
Some(RpcAccountInfoConfig { Some(RpcAccountInfoConfig {
commitment: Some(CommitmentConfig::root()), commitment: Some(CommitmentConfig::root()),
encoding: None, encoding: None,
data_slice: None,
}), }),
); );

View File

@ -265,7 +265,7 @@ fn filter_account_result(
} else { } else {
return ( return (
Box::new(iter::once(UiAccount::encode( Box::new(iter::once(UiAccount::encode(
pubkey, account, encoding, None, pubkey, account, encoding, None, None,
))), ))),
fork, fork,
); );
@ -316,7 +316,7 @@ fn filter_program_results(
Box::new( Box::new(
keyed_accounts.map(move |(pubkey, account)| RpcKeyedAccount { keyed_accounts.map(move |(pubkey, account)| RpcKeyedAccount {
pubkey: pubkey.to_string(), pubkey: pubkey.to_string(),
account: UiAccount::encode(&pubkey, account, encoding.clone(), None), account: UiAccount::encode(&pubkey, account, encoding.clone(), None, None),
}), }),
) )
}; };
@ -1033,6 +1033,7 @@ pub(crate) mod tests {
Some(RpcAccountInfoConfig { Some(RpcAccountInfoConfig {
commitment: Some(CommitmentConfig::recent()), commitment: Some(CommitmentConfig::recent()),
encoding: None, encoding: None,
data_slice: None,
}), }),
sub_id.clone(), sub_id.clone(),
subscriber, subscriber,
@ -1517,6 +1518,7 @@ pub(crate) mod tests {
Some(RpcAccountInfoConfig { Some(RpcAccountInfoConfig {
commitment: Some(CommitmentConfig::single_gossip()), commitment: Some(CommitmentConfig::single_gossip()),
encoding: None, encoding: None,
data_slice: None,
}), }),
sub_id0.clone(), sub_id0.clone(),
subscriber0, subscriber0,
@ -1585,6 +1587,7 @@ pub(crate) mod tests {
Some(RpcAccountInfoConfig { Some(RpcAccountInfoConfig {
commitment: Some(CommitmentConfig::single_gossip()), commitment: Some(CommitmentConfig::single_gossip()),
encoding: None, encoding: None,
data_slice: None,
}), }),
sub_id1.clone(), sub_id1.clone(),
subscriber1, subscriber1,

View File

@ -105,6 +105,7 @@ fn test_rpc_send_tx() {
let config = RpcAccountInfoConfig { let config = RpcAccountInfoConfig {
encoding: Some(UiAccountEncoding::Binary64), encoding: Some(UiAccountEncoding::Binary64),
commitment: None, commitment: None,
data_slice: None,
}; };
let req = json_req!( let req = json_req!(
"getAccountInfo", "getAccountInfo",

View File

@ -158,6 +158,7 @@ Returns all information associated with the account of provided Pubkey
- (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment) - (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment)
- (optional) `encoding: <string>` - encoding for Account data, either "binary", "binary64", or jsonParsed". If parameter not provided, the default encoding is "binary". "binary" is base-58 encoded and limited to Account data of less than 128 bytes. "binary64" will return base64 encoded data for Account data of any size. - (optional) `encoding: <string>` - encoding for Account data, either "binary", "binary64", or jsonParsed". If parameter not provided, the default encoding is "binary". "binary" is base-58 encoded and limited to Account data of less than 128 bytes. "binary64" will return base64 encoded data for Account data of any size.
Parsed-JSON encoding attempts to use program-specific state parsers to return more human-readable and explicit account state data. If parsed-JSON is requested but a parser cannot be found, the field falls back to binary encoding, detectable when the `data` field is type `<string>`. **jsonParsed encoding is UNSTABLE** Parsed-JSON encoding attempts to use program-specific state parsers to return more human-readable and explicit account state data. If parsed-JSON is requested but a parser cannot be found, the field falls back to binary encoding, detectable when the `data` field is type `<string>`. **jsonParsed encoding is UNSTABLE**
- (optional) `dataSlice: <object>` - limit the returned account data using the provided `offset: <usize>` and `length: <usize>` fields; only available for "binary" or "binary64" encoding.
#### Results: #### Results:
@ -845,6 +846,7 @@ Returns all accounts owned by the provided program Pubkey
- (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment) - (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment)
- (optional) `encoding: <string>` - encoding for Account data, either "binary" or jsonParsed". If parameter not provided, the default encoding is binary. - (optional) `encoding: <string>` - encoding for Account data, either "binary" or jsonParsed". If parameter not provided, the default encoding is binary.
Parsed-JSON encoding attempts to use program-specific state parsers to return more human-readable and explicit account state data. If parsed-JSON is requested but a parser cannot be found, the field falls back to binary encoding, detectable when the `data` field is type `<string>`. **jsonParsed encoding is UNSTABLE** Parsed-JSON encoding attempts to use program-specific state parsers to return more human-readable and explicit account state data. If parsed-JSON is requested but a parser cannot be found, the field falls back to binary encoding, detectable when the `data` field is type `<string>`. **jsonParsed encoding is UNSTABLE**
- (optional) `dataSlice: <object>` - limit the returned account data using the provided `offset: <usize>` and `length: <usize>` fields; only available for "binary" or "binary64" encoding.
- (optional) `filters: <array>` - filter results using various [filter objects](jsonrpc-api.md#filters); account must meet all filter criteria to be included in results - (optional) `filters: <array>` - filter results using various [filter objects](jsonrpc-api.md#filters); account must meet all filter criteria to be included in results
##### Filters: ##### Filters:
@ -1099,6 +1101,7 @@ Returns all SPL Token accounts by approved Delegate. **UNSTABLE**
- (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment) - (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment)
- (optional) `encoding: <string>` - encoding for Account data, either "binary" or jsonParsed". If parameter not provided, the default encoding is binary. - (optional) `encoding: <string>` - encoding for Account data, either "binary" or jsonParsed". If parameter not provided, the default encoding is binary.
Parsed-JSON encoding attempts to use program-specific state parsers to return more human-readable and explicit account state data. If parsed-JSON is requested but a parser cannot be found, the field falls back to binary encoding, detectable when the `data` field is type `<string>`. **jsonParsed encoding is UNSTABLE** Parsed-JSON encoding attempts to use program-specific state parsers to return more human-readable and explicit account state data. If parsed-JSON is requested but a parser cannot be found, the field falls back to binary encoding, detectable when the `data` field is type `<string>`. **jsonParsed encoding is UNSTABLE**
- (optional) `dataSlice: <object>` - limit the returned account data using the provided `offset: <usize>` and `length: <usize>` fields; only available for "binary" or "binary64" encoding.
#### Results: #### Results:
@ -1135,6 +1138,7 @@ Returns all SPL Token accounts by token owner. **UNSTABLE**
- (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment) - (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment)
- (optional) `encoding: <string>` - encoding for Account data, either "binary" or jsonParsed". If parameter not provided, the default encoding is binary. - (optional) `encoding: <string>` - encoding for Account data, either "binary" or jsonParsed". If parameter not provided, the default encoding is binary.
Parsed-JSON encoding attempts to use program-specific state parsers to return more human-readable and explicit account state data. If parsed-JSON is requested but a parser cannot be found, the field falls back to binary encoding, detectable when the `data` field is type `<string>`. **jsonParsed encoding is UNSTABLE** Parsed-JSON encoding attempts to use program-specific state parsers to return more human-readable and explicit account state data. If parsed-JSON is requested but a parser cannot be found, the field falls back to binary encoding, detectable when the `data` field is type `<string>`. **jsonParsed encoding is UNSTABLE**
- (optional) `dataSlice: <object>` - limit the returned account data using the provided `offset: <usize>` and `length: <usize>` fields; only available for "binary" or "binary64" encoding.
#### Results: #### Results: