2020-08-04 23:58:58 -07:00
|
|
|
use crate::parse_instruction::{ParsableProgram, ParseInstructionError, ParsedInstructionEnum};
|
2020-07-31 12:26:09 -07:00
|
|
|
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],
|
2020-08-04 23:58:58 -07:00
|
|
|
) -> Result<ParsedInstructionEnum, ParseInstructionError> {
|
2020-07-31 12:26:09 -07:00
|
|
|
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!({
|
|
|
|
"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()),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
2020-08-04 23:58:58 -07:00
|
|
|
Ok(ParsedInstructionEnum {
|
|
|
|
instruction_type: "initializeMint".to_string(),
|
|
|
|
info: value,
|
|
|
|
})
|
2020-07-31 12:26:09 -07:00
|
|
|
}
|
|
|
|
TokenInstruction::InitializeAccount => {
|
|
|
|
if instruction.accounts.len() < 3 {
|
|
|
|
return Err(ParseInstructionError::InstructionKeyMismatch(
|
|
|
|
ParsableProgram::SplToken,
|
|
|
|
));
|
|
|
|
}
|
2020-08-04 23:58:58 -07:00
|
|
|
Ok(ParsedInstructionEnum {
|
|
|
|
instruction_type: "initializeAccount".to_string(),
|
|
|
|
info: json!({
|
|
|
|
"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(),
|
|
|
|
}),
|
|
|
|
})
|
2020-07-31 12:26:09 -07:00
|
|
|
}
|
|
|
|
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());
|
|
|
|
}
|
2020-08-04 23:58:58 -07:00
|
|
|
Ok(ParsedInstructionEnum {
|
|
|
|
instruction_type: "initializeMultisig".to_string(),
|
|
|
|
info: json!({
|
|
|
|
"multisig": account_keys[instruction.accounts[0] as usize].to_string(),
|
|
|
|
"signers": signers,
|
|
|
|
"m": m,
|
|
|
|
}),
|
|
|
|
})
|
2020-07-31 12:26:09 -07:00
|
|
|
}
|
|
|
|
TokenInstruction::Transfer { amount } => {
|
|
|
|
if instruction.accounts.len() < 3 {
|
|
|
|
return Err(ParseInstructionError::InstructionKeyMismatch(
|
|
|
|
ParsableProgram::SplToken,
|
|
|
|
));
|
|
|
|
}
|
|
|
|
let mut value = json!({
|
|
|
|
"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",
|
|
|
|
);
|
2020-08-04 23:58:58 -07:00
|
|
|
Ok(ParsedInstructionEnum {
|
|
|
|
instruction_type: "transfer".to_string(),
|
|
|
|
info: value,
|
|
|
|
})
|
2020-07-31 12:26:09 -07:00
|
|
|
}
|
|
|
|
TokenInstruction::Approve { amount } => {
|
|
|
|
if instruction.accounts.len() < 3 {
|
|
|
|
return Err(ParseInstructionError::InstructionKeyMismatch(
|
|
|
|
ParsableProgram::SplToken,
|
|
|
|
));
|
|
|
|
}
|
|
|
|
let mut value = json!({
|
|
|
|
"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",
|
|
|
|
);
|
2020-08-04 23:58:58 -07:00
|
|
|
Ok(ParsedInstructionEnum {
|
|
|
|
instruction_type: "approve".to_string(),
|
|
|
|
info: value,
|
|
|
|
})
|
2020-07-31 12:26:09 -07:00
|
|
|
}
|
|
|
|
TokenInstruction::Revoke => {
|
|
|
|
if instruction.accounts.len() < 2 {
|
|
|
|
return Err(ParseInstructionError::InstructionKeyMismatch(
|
|
|
|
ParsableProgram::SplToken,
|
|
|
|
));
|
|
|
|
}
|
|
|
|
let mut value = json!({
|
|
|
|
"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",
|
|
|
|
);
|
2020-08-04 23:58:58 -07:00
|
|
|
Ok(ParsedInstructionEnum {
|
|
|
|
instruction_type: "revoke".to_string(),
|
|
|
|
info: value,
|
|
|
|
})
|
2020-07-31 12:26:09 -07:00
|
|
|
}
|
|
|
|
TokenInstruction::SetOwner => {
|
|
|
|
if instruction.accounts.len() < 3 {
|
|
|
|
return Err(ParseInstructionError::InstructionKeyMismatch(
|
|
|
|
ParsableProgram::SplToken,
|
|
|
|
));
|
|
|
|
}
|
|
|
|
let mut value = json!({
|
|
|
|
"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",
|
|
|
|
);
|
2020-08-04 23:58:58 -07:00
|
|
|
Ok(ParsedInstructionEnum {
|
|
|
|
instruction_type: "setOwner".to_string(),
|
|
|
|
info: value,
|
|
|
|
})
|
2020-07-31 12:26:09 -07:00
|
|
|
}
|
|
|
|
TokenInstruction::MintTo { amount } => {
|
|
|
|
if instruction.accounts.len() < 3 {
|
|
|
|
return Err(ParseInstructionError::InstructionKeyMismatch(
|
|
|
|
ParsableProgram::SplToken,
|
|
|
|
));
|
|
|
|
}
|
|
|
|
let mut value = json!({
|
|
|
|
"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",
|
|
|
|
);
|
2020-08-04 23:58:58 -07:00
|
|
|
Ok(ParsedInstructionEnum {
|
|
|
|
instruction_type: "mintTo".to_string(),
|
|
|
|
info: value,
|
|
|
|
})
|
2020-07-31 12:26:09 -07:00
|
|
|
}
|
|
|
|
TokenInstruction::Burn { amount } => {
|
|
|
|
if instruction.accounts.len() < 2 {
|
|
|
|
return Err(ParseInstructionError::InstructionKeyMismatch(
|
|
|
|
ParsableProgram::SplToken,
|
|
|
|
));
|
|
|
|
}
|
|
|
|
let mut value = json!({
|
|
|
|
"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",
|
|
|
|
);
|
2020-08-04 23:58:58 -07:00
|
|
|
Ok(ParsedInstructionEnum {
|
|
|
|
instruction_type: "burn".to_string(),
|
|
|
|
info: value,
|
|
|
|
})
|
2020-07-31 12:26:09 -07:00
|
|
|
}
|
|
|
|
TokenInstruction::CloseAccount => {
|
|
|
|
if instruction.accounts.len() < 3 {
|
|
|
|
return Err(ParseInstructionError::InstructionKeyMismatch(
|
|
|
|
ParsableProgram::SplToken,
|
|
|
|
));
|
|
|
|
}
|
|
|
|
let mut value = json!({
|
|
|
|
"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",
|
|
|
|
);
|
2020-08-04 23:58:58 -07:00
|
|
|
Ok(ParsedInstructionEnum {
|
|
|
|
instruction_type: "closeAccount".to_string(),
|
|
|
|
info: value,
|
|
|
|
})
|
2020-07-31 12:26:09 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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]
|
2020-08-14 11:43:14 -07:00
|
|
|
#[allow(clippy::same_item_push)]
|
2020-07-31 12:26:09 -07:00
|
|
|
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(),
|
2020-08-04 23:58:58 -07:00
|
|
|
ParsedInstructionEnum {
|
|
|
|
instruction_type: "initializeMint".to_string(),
|
|
|
|
info: json!({
|
|
|
|
"mint": keys[0].to_string(),
|
|
|
|
"amount": 42,
|
|
|
|
"decimals": 2,
|
|
|
|
"account": keys[1].to_string(),
|
|
|
|
"owner": keys[2].to_string(),
|
|
|
|
})
|
|
|
|
}
|
2020-07-31 12:26:09 -07:00
|
|
|
);
|
|
|
|
|
|
|
|
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(),
|
2020-08-04 23:58:58 -07:00
|
|
|
ParsedInstructionEnum {
|
|
|
|
instruction_type: "initializeMint".to_string(),
|
|
|
|
info: json!({
|
|
|
|
"mint": keys[0].to_string(),
|
|
|
|
"amount": 42,
|
|
|
|
"decimals": 2,
|
|
|
|
"account": keys[1].to_string(),
|
|
|
|
})
|
|
|
|
}
|
2020-07-31 12:26:09 -07:00
|
|
|
);
|
|
|
|
|
|
|
|
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(),
|
2020-08-04 23:58:58 -07:00
|
|
|
ParsedInstructionEnum {
|
|
|
|
instruction_type: "initializeMint".to_string(),
|
|
|
|
info: json!({
|
|
|
|
"mint": keys[0].to_string(),
|
|
|
|
"amount": 0,
|
|
|
|
"decimals": 2,
|
|
|
|
"owner": keys[1].to_string(),
|
|
|
|
})
|
|
|
|
}
|
2020-07-31 12:26:09 -07:00
|
|
|
);
|
|
|
|
|
|
|
|
// 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(),
|
2020-08-04 23:58:58 -07:00
|
|
|
ParsedInstructionEnum {
|
|
|
|
instruction_type: "initializeAccount".to_string(),
|
|
|
|
info: json!({
|
|
|
|
"account": keys[0].to_string(),
|
|
|
|
"mint": keys[1].to_string(),
|
|
|
|
"owner": keys[2].to_string(),
|
|
|
|
})
|
|
|
|
}
|
2020-07-31 12:26:09 -07:00
|
|
|
);
|
|
|
|
|
|
|
|
// 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(),
|
2020-08-04 23:58:58 -07:00
|
|
|
ParsedInstructionEnum {
|
|
|
|
instruction_type: "initializeMultisig".to_string(),
|
|
|
|
info: json!({
|
|
|
|
"multisig": keys[0].to_string(),
|
|
|
|
"m": 2,
|
|
|
|
"signers": keys[1..4].iter().map(|key| key.to_string()).collect::<Vec<String>>(),
|
|
|
|
})
|
|
|
|
}
|
2020-07-31 12:26:09 -07:00
|
|
|
);
|
|
|
|
|
|
|
|
// 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(),
|
2020-08-04 23:58:58 -07:00
|
|
|
ParsedInstructionEnum {
|
|
|
|
instruction_type: "transfer".to_string(),
|
|
|
|
info: json!({
|
|
|
|
"source": keys[1].to_string(),
|
|
|
|
"destination": keys[2].to_string(),
|
|
|
|
"authority": keys[0].to_string(),
|
|
|
|
"amount": 42,
|
|
|
|
})
|
|
|
|
}
|
2020-07-31 12:26:09 -07:00
|
|
|
);
|
|
|
|
|
|
|
|
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(),
|
2020-08-04 23:58:58 -07:00
|
|
|
ParsedInstructionEnum {
|
|
|
|
instruction_type: "transfer".to_string(),
|
|
|
|
info: json!({
|
|
|
|
"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,
|
|
|
|
})
|
|
|
|
}
|
2020-07-31 12:26:09 -07:00
|
|
|
);
|
|
|
|
|
|
|
|
// 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(),
|
2020-08-04 23:58:58 -07:00
|
|
|
ParsedInstructionEnum {
|
|
|
|
instruction_type: "approve".to_string(),
|
|
|
|
info: json!({
|
|
|
|
"source": keys[1].to_string(),
|
|
|
|
"delegate": keys[2].to_string(),
|
|
|
|
"owner": keys[0].to_string(),
|
|
|
|
"amount": 42,
|
|
|
|
})
|
|
|
|
}
|
2020-07-31 12:26:09 -07:00
|
|
|
);
|
|
|
|
|
|
|
|
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(),
|
2020-08-04 23:58:58 -07:00
|
|
|
ParsedInstructionEnum {
|
|
|
|
instruction_type: "approve".to_string(),
|
|
|
|
info: json!({
|
|
|
|
"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,
|
|
|
|
})
|
|
|
|
}
|
2020-07-31 12:26:09 -07:00
|
|
|
);
|
|
|
|
|
|
|
|
// 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(),
|
2020-08-04 23:58:58 -07:00
|
|
|
ParsedInstructionEnum {
|
|
|
|
instruction_type: "revoke".to_string(),
|
|
|
|
info: json!({
|
|
|
|
"source": keys[1].to_string(),
|
|
|
|
"owner": keys[0].to_string(),
|
|
|
|
})
|
|
|
|
}
|
2020-07-31 12:26:09 -07:00
|
|
|
);
|
|
|
|
|
|
|
|
// 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(),
|
2020-08-04 23:58:58 -07:00
|
|
|
ParsedInstructionEnum {
|
|
|
|
instruction_type: "setOwner".to_string(),
|
|
|
|
info: json!({
|
|
|
|
"owned": keys[1].to_string(),
|
|
|
|
"newOwner": keys[2].to_string(),
|
|
|
|
"owner": keys[0].to_string(),
|
|
|
|
})
|
|
|
|
}
|
2020-07-31 12:26:09 -07:00
|
|
|
);
|
|
|
|
|
|
|
|
// 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(),
|
2020-08-04 23:58:58 -07:00
|
|
|
ParsedInstructionEnum {
|
|
|
|
instruction_type: "mintTo".to_string(),
|
|
|
|
info: json!({
|
|
|
|
"mint": keys[1].to_string(),
|
|
|
|
"account": keys[2].to_string(),
|
|
|
|
"owner": keys[0].to_string(),
|
|
|
|
"amount": 42,
|
|
|
|
})
|
|
|
|
}
|
2020-07-31 12:26:09 -07:00
|
|
|
);
|
|
|
|
|
|
|
|
// 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(),
|
2020-08-04 23:58:58 -07:00
|
|
|
ParsedInstructionEnum {
|
|
|
|
instruction_type: "burn".to_string(),
|
|
|
|
info: json!({
|
|
|
|
"account": keys[1].to_string(),
|
|
|
|
"authority": keys[0].to_string(),
|
|
|
|
"amount": 42,
|
|
|
|
})
|
|
|
|
}
|
2020-07-31 12:26:09 -07:00
|
|
|
);
|
|
|
|
|
|
|
|
// 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(),
|
2020-08-04 23:58:58 -07:00
|
|
|
ParsedInstructionEnum {
|
|
|
|
instruction_type: "closeAccount".to_string(),
|
|
|
|
info: json!({
|
|
|
|
"account": keys[1].to_string(),
|
|
|
|
"destination": keys[2].to_string(),
|
|
|
|
"owner": keys[0].to_string(),
|
|
|
|
})
|
|
|
|
}
|
2020-07-31 12:26:09 -07:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2020-08-14 11:43:14 -07:00
|
|
|
#[allow(clippy::same_item_push)]
|
2020-07-31 12:26:09 -07:00
|
|
|
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());
|
|
|
|
}
|
|
|
|
}
|