* Decode token instructions (#11281)
* Token->SplToken
* Add spl_token instruction parsing
* Rebase on master
* Gracefully fail key len mismatches
(cherry picked from commit 0f551d4f75
)
# Conflicts:
# Cargo.lock
# transaction-status/Cargo.toml
* Fix conflicts
Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
Co-authored-by: Tyera Eulberg <tyera@solana.com>
This commit is contained in:
3
Cargo.lock
generated
3
Cargo.lock
generated
@ -4881,8 +4881,11 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
"solana-account-decoder",
|
||||||
"solana-sdk 1.2.16",
|
"solana-sdk 1.2.16",
|
||||||
"spl-memo",
|
"spl-memo",
|
||||||
|
"spl-token",
|
||||||
|
"thiserror",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -16,7 +16,7 @@ lazy_static! {
|
|||||||
pub static ref PARSABLE_PROGRAM_IDS: HashMap<Pubkey, ParsableAccount> = {
|
pub static ref PARSABLE_PROGRAM_IDS: HashMap<Pubkey, ParsableAccount> = {
|
||||||
let mut m = HashMap::new();
|
let mut m = HashMap::new();
|
||||||
m.insert(*SYSTEM_PROGRAM_ID, ParsableAccount::Nonce);
|
m.insert(*SYSTEM_PROGRAM_ID, ParsableAccount::Nonce);
|
||||||
m.insert(*TOKEN_PROGRAM_ID, ParsableAccount::Token);
|
m.insert(*TOKEN_PROGRAM_ID, ParsableAccount::SplToken);
|
||||||
m.insert(*VOTE_PROGRAM_ID, ParsableAccount::Vote);
|
m.insert(*VOTE_PROGRAM_ID, ParsableAccount::Vote);
|
||||||
m
|
m
|
||||||
};
|
};
|
||||||
@ -41,7 +41,7 @@ pub enum ParseAccountError {
|
|||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub enum ParsableAccount {
|
pub enum ParsableAccount {
|
||||||
Nonce,
|
Nonce,
|
||||||
Token,
|
SplToken,
|
||||||
Vote,
|
Vote,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -51,7 +51,7 @@ pub fn parse_account_data(program_id: &Pubkey, data: &[u8]) -> Result<Value, Par
|
|||||||
.ok_or_else(|| ParseAccountError::ProgramNotParsable)?;
|
.ok_or_else(|| ParseAccountError::ProgramNotParsable)?;
|
||||||
let parsed_json = match program_name {
|
let parsed_json = match program_name {
|
||||||
ParsableAccount::Nonce => serde_json::to_value(parse_nonce(data)?)?,
|
ParsableAccount::Nonce => serde_json::to_value(parse_nonce(data)?)?,
|
||||||
ParsableAccount::Token => serde_json::to_value(parse_token(data)?)?,
|
ParsableAccount::SplToken => serde_json::to_value(parse_token(data)?)?,
|
||||||
ParsableAccount::Vote => serde_json::to_value(parse_vote(data)?)?,
|
ParsableAccount::Vote => serde_json::to_value(parse_vote(data)?)?,
|
||||||
};
|
};
|
||||||
Ok(json!({
|
Ok(json!({
|
||||||
|
@ -16,7 +16,7 @@ pub fn parse_token(data: &[u8]) -> Result<TokenAccountType, ParseAccountError> {
|
|||||||
let mut data = data.to_vec();
|
let mut data = data.to_vec();
|
||||||
if data.len() == size_of::<Account>() {
|
if data.len() == size_of::<Account>() {
|
||||||
let account: Account = *State::unpack(&mut data)
|
let account: Account = *State::unpack(&mut data)
|
||||||
.map_err(|_| ParseAccountError::AccountNotParsable(ParsableAccount::Token))?;
|
.map_err(|_| ParseAccountError::AccountNotParsable(ParsableAccount::SplToken))?;
|
||||||
Ok(TokenAccountType::Account(UiTokenAccount {
|
Ok(TokenAccountType::Account(UiTokenAccount {
|
||||||
mint: account.mint.to_string(),
|
mint: account.mint.to_string(),
|
||||||
owner: account.owner.to_string(),
|
owner: account.owner.to_string(),
|
||||||
@ -31,7 +31,7 @@ pub fn parse_token(data: &[u8]) -> Result<TokenAccountType, ParseAccountError> {
|
|||||||
}))
|
}))
|
||||||
} else if data.len() == size_of::<Mint>() {
|
} else if data.len() == size_of::<Mint>() {
|
||||||
let mint: Mint = *State::unpack(&mut data)
|
let mint: Mint = *State::unpack(&mut data)
|
||||||
.map_err(|_| ParseAccountError::AccountNotParsable(ParsableAccount::Token))?;
|
.map_err(|_| ParseAccountError::AccountNotParsable(ParsableAccount::SplToken))?;
|
||||||
Ok(TokenAccountType::Mint(UiMint {
|
Ok(TokenAccountType::Mint(UiMint {
|
||||||
owner: match mint.owner {
|
owner: match mint.owner {
|
||||||
COption::Some(pubkey) => Some(pubkey.to_string()),
|
COption::Some(pubkey) => Some(pubkey.to_string()),
|
||||||
@ -42,7 +42,7 @@ pub fn parse_token(data: &[u8]) -> Result<TokenAccountType, ParseAccountError> {
|
|||||||
}))
|
}))
|
||||||
} else if data.len() == size_of::<Multisig>() {
|
} else if data.len() == size_of::<Multisig>() {
|
||||||
let multisig: Multisig = *State::unpack(&mut data)
|
let multisig: Multisig = *State::unpack(&mut data)
|
||||||
.map_err(|_| ParseAccountError::AccountNotParsable(ParsableAccount::Token))?;
|
.map_err(|_| ParseAccountError::AccountNotParsable(ParsableAccount::SplToken))?;
|
||||||
Ok(TokenAccountType::Multisig(UiMultisig {
|
Ok(TokenAccountType::Multisig(UiMultisig {
|
||||||
num_required_signers: multisig.m,
|
num_required_signers: multisig.m,
|
||||||
num_valid_signers: multisig.n,
|
num_valid_signers: multisig.n,
|
||||||
@ -61,7 +61,7 @@ pub fn parse_token(data: &[u8]) -> Result<TokenAccountType, ParseAccountError> {
|
|||||||
}))
|
}))
|
||||||
} else {
|
} else {
|
||||||
Err(ParseAccountError::AccountNotParsable(
|
Err(ParseAccountError::AccountNotParsable(
|
||||||
ParsableAccount::Token,
|
ParsableAccount::SplToken,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,11 +13,14 @@ bincode = "1.2.1"
|
|||||||
bs58 = "0.3.1"
|
bs58 = "0.3.1"
|
||||||
Inflector = "0.11.4"
|
Inflector = "0.11.4"
|
||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
|
solana-account-decoder = { path = "../account-decoder", version = "1.2.16" }
|
||||||
solana-sdk = { path = "../sdk", version = "1.2.16" }
|
solana-sdk = { path = "../sdk", version = "1.2.16" }
|
||||||
spl-memo-v1-0 = { package = "spl-memo", version = "1.0.4", features = ["skip-no-mangle"] }
|
spl-memo-v1-0 = { package = "spl-memo", version = "1.0.4", features = ["skip-no-mangle"] }
|
||||||
|
spl-token-v1-0 = { package = "spl-token", version = "1.0.3", features = ["skip-no-mangle"] }
|
||||||
serde = "1.0.110"
|
serde = "1.0.110"
|
||||||
serde_derive = "1.0.103"
|
serde_derive = "1.0.103"
|
||||||
serde_json = "1.0.54"
|
serde_json = "1.0.54"
|
||||||
|
thiserror = "1.0"
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
targets = ["x86_64-unknown-linux-gnu"]
|
targets = ["x86_64-unknown-linux-gnu"]
|
||||||
|
@ -5,6 +5,7 @@ extern crate serde_derive;
|
|||||||
|
|
||||||
pub mod parse_accounts;
|
pub mod parse_accounts;
|
||||||
pub mod parse_instruction;
|
pub mod parse_instruction;
|
||||||
|
pub mod parse_token;
|
||||||
|
|
||||||
use crate::{parse_accounts::parse_accounts, parse_instruction::parse};
|
use crate::{parse_accounts::parse_accounts, parse_instruction::parse};
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
@ -220,7 +221,11 @@ impl EncodedTransaction {
|
|||||||
.map(|instruction| {
|
.map(|instruction| {
|
||||||
let program_id =
|
let program_id =
|
||||||
instruction.program_id(&transaction.message.account_keys);
|
instruction.program_id(&transaction.message.account_keys);
|
||||||
if let Some(parsed_instruction) = parse(program_id, instruction) {
|
if let Ok(parsed_instruction) = parse(
|
||||||
|
program_id,
|
||||||
|
instruction,
|
||||||
|
&transaction.message.account_keys,
|
||||||
|
) {
|
||||||
UiInstruction::Parsed(parsed_instruction)
|
UiInstruction::Parsed(parsed_instruction)
|
||||||
} else {
|
} else {
|
||||||
UiInstruction::Compiled(instruction.into())
|
UiInstruction::Compiled(instruction.into())
|
||||||
|
@ -1,34 +1,60 @@
|
|||||||
|
use crate::parse_token::parse_token;
|
||||||
use inflector::Inflector;
|
use inflector::Inflector;
|
||||||
use serde_json::{json, Value};
|
use serde_json::{json, Value};
|
||||||
|
use solana_account_decoder::parse_token::spl_token_id_v1_0;
|
||||||
use solana_sdk::{instruction::CompiledInstruction, pubkey::Pubkey};
|
use solana_sdk::{instruction::CompiledInstruction, pubkey::Pubkey};
|
||||||
use std::{
|
use std::{
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
str::{from_utf8, FromStr},
|
str::{from_utf8, FromStr},
|
||||||
};
|
};
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
static ref MEMO_PROGRAM_ID: Pubkey =
|
static ref MEMO_PROGRAM_ID: Pubkey =
|
||||||
Pubkey::from_str(&spl_memo_v1_0::id().to_string()).unwrap();
|
Pubkey::from_str(&spl_memo_v1_0::id().to_string()).unwrap();
|
||||||
|
static ref TOKEN_PROGRAM_ID: Pubkey = spl_token_id_v1_0();
|
||||||
static ref PARSABLE_PROGRAM_IDS: HashMap<Pubkey, ParsableProgram> = {
|
static ref PARSABLE_PROGRAM_IDS: HashMap<Pubkey, ParsableProgram> = {
|
||||||
let mut m = HashMap::new();
|
let mut m = HashMap::new();
|
||||||
m.insert(*MEMO_PROGRAM_ID, ParsableProgram::SplMemo);
|
m.insert(*MEMO_PROGRAM_ID, ParsableProgram::SplMemo);
|
||||||
|
m.insert(*TOKEN_PROGRAM_ID, ParsableProgram::SplToken);
|
||||||
m
|
m
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Error, Debug, PartialEq)]
|
||||||
#[serde(rename_all = "camelCase")]
|
pub enum ParseInstructionError {
|
||||||
enum ParsableProgram {
|
#[error("{0:?} instruction not parsable")]
|
||||||
SplMemo,
|
InstructionNotParsable(ParsableProgram),
|
||||||
|
|
||||||
|
#[error("{0:?} instruction key mismatch")]
|
||||||
|
InstructionKeyMismatch(ParsableProgram),
|
||||||
|
|
||||||
|
#[error("Program not parsable")]
|
||||||
|
ProgramNotParsable,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse(program_id: &Pubkey, instruction: &CompiledInstruction) -> Option<Value> {
|
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
||||||
PARSABLE_PROGRAM_IDS.get(program_id).map(|program_name| {
|
#[serde(rename_all = "camelCase")]
|
||||||
let parsed_json = match program_name {
|
pub enum ParsableProgram {
|
||||||
ParsableProgram::SplMemo => parse_memo(instruction),
|
SplMemo,
|
||||||
};
|
SplToken,
|
||||||
json!({ format!("{:?}", program_name).to_kebab_case(): parsed_json })
|
}
|
||||||
})
|
|
||||||
|
pub fn parse(
|
||||||
|
program_id: &Pubkey,
|
||||||
|
instruction: &CompiledInstruction,
|
||||||
|
account_keys: &[Pubkey],
|
||||||
|
) -> Result<Value, ParseInstructionError> {
|
||||||
|
let program_name = PARSABLE_PROGRAM_IDS
|
||||||
|
.get(program_id)
|
||||||
|
.ok_or_else(|| ParseInstructionError::ProgramNotParsable)?;
|
||||||
|
let parsed_json = match program_name {
|
||||||
|
ParsableProgram::SplMemo => parse_memo(instruction),
|
||||||
|
ParsableProgram::SplToken => parse_token(instruction, account_keys)?,
|
||||||
|
};
|
||||||
|
Ok(json!({
|
||||||
|
format!("{:?}", program_name).to_kebab_case(): parsed_json
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_memo(instruction: &CompiledInstruction) -> Value {
|
fn parse_memo(instruction: &CompiledInstruction) -> Value {
|
||||||
@ -50,11 +76,14 @@ mod test {
|
|||||||
"spl-memo": "🦖"
|
"spl-memo": "🦖"
|
||||||
});
|
});
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse(&MEMO_PROGRAM_ID, &memo_instruction),
|
parse(&MEMO_PROGRAM_ID, &memo_instruction, &[]).unwrap(),
|
||||||
Some(expected_json)
|
expected_json
|
||||||
);
|
);
|
||||||
|
|
||||||
let non_parsable_program_id = Pubkey::new(&[1; 32]);
|
let non_parsable_program_id = Pubkey::new(&[1; 32]);
|
||||||
assert_eq!(parse(&non_parsable_program_id, &memo_instruction), None);
|
assert_eq!(
|
||||||
|
parse(&non_parsable_program_id, &memo_instruction, &[]).unwrap_err(),
|
||||||
|
ParseInstructionError::ProgramNotParsable
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
846
transaction-status/src/parse_token.rs
Normal file
846
transaction-status/src/parse_token.rs
Normal file
@ -0,0 +1,846 @@
|
|||||||
|
use crate::parse_instruction::{ParsableProgram, ParseInstructionError};
|
||||||
|
use serde_json::{json, Map, Value};
|
||||||
|
use solana_sdk::{instruction::CompiledInstruction, pubkey::Pubkey};
|
||||||
|
use spl_token_v1_0::instruction::TokenInstruction;
|
||||||
|
|
||||||
|
pub fn parse_token(
|
||||||
|
instruction: &CompiledInstruction,
|
||||||
|
account_keys: &[Pubkey],
|
||||||
|
) -> Result<Value, ParseInstructionError> {
|
||||||
|
let token_instruction = TokenInstruction::unpack(&instruction.data)
|
||||||
|
.map_err(|_| ParseInstructionError::InstructionNotParsable(ParsableProgram::SplToken))?;
|
||||||
|
if instruction.accounts.len() > account_keys.len() {
|
||||||
|
// Runtime should prevent this from ever happening
|
||||||
|
return Err(ParseInstructionError::InstructionKeyMismatch(
|
||||||
|
ParsableProgram::SplToken,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
match token_instruction {
|
||||||
|
TokenInstruction::InitializeMint { amount, decimals } => {
|
||||||
|
if instruction.accounts.len() < 2 {
|
||||||
|
return Err(ParseInstructionError::InstructionKeyMismatch(
|
||||||
|
ParsableProgram::SplToken,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
let mut value = json!({
|
||||||
|
"type": "initializeMint",
|
||||||
|
"mint": account_keys[instruction.accounts[0] as usize].to_string(),
|
||||||
|
"amount": amount,
|
||||||
|
"decimals":decimals,
|
||||||
|
});
|
||||||
|
let map = value.as_object_mut().unwrap();
|
||||||
|
if amount == 0 {
|
||||||
|
map.insert(
|
||||||
|
"owner".to_string(),
|
||||||
|
json!(account_keys[instruction.accounts[1] as usize].to_string()),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
map.insert(
|
||||||
|
"account".to_string(),
|
||||||
|
json!(account_keys[instruction.accounts[1] as usize].to_string()),
|
||||||
|
);
|
||||||
|
if let Some(i) = instruction.accounts.get(2) {
|
||||||
|
map.insert(
|
||||||
|
"owner".to_string(),
|
||||||
|
json!(account_keys[*i as usize].to_string()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(value)
|
||||||
|
}
|
||||||
|
TokenInstruction::InitializeAccount => {
|
||||||
|
if instruction.accounts.len() < 3 {
|
||||||
|
return Err(ParseInstructionError::InstructionKeyMismatch(
|
||||||
|
ParsableProgram::SplToken,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
Ok(json!({
|
||||||
|
"type": "initializeAccount",
|
||||||
|
"account": account_keys[instruction.accounts[0] as usize].to_string(),
|
||||||
|
"mint": account_keys[instruction.accounts[1] as usize].to_string(),
|
||||||
|
"owner": account_keys[instruction.accounts[2] as usize].to_string(),
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
TokenInstruction::InitializeMultisig { m } => {
|
||||||
|
if instruction.accounts.len() < 2 {
|
||||||
|
return Err(ParseInstructionError::InstructionKeyMismatch(
|
||||||
|
ParsableProgram::SplToken,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
let mut signers: Vec<String> = vec![];
|
||||||
|
for i in instruction.accounts[1..].iter() {
|
||||||
|
signers.push(account_keys[*i as usize].to_string());
|
||||||
|
}
|
||||||
|
Ok(json!({
|
||||||
|
"type": "initializeMultisig",
|
||||||
|
"multisig": account_keys[instruction.accounts[0] as usize].to_string(),
|
||||||
|
"signers": signers,
|
||||||
|
"m": m,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
TokenInstruction::Transfer { amount } => {
|
||||||
|
if instruction.accounts.len() < 3 {
|
||||||
|
return Err(ParseInstructionError::InstructionKeyMismatch(
|
||||||
|
ParsableProgram::SplToken,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
let mut value = json!({
|
||||||
|
"type": "transfer",
|
||||||
|
"source": account_keys[instruction.accounts[0] as usize].to_string(),
|
||||||
|
"destination": account_keys[instruction.accounts[1] as usize].to_string(),
|
||||||
|
"amount": amount,
|
||||||
|
});
|
||||||
|
let mut map = value.as_object_mut().unwrap();
|
||||||
|
parse_signers(
|
||||||
|
&mut map,
|
||||||
|
2,
|
||||||
|
account_keys,
|
||||||
|
&instruction.accounts,
|
||||||
|
"authority",
|
||||||
|
"multisigAuthority",
|
||||||
|
);
|
||||||
|
Ok(value)
|
||||||
|
}
|
||||||
|
TokenInstruction::Approve { amount } => {
|
||||||
|
if instruction.accounts.len() < 3 {
|
||||||
|
return Err(ParseInstructionError::InstructionKeyMismatch(
|
||||||
|
ParsableProgram::SplToken,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
let mut value = json!({
|
||||||
|
"type": "approve",
|
||||||
|
"source": account_keys[instruction.accounts[0] as usize].to_string(),
|
||||||
|
"delegate": account_keys[instruction.accounts[1] as usize].to_string(),
|
||||||
|
"amount": amount,
|
||||||
|
});
|
||||||
|
let mut map = value.as_object_mut().unwrap();
|
||||||
|
parse_signers(
|
||||||
|
&mut map,
|
||||||
|
2,
|
||||||
|
account_keys,
|
||||||
|
&instruction.accounts,
|
||||||
|
"owner",
|
||||||
|
"multisigOwner",
|
||||||
|
);
|
||||||
|
Ok(value)
|
||||||
|
}
|
||||||
|
TokenInstruction::Revoke => {
|
||||||
|
if instruction.accounts.len() < 2 {
|
||||||
|
return Err(ParseInstructionError::InstructionKeyMismatch(
|
||||||
|
ParsableProgram::SplToken,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
let mut value = json!({
|
||||||
|
"type": "revoke",
|
||||||
|
"source": account_keys[instruction.accounts[0] as usize].to_string(),
|
||||||
|
});
|
||||||
|
let mut map = value.as_object_mut().unwrap();
|
||||||
|
parse_signers(
|
||||||
|
&mut map,
|
||||||
|
1,
|
||||||
|
account_keys,
|
||||||
|
&instruction.accounts,
|
||||||
|
"owner",
|
||||||
|
"multisigOwner",
|
||||||
|
);
|
||||||
|
Ok(value)
|
||||||
|
}
|
||||||
|
TokenInstruction::SetOwner => {
|
||||||
|
if instruction.accounts.len() < 3 {
|
||||||
|
return Err(ParseInstructionError::InstructionKeyMismatch(
|
||||||
|
ParsableProgram::SplToken,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
let mut value = json!({
|
||||||
|
"type": "setOwner",
|
||||||
|
"owned": account_keys[instruction.accounts[0] as usize].to_string(),
|
||||||
|
"newOwner": account_keys[instruction.accounts[1] as usize].to_string(),
|
||||||
|
});
|
||||||
|
let mut map = value.as_object_mut().unwrap();
|
||||||
|
parse_signers(
|
||||||
|
&mut map,
|
||||||
|
2,
|
||||||
|
account_keys,
|
||||||
|
&instruction.accounts,
|
||||||
|
"owner",
|
||||||
|
"multisigOwner",
|
||||||
|
);
|
||||||
|
Ok(value)
|
||||||
|
}
|
||||||
|
TokenInstruction::MintTo { amount } => {
|
||||||
|
if instruction.accounts.len() < 3 {
|
||||||
|
return Err(ParseInstructionError::InstructionKeyMismatch(
|
||||||
|
ParsableProgram::SplToken,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
let mut value = json!({
|
||||||
|
"type": "mintTo",
|
||||||
|
"mint": account_keys[instruction.accounts[0] as usize].to_string(),
|
||||||
|
"account": account_keys[instruction.accounts[1] as usize].to_string(),
|
||||||
|
"amount": amount,
|
||||||
|
});
|
||||||
|
let mut map = value.as_object_mut().unwrap();
|
||||||
|
parse_signers(
|
||||||
|
&mut map,
|
||||||
|
2,
|
||||||
|
account_keys,
|
||||||
|
&instruction.accounts,
|
||||||
|
"owner",
|
||||||
|
"multisigOwner",
|
||||||
|
);
|
||||||
|
Ok(value)
|
||||||
|
}
|
||||||
|
TokenInstruction::Burn { amount } => {
|
||||||
|
if instruction.accounts.len() < 2 {
|
||||||
|
return Err(ParseInstructionError::InstructionKeyMismatch(
|
||||||
|
ParsableProgram::SplToken,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
let mut value = json!({
|
||||||
|
"type": "burn",
|
||||||
|
"account": account_keys[instruction.accounts[0] as usize].to_string(),
|
||||||
|
"amount": amount,
|
||||||
|
});
|
||||||
|
let mut map = value.as_object_mut().unwrap();
|
||||||
|
parse_signers(
|
||||||
|
&mut map,
|
||||||
|
1,
|
||||||
|
account_keys,
|
||||||
|
&instruction.accounts,
|
||||||
|
"authority",
|
||||||
|
"multisigAuthority",
|
||||||
|
);
|
||||||
|
Ok(value)
|
||||||
|
}
|
||||||
|
TokenInstruction::CloseAccount => {
|
||||||
|
if instruction.accounts.len() < 3 {
|
||||||
|
return Err(ParseInstructionError::InstructionKeyMismatch(
|
||||||
|
ParsableProgram::SplToken,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
let mut value = json!({
|
||||||
|
"type": "closeAccount",
|
||||||
|
"account": account_keys[instruction.accounts[0] as usize].to_string(),
|
||||||
|
"destination": account_keys[instruction.accounts[1] as usize].to_string(),
|
||||||
|
});
|
||||||
|
let mut map = value.as_object_mut().unwrap();
|
||||||
|
parse_signers(
|
||||||
|
&mut map,
|
||||||
|
2,
|
||||||
|
account_keys,
|
||||||
|
&instruction.accounts,
|
||||||
|
"owner",
|
||||||
|
"multisigOwner",
|
||||||
|
);
|
||||||
|
Ok(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_signers(
|
||||||
|
map: &mut Map<String, Value>,
|
||||||
|
last_nonsigner_index: usize,
|
||||||
|
account_keys: &[Pubkey],
|
||||||
|
accounts: &[u8],
|
||||||
|
owner_field_name: &str,
|
||||||
|
multisig_field_name: &str,
|
||||||
|
) {
|
||||||
|
if accounts.len() > last_nonsigner_index + 1 {
|
||||||
|
let mut signers: Vec<String> = vec![];
|
||||||
|
for i in accounts[last_nonsigner_index + 1..].iter() {
|
||||||
|
signers.push(account_keys[*i as usize].to_string());
|
||||||
|
}
|
||||||
|
map.insert(
|
||||||
|
multisig_field_name.to_string(),
|
||||||
|
json!(account_keys[accounts[last_nonsigner_index] as usize].to_string()),
|
||||||
|
);
|
||||||
|
map.insert("signers".to_string(), json!(signers));
|
||||||
|
} else {
|
||||||
|
map.insert(
|
||||||
|
owner_field_name.to_string(),
|
||||||
|
json!(account_keys[accounts[last_nonsigner_index] as usize].to_string()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
use solana_sdk::instruction::CompiledInstruction;
|
||||||
|
use spl_token_v1_0::{
|
||||||
|
instruction::*,
|
||||||
|
solana_sdk::{
|
||||||
|
instruction::CompiledInstruction as SplTokenCompiledInstruction, message::Message,
|
||||||
|
pubkey::Pubkey as SplTokenPubkey,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
fn convert_pubkey(pubkey: Pubkey) -> SplTokenPubkey {
|
||||||
|
SplTokenPubkey::from_str(&pubkey.to_string()).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn convert_compiled_instruction(
|
||||||
|
instruction: &SplTokenCompiledInstruction,
|
||||||
|
) -> CompiledInstruction {
|
||||||
|
CompiledInstruction {
|
||||||
|
program_id_index: instruction.program_id_index,
|
||||||
|
accounts: instruction.accounts.clone(),
|
||||||
|
data: instruction.data.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_parse_token() {
|
||||||
|
let mut keys: Vec<Pubkey> = vec![];
|
||||||
|
for _ in 0..10 {
|
||||||
|
keys.push(Pubkey::new_rand());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test InitializeMint variations
|
||||||
|
let initialize_mint_ix = initialize_mint(
|
||||||
|
&spl_token_v1_0::id(),
|
||||||
|
&convert_pubkey(keys[0]),
|
||||||
|
Some(&convert_pubkey(keys[1])),
|
||||||
|
Some(&convert_pubkey(keys[2])),
|
||||||
|
42,
|
||||||
|
2,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let message = Message::new(&[initialize_mint_ix], None);
|
||||||
|
let compiled_instruction = convert_compiled_instruction(&message.instructions[0]);
|
||||||
|
assert_eq!(
|
||||||
|
parse_token(&compiled_instruction, &keys).unwrap(),
|
||||||
|
json!({
|
||||||
|
"type": "initializeMint",
|
||||||
|
"mint": keys[0].to_string(),
|
||||||
|
"amount": 42,
|
||||||
|
"decimals": 2,
|
||||||
|
"account": keys[1].to_string(),
|
||||||
|
"owner": keys[2].to_string(),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
let initialize_mint_ix = initialize_mint(
|
||||||
|
&spl_token_v1_0::id(),
|
||||||
|
&convert_pubkey(keys[0]),
|
||||||
|
Some(&convert_pubkey(keys[1])),
|
||||||
|
None,
|
||||||
|
42,
|
||||||
|
2,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let message = Message::new(&[initialize_mint_ix], None);
|
||||||
|
let compiled_instruction = convert_compiled_instruction(&message.instructions[0]);
|
||||||
|
assert_eq!(
|
||||||
|
parse_token(&compiled_instruction, &keys).unwrap(),
|
||||||
|
json!({
|
||||||
|
"type": "initializeMint",
|
||||||
|
"mint": keys[0].to_string(),
|
||||||
|
"amount": 42,
|
||||||
|
"decimals": 2,
|
||||||
|
"account": keys[1].to_string(),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
let initialize_mint_ix = initialize_mint(
|
||||||
|
&spl_token_v1_0::id(),
|
||||||
|
&convert_pubkey(keys[0]),
|
||||||
|
None,
|
||||||
|
Some(&convert_pubkey(keys[1])),
|
||||||
|
0,
|
||||||
|
2,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let message = Message::new(&[initialize_mint_ix], None);
|
||||||
|
let compiled_instruction = convert_compiled_instruction(&message.instructions[0]);
|
||||||
|
assert_eq!(
|
||||||
|
parse_token(&compiled_instruction, &keys).unwrap(),
|
||||||
|
json!({
|
||||||
|
"type": "initializeMint",
|
||||||
|
"mint": keys[0].to_string(),
|
||||||
|
"amount": 0,
|
||||||
|
"decimals": 2,
|
||||||
|
"owner": keys[1].to_string(),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
// Test InitializeAccount
|
||||||
|
let initialize_account_ix = initialize_account(
|
||||||
|
&spl_token_v1_0::id(),
|
||||||
|
&convert_pubkey(keys[0]),
|
||||||
|
&convert_pubkey(keys[1]),
|
||||||
|
&convert_pubkey(keys[2]),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let message = Message::new(&[initialize_account_ix], None);
|
||||||
|
let compiled_instruction = convert_compiled_instruction(&message.instructions[0]);
|
||||||
|
assert_eq!(
|
||||||
|
parse_token(&compiled_instruction, &keys).unwrap(),
|
||||||
|
json!({
|
||||||
|
"type": "initializeAccount",
|
||||||
|
"account": keys[0].to_string(),
|
||||||
|
"mint": keys[1].to_string(),
|
||||||
|
"owner": keys[2].to_string(),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
// Test InitializeMultisig
|
||||||
|
let initialize_multisig_ix = initialize_multisig(
|
||||||
|
&spl_token_v1_0::id(),
|
||||||
|
&convert_pubkey(keys[0]),
|
||||||
|
&[
|
||||||
|
&convert_pubkey(keys[1]),
|
||||||
|
&convert_pubkey(keys[2]),
|
||||||
|
&convert_pubkey(keys[3]),
|
||||||
|
],
|
||||||
|
2,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let message = Message::new(&[initialize_multisig_ix], None);
|
||||||
|
let compiled_instruction = convert_compiled_instruction(&message.instructions[0]);
|
||||||
|
assert_eq!(
|
||||||
|
parse_token(&compiled_instruction, &keys).unwrap(),
|
||||||
|
json!({
|
||||||
|
"type": "initializeMultisig",
|
||||||
|
"multisig": keys[0].to_string(),
|
||||||
|
"m": 2,
|
||||||
|
"signers": keys[1..4].iter().map(|key| key.to_string()).collect::<Vec<String>>(),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
// Test Transfer, incl multisig
|
||||||
|
let transfer_ix = transfer(
|
||||||
|
&spl_token_v1_0::id(),
|
||||||
|
&convert_pubkey(keys[1]),
|
||||||
|
&convert_pubkey(keys[2]),
|
||||||
|
&convert_pubkey(keys[0]),
|
||||||
|
&[],
|
||||||
|
42,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let message = Message::new(&[transfer_ix], None);
|
||||||
|
let compiled_instruction = convert_compiled_instruction(&message.instructions[0]);
|
||||||
|
assert_eq!(
|
||||||
|
parse_token(&compiled_instruction, &keys).unwrap(),
|
||||||
|
json!({
|
||||||
|
"type": "transfer",
|
||||||
|
"source": keys[1].to_string(),
|
||||||
|
"destination": keys[2].to_string(),
|
||||||
|
"authority": keys[0].to_string(),
|
||||||
|
"amount": 42,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
let transfer_ix = transfer(
|
||||||
|
&spl_token_v1_0::id(),
|
||||||
|
&convert_pubkey(keys[2]),
|
||||||
|
&convert_pubkey(keys[3]),
|
||||||
|
&convert_pubkey(keys[4]),
|
||||||
|
&[&convert_pubkey(keys[0]), &convert_pubkey(keys[1])],
|
||||||
|
42,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let message = Message::new(&[transfer_ix], None);
|
||||||
|
let compiled_instruction = convert_compiled_instruction(&message.instructions[0]);
|
||||||
|
assert_eq!(
|
||||||
|
parse_token(&compiled_instruction, &keys).unwrap(),
|
||||||
|
json!({
|
||||||
|
"type": "transfer",
|
||||||
|
"source": keys[2].to_string(),
|
||||||
|
"destination": keys[3].to_string(),
|
||||||
|
"multisigAuthority": keys[4].to_string(),
|
||||||
|
"signers": keys[0..2].iter().map(|key| key.to_string()).collect::<Vec<String>>(),
|
||||||
|
"amount": 42,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
// Test Approve, incl multisig
|
||||||
|
let approve_ix = approve(
|
||||||
|
&spl_token_v1_0::id(),
|
||||||
|
&convert_pubkey(keys[1]),
|
||||||
|
&convert_pubkey(keys[2]),
|
||||||
|
&convert_pubkey(keys[0]),
|
||||||
|
&[],
|
||||||
|
42,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let message = Message::new(&[approve_ix], None);
|
||||||
|
let compiled_instruction = convert_compiled_instruction(&message.instructions[0]);
|
||||||
|
assert_eq!(
|
||||||
|
parse_token(&compiled_instruction, &keys).unwrap(),
|
||||||
|
json!({
|
||||||
|
"type": "approve",
|
||||||
|
"source": keys[1].to_string(),
|
||||||
|
"delegate": keys[2].to_string(),
|
||||||
|
"owner": keys[0].to_string(),
|
||||||
|
"amount": 42,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
let approve_ix = approve(
|
||||||
|
&spl_token_v1_0::id(),
|
||||||
|
&convert_pubkey(keys[2]),
|
||||||
|
&convert_pubkey(keys[3]),
|
||||||
|
&convert_pubkey(keys[4]),
|
||||||
|
&[&convert_pubkey(keys[0]), &convert_pubkey(keys[1])],
|
||||||
|
42,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let message = Message::new(&[approve_ix], None);
|
||||||
|
let compiled_instruction = convert_compiled_instruction(&message.instructions[0]);
|
||||||
|
assert_eq!(
|
||||||
|
parse_token(&compiled_instruction, &keys).unwrap(),
|
||||||
|
json!({
|
||||||
|
"type": "approve",
|
||||||
|
"source": keys[2].to_string(),
|
||||||
|
"delegate": keys[3].to_string(),
|
||||||
|
"multisigOwner": keys[4].to_string(),
|
||||||
|
"signers": keys[0..2].iter().map(|key| key.to_string()).collect::<Vec<String>>(),
|
||||||
|
"amount": 42,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
// Test Revoke
|
||||||
|
let revoke_ix = revoke(
|
||||||
|
&spl_token_v1_0::id(),
|
||||||
|
&convert_pubkey(keys[1]),
|
||||||
|
&convert_pubkey(keys[0]),
|
||||||
|
&[],
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let message = Message::new(&[revoke_ix], None);
|
||||||
|
let compiled_instruction = convert_compiled_instruction(&message.instructions[0]);
|
||||||
|
assert_eq!(
|
||||||
|
parse_token(&compiled_instruction, &keys).unwrap(),
|
||||||
|
json!({
|
||||||
|
"type": "revoke",
|
||||||
|
"source": keys[1].to_string(),
|
||||||
|
"owner": keys[0].to_string(),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
// Test SetOwner
|
||||||
|
let set_owner_ix = set_owner(
|
||||||
|
&spl_token_v1_0::id(),
|
||||||
|
&convert_pubkey(keys[1]),
|
||||||
|
&convert_pubkey(keys[2]),
|
||||||
|
&convert_pubkey(keys[0]),
|
||||||
|
&[],
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let message = Message::new(&[set_owner_ix], None);
|
||||||
|
let compiled_instruction = convert_compiled_instruction(&message.instructions[0]);
|
||||||
|
assert_eq!(
|
||||||
|
parse_token(&compiled_instruction, &keys).unwrap(),
|
||||||
|
json!({
|
||||||
|
"type": "setOwner",
|
||||||
|
"owned": keys[1].to_string(),
|
||||||
|
"newOwner": keys[2].to_string(),
|
||||||
|
"owner": keys[0].to_string(),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
// Test MintTo
|
||||||
|
let mint_to_ix = mint_to(
|
||||||
|
&spl_token_v1_0::id(),
|
||||||
|
&convert_pubkey(keys[1]),
|
||||||
|
&convert_pubkey(keys[2]),
|
||||||
|
&convert_pubkey(keys[0]),
|
||||||
|
&[],
|
||||||
|
42,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let message = Message::new(&[mint_to_ix], None);
|
||||||
|
let compiled_instruction = convert_compiled_instruction(&message.instructions[0]);
|
||||||
|
assert_eq!(
|
||||||
|
parse_token(&compiled_instruction, &keys).unwrap(),
|
||||||
|
json!({
|
||||||
|
"type": "mintTo",
|
||||||
|
"mint": keys[1].to_string(),
|
||||||
|
"account": keys[2].to_string(),
|
||||||
|
"owner": keys[0].to_string(),
|
||||||
|
"amount": 42,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
// Test Burn
|
||||||
|
let burn_ix = burn(
|
||||||
|
&spl_token_v1_0::id(),
|
||||||
|
&convert_pubkey(keys[1]),
|
||||||
|
&convert_pubkey(keys[0]),
|
||||||
|
&[],
|
||||||
|
42,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let message = Message::new(&[burn_ix], None);
|
||||||
|
let compiled_instruction = convert_compiled_instruction(&message.instructions[0]);
|
||||||
|
assert_eq!(
|
||||||
|
parse_token(&compiled_instruction, &keys).unwrap(),
|
||||||
|
json!({
|
||||||
|
"type": "burn",
|
||||||
|
"account": keys[1].to_string(),
|
||||||
|
"authority": keys[0].to_string(),
|
||||||
|
"amount": 42,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
// Test CloseAccount
|
||||||
|
let close_account_ix = close_account(
|
||||||
|
&spl_token_v1_0::id(),
|
||||||
|
&convert_pubkey(keys[1]),
|
||||||
|
&convert_pubkey(keys[2]),
|
||||||
|
&convert_pubkey(keys[0]),
|
||||||
|
&[],
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let message = Message::new(&[close_account_ix], None);
|
||||||
|
let compiled_instruction = convert_compiled_instruction(&message.instructions[0]);
|
||||||
|
assert_eq!(
|
||||||
|
parse_token(&compiled_instruction, &keys).unwrap(),
|
||||||
|
json!({
|
||||||
|
"type": "closeAccount",
|
||||||
|
"account": keys[1].to_string(),
|
||||||
|
"destination": keys[2].to_string(),
|
||||||
|
"owner": keys[0].to_string(),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_token_ix_not_enough_keys() {
|
||||||
|
let mut keys: Vec<Pubkey> = vec![];
|
||||||
|
for _ in 0..10 {
|
||||||
|
keys.push(Pubkey::new_rand());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test InitializeMint variations
|
||||||
|
let initialize_mint_ix = initialize_mint(
|
||||||
|
&spl_token_v1_0::id(),
|
||||||
|
&convert_pubkey(keys[0]),
|
||||||
|
Some(&convert_pubkey(keys[1])),
|
||||||
|
Some(&convert_pubkey(keys[2])),
|
||||||
|
42,
|
||||||
|
2,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let message = Message::new(&[initialize_mint_ix], None);
|
||||||
|
let mut compiled_instruction = convert_compiled_instruction(&message.instructions[0]);
|
||||||
|
assert!(parse_token(&compiled_instruction, &keys[0..2]).is_err());
|
||||||
|
compiled_instruction.accounts =
|
||||||
|
compiled_instruction.accounts[0..compiled_instruction.accounts.len() - 2].to_vec();
|
||||||
|
assert!(parse_token(&compiled_instruction, &keys).is_err());
|
||||||
|
|
||||||
|
let initialize_mint_ix = initialize_mint(
|
||||||
|
&spl_token_v1_0::id(),
|
||||||
|
&convert_pubkey(keys[0]),
|
||||||
|
Some(&convert_pubkey(keys[1])),
|
||||||
|
None,
|
||||||
|
42,
|
||||||
|
2,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let message = Message::new(&[initialize_mint_ix], None);
|
||||||
|
let mut compiled_instruction = convert_compiled_instruction(&message.instructions[0]);
|
||||||
|
assert!(parse_token(&compiled_instruction, &keys[0..1]).is_err());
|
||||||
|
compiled_instruction.accounts =
|
||||||
|
compiled_instruction.accounts[0..compiled_instruction.accounts.len() - 1].to_vec();
|
||||||
|
assert!(parse_token(&compiled_instruction, &keys).is_err());
|
||||||
|
|
||||||
|
let initialize_mint_ix = initialize_mint(
|
||||||
|
&spl_token_v1_0::id(),
|
||||||
|
&convert_pubkey(keys[0]),
|
||||||
|
None,
|
||||||
|
Some(&convert_pubkey(keys[1])),
|
||||||
|
0,
|
||||||
|
2,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let message = Message::new(&[initialize_mint_ix], None);
|
||||||
|
let mut compiled_instruction = convert_compiled_instruction(&message.instructions[0]);
|
||||||
|
assert!(parse_token(&compiled_instruction, &keys[0..1]).is_err());
|
||||||
|
compiled_instruction.accounts =
|
||||||
|
compiled_instruction.accounts[0..compiled_instruction.accounts.len() - 1].to_vec();
|
||||||
|
assert!(parse_token(&compiled_instruction, &keys).is_err());
|
||||||
|
|
||||||
|
// Test InitializeAccount
|
||||||
|
let initialize_account_ix = initialize_account(
|
||||||
|
&spl_token_v1_0::id(),
|
||||||
|
&convert_pubkey(keys[0]),
|
||||||
|
&convert_pubkey(keys[1]),
|
||||||
|
&convert_pubkey(keys[2]),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let message = Message::new(&[initialize_account_ix], None);
|
||||||
|
let mut compiled_instruction = convert_compiled_instruction(&message.instructions[0]);
|
||||||
|
assert!(parse_token(&compiled_instruction, &keys[0..2]).is_err());
|
||||||
|
compiled_instruction.accounts =
|
||||||
|
compiled_instruction.accounts[0..compiled_instruction.accounts.len() - 1].to_vec();
|
||||||
|
assert!(parse_token(&compiled_instruction, &keys).is_err());
|
||||||
|
|
||||||
|
// Test InitializeMultisig
|
||||||
|
let initialize_multisig_ix = initialize_multisig(
|
||||||
|
&spl_token_v1_0::id(),
|
||||||
|
&convert_pubkey(keys[0]),
|
||||||
|
&[
|
||||||
|
&convert_pubkey(keys[1]),
|
||||||
|
&convert_pubkey(keys[2]),
|
||||||
|
&convert_pubkey(keys[3]),
|
||||||
|
],
|
||||||
|
2,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let message = Message::new(&[initialize_multisig_ix], None);
|
||||||
|
let mut compiled_instruction = convert_compiled_instruction(&message.instructions[0]);
|
||||||
|
assert!(parse_token(&compiled_instruction, &keys[0..3]).is_err());
|
||||||
|
compiled_instruction.accounts =
|
||||||
|
compiled_instruction.accounts[0..compiled_instruction.accounts.len() - 3].to_vec();
|
||||||
|
assert!(parse_token(&compiled_instruction, &keys).is_err());
|
||||||
|
|
||||||
|
// Test Transfer, incl multisig
|
||||||
|
let transfer_ix = transfer(
|
||||||
|
&spl_token_v1_0::id(),
|
||||||
|
&convert_pubkey(keys[1]),
|
||||||
|
&convert_pubkey(keys[2]),
|
||||||
|
&convert_pubkey(keys[0]),
|
||||||
|
&[],
|
||||||
|
42,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let message = Message::new(&[transfer_ix], None);
|
||||||
|
let mut compiled_instruction = convert_compiled_instruction(&message.instructions[0]);
|
||||||
|
assert!(parse_token(&compiled_instruction, &keys[0..2]).is_err());
|
||||||
|
compiled_instruction.accounts =
|
||||||
|
compiled_instruction.accounts[0..compiled_instruction.accounts.len() - 1].to_vec();
|
||||||
|
assert!(parse_token(&compiled_instruction, &keys).is_err());
|
||||||
|
|
||||||
|
let transfer_ix = transfer(
|
||||||
|
&spl_token_v1_0::id(),
|
||||||
|
&convert_pubkey(keys[2]),
|
||||||
|
&convert_pubkey(keys[3]),
|
||||||
|
&convert_pubkey(keys[4]),
|
||||||
|
&[&convert_pubkey(keys[0]), &convert_pubkey(keys[1])],
|
||||||
|
42,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let message = Message::new(&[transfer_ix], None);
|
||||||
|
let mut compiled_instruction = convert_compiled_instruction(&message.instructions[0]);
|
||||||
|
assert!(parse_token(&compiled_instruction, &keys[0..4]).is_err());
|
||||||
|
compiled_instruction.accounts =
|
||||||
|
compiled_instruction.accounts[0..compiled_instruction.accounts.len() - 3].to_vec();
|
||||||
|
assert!(parse_token(&compiled_instruction, &keys).is_err());
|
||||||
|
|
||||||
|
// Test Approve, incl multisig
|
||||||
|
let approve_ix = approve(
|
||||||
|
&spl_token_v1_0::id(),
|
||||||
|
&convert_pubkey(keys[1]),
|
||||||
|
&convert_pubkey(keys[2]),
|
||||||
|
&convert_pubkey(keys[0]),
|
||||||
|
&[],
|
||||||
|
42,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let message = Message::new(&[approve_ix], None);
|
||||||
|
let mut compiled_instruction = convert_compiled_instruction(&message.instructions[0]);
|
||||||
|
assert!(parse_token(&compiled_instruction, &keys[0..2]).is_err());
|
||||||
|
compiled_instruction.accounts =
|
||||||
|
compiled_instruction.accounts[0..compiled_instruction.accounts.len() - 1].to_vec();
|
||||||
|
assert!(parse_token(&compiled_instruction, &keys).is_err());
|
||||||
|
|
||||||
|
let approve_ix = approve(
|
||||||
|
&spl_token_v1_0::id(),
|
||||||
|
&convert_pubkey(keys[2]),
|
||||||
|
&convert_pubkey(keys[3]),
|
||||||
|
&convert_pubkey(keys[4]),
|
||||||
|
&[&convert_pubkey(keys[0]), &convert_pubkey(keys[1])],
|
||||||
|
42,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let message = Message::new(&[approve_ix], None);
|
||||||
|
let mut compiled_instruction = convert_compiled_instruction(&message.instructions[0]);
|
||||||
|
assert!(parse_token(&compiled_instruction, &keys[0..4]).is_err());
|
||||||
|
compiled_instruction.accounts =
|
||||||
|
compiled_instruction.accounts[0..compiled_instruction.accounts.len() - 3].to_vec();
|
||||||
|
assert!(parse_token(&compiled_instruction, &keys).is_err());
|
||||||
|
|
||||||
|
// Test Revoke
|
||||||
|
let revoke_ix = revoke(
|
||||||
|
&spl_token_v1_0::id(),
|
||||||
|
&convert_pubkey(keys[1]),
|
||||||
|
&convert_pubkey(keys[0]),
|
||||||
|
&[],
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let message = Message::new(&[revoke_ix], None);
|
||||||
|
let mut compiled_instruction = convert_compiled_instruction(&message.instructions[0]);
|
||||||
|
assert!(parse_token(&compiled_instruction, &keys[0..1]).is_err());
|
||||||
|
compiled_instruction.accounts =
|
||||||
|
compiled_instruction.accounts[0..compiled_instruction.accounts.len() - 1].to_vec();
|
||||||
|
assert!(parse_token(&compiled_instruction, &keys).is_err());
|
||||||
|
|
||||||
|
// Test SetOwner
|
||||||
|
let set_owner_ix = set_owner(
|
||||||
|
&spl_token_v1_0::id(),
|
||||||
|
&convert_pubkey(keys[1]),
|
||||||
|
&convert_pubkey(keys[2]),
|
||||||
|
&convert_pubkey(keys[0]),
|
||||||
|
&[],
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let message = Message::new(&[set_owner_ix], None);
|
||||||
|
let mut compiled_instruction = convert_compiled_instruction(&message.instructions[0]);
|
||||||
|
assert!(parse_token(&compiled_instruction, &keys[0..2]).is_err());
|
||||||
|
compiled_instruction.accounts =
|
||||||
|
compiled_instruction.accounts[0..compiled_instruction.accounts.len() - 1].to_vec();
|
||||||
|
assert!(parse_token(&compiled_instruction, &keys).is_err());
|
||||||
|
|
||||||
|
// Test MintTo
|
||||||
|
let mint_to_ix = mint_to(
|
||||||
|
&spl_token_v1_0::id(),
|
||||||
|
&convert_pubkey(keys[1]),
|
||||||
|
&convert_pubkey(keys[2]),
|
||||||
|
&convert_pubkey(keys[0]),
|
||||||
|
&[],
|
||||||
|
42,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let message = Message::new(&[mint_to_ix], None);
|
||||||
|
let mut compiled_instruction = convert_compiled_instruction(&message.instructions[0]);
|
||||||
|
assert!(parse_token(&compiled_instruction, &keys[0..2]).is_err());
|
||||||
|
compiled_instruction.accounts =
|
||||||
|
compiled_instruction.accounts[0..compiled_instruction.accounts.len() - 1].to_vec();
|
||||||
|
assert!(parse_token(&compiled_instruction, &keys).is_err());
|
||||||
|
|
||||||
|
// Test Burn
|
||||||
|
let burn_ix = burn(
|
||||||
|
&spl_token_v1_0::id(),
|
||||||
|
&convert_pubkey(keys[1]),
|
||||||
|
&convert_pubkey(keys[0]),
|
||||||
|
&[],
|
||||||
|
42,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let message = Message::new(&[burn_ix], None);
|
||||||
|
let mut compiled_instruction = convert_compiled_instruction(&message.instructions[0]);
|
||||||
|
assert!(parse_token(&compiled_instruction, &keys[0..1]).is_err());
|
||||||
|
compiled_instruction.accounts =
|
||||||
|
compiled_instruction.accounts[0..compiled_instruction.accounts.len() - 1].to_vec();
|
||||||
|
assert!(parse_token(&compiled_instruction, &keys).is_err());
|
||||||
|
|
||||||
|
// Test CloseAccount
|
||||||
|
let close_account_ix = close_account(
|
||||||
|
&spl_token_v1_0::id(),
|
||||||
|
&convert_pubkey(keys[1]),
|
||||||
|
&convert_pubkey(keys[2]),
|
||||||
|
&convert_pubkey(keys[0]),
|
||||||
|
&[],
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let message = Message::new(&[close_account_ix], None);
|
||||||
|
let mut compiled_instruction = convert_compiled_instruction(&message.instructions[0]);
|
||||||
|
assert!(parse_token(&compiled_instruction, &keys[0..2]).is_err());
|
||||||
|
compiled_instruction.accounts =
|
||||||
|
compiled_instruction.accounts[0..compiled_instruction.accounts.len() - 1].to_vec();
|
||||||
|
assert!(parse_token(&compiled_instruction, &keys).is_err());
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user