use num_derive::{FromPrimitive, ToPrimitive};
use thiserror::Error;
use crate::account::AccountMeta;
use crate::decode_error::DecodeError;
use crate::instruction::Instruction;
use crate::pubkey::Pubkey;
use crate::system_program::SYSTEM_PROGRAM_ID;
#[derive(Error, Debug, Serialize, Clone, PartialEq, Eq, FromPrimitive, ToPrimitive)]
pub enum SystemError {
#[error("an account with the same address already exists")]
AccountAlreadyInUse,
#[error("account does not have enough ARCH to perform the operation")]
ResultWithNegativeLamports,
#[error("cannot assign account to this program id")]
InvalidProgramId,
#[error("cannot allocate account data of this length")]
InvalidAccountDataLength,
#[error("length of requested seed is too long")]
MaxSeedLengthExceeded,
#[error("provided address does not match addressed derived from seed")]
AddressWithSeedMismatch,
#[error("advancing stored nonce requires a populated RecentBlockhashes sysvar")]
NonceNoRecentBlockhashes,
#[error("stored nonce is still in recent_blockhashes")]
NonceBlockhashNotExpired,
#[error("specified nonce does not match stored nonce")]
NonceUnexpectedBlockhashValue,
}
impl<T> DecodeError<T> for SystemError {
fn type_of() -> &'static str {
"SystemError"
}
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
pub enum SystemInstruction {
CreateAccount {
lamports: u64,
space: u64,
owner: Pubkey,
},
CreateAccountWithAnchor {
lamports: u64,
space: u64,
owner: Pubkey,
txid: [u8; 32],
vout: u32,
},
Assign {
owner: Pubkey,
},
Anchor {
txid: [u8; 32],
vout: u32,
},
SignInput {
index: u32,
},
Transfer {
lamports: u64,
},
Allocate {
space: u64,
},
CreateAccountWithSeed {
base: Pubkey,
seed: String,
lamports: u64,
space: u64,
owner: Pubkey,
},
AllocateWithSeed {
base: Pubkey,
seed: String,
space: u64,
owner: Pubkey,
},
AssignWithSeed {
base: Pubkey,
seed: String,
owner: Pubkey,
},
TransferWithSeed {
lamports: u64,
from_seed: String,
from_owner: Pubkey,
},
}
pub fn create_account(
from_pubkey: &Pubkey,
to_pubkey: &Pubkey,
lamports: u64,
space: u64,
owner: &Pubkey,
) -> Instruction {
let account_metas = vec![
AccountMeta::new(*from_pubkey, true),
AccountMeta::new(*to_pubkey, true),
];
Instruction::new_with_bincode(
SYSTEM_PROGRAM_ID,
&SystemInstruction::CreateAccount {
lamports,
space,
owner: *owner,
},
account_metas,
)
}
pub fn create_account_with_anchor(
from_pubkey: &Pubkey,
to_pubkey: &Pubkey,
lamports: u64,
space: u64,
owner: &Pubkey,
txid: [u8; 32],
vout: u32,
) -> Instruction {
let account_metas = vec![
AccountMeta::new(*from_pubkey, true),
AccountMeta::new(*to_pubkey, true),
];
Instruction::new_with_bincode(
SYSTEM_PROGRAM_ID,
&SystemInstruction::CreateAccountWithAnchor {
lamports,
space,
owner: *owner,
txid,
vout,
},
account_metas,
)
}
pub fn assign(pubkey: &Pubkey, owner: &Pubkey) -> Instruction {
let account_metas = vec![AccountMeta::new(*pubkey, true)];
Instruction::new_with_bincode(
SYSTEM_PROGRAM_ID,
&SystemInstruction::Assign { owner: *owner },
account_metas,
)
}
pub fn transfer(from_pubkey: &Pubkey, to_pubkey: &Pubkey, lamports: u64) -> Instruction {
let account_metas = vec![
AccountMeta::new(*from_pubkey, true),
AccountMeta::new(*to_pubkey, false),
];
Instruction::new_with_bincode(
SYSTEM_PROGRAM_ID,
&SystemInstruction::Transfer { lamports },
account_metas,
)
}
pub fn allocate(pubkey: &Pubkey, space: u64) -> Instruction {
let account_metas = vec![AccountMeta::new(*pubkey, true)];
Instruction::new_with_bincode(
SYSTEM_PROGRAM_ID,
&SystemInstruction::Allocate { space },
account_metas,
)
}
pub fn anchor(pubkey: &Pubkey, txid: [u8; 32], vout: u32) -> Instruction {
let account_metas = vec![AccountMeta::new(*pubkey, true)];
Instruction::new_with_bincode(
SYSTEM_PROGRAM_ID,
&SystemInstruction::Anchor { txid, vout },
account_metas,
)
}
pub fn sign_input(index: u32, signer: &Pubkey) -> Instruction {
let account_metas = vec![AccountMeta::new(*signer, true)];
Instruction::new_with_bincode(
SYSTEM_PROGRAM_ID,
&SystemInstruction::SignInput { index },
account_metas,
)
}
pub fn create_account_with_seed(
from_pubkey: &Pubkey,
to_pubkey: &Pubkey, base: &Pubkey,
seed: &str,
lamports: u64,
space: u64,
owner: &Pubkey,
) -> Instruction {
let mut account_metas = vec![
AccountMeta::new(*from_pubkey, true),
AccountMeta::new(*to_pubkey, false),
];
if base != from_pubkey {
account_metas.push(AccountMeta::new_readonly(*base, true));
}
Instruction::new_with_bincode(
SYSTEM_PROGRAM_ID,
&SystemInstruction::CreateAccountWithSeed {
base: *base,
seed: seed.to_string(),
lamports,
space,
owner: *owner,
},
account_metas,
)
}
pub fn assign_with_seed(
address: &Pubkey, base: &Pubkey,
seed: &str,
owner: &Pubkey,
) -> Instruction {
let account_metas = vec![
AccountMeta::new(*address, false),
AccountMeta::new_readonly(*base, true),
];
Instruction::new_with_bincode(
SYSTEM_PROGRAM_ID,
&SystemInstruction::AssignWithSeed {
base: *base,
seed: seed.to_string(),
owner: *owner,
},
account_metas,
)
}
pub fn transfer_with_seed(
from_pubkey: &Pubkey, from_base: &Pubkey,
from_seed: String,
from_owner: &Pubkey,
to_pubkey: &Pubkey,
lamports: u64,
) -> Instruction {
let account_metas = vec![
AccountMeta::new(*from_pubkey, false),
AccountMeta::new_readonly(*from_base, true),
AccountMeta::new(*to_pubkey, false),
];
Instruction::new_with_bincode(
SYSTEM_PROGRAM_ID,
&SystemInstruction::TransferWithSeed {
lamports,
from_seed,
from_owner: *from_owner,
},
account_metas,
)
}
pub fn allocate_with_seed(
address: &Pubkey, base: &Pubkey,
seed: &str,
space: u64,
owner: &Pubkey,
) -> Instruction {
let account_metas = vec![
AccountMeta::new(*address, false),
AccountMeta::new_readonly(*base, true),
];
Instruction::new_with_bincode(
SYSTEM_PROGRAM_ID,
&SystemInstruction::AllocateWithSeed {
base: *base,
seed: seed.to_string(),
space,
owner: *owner,
},
account_metas,
)
}