use crate::base58::Pubkey;
use crate::versioned_tx::{build_unsigned, AccountMeta, Blockhash, Instruction, VersionedTransaction};
pub type DurableNonce = Blockhash;
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum NonceVersion {
Legacy,
Current,
}
#[derive(Clone, PartialEq, Eq, Debug)]
pub enum NonceState {
Uninitialized,
Initialized(NonceData),
}
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct NonceData {
pub authority: Pubkey,
pub durable_nonce: DurableNonce,
pub lamports_per_signature: u64,
}
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct NonceAccount {
pub version: NonceVersion,
pub state: NonceState,
}
#[derive(Clone, PartialEq, Eq, Debug)]
pub enum NonceError {
InvalidLength,
UnsupportedVersion(u32),
BadStateTag(u32),
}
pub const RECENT_BLOCKHASHES_ID: Pubkey = Pubkey::from_bytes([
0x06, 0xa7, 0xd5, 0x17, 0x19, 0x2c, 0x56, 0x8e, 0xe0, 0x8a, 0x84, 0x5f, 0x73, 0xd2, 0x97, 0x88,
0xcf, 0x03, 0x5c, 0x31, 0x45, 0xb2, 0x1a, 0xb3, 0x44, 0xd8, 0x06, 0x2e, 0xa9, 0x40, 0x00, 0x00,
]);
const ADVANCE_NONCE_ACCOUNT_IX_DATA: [u8; 4] = [0x04, 0x00, 0x00, 0x00];
const AUTHORIZE_NONCE_ACCOUNT_DISCRIMINATOR: [u8; 4] = [0x07, 0x00, 0x00, 0x00];
pub fn nonce_advance_ix(nonce: Pubkey, authority: Pubkey) -> Instruction {
Instruction {
program_id: Pubkey::SYSTEM,
accounts: vec![
AccountMeta::writable(nonce),
AccountMeta::readonly(RECENT_BLOCKHASHES_ID),
AccountMeta::signer_readonly(authority),
],
data: ADVANCE_NONCE_ACCOUNT_IX_DATA.to_vec(),
}
}
pub fn nonce_authorize_ix(nonce: Pubkey, authority: Pubkey, new_authority: Pubkey) -> Instruction {
let mut data = AUTHORIZE_NONCE_ACCOUNT_DISCRIMINATOR.to_vec();
data.extend_from_slice(new_authority.as_bytes());
Instruction {
program_id: Pubkey::SYSTEM,
accounts: vec![
AccountMeta::writable(nonce),
AccountMeta::signer_readonly(authority),
],
data,
}
}
pub fn parse_nonce_account(data: &[u8]) -> Result<NonceAccount, NonceError> {
if data.len() < 8 {
return Err(NonceError::InvalidLength);
}
let version_tag = u32::from_le_bytes(data[0..4].try_into().unwrap());
let version = match version_tag {
0 => NonceVersion::Legacy,
1 => NonceVersion::Current,
other => return Err(NonceError::UnsupportedVersion(other)),
};
let state_tag = u32::from_le_bytes(data[4..8].try_into().unwrap());
let state = match state_tag {
0 => NonceState::Uninitialized,
1 => {
if data.len() < 80 {
return Err(NonceError::InvalidLength);
}
let authority = Pubkey::from_bytes(data[8..40].try_into().unwrap());
let durable_nonce = data[40..72].try_into().unwrap();
let lamports_per_signature = u64::from_le_bytes(data[72..80].try_into().unwrap());
NonceState::Initialized(NonceData {
authority,
durable_nonce,
lamports_per_signature,
})
}
other => return Err(NonceError::BadStateTag(other)),
};
Ok(NonceAccount { version, state })
}
pub fn build_with_durable_nonce(
user_ixs: &[Instruction],
payer: Pubkey,
nonce_account: Pubkey,
nonce: DurableNonce,
authority: Pubkey,
) -> VersionedTransaction {
let advance = nonce_advance_ix(nonce_account, authority);
let mut all = Vec::with_capacity(user_ixs.len() + 1);
all.push(advance);
all.extend(user_ixs.iter().cloned());
build_unsigned(&all, payer, nonce)
}