use crate::{
StateProof,
tn_public_address::tn_pubkey_to_address_string,
txn_lib::{TnPubkey, Transaction},
};
use anyhow::{Result, bail};
use hex;
use std::collections::HashMap;
pub const NOOP_PROGRAM: [u8; 32] = {
let mut arr = [0u8; 32];
arr[31] = 0x03;
arr
};
pub const SYSTEM_PROGRAM: [u8; 32] = {
let mut arr = [0u8; 32];
arr[31] = 0x01;
arr
};
pub const EOA_PROGRAM: [u8; 32] = {
let arr = [0u8; 32];
arr
};
pub const UPLOADER_PROGRAM: [u8; 32] = {
let mut arr = [0u8; 32];
arr[31] = 0x02;
arr
};
pub const FAUCET_PROGRAM: [u8; 32] = {
let mut arr = [0u8; 32];
arr[31] = 0xFA;
arr
};
pub const CONSENSUS_VALIDATOR_PROGRAM: [u8; 32] = {
let mut arr = [0u8; 32];
arr[30] = 0x0C;
arr[31] = 0x01;
arr
};
pub const TOKEN_PROGRAM: [u8; 32] = {
let mut arr = [0u8; 32];
arr[31] = 0xAA;
arr
};
pub const ATTESTOR_TABLE: [u8; 32] = {
let mut arr = [0u8; 32];
arr[30] = 0x0C;
arr[31] = 0x02;
arr
};
pub const CONVERTED_VAULT: [u8; 32] = {
let mut arr = [0u8; 32];
arr[30] = 0x0C;
arr[31] = 0x04;
arr
};
pub const UNCLAIMED_VAULT: [u8; 32] = {
let mut arr = [0u8; 32];
arr[30] = 0x0C;
arr[31] = 0x05;
arr
};
const CONSENSUS_VALIDATOR_DEFAULT_EXPIRY_AFTER: u32 = 100;
const CONSENSUS_VALIDATOR_DEFAULT_COMPUTE_UNITS: u32 = 500_000_000;
const CONSENSUS_VALIDATOR_DEFAULT_STATE_UNITS: u16 = 50_000;
const CONSENSUS_VALIDATOR_DEFAULT_MEMORY_UNITS: u16 = 50_000;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ConsensusValidatorAccounts {
pub program: TnPubkey,
pub attestor_table: TnPubkey,
pub token_program: TnPubkey,
pub converted_vault: TnPubkey,
pub unclaimed_vault: TnPubkey,
}
impl Default for ConsensusValidatorAccounts {
fn default() -> Self {
Self {
program: CONSENSUS_VALIDATOR_PROGRAM,
attestor_table: ATTESTOR_TABLE,
token_program: TOKEN_PROGRAM,
converted_vault: CONVERTED_VAULT,
unclaimed_vault: UNCLAIMED_VAULT,
}
}
}
fn build_consensus_validator_tx(
fee_payer: TnPubkey,
program: TnPubkey,
fee: u64,
nonce: u64,
start_slot: u64,
) -> Transaction {
Transaction::new(fee_payer, program, fee, nonce)
.with_start_slot(start_slot)
.with_expiry_after(CONSENSUS_VALIDATOR_DEFAULT_EXPIRY_AFTER)
.with_compute_units(CONSENSUS_VALIDATOR_DEFAULT_COMPUTE_UNITS)
.with_state_units(CONSENSUS_VALIDATOR_DEFAULT_STATE_UNITS)
.with_memory_units(CONSENSUS_VALIDATOR_DEFAULT_MEMORY_UNITS)
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct UploaderWriteOptions {
pub skip_elf_check: bool,
}
#[derive(Debug, Clone)]
pub struct TransactionBuilder {
}
impl TransactionBuilder {
pub fn build_create_with_fee_payer_proof(
fee_payer: TnPubkey,
start_slot: u64,
fee_payer_state_proof: &StateProof,
) -> Result<Transaction> {
let tx = Transaction::new(fee_payer, NOOP_PROGRAM, 0, 0)
.with_fee_payer_state_proof(fee_payer_state_proof)
.with_start_slot(start_slot)
.with_expiry_after(100)
.with_compute_units(10_000)
.with_memory_units(10_000)
.with_state_units(10_000);
Ok(tx)
}
pub fn build_transfer(
fee_payer: TnPubkey,
program: TnPubkey,
to_account: TnPubkey,
amount: u64,
fee: u64,
nonce: u64,
start_slot: u64,
) -> Result<Transaction> {
let from_account_idx = 0u16; let to_account_idx = 2u16; let instruction_data =
build_transfer_instruction(from_account_idx, to_account_idx, amount)?;
let tx = Transaction::new(fee_payer, program, fee, nonce)
.with_start_slot(start_slot)
.add_rw_account(to_account) .with_instructions(instruction_data)
.with_expiry_after(100)
.with_compute_units(10000)
.with_memory_units(10000)
.with_state_units(10000);
Ok(tx)
}
#[allow(clippy::too_many_arguments)]
pub fn build_delete_account(
fee_payer: TnPubkey,
program: TnPubkey,
eoa_account: TnPubkey,
signature: &[u8; 64],
chain_id: u16,
fee: u64,
nonce: u64,
start_slot: u64,
) -> Result<Transaction> {
let eoa_account_idx = 2u16; let instruction_data = build_delete_account_instruction(eoa_account_idx, signature)?;
let tx = Transaction::new(fee_payer, program, fee, nonce)
.with_start_slot(start_slot)
.with_chain_id(chain_id)
.add_rw_account(eoa_account)
.with_instructions(instruction_data)
.with_expiry_after(100)
.with_compute_units(10_000)
.with_memory_units(10_000)
.with_state_units(10_000);
Ok(tx)
}
pub fn build_create_account(
fee_payer: TnPubkey,
program: TnPubkey,
target_account: TnPubkey,
seed: &str,
state_proof: Option<&[u8]>,
fee: u64,
nonce: u64,
start_slot: u64,
) -> Result<Transaction> {
let target_account_idx = 2u16; let instruction_data =
build_create_account_instruction(target_account_idx, seed, state_proof)?;
let tx = Transaction::new(fee_payer, program, fee, nonce)
.with_start_slot(start_slot)
.add_rw_account(target_account)
.with_instructions(instruction_data)
.with_expiry_after(100)
.with_compute_units(10_000)
.with_memory_units(10_000)
.with_state_units(10_000);
Ok(tx)
}
pub fn build_create_ephemeral_account(
fee_payer: TnPubkey,
program: TnPubkey,
target_account: TnPubkey,
seed: &[u8; 32],
fee: u64,
nonce: u64,
start_slot: u64,
) -> Result<Transaction> {
let target_account_idx = 2u16; let instruction_data = build_ephemeral_account_instruction(target_account_idx, seed)?;
let tx = Transaction::new(fee_payer, program, fee, nonce)
.with_start_slot(start_slot)
.add_rw_account(target_account)
.with_instructions(instruction_data)
.with_expiry_after(100)
.with_compute_units(50_000)
.with_memory_units(10_000)
.with_state_units(10_000);
Ok(tx)
}
pub fn build_resize_account(
fee_payer: TnPubkey,
program: TnPubkey,
target_account: TnPubkey,
new_size: u64,
fee: u64,
nonce: u64,
start_slot: u64,
) -> Result<Transaction> {
let target_account_idx = 2u16; let instruction_data = build_resize_instruction(target_account_idx, new_size)?;
let tx = Transaction::new(fee_payer, program, fee, nonce)
.with_start_slot(start_slot)
.with_expiry_after(100)
.with_compute_units(100032)
.with_state_units(1 + new_size.checked_div(4096).unwrap() as u16)
.with_memory_units(10000)
.add_rw_account(target_account)
.with_instructions(instruction_data)
.with_expiry_after(100)
.with_compute_units(10_000 + 2 * new_size as u32)
.with_memory_units(10_000)
.with_state_units(10_000);
Ok(tx)
}
pub fn build_compress_account(
fee_payer: TnPubkey,
program: TnPubkey,
target_account: TnPubkey,
state_proof: &[u8],
fee: u64,
nonce: u64,
start_slot: u64,
account_size: u32,
) -> Result<Transaction> {
let target_account_idx = 2u16; let instruction_data = build_compress_instruction(target_account_idx, state_proof)?;
let tx = Transaction::new(fee_payer, program, fee, nonce)
.with_start_slot(start_slot)
.with_may_compress_account()
.add_rw_account(target_account)
.with_instructions(instruction_data)
.with_expiry_after(100)
.with_compute_units(100_300 + account_size * 2)
.with_memory_units(10000)
.with_state_units(10000);
Ok(tx)
}
pub fn build_decompress_account(
fee_payer: TnPubkey,
program: TnPubkey,
target_account: TnPubkey,
account_data: &[u8],
state_proof: &[u8],
fee: u64,
nonce: u64,
start_slot: u64,
) -> Result<Transaction> {
let target_account_idx = 2u16; let instruction_data =
build_decompress_instruction(target_account_idx, account_data, state_proof)?;
let tx = Transaction::new(fee_payer, program, fee, nonce)
.with_start_slot(start_slot)
.add_rw_account(target_account)
.with_instructions(instruction_data)
.with_compute_units(100_300 + account_data.len() as u32 * 2)
.with_state_units(10_000)
.with_memory_units(10_000)
.with_expiry_after(100);
Ok(tx)
}
pub fn build_write_data(
fee_payer: TnPubkey,
program: TnPubkey,
target_account: TnPubkey,
offset: u16,
data: &[u8],
fee: u64,
nonce: u64,
start_slot: u64,
) -> Result<Transaction> {
let target_account_idx = 2u16; let instruction_data = build_write_instruction(target_account_idx, offset, data)?;
let tx = Transaction::new(fee_payer, program, fee, nonce)
.with_start_slot(start_slot)
.with_expiry_after(100)
.with_compute_units(100045)
.with_state_units(10000)
.with_memory_units(10000)
.add_rw_account(target_account)
.with_instructions(instruction_data);
Ok(tx)
}
}
fn build_transfer_instruction(
from_account_idx: u16,
to_account_idx: u16,
amount: u64,
) -> Result<Vec<u8>> {
let mut instruction = Vec::new();
instruction.extend_from_slice(&1u32.to_le_bytes());
instruction.extend_from_slice(&amount.to_le_bytes());
instruction.extend_from_slice(&from_account_idx.to_le_bytes());
instruction.extend_from_slice(&to_account_idx.to_le_bytes());
Ok(instruction)
}
pub const EOA_DELETE_MSG_TAG: &[u8; 16] = b"tn_eoa_delete_v1";
pub const EOA_DELETE_MSG_SZ: usize = 16 + 2 + 32 + 32;
pub fn build_eoa_delete_message(
chain_id: u16,
fee_payer: &TnPubkey,
eoa: &TnPubkey,
) -> [u8; EOA_DELETE_MSG_SZ] {
let mut msg = [0u8; EOA_DELETE_MSG_SZ];
msg[0..16].copy_from_slice(EOA_DELETE_MSG_TAG);
msg[16..18].copy_from_slice(&chain_id.to_le_bytes());
msg[18..50].copy_from_slice(fee_payer);
msg[50..82].copy_from_slice(eoa);
msg
}
fn build_delete_account_instruction(eoa_account_idx: u16, signature: &[u8; 64]) -> Result<Vec<u8>> {
let mut instruction = Vec::with_capacity(4 + 2 + 64);
instruction.extend_from_slice(&2u32.to_le_bytes());
instruction.extend_from_slice(&eoa_account_idx.to_le_bytes());
instruction.extend_from_slice(signature);
Ok(instruction)
}
fn build_create_account_instruction(
target_account_idx: u16,
seed: &str,
state_proof: Option<&[u8]>,
) -> Result<Vec<u8>> {
let mut instruction = Vec::new();
instruction.push(0x00);
instruction.extend_from_slice(&target_account_idx.to_le_bytes());
let seed_bytes =
hex::decode(seed).map_err(|e| anyhow::anyhow!("Failed to decode hex seed: {}", e))?;
instruction.extend_from_slice(&(seed_bytes.len() as u64).to_le_bytes());
let has_proof = state_proof.is_some();
instruction.push(if has_proof { 1u8 } else { 0u8 });
instruction.extend_from_slice(&seed_bytes);
if let Some(proof) = state_proof {
instruction.extend_from_slice(proof);
}
Ok(instruction)
}
fn build_ephemeral_account_instruction(
target_account_idx: u16,
seed: &[u8; 32],
) -> Result<Vec<u8>> {
let mut instruction = Vec::new();
instruction.push(0x01);
instruction.extend_from_slice(&target_account_idx.to_le_bytes());
instruction.extend_from_slice(&(seed.len() as u64).to_le_bytes());
instruction.extend_from_slice(seed);
Ok(instruction)
}
fn build_resize_instruction(target_account_idx: u16, new_size: u64) -> Result<Vec<u8>> {
let mut instruction = Vec::new();
instruction.push(0x04);
instruction.extend_from_slice(&target_account_idx.to_le_bytes());
instruction.extend_from_slice(&new_size.to_le_bytes());
Ok(instruction)
}
fn build_write_instruction(target_account_idx: u16, offset: u16, data: &[u8]) -> Result<Vec<u8>> {
let mut instruction = Vec::new();
instruction.push(0xC8);
instruction.extend_from_slice(&target_account_idx.to_le_bytes());
instruction.extend_from_slice(&offset.to_le_bytes());
instruction.extend_from_slice(&(data.len() as u16).to_le_bytes());
instruction.extend_from_slice(data);
Ok(instruction)
}
fn build_compress_instruction(target_account_idx: u16, state_proof: &[u8]) -> Result<Vec<u8>> {
let mut instruction = Vec::new();
instruction.push(0x05);
instruction.extend_from_slice(&target_account_idx.to_le_bytes());
instruction.extend_from_slice(state_proof);
Ok(instruction)
}
fn build_decompress_instruction(
target_account_idx: u16,
account_data: &[u8],
state_proof: &[u8],
) -> Result<Vec<u8>> {
let mut instruction = Vec::new();
instruction.push(0x06);
instruction.extend_from_slice(&target_account_idx.to_le_bytes());
instruction.extend_from_slice(&(account_data.len() as u64).to_le_bytes());
instruction.extend_from_slice(account_data);
instruction.extend_from_slice(state_proof);
Ok(instruction)
}
pub fn generate_ephemeral_address(seed: &str) -> Result<String> {
let owner_pubkey = [0u8; 32];
let seed_bytes =
hex::decode(seed).map_err(|e| anyhow::anyhow!("Failed to decode hex seed: {}", e))?;
let mut seed_32 = [0u8; 32];
let copy_len = std::cmp::min(seed_bytes.len(), 32);
seed_32[..copy_len].copy_from_slice(&seed_bytes[..copy_len]);
Ok(
crate::tn_public_address::create_program_defined_account_address_string(
&owner_pubkey,
true, &seed_32,
),
)
}
pub fn generate_system_derived_address(seed: &str, is_ephemeral: bool) -> Result<String> {
let seed_bytes =
hex::decode(seed).map_err(|e| anyhow::anyhow!("Failed to decode hex seed: {}", e))?;
let pubkey = generate_derived_address(&seed_bytes, &[0u8; 32], is_ephemeral)?;
Ok(tn_pubkey_to_address_string(&pubkey))
}
pub fn generate_derived_address(
seed: &[u8],
owner_pubkey: &[u8; 32],
is_ephemeral: bool,
) -> Result<[u8; 32]> {
use sha2::{Digest, Sha256};
let mut hasher = Sha256::new();
hasher.update(&owner_pubkey);
hasher.update(&[is_ephemeral as u8]);
hasher.update(&seed);
Ok(hasher.finalize().into())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_ephemeral_address_generation() {
let hex_seed1 = hex::encode("test_seed_123");
let hex_seed2 = hex::encode("test_seed_123");
let hex_seed3 = hex::encode("different_seed");
let addr1 = generate_ephemeral_address(&hex_seed1).unwrap();
let addr2 = generate_ephemeral_address(&hex_seed2).unwrap();
let addr3 = generate_ephemeral_address(&hex_seed3).unwrap();
assert_eq!(addr1, addr2);
assert_ne!(addr1, addr3);
assert!(addr1.starts_with("ta"));
assert!(addr2.starts_with("ta"));
assert!(addr3.starts_with("ta"));
assert_eq!(addr1.len(), 46);
assert_eq!(addr2.len(), 46);
assert_eq!(addr3.len(), 46);
}
#[test]
fn test_eoa_transfer_instruction_format() {
let from_idx = 0u16;
let to_idx = 2u16;
let amount = 1000u64;
let instruction = build_transfer_instruction(from_idx, to_idx, amount).unwrap();
assert_eq!(instruction.len(), 16, "Instruction should be 16 bytes");
let discriminant = u32::from_le_bytes([
instruction[0],
instruction[1],
instruction[2],
instruction[3],
]);
assert_eq!(discriminant, 1, "Discriminant should be 1 for TRANSFER");
let parsed_amount = u64::from_le_bytes([
instruction[4],
instruction[5],
instruction[6],
instruction[7],
instruction[8],
instruction[9],
instruction[10],
instruction[11],
]);
assert_eq!(parsed_amount, amount, "Amount should match input");
let parsed_from = u16::from_le_bytes([instruction[12], instruction[13]]);
assert_eq!(parsed_from, from_idx, "From index should match input");
let parsed_to = u16::from_le_bytes([instruction[14], instruction[15]]);
assert_eq!(parsed_to, to_idx, "To index should match input");
}
#[test]
fn test_eoa_delete_instruction_format() {
let eoa_idx = 2u16;
let signature = [7u8; 64];
let instruction = build_delete_account_instruction(eoa_idx, &signature).unwrap();
assert_eq!(instruction.len(), 70, "Instruction should be 70 bytes");
let discriminant = u32::from_le_bytes([
instruction[0],
instruction[1],
instruction[2],
instruction[3],
]);
assert_eq!(
discriminant, 2,
"Discriminant should be 2 for DELETE_ACCOUNT"
);
let parsed_idx = u16::from_le_bytes([instruction[4], instruction[5]]);
assert_eq!(parsed_idx, eoa_idx, "EOA index should match input");
assert_eq!(
&instruction[6..70],
&signature,
"Signature should match input"
);
}
#[test]
fn test_eoa_delete_message_layout() {
let chain_id = 0x0102u16;
let fee_payer = [0xAAu8; 32];
let eoa = [0xBBu8; 32];
let msg = build_eoa_delete_message(chain_id, &fee_payer, &eoa);
assert_eq!(msg.len(), 82, "Message should be 82 bytes");
assert_eq!(&msg[0..16], b"tn_eoa_delete_v1", "Tag mismatch");
assert_eq!(msg[16], 0x02, "chain_id low byte");
assert_eq!(msg[17], 0x01, "chain_id high byte");
assert_eq!(&msg[18..50], &fee_payer, "Fee payer mismatch");
assert_eq!(&msg[50..82], &eoa, "EOA pubkey mismatch");
}
#[test]
fn test_faucet_deposit_instruction_layout_with_fee_payer_depositor() {
let fee_payer = [1u8; 32];
let faucet_program = FAUCET_PROGRAM;
let faucet_account = [2u8; 32];
let depositor_account = fee_payer;
let amount = 500u64;
let tx = TransactionBuilder::build_faucet_deposit(
fee_payer,
faucet_program,
faucet_account,
depositor_account,
EOA_PROGRAM,
amount,
0,
42,
100,
)
.expect("build faucet deposit");
let rw_accs = tx.rw_accs.expect("rw accounts must exist");
assert_eq!(rw_accs.len(), 1);
assert_eq!(rw_accs[0], faucet_account);
let ro_accs = tx.r_accs.expect("ro accounts must exist");
assert_eq!(ro_accs.len(), 1);
assert_eq!(ro_accs[0], EOA_PROGRAM);
let instruction = tx.instructions.expect("instruction bytes must exist");
assert_eq!(
instruction.len(),
18,
"Deposit instruction must be 18 bytes"
);
let discriminant = u32::from_le_bytes([
instruction[0],
instruction[1],
instruction[2],
instruction[3],
]);
assert_eq!(discriminant, 0, "Deposit discriminant should be 0");
let faucet_idx = u16::from_le_bytes([instruction[4], instruction[5]]);
let depositor_idx = u16::from_le_bytes([instruction[6], instruction[7]]);
let eoa_idx = u16::from_le_bytes([instruction[8], instruction[9]]);
let parsed_amount = u64::from_le_bytes([
instruction[10],
instruction[11],
instruction[12],
instruction[13],
instruction[14],
instruction[15],
instruction[16],
instruction[17],
]);
assert_eq!(faucet_idx, 2, "Faucet account should be first RW account");
assert_eq!(depositor_idx, 0, "Depositor shares the fee payer index");
assert_eq!(eoa_idx, 3, "EOA program should follow RW accounts");
assert_eq!(parsed_amount, amount, "Amount should match input");
}
#[test]
fn test_build_token_initialize_mint() {
let fee_payer = [1u8; 32];
let token_program = [2u8; 32];
let mint_account = [3u8; 32];
let creator = [4u8; 32];
let mint_authority = [5u8; 32];
let freeze_authority = [6u8; 32];
let decimals = 9u8;
let ticker = "TEST";
let seed = [7u8; 32];
let state_proof = vec![8u8; 64];
let result = TransactionBuilder::build_token_initialize_mint(
fee_payer,
token_program,
mint_account,
creator,
mint_authority,
Some(freeze_authority),
decimals,
ticker,
seed,
state_proof.clone(),
1000, 1, 100, );
assert!(
result.is_ok(),
"Should build valid transaction with freeze authority"
);
let tx = result.unwrap();
assert!(
tx.instructions.is_some(),
"Transaction should have instructions"
);
let result_no_freeze = TransactionBuilder::build_token_initialize_mint(
fee_payer,
token_program,
mint_account,
creator,
mint_authority,
None,
decimals,
ticker,
seed,
state_proof,
1000,
1,
100,
);
assert!(
result_no_freeze.is_ok(),
"Should build valid transaction without freeze authority"
);
}
#[test]
fn test_build_token_initialize_mint_instruction_format() {
let mint_account_idx = 2u16;
let decimals = 9u8;
let creator = [1u8; 32];
let mint_authority = [2u8; 32];
let freeze_authority = [3u8; 32];
let ticker = "TST";
let seed = [4u8; 32];
let state_proof = vec![5u8; 10];
let instruction = build_token_initialize_mint_instruction(
mint_account_idx,
decimals,
creator,
mint_authority,
Some(freeze_authority),
ticker,
seed,
state_proof.clone(),
)
.unwrap();
let expected_min_size = 1 + 2 + 1 + 32 + 32 + 32 + 1 + 1 + 8 + 32 + state_proof.len();
assert_eq!(instruction.len(), expected_min_size);
assert_eq!(
instruction[0], 0,
"First byte should be InitializeMint tag (0)"
);
let parsed_idx = u16::from_le_bytes([instruction[1], instruction[2]]);
assert_eq!(parsed_idx, mint_account_idx);
assert_eq!(instruction[3], decimals);
assert_eq!(&instruction[4..36], &creator);
assert_eq!(&instruction[36..68], &mint_authority);
assert_eq!(&instruction[68..100], &freeze_authority);
assert_eq!(instruction[100], 1);
}
#[test]
fn test_token_initialize_mint_creator_vs_mint_authority() {
let fee_payer = [1u8; 32];
let token_program = [2u8; 32];
let mint_account = [3u8; 32];
let creator = [4u8; 32];
let mint_authority = [5u8; 32]; let seed = [6u8; 32];
let state_proof = vec![7u8; 32];
let result = TransactionBuilder::build_token_initialize_mint(
fee_payer,
token_program,
mint_account,
creator,
mint_authority,
None,
9,
"TEST",
seed,
state_proof,
1000,
1,
100,
);
assert!(
result.is_ok(),
"Should allow different creator and mint_authority"
);
let result_same = TransactionBuilder::build_token_initialize_mint(
fee_payer,
token_program,
mint_account,
creator,
creator, None,
9,
"TEST",
seed,
vec![7u8; 32],
1000,
1,
100,
);
assert!(
result_same.is_ok(),
"Should allow same creator and mint_authority"
);
}
#[test]
fn test_build_activate_with_custom_accounts_sorts_accounts_and_indices() {
let fee_payer = [0x10u8; 32];
let accounts = ConsensusValidatorAccounts {
program: [0xf0u8; 32],
attestor_table: [0x30u8; 32],
token_program: [0x90u8; 32],
converted_vault: [0x40u8; 32],
unclaimed_vault: [0x50u8; 32],
};
let source_token_account = [0x20u8; 32];
let claim_authority = [0x60u8; 32];
let bls_pk = [0x77u8; 96];
let tx = TransactionBuilder::build_activate_with_accounts(
fee_payer,
accounts,
source_token_account,
bls_pk,
claim_authority,
123,
0,
9,
44,
)
.expect("activate transaction should build");
assert_eq!(tx.program, accounts.program);
assert_eq!(
tx.rw_accs,
Some(vec![
source_token_account,
accounts.attestor_table,
accounts.converted_vault
])
);
assert_eq!(tx.r_accs, Some(vec![accounts.token_program]));
let instruction = tx.instructions.expect("instruction bytes must exist");
assert_eq!(
u32::from_le_bytes(instruction[0..4].try_into().unwrap()),
1,
"activate discriminant should be 1"
);
assert_eq!(
u64::from_le_bytes(instruction[4..12].try_into().unwrap()),
3,
"attestor table should resolve to rw index 3"
);
assert_eq!(
u16::from_le_bytes(instruction[12..14].try_into().unwrap()),
5,
"token program should resolve to ro index 5"
);
assert_eq!(
u16::from_le_bytes(instruction[14..16].try_into().unwrap()),
2,
"source token account should resolve to rw index 2"
);
assert_eq!(
u16::from_le_bytes(instruction[16..18].try_into().unwrap()),
4,
"converted vault should resolve to rw index 4"
);
assert_eq!(
u16::from_le_bytes(instruction[18..20].try_into().unwrap()),
0,
"identity should remain fee payer index 0"
);
}
#[test]
fn test_build_deactivate_instruction_format() {
let instruction = build_deactivate_instruction(7, 8, 9, 10, 0).unwrap();
assert_eq!(instruction.len(), 20);
assert_eq!(
u32::from_le_bytes(instruction[0..4].try_into().unwrap()),
2,
"deactivate discriminant should be 2"
);
assert_eq!(
u64::from_le_bytes(instruction[4..12].try_into().unwrap()),
7
);
assert_eq!(
u16::from_le_bytes(instruction[12..14].try_into().unwrap()),
8
);
assert_eq!(
u16::from_le_bytes(instruction[14..16].try_into().unwrap()),
9
);
assert_eq!(
u16::from_le_bytes(instruction[16..18].try_into().unwrap()),
10
);
assert_eq!(
u16::from_le_bytes(instruction[18..20].try_into().unwrap()),
0
);
}
#[test]
fn test_build_convert_tokens_instruction_format() {
let instruction = build_convert_tokens_instruction(11, 12, 13, 14, 0, 500).unwrap();
assert_eq!(instruction.len(), 28);
assert_eq!(
u32::from_le_bytes(instruction[0..4].try_into().unwrap()),
3,
"convert-tokens discriminant should be 3"
);
assert_eq!(
u64::from_le_bytes(instruction[4..12].try_into().unwrap()),
11
);
assert_eq!(
u16::from_le_bytes(instruction[12..14].try_into().unwrap()),
12
);
assert_eq!(
u16::from_le_bytes(instruction[14..16].try_into().unwrap()),
13
);
assert_eq!(
u16::from_le_bytes(instruction[16..18].try_into().unwrap()),
14
);
assert_eq!(
u16::from_le_bytes(instruction[18..20].try_into().unwrap()),
0
);
assert_eq!(
u64::from_le_bytes(instruction[20..28].try_into().unwrap()),
500
);
}
#[test]
fn test_build_claim_instruction_format() {
let subject_attestor = [0xabu8; 32];
let instruction = build_claim_instruction(15, 16, 17, 18, 0, subject_attestor).unwrap();
assert_eq!(instruction.len(), 52);
assert_eq!(
u32::from_le_bytes(instruction[0..4].try_into().unwrap()),
4,
"claim discriminant should be 4"
);
assert_eq!(
u64::from_le_bytes(instruction[4..12].try_into().unwrap()),
15
);
assert_eq!(
u16::from_le_bytes(instruction[12..14].try_into().unwrap()),
16
);
assert_eq!(
u16::from_le_bytes(instruction[14..16].try_into().unwrap()),
17
);
assert_eq!(
u16::from_le_bytes(instruction[16..18].try_into().unwrap()),
18
);
assert_eq!(
u16::from_le_bytes(instruction[18..20].try_into().unwrap()),
0
);
assert_eq!(&instruction[20..52], &subject_attestor);
}
#[test]
fn test_build_set_claim_authority_instruction_format() {
let subject_attestor = [0x55u8; 32];
let new_claim_authority = [0x66u8; 32];
let instruction =
build_set_claim_authority_instruction(19, 0, subject_attestor, new_claim_authority)
.unwrap();
assert_eq!(instruction.len(), 78);
assert_eq!(
u32::from_le_bytes(instruction[0..4].try_into().unwrap()),
5,
"set-claim-authority discriminant should be 5"
);
assert_eq!(
u64::from_le_bytes(instruction[4..12].try_into().unwrap()),
19
);
assert_eq!(
u16::from_le_bytes(instruction[12..14].try_into().unwrap()),
0
);
assert_eq!(&instruction[14..46], &subject_attestor);
assert_eq!(&instruction[46..78], &new_claim_authority);
}
}
pub const TN_UPLOADER_PROGRAM_INSTRUCTION_CREATE: u32 = 0x00;
pub const TN_UPLOADER_PROGRAM_INSTRUCTION_WRITE: u32 = 0x01;
pub const TN_UPLOADER_PROGRAM_INSTRUCTION_DESTROY: u32 = 0x02;
pub const TN_UPLOADER_PROGRAM_INSTRUCTION_FINALIZE: u32 = 0x03;
#[repr(C, packed)]
#[derive(Debug, Clone, Copy)]
pub struct UploaderCreateArgs {
pub buffer_account_idx: u16,
pub meta_account_idx: u16,
pub authority_account_idx: u16,
pub buffer_account_sz: u32,
pub expected_account_hash: [u8; 32],
pub seed_len: u32,
}
#[repr(C, packed)]
#[derive(Debug, Clone, Copy)]
pub struct UploaderWriteArgs {
pub buffer_account_idx: u16,
pub meta_account_idx: u16,
pub data_len: u32,
pub data_offset: u32,
}
#[repr(C, packed)]
#[derive(Debug, Clone, Copy)]
pub struct UploaderFinalizeArgs {
pub buffer_account_idx: u16,
pub meta_account_idx: u16,
pub expected_account_hash: [u8; 32],
}
#[repr(C, packed)]
#[derive(Debug, Clone, Copy)]
pub struct UploaderDestroyArgs {
pub buffer_account_idx: u16,
pub meta_account_idx: u16,
}
pub const MANAGER_INSTRUCTION_CREATE_PERMANENT: u8 = 0x00;
pub const MANAGER_INSTRUCTION_CREATE_EPHEMERAL: u8 = 0x01;
pub const MANAGER_INSTRUCTION_UPGRADE: u8 = 0x02;
pub const MANAGER_INSTRUCTION_SET_PAUSE: u8 = 0x03;
pub const MANAGER_INSTRUCTION_DESTROY: u8 = 0x04;
pub const MANAGER_INSTRUCTION_FINALIZE: u8 = 0x05;
pub const MANAGER_INSTRUCTION_SET_AUTHORITY: u8 = 0x06;
pub const MANAGER_INSTRUCTION_CLAIM_AUTHORITY: u8 = 0x07;
pub const ABI_MANAGER_INSTRUCTION_CREATE_META_OFFICIAL_PERMANENT: u8 = 0x00;
pub const ABI_MANAGER_INSTRUCTION_CREATE_META_OFFICIAL_EPHEMERAL: u8 = 0x01;
pub const ABI_MANAGER_INSTRUCTION_CREATE_META_EXTERNAL_PERMANENT: u8 = 0x02;
pub const ABI_MANAGER_INSTRUCTION_CREATE_META_EXTERNAL_EPHEMERAL: u8 = 0x03;
pub const ABI_MANAGER_INSTRUCTION_CREATE_ABI_OFFICIAL_PERMANENT: u8 = 0x04;
pub const ABI_MANAGER_INSTRUCTION_CREATE_ABI_OFFICIAL_EPHEMERAL: u8 = 0x05;
pub const ABI_MANAGER_INSTRUCTION_CREATE_ABI_EXTERNAL_PERMANENT: u8 = 0x06;
pub const ABI_MANAGER_INSTRUCTION_CREATE_ABI_EXTERNAL_EPHEMERAL: u8 = 0x07;
pub const ABI_MANAGER_INSTRUCTION_UPGRADE_ABI_OFFICIAL: u8 = 0x08;
pub const ABI_MANAGER_INSTRUCTION_UPGRADE_ABI_EXTERNAL: u8 = 0x09;
pub const ABI_MANAGER_INSTRUCTION_CLOSE_ABI_OFFICIAL: u8 = 0x0a;
pub const ABI_MANAGER_INSTRUCTION_CLOSE_ABI_EXTERNAL: u8 = 0x0b;
pub const ABI_MANAGER_INSTRUCTION_FINALIZE_ABI_OFFICIAL: u8 = 0x0c;
pub const ABI_MANAGER_INSTRUCTION_FINALIZE_ABI_EXTERNAL: u8 = 0x0d;
#[repr(C, packed)]
#[derive(Debug, Clone, Copy)]
pub struct ManagerHeaderArgs {
pub discriminant: u8,
pub meta_account_idx: u16,
pub program_account_idx: u16,
}
#[repr(C, packed)]
#[derive(Debug, Clone, Copy)]
pub struct ManagerCreateArgs {
pub discriminant: u8,
pub meta_account_idx: u16,
pub program_account_idx: u16,
pub srcbuf_account_idx: u16,
pub srcbuf_offset: u32,
pub srcbuf_size: u32,
pub authority_account_idx: u16,
pub seed_len: u32,
}
#[repr(C, packed)]
#[derive(Debug, Clone, Copy)]
pub struct ManagerUpgradeArgs {
pub discriminant: u8,
pub meta_account_idx: u16,
pub program_account_idx: u16,
pub srcbuf_account_idx: u16,
pub srcbuf_offset: u32,
pub srcbuf_size: u32,
}
#[repr(C, packed)]
#[derive(Debug, Clone, Copy)]
pub struct AbiManagerCreateMetaOfficialArgs {
pub abi_meta_account_idx: u16,
pub program_meta_account_idx: u16,
pub authority_account_idx: u16,
}
#[repr(C, packed)]
#[derive(Debug, Clone, Copy)]
pub struct AbiManagerCreateMetaExternalArgs {
pub abi_meta_account_idx: u16,
pub authority_account_idx: u16,
pub target_program: TnPubkey,
pub seed: [u8; 32],
}
#[repr(C, packed)]
#[derive(Debug, Clone, Copy)]
pub struct AbiManagerCreateAbiOfficialArgs {
pub abi_meta_account_idx: u16,
pub program_meta_account_idx: u16,
pub abi_account_idx: u16,
pub srcbuf_account_idx: u16,
pub srcbuf_offset: u32,
pub srcbuf_size: u32,
pub authority_account_idx: u16,
}
#[repr(C, packed)]
#[derive(Debug, Clone, Copy)]
pub struct AbiManagerCreateAbiExternalArgs {
pub abi_meta_account_idx: u16,
pub abi_account_idx: u16,
pub srcbuf_account_idx: u16,
pub srcbuf_offset: u32,
pub srcbuf_size: u32,
pub authority_account_idx: u16,
}
#[repr(C, packed)]
#[derive(Debug, Clone, Copy)]
pub struct AbiManagerUpgradeAbiOfficialArgs {
pub abi_meta_account_idx: u16,
pub program_meta_account_idx: u16,
pub abi_account_idx: u16,
pub srcbuf_account_idx: u16,
pub srcbuf_offset: u32,
pub srcbuf_size: u32,
pub authority_account_idx: u16,
}
#[repr(C, packed)]
#[derive(Debug, Clone, Copy)]
pub struct AbiManagerUpgradeAbiExternalArgs {
pub abi_meta_account_idx: u16,
pub abi_account_idx: u16,
pub srcbuf_account_idx: u16,
pub srcbuf_offset: u32,
pub srcbuf_size: u32,
pub authority_account_idx: u16,
}
#[repr(C, packed)]
#[derive(Debug, Clone, Copy)]
pub struct AbiManagerFinalizeAbiOfficialArgs {
pub abi_meta_account_idx: u16,
pub program_meta_account_idx: u16,
pub abi_account_idx: u16,
pub authority_account_idx: u16,
}
#[repr(C, packed)]
#[derive(Debug, Clone, Copy)]
pub struct AbiManagerFinalizeAbiExternalArgs {
pub abi_meta_account_idx: u16,
pub abi_account_idx: u16,
pub authority_account_idx: u16,
}
#[repr(C, packed)]
#[derive(Debug, Clone, Copy)]
pub struct AbiManagerCloseAbiOfficialArgs {
pub abi_meta_account_idx: u16,
pub program_meta_account_idx: u16,
pub abi_account_idx: u16,
pub authority_account_idx: u16,
}
#[repr(C, packed)]
#[derive(Debug, Clone, Copy)]
pub struct AbiManagerCloseAbiExternalArgs {
pub abi_meta_account_idx: u16,
pub abi_account_idx: u16,
pub authority_account_idx: u16,
}
#[repr(C, packed)]
#[derive(Debug, Clone, Copy)]
pub struct ManagerSetPauseArgs {
pub discriminant: u8,
pub meta_account_idx: u16,
pub program_account_idx: u16,
pub is_paused: u8,
}
#[repr(C, packed)]
#[derive(Debug, Clone, Copy)]
pub struct ManagerSetAuthorityArgs {
pub discriminant: u8,
pub meta_account_idx: u16,
pub program_account_idx: u16,
pub authority_candidate: [u8; 32],
}
pub const TN_TEST_UPLOADER_PROGRAM_DISCRIMINANT_CREATE: u8 = 0x00;
pub const TN_TEST_UPLOADER_PROGRAM_DISCRIMINANT_WRITE: u8 = 0x01;
#[repr(C, packed)]
#[derive(Debug, Clone, Copy)]
pub struct TestUploaderCreateArgs {
pub account_idx: u16,
pub is_ephemeral: u8,
pub account_sz: u32,
pub seed_len: u32,
}
#[repr(C, packed)]
#[derive(Debug, Clone, Copy)]
pub struct TestUploaderWriteArgs {
pub target_account_idx: u16,
pub target_offset: u32,
pub data_len: u32,
}
#[repr(C, packed)]
#[derive(Debug, Clone, Copy)]
pub struct SystemProgramDecompress2Args {
pub target_account_idx: u16,
pub meta_account_idx: u16,
pub data_account_idx: u16,
pub data_offset: u32,
}
fn uploader_data_has_elf_magic(data: &[u8]) -> bool {
let elf_magic = [0x7f, b'E', b'L', b'F'];
data.starts_with(&elf_magic)
}
impl TransactionBuilder {
pub fn build_uploader_create(
fee_payer: TnPubkey,
uploader_program: TnPubkey,
meta_account: TnPubkey,
buffer_account: TnPubkey,
buffer_size: u32,
expected_hash: [u8; 32],
seed: &[u8],
fee: u64,
nonce: u64,
start_slot: u64,
) -> Result<Transaction> {
let authority_account_idx = 0u16;
let mut tx = Transaction::new(fee_payer, uploader_program, fee, nonce)
.with_start_slot(start_slot)
.with_expiry_after(10)
.with_compute_units(50_000 + 2 * buffer_size as u32)
.with_memory_units(10_000)
.with_state_units(10_000);
let mut meta_account_idx = 2u16;
let mut buffer_account_idx = 3u16;
if meta_account > buffer_account {
meta_account_idx = 3u16;
buffer_account_idx = 2u16;
tx = tx
.add_rw_account(buffer_account)
.add_rw_account(meta_account)
} else {
tx = tx
.add_rw_account(meta_account)
.add_rw_account(buffer_account)
}
let instruction_data = build_uploader_create_instruction(
buffer_account_idx,
meta_account_idx,
authority_account_idx,
buffer_size,
expected_hash,
seed,
)?;
tx = tx.with_instructions(instruction_data);
Ok(tx)
}
pub fn build_uploader_write(
fee_payer: TnPubkey,
uploader_program: TnPubkey,
meta_account: TnPubkey,
buffer_account: TnPubkey,
data: &[u8],
offset: u32,
fee: u64,
nonce: u64,
start_slot: u64,
) -> Result<Transaction> {
Self::build_uploader_write_with_options(
fee_payer,
uploader_program,
meta_account,
buffer_account,
data,
offset,
fee,
nonce,
start_slot,
UploaderWriteOptions::default(),
)
}
pub fn build_uploader_write_with_options(
fee_payer: TnPubkey,
uploader_program: TnPubkey,
meta_account: TnPubkey,
buffer_account: TnPubkey,
data: &[u8],
offset: u32,
fee: u64,
nonce: u64,
start_slot: u64,
options: UploaderWriteOptions,
) -> Result<Transaction> {
if !options.skip_elf_check && offset == 0 && uploader_data_has_elf_magic(data) {
bail!(
"uploader data begins with ELF magic; upload a Thru program binary, not an ELF executable"
);
}
let mut tx = Transaction::new(fee_payer, uploader_program, fee, nonce)
.with_start_slot(start_slot)
.with_expiry_after(10000)
.with_compute_units(500_000_000)
.with_memory_units(5000)
.with_state_units(5000);
let mut meta_account_idx = 2u16;
let mut buffer_account_idx = 3u16;
if meta_account > buffer_account {
meta_account_idx = 3u16;
buffer_account_idx = 2u16;
tx = tx
.add_rw_account(buffer_account)
.add_rw_account(meta_account)
} else {
tx = tx
.add_rw_account(meta_account)
.add_rw_account(buffer_account)
}
let instruction_data =
build_uploader_write_instruction(buffer_account_idx, meta_account_idx, data, offset)?;
tx = tx.with_instructions(instruction_data);
Ok(tx)
}
pub fn build_uploader_finalize(
fee_payer: TnPubkey,
uploader_program: TnPubkey,
meta_account: TnPubkey,
buffer_account: TnPubkey,
buffer_size: u32,
expected_hash: [u8; 32],
fee: u64,
nonce: u64,
start_slot: u64,
) -> Result<Transaction> {
let mut tx = Transaction::new(fee_payer, uploader_program, fee, nonce)
.with_start_slot(start_slot)
.with_expiry_after(10000)
.with_compute_units(50_000 + 200 * buffer_size as u32)
.with_memory_units(5000)
.with_state_units(5000);
let mut meta_account_idx = 2u16;
let mut buffer_account_idx = 3u16;
if meta_account > buffer_account {
meta_account_idx = 3u16;
buffer_account_idx = 2u16;
tx = tx
.add_rw_account(buffer_account)
.add_rw_account(meta_account)
} else {
tx = tx
.add_rw_account(meta_account)
.add_rw_account(buffer_account)
}
let instruction_data = build_uploader_finalize_instruction(
buffer_account_idx,
meta_account_idx,
expected_hash,
)?;
tx = tx.with_instructions(instruction_data);
Ok(tx)
}
pub fn build_uploader_destroy(
fee_payer: TnPubkey,
uploader_program: TnPubkey,
meta_account: TnPubkey,
buffer_account: TnPubkey,
fee: u64,
nonce: u64,
start_slot: u64,
) -> Result<Transaction> {
let mut tx = Transaction::new(fee_payer, uploader_program, fee, nonce)
.with_start_slot(start_slot)
.with_expiry_after(10000)
.with_compute_units(50000)
.with_memory_units(5000)
.with_state_units(5000);
let mut meta_account_idx = 2u16;
let mut buffer_account_idx = 3u16;
if meta_account > buffer_account {
meta_account_idx = 3u16;
buffer_account_idx = 2u16;
tx = tx
.add_rw_account(buffer_account)
.add_rw_account(meta_account)
} else {
tx = tx
.add_rw_account(meta_account)
.add_rw_account(buffer_account)
}
let instruction_data =
build_uploader_destroy_instruction(buffer_account_idx, meta_account_idx)?;
tx = tx.with_instructions(instruction_data);
Ok(tx)
}
pub fn build_manager_create(
fee_payer: TnPubkey,
manager_program: TnPubkey,
meta_account: TnPubkey,
program_account: TnPubkey,
srcbuf_account: TnPubkey,
authority_account: TnPubkey,
srcbuf_offset: u32,
srcbuf_size: u32,
seed: &[u8],
is_ephemeral: bool,
meta_proof: Option<&[u8]>,
program_proof: Option<&[u8]>,
fee: u64,
nonce: u64,
start_slot: u64,
) -> Result<Transaction> {
let mut tx = Transaction::new(fee_payer, manager_program, fee, nonce)
.with_start_slot(start_slot)
.with_expiry_after(10000)
.with_compute_units(500_000_000)
.with_memory_units(5000)
.with_state_units(5000);
let authority_is_fee_payer = authority_account == fee_payer;
let mut rw_accounts = vec![(meta_account, "meta"), (program_account, "program")];
let mut r_accounts = vec![(srcbuf_account, "srcbuf")];
if !authority_is_fee_payer {
r_accounts.push((authority_account, "authority"));
}
rw_accounts.sort_by(|a, b| a.0.cmp(&b.0));
r_accounts.sort_by(|a, b| a.0.cmp(&b.0));
let mut accounts = rw_accounts;
accounts.extend(r_accounts);
let mut meta_account_idx = 0u16;
let mut program_account_idx = 0u16;
let mut srcbuf_account_idx = 0u16;
let mut authority_account_idx = if authority_is_fee_payer {
0u16 } else {
0u16 };
for (i, (account, account_type)) in accounts.iter().enumerate() {
let idx = (i + 2) as u16; match *account_type {
"meta" => {
meta_account_idx = idx;
tx = tx.add_rw_account(*account);
}
"program" => {
program_account_idx = idx;
tx = tx.add_rw_account(*account);
}
"srcbuf" => {
srcbuf_account_idx = idx;
tx = tx.add_r_account(*account);
}
"authority" => {
authority_account_idx = idx;
tx = tx.add_r_account(*account);
}
_ => unreachable!(),
}
}
let discriminant = if is_ephemeral {
MANAGER_INSTRUCTION_CREATE_EPHEMERAL
} else {
MANAGER_INSTRUCTION_CREATE_PERMANENT
};
let combined_proof = if let (Some(meta), Some(program)) = (meta_proof, program_proof) {
let mut combined = Vec::with_capacity(meta.len() + program.len());
combined.extend_from_slice(meta);
combined.extend_from_slice(program);
Some(combined)
} else {
None
};
let instruction_data = build_manager_create_instruction(
discriminant,
meta_account_idx,
program_account_idx,
srcbuf_account_idx,
authority_account_idx,
srcbuf_offset,
srcbuf_size,
seed,
combined_proof.as_deref(),
)?;
tx = tx.with_instructions(instruction_data);
Ok(tx)
}
pub fn build_abi_manager_create_meta_official(
fee_payer: TnPubkey,
abi_manager_program: TnPubkey,
program_meta_account: TnPubkey,
abi_meta_account: TnPubkey,
authority_account: TnPubkey,
is_ephemeral: bool,
abi_meta_proof: Option<&[u8]>,
fee: u64,
nonce: u64,
start_slot: u64,
) -> Result<Transaction> {
let mut tx = Transaction::new(fee_payer, abi_manager_program, fee, nonce)
.with_start_slot(start_slot)
.with_expiry_after(10000)
.with_compute_units(500_000_000)
.with_memory_units(5000)
.with_state_units(5000);
let authority_is_fee_payer = authority_account == fee_payer;
let mut rw_accounts = vec![(abi_meta_account, "abi_meta")];
let mut r_accounts = vec![(program_meta_account, "program_meta")];
if !authority_is_fee_payer {
r_accounts.push((authority_account, "authority"));
}
rw_accounts.sort_by(|a, b| a.0.cmp(&b.0));
r_accounts.sort_by(|a, b| a.0.cmp(&b.0));
let mut accounts = rw_accounts;
accounts.extend(r_accounts);
let mut abi_meta_account_idx = 0u16;
let mut program_meta_account_idx = 0u16;
let mut authority_account_idx = 0u16;
for (i, (account, account_type)) in accounts.iter().enumerate() {
let idx = (i + 2) as u16;
match *account_type {
"abi_meta" => {
abi_meta_account_idx = idx;
tx = tx.add_rw_account(*account);
}
"program_meta" => {
program_meta_account_idx = idx;
tx = tx.add_r_account(*account);
}
"authority" => {
authority_account_idx = idx;
tx = tx.add_r_account(*account);
}
_ => unreachable!(),
}
}
let authority_idx = if authority_is_fee_payer {
0u16
} else {
authority_account_idx
};
let discriminant = if is_ephemeral {
ABI_MANAGER_INSTRUCTION_CREATE_META_OFFICIAL_EPHEMERAL
} else {
ABI_MANAGER_INSTRUCTION_CREATE_META_OFFICIAL_PERMANENT
};
let instruction_data = build_abi_manager_create_meta_official_instruction(
discriminant,
abi_meta_account_idx,
program_meta_account_idx,
authority_idx,
abi_meta_proof,
)?;
tx = tx.with_instructions(instruction_data);
Ok(tx)
}
pub fn build_abi_manager_create_meta_external(
fee_payer: TnPubkey,
abi_manager_program: TnPubkey,
abi_meta_account: TnPubkey,
authority_account: TnPubkey,
target_program: TnPubkey,
seed: [u8; 32],
is_ephemeral: bool,
abi_meta_proof: Option<&[u8]>,
fee: u64,
nonce: u64,
start_slot: u64,
) -> Result<Transaction> {
let mut tx = Transaction::new(fee_payer, abi_manager_program, fee, nonce)
.with_start_slot(start_slot)
.with_expiry_after(10000)
.with_compute_units(500_000_000)
.with_memory_units(5000)
.with_state_units(5000);
let authority_is_fee_payer = authority_account == fee_payer;
let mut rw_accounts = vec![(abi_meta_account, "abi_meta")];
let mut r_accounts = Vec::new();
if !authority_is_fee_payer {
r_accounts.push((authority_account, "authority"));
}
rw_accounts.sort_by(|a, b| a.0.cmp(&b.0));
r_accounts.sort_by(|a, b| a.0.cmp(&b.0));
let mut accounts = rw_accounts;
accounts.extend(r_accounts);
let mut abi_meta_account_idx = 0u16;
let mut authority_account_idx = 0u16;
for (i, (account, account_type)) in accounts.iter().enumerate() {
let idx = (i + 2) as u16;
match *account_type {
"abi_meta" => {
abi_meta_account_idx = idx;
tx = tx.add_rw_account(*account);
}
"authority" => {
authority_account_idx = idx;
tx = tx.add_r_account(*account);
}
_ => unreachable!(),
}
}
let authority_idx = if authority_is_fee_payer {
0u16
} else {
authority_account_idx
};
let discriminant = if is_ephemeral {
ABI_MANAGER_INSTRUCTION_CREATE_META_EXTERNAL_EPHEMERAL
} else {
ABI_MANAGER_INSTRUCTION_CREATE_META_EXTERNAL_PERMANENT
};
let instruction_data = build_abi_manager_create_meta_external_instruction(
discriminant,
abi_meta_account_idx,
authority_idx,
target_program,
seed,
abi_meta_proof,
)?;
tx = tx.with_instructions(instruction_data);
Ok(tx)
}
#[allow(clippy::too_many_arguments)]
pub fn build_abi_manager_create_abi_official(
fee_payer: TnPubkey,
abi_manager_program: TnPubkey,
abi_meta_account: TnPubkey,
program_meta_account: TnPubkey,
abi_account: TnPubkey,
srcbuf_account: TnPubkey,
authority_account: TnPubkey,
srcbuf_offset: u32,
srcbuf_size: u32,
is_ephemeral: bool,
abi_proof: Option<&[u8]>,
fee: u64,
nonce: u64,
start_slot: u64,
) -> Result<Transaction> {
let mut tx = Transaction::new(fee_payer, abi_manager_program, fee, nonce)
.with_start_slot(start_slot)
.with_expiry_after(10000)
.with_compute_units(500_000_000)
.with_memory_units(5000)
.with_state_units(5000);
let authority_is_fee_payer = authority_account == fee_payer;
let mut rw_accounts = vec![(abi_account, "abi")];
let mut r_accounts = vec![
(abi_meta_account, "abi_meta"),
(program_meta_account, "program_meta"),
(srcbuf_account, "srcbuf"),
];
if !authority_is_fee_payer {
r_accounts.push((authority_account, "authority"));
}
rw_accounts.sort_by(|a, b| a.0.cmp(&b.0));
r_accounts.sort_by(|a, b| a.0.cmp(&b.0));
let mut accounts = rw_accounts;
accounts.extend(r_accounts);
let mut abi_meta_account_idx = 0u16;
let mut program_meta_account_idx = 0u16;
let mut abi_account_idx = 0u16;
let mut srcbuf_account_idx = 0u16;
let mut authority_account_idx = 0u16;
for (i, (account, account_type)) in accounts.iter().enumerate() {
let idx = (i + 2) as u16;
match *account_type {
"abi_meta" => {
abi_meta_account_idx = idx;
tx = tx.add_r_account(*account);
}
"program_meta" => {
program_meta_account_idx = idx;
tx = tx.add_r_account(*account);
}
"abi" => {
abi_account_idx = idx;
tx = tx.add_rw_account(*account);
}
"srcbuf" => {
srcbuf_account_idx = idx;
tx = tx.add_r_account(*account);
}
"authority" => {
authority_account_idx = idx;
tx = tx.add_r_account(*account);
}
_ => unreachable!(),
}
}
let authority_idx = if authority_is_fee_payer {
0u16
} else {
authority_account_idx
};
let discriminant = if is_ephemeral {
ABI_MANAGER_INSTRUCTION_CREATE_ABI_OFFICIAL_EPHEMERAL
} else {
ABI_MANAGER_INSTRUCTION_CREATE_ABI_OFFICIAL_PERMANENT
};
let instruction_data = build_abi_manager_create_abi_official_instruction(
discriminant,
abi_meta_account_idx,
program_meta_account_idx,
abi_account_idx,
srcbuf_account_idx,
srcbuf_offset,
srcbuf_size,
authority_idx,
abi_proof,
)?;
tx = tx.with_instructions(instruction_data);
Ok(tx)
}
#[allow(clippy::too_many_arguments)]
pub fn build_abi_manager_create_abi_external(
fee_payer: TnPubkey,
abi_manager_program: TnPubkey,
abi_meta_account: TnPubkey,
abi_account: TnPubkey,
srcbuf_account: TnPubkey,
authority_account: TnPubkey,
srcbuf_offset: u32,
srcbuf_size: u32,
is_ephemeral: bool,
abi_proof: Option<&[u8]>,
fee: u64,
nonce: u64,
start_slot: u64,
) -> Result<Transaction> {
let mut tx = Transaction::new(fee_payer, abi_manager_program, fee, nonce)
.with_start_slot(start_slot)
.with_expiry_after(10000)
.with_compute_units(500_000_000)
.with_memory_units(5000)
.with_state_units(5000);
let authority_is_fee_payer = authority_account == fee_payer;
let mut rw_accounts = vec![(abi_account, "abi")];
let mut r_accounts = vec![(abi_meta_account, "abi_meta"), (srcbuf_account, "srcbuf")];
if !authority_is_fee_payer {
r_accounts.push((authority_account, "authority"));
}
rw_accounts.sort_by(|a, b| a.0.cmp(&b.0));
r_accounts.sort_by(|a, b| a.0.cmp(&b.0));
let mut accounts = rw_accounts;
accounts.extend(r_accounts);
let mut abi_meta_account_idx = 0u16;
let mut abi_account_idx = 0u16;
let mut srcbuf_account_idx = 0u16;
let mut authority_account_idx = 0u16;
for (i, (account, account_type)) in accounts.iter().enumerate() {
let idx = (i + 2) as u16;
match *account_type {
"abi_meta" => {
abi_meta_account_idx = idx;
tx = tx.add_r_account(*account);
}
"abi" => {
abi_account_idx = idx;
tx = tx.add_rw_account(*account);
}
"srcbuf" => {
srcbuf_account_idx = idx;
tx = tx.add_r_account(*account);
}
"authority" => {
authority_account_idx = idx;
tx = tx.add_r_account(*account);
}
_ => unreachable!(),
}
}
let authority_idx = if authority_is_fee_payer {
0u16
} else {
authority_account_idx
};
let discriminant = if is_ephemeral {
ABI_MANAGER_INSTRUCTION_CREATE_ABI_EXTERNAL_EPHEMERAL
} else {
ABI_MANAGER_INSTRUCTION_CREATE_ABI_EXTERNAL_PERMANENT
};
let instruction_data = build_abi_manager_create_abi_external_instruction(
discriminant,
abi_meta_account_idx,
abi_account_idx,
srcbuf_account_idx,
srcbuf_offset,
srcbuf_size,
authority_idx,
abi_proof,
)?;
tx = tx.with_instructions(instruction_data);
Ok(tx)
}
#[allow(clippy::too_many_arguments)]
pub fn build_abi_manager_upgrade_abi_official(
fee_payer: TnPubkey,
abi_manager_program: TnPubkey,
abi_meta_account: TnPubkey,
program_meta_account: TnPubkey,
abi_account: TnPubkey,
srcbuf_account: TnPubkey,
authority_account: TnPubkey,
srcbuf_offset: u32,
srcbuf_size: u32,
fee: u64,
nonce: u64,
start_slot: u64,
) -> Result<Transaction> {
let mut tx = Transaction::new(fee_payer, abi_manager_program, fee, nonce)
.with_start_slot(start_slot)
.with_expiry_after(10000)
.with_compute_units(500_000_000)
.with_memory_units(5000)
.with_state_units(5000);
let authority_is_fee_payer = authority_account == fee_payer;
let mut rw_accounts = vec![(abi_account, "abi")];
let mut r_accounts = vec![
(abi_meta_account, "abi_meta"),
(program_meta_account, "program_meta"),
(srcbuf_account, "srcbuf"),
];
if !authority_is_fee_payer {
r_accounts.push((authority_account, "authority"));
}
rw_accounts.sort_by(|a, b| a.0.cmp(&b.0));
r_accounts.sort_by(|a, b| a.0.cmp(&b.0));
let mut accounts = rw_accounts;
accounts.extend(r_accounts);
let mut abi_meta_account_idx = 0u16;
let mut program_meta_account_idx = 0u16;
let mut abi_account_idx = 0u16;
let mut srcbuf_account_idx = 0u16;
let mut authority_account_idx = 0u16;
for (i, (account, account_type)) in accounts.iter().enumerate() {
let idx = (i + 2) as u16;
match *account_type {
"abi_meta" => {
abi_meta_account_idx = idx;
tx = tx.add_r_account(*account);
}
"program_meta" => {
program_meta_account_idx = idx;
tx = tx.add_r_account(*account);
}
"abi" => {
abi_account_idx = idx;
tx = tx.add_rw_account(*account);
}
"srcbuf" => {
srcbuf_account_idx = idx;
tx = tx.add_r_account(*account);
}
"authority" => {
authority_account_idx = idx;
tx = tx.add_r_account(*account);
}
_ => unreachable!(),
}
}
let authority_idx = if authority_is_fee_payer {
0u16
} else {
authority_account_idx
};
let instruction_data = build_abi_manager_upgrade_abi_official_instruction(
abi_meta_account_idx,
program_meta_account_idx,
abi_account_idx,
srcbuf_account_idx,
srcbuf_offset,
srcbuf_size,
authority_idx,
)?;
tx = tx.with_instructions(instruction_data);
Ok(tx)
}
#[allow(clippy::too_many_arguments)]
pub fn build_abi_manager_upgrade_abi_external(
fee_payer: TnPubkey,
abi_manager_program: TnPubkey,
abi_meta_account: TnPubkey,
abi_account: TnPubkey,
srcbuf_account: TnPubkey,
authority_account: TnPubkey,
srcbuf_offset: u32,
srcbuf_size: u32,
fee: u64,
nonce: u64,
start_slot: u64,
) -> Result<Transaction> {
let mut tx = Transaction::new(fee_payer, abi_manager_program, fee, nonce)
.with_start_slot(start_slot)
.with_expiry_after(10000)
.with_compute_units(500_000_000)
.with_memory_units(5000)
.with_state_units(5000);
let authority_is_fee_payer = authority_account == fee_payer;
let mut rw_accounts = vec![(abi_account, "abi")];
let mut r_accounts = vec![(abi_meta_account, "abi_meta"), (srcbuf_account, "srcbuf")];
if !authority_is_fee_payer {
r_accounts.push((authority_account, "authority"));
}
rw_accounts.sort_by(|a, b| a.0.cmp(&b.0));
r_accounts.sort_by(|a, b| a.0.cmp(&b.0));
let mut accounts = rw_accounts;
accounts.extend(r_accounts);
let mut abi_meta_account_idx = 0u16;
let mut abi_account_idx = 0u16;
let mut srcbuf_account_idx = 0u16;
let mut authority_account_idx = 0u16;
for (i, (account, account_type)) in accounts.iter().enumerate() {
let idx = (i + 2) as u16;
match *account_type {
"abi_meta" => {
abi_meta_account_idx = idx;
tx = tx.add_r_account(*account);
}
"abi" => {
abi_account_idx = idx;
tx = tx.add_rw_account(*account);
}
"srcbuf" => {
srcbuf_account_idx = idx;
tx = tx.add_r_account(*account);
}
"authority" => {
authority_account_idx = idx;
tx = tx.add_r_account(*account);
}
_ => unreachable!(),
}
}
let authority_idx = if authority_is_fee_payer {
0u16
} else {
authority_account_idx
};
let instruction_data = build_abi_manager_upgrade_abi_external_instruction(
abi_meta_account_idx,
abi_account_idx,
srcbuf_account_idx,
srcbuf_offset,
srcbuf_size,
authority_idx,
)?;
tx = tx.with_instructions(instruction_data);
Ok(tx)
}
pub fn build_abi_manager_finalize_abi_official(
fee_payer: TnPubkey,
abi_manager_program: TnPubkey,
abi_meta_account: TnPubkey,
program_meta_account: TnPubkey,
abi_account: TnPubkey,
authority_account: TnPubkey,
fee: u64,
nonce: u64,
start_slot: u64,
) -> Result<Transaction> {
let mut tx = Transaction::new(fee_payer, abi_manager_program, fee, nonce)
.with_start_slot(start_slot)
.with_expiry_after(10000)
.with_compute_units(500_000_000)
.with_memory_units(5000)
.with_state_units(5000);
let authority_is_fee_payer = authority_account == fee_payer;
let mut rw_accounts = vec![(abi_account, "abi")];
let mut r_accounts = vec![
(abi_meta_account, "abi_meta"),
(program_meta_account, "program_meta"),
];
if !authority_is_fee_payer {
r_accounts.push((authority_account, "authority"));
}
rw_accounts.sort_by(|a, b| a.0.cmp(&b.0));
r_accounts.sort_by(|a, b| a.0.cmp(&b.0));
let mut accounts = rw_accounts;
accounts.extend(r_accounts);
let mut abi_meta_account_idx = 0u16;
let mut program_meta_account_idx = 0u16;
let mut abi_account_idx = 0u16;
let mut authority_account_idx = 0u16;
for (i, (account, account_type)) in accounts.iter().enumerate() {
let idx = (i + 2) as u16;
match *account_type {
"abi_meta" => {
abi_meta_account_idx = idx;
tx = tx.add_r_account(*account);
}
"program_meta" => {
program_meta_account_idx = idx;
tx = tx.add_r_account(*account);
}
"abi" => {
abi_account_idx = idx;
tx = tx.add_rw_account(*account);
}
"authority" => {
authority_account_idx = idx;
tx = tx.add_r_account(*account);
}
_ => unreachable!(),
}
}
let authority_idx = if authority_is_fee_payer {
0u16
} else {
authority_account_idx
};
let instruction_data = build_abi_manager_finalize_abi_official_instruction(
abi_meta_account_idx,
program_meta_account_idx,
abi_account_idx,
authority_idx,
)?;
tx = tx.with_instructions(instruction_data);
Ok(tx)
}
pub fn build_abi_manager_finalize_abi_external(
fee_payer: TnPubkey,
abi_manager_program: TnPubkey,
abi_meta_account: TnPubkey,
abi_account: TnPubkey,
authority_account: TnPubkey,
fee: u64,
nonce: u64,
start_slot: u64,
) -> Result<Transaction> {
let mut tx = Transaction::new(fee_payer, abi_manager_program, fee, nonce)
.with_start_slot(start_slot)
.with_expiry_after(10000)
.with_compute_units(500_000_000)
.with_memory_units(5000)
.with_state_units(5000);
let authority_is_fee_payer = authority_account == fee_payer;
let mut rw_accounts = vec![(abi_account, "abi")];
let mut r_accounts = vec![(abi_meta_account, "abi_meta")];
if !authority_is_fee_payer {
r_accounts.push((authority_account, "authority"));
}
rw_accounts.sort_by(|a, b| a.0.cmp(&b.0));
r_accounts.sort_by(|a, b| a.0.cmp(&b.0));
let mut accounts = rw_accounts;
accounts.extend(r_accounts);
let mut abi_meta_account_idx = 0u16;
let mut abi_account_idx = 0u16;
let mut authority_account_idx = 0u16;
for (i, (account, account_type)) in accounts.iter().enumerate() {
let idx = (i + 2) as u16;
match *account_type {
"abi_meta" => {
abi_meta_account_idx = idx;
tx = tx.add_r_account(*account);
}
"abi" => {
abi_account_idx = idx;
tx = tx.add_rw_account(*account);
}
"authority" => {
authority_account_idx = idx;
tx = tx.add_r_account(*account);
}
_ => unreachable!(),
}
}
let authority_idx = if authority_is_fee_payer {
0u16
} else {
authority_account_idx
};
let instruction_data = build_abi_manager_finalize_abi_external_instruction(
abi_meta_account_idx,
abi_account_idx,
authority_idx,
)?;
tx = tx.with_instructions(instruction_data);
Ok(tx)
}
pub fn build_abi_manager_close_abi_official(
fee_payer: TnPubkey,
abi_manager_program: TnPubkey,
abi_meta_account: TnPubkey,
program_meta_account: TnPubkey,
abi_account: TnPubkey,
authority_account: TnPubkey,
fee: u64,
nonce: u64,
start_slot: u64,
) -> Result<Transaction> {
let mut tx = Transaction::new(fee_payer, abi_manager_program, fee, nonce)
.with_start_slot(start_slot)
.with_expiry_after(10000)
.with_compute_units(500_000_000)
.with_memory_units(5000)
.with_state_units(5000);
let authority_is_fee_payer = authority_account == fee_payer;
let mut rw_accounts = vec![(abi_account, "abi")];
let mut r_accounts = vec![
(abi_meta_account, "abi_meta"),
(program_meta_account, "program_meta"),
];
if !authority_is_fee_payer {
r_accounts.push((authority_account, "authority"));
}
rw_accounts.sort_by(|a, b| a.0.cmp(&b.0));
r_accounts.sort_by(|a, b| a.0.cmp(&b.0));
let mut accounts = rw_accounts;
accounts.extend(r_accounts);
let mut abi_meta_account_idx = 0u16;
let mut program_meta_account_idx = 0u16;
let mut abi_account_idx = 0u16;
let mut authority_account_idx = 0u16;
for (i, (account, account_type)) in accounts.iter().enumerate() {
let idx = (i + 2) as u16;
match *account_type {
"abi_meta" => {
abi_meta_account_idx = idx;
tx = tx.add_r_account(*account);
}
"program_meta" => {
program_meta_account_idx = idx;
tx = tx.add_r_account(*account);
}
"abi" => {
abi_account_idx = idx;
tx = tx.add_rw_account(*account);
}
"authority" => {
authority_account_idx = idx;
tx = tx.add_r_account(*account);
}
_ => unreachable!(),
}
}
let authority_idx = if authority_is_fee_payer {
0u16
} else {
authority_account_idx
};
let instruction_data = build_abi_manager_close_abi_official_instruction(
abi_meta_account_idx,
program_meta_account_idx,
abi_account_idx,
authority_idx,
)?;
tx = tx.with_instructions(instruction_data);
Ok(tx)
}
pub fn build_abi_manager_close_abi_external(
fee_payer: TnPubkey,
abi_manager_program: TnPubkey,
abi_meta_account: TnPubkey,
abi_account: TnPubkey,
authority_account: TnPubkey,
fee: u64,
nonce: u64,
start_slot: u64,
) -> Result<Transaction> {
let mut tx = Transaction::new(fee_payer, abi_manager_program, fee, nonce)
.with_start_slot(start_slot)
.with_expiry_after(10000)
.with_compute_units(500_000_000)
.with_memory_units(5000)
.with_state_units(5000);
let authority_is_fee_payer = authority_account == fee_payer;
let mut rw_accounts = vec![(abi_account, "abi")];
let mut r_accounts = vec![(abi_meta_account, "abi_meta")];
if !authority_is_fee_payer {
r_accounts.push((authority_account, "authority"));
}
rw_accounts.sort_by(|a, b| a.0.cmp(&b.0));
r_accounts.sort_by(|a, b| a.0.cmp(&b.0));
let mut accounts = rw_accounts;
accounts.extend(r_accounts);
let mut abi_meta_account_idx = 0u16;
let mut abi_account_idx = 0u16;
let mut authority_account_idx = 0u16;
for (i, (account, account_type)) in accounts.iter().enumerate() {
let idx = (i + 2) as u16;
match *account_type {
"abi_meta" => {
abi_meta_account_idx = idx;
tx = tx.add_r_account(*account);
}
"abi" => {
abi_account_idx = idx;
tx = tx.add_rw_account(*account);
}
"authority" => {
authority_account_idx = idx;
tx = tx.add_r_account(*account);
}
_ => unreachable!(),
}
}
let authority_idx = if authority_is_fee_payer {
0u16
} else {
authority_account_idx
};
let instruction_data = build_abi_manager_close_abi_external_instruction(
abi_meta_account_idx,
abi_account_idx,
authority_idx,
)?;
tx = tx.with_instructions(instruction_data);
Ok(tx)
}
pub fn build_manager_upgrade(
fee_payer: TnPubkey,
manager_program: TnPubkey,
meta_account: TnPubkey,
program_account: TnPubkey,
srcbuf_account: TnPubkey,
srcbuf_offset: u32,
srcbuf_size: u32,
fee: u64,
nonce: u64,
start_slot: u64,
) -> Result<Transaction> {
let mut tx = Transaction::new(fee_payer, manager_program, fee, nonce)
.with_start_slot(start_slot)
.with_expiry_after(10000)
.with_compute_units(500_000_000)
.with_memory_units(5000)
.with_state_units(5000);
let mut rw_accounts = vec![(meta_account, "meta"), (program_account, "program")];
let mut r_accounts = vec![(srcbuf_account, "srcbuf")];
rw_accounts.sort_by(|a, b| a.0.cmp(&b.0));
r_accounts.sort_by(|a, b| a.0.cmp(&b.0));
let mut accounts = rw_accounts;
accounts.extend(r_accounts);
let mut meta_account_idx = 0u16;
let mut program_account_idx = 0u16;
let mut srcbuf_account_idx = 0u16;
for (i, (account, account_type)) in accounts.iter().enumerate() {
let idx = (i + 2) as u16; match *account_type {
"meta" => {
meta_account_idx = idx;
tx = tx.add_rw_account(*account);
}
"program" => {
program_account_idx = idx;
tx = tx.add_rw_account(*account);
}
"srcbuf" => {
srcbuf_account_idx = idx;
tx = tx.add_r_account(*account);
}
_ => unreachable!(),
}
}
let instruction_data = build_manager_upgrade_instruction(
meta_account_idx,
program_account_idx,
srcbuf_account_idx,
srcbuf_offset,
srcbuf_size,
)?;
tx = tx.with_instructions(instruction_data);
Ok(tx)
}
pub fn build_manager_set_pause(
fee_payer: TnPubkey,
manager_program: TnPubkey,
meta_account: TnPubkey,
program_account: TnPubkey,
is_paused: bool,
fee: u64,
nonce: u64,
start_slot: u64,
) -> Result<Transaction> {
let mut tx = Transaction::new(fee_payer, manager_program, fee, nonce)
.with_start_slot(start_slot)
.with_expiry_after(10000)
.with_compute_units(100_000_000)
.with_memory_units(5000)
.with_state_units(5000);
let mut accounts = vec![(meta_account, "meta"), (program_account, "program")];
accounts.sort_by(|a, b| a.0.cmp(&b.0));
let mut meta_account_idx = 0u16;
let mut program_account_idx = 0u16;
for (i, (account, account_type)) in accounts.iter().enumerate() {
let idx = (i + 2) as u16;
match *account_type {
"meta" => {
meta_account_idx = idx;
tx = tx.add_rw_account(*account);
}
"program" => {
program_account_idx = idx;
tx = tx.add_rw_account(*account);
}
_ => unreachable!(),
}
}
let instruction_data =
build_manager_set_pause_instruction(meta_account_idx, program_account_idx, is_paused)?;
tx = tx.with_instructions(instruction_data);
Ok(tx)
}
pub fn build_manager_simple(
fee_payer: TnPubkey,
manager_program: TnPubkey,
meta_account: TnPubkey,
program_account: TnPubkey,
instruction_type: u8,
fee: u64,
nonce: u64,
start_slot: u64,
) -> Result<Transaction> {
let mut tx = Transaction::new(fee_payer, manager_program, fee, nonce)
.with_start_slot(start_slot)
.with_expiry_after(10000)
.with_compute_units(100_000_000)
.with_memory_units(5000)
.with_state_units(5000);
let mut accounts = vec![(meta_account, "meta"), (program_account, "program")];
accounts.sort_by(|a, b| a.0.cmp(&b.0));
let mut meta_account_idx = 0u16;
let mut program_account_idx = 0u16;
for (i, (account, account_type)) in accounts.iter().enumerate() {
let idx = (i + 2) as u16;
match *account_type {
"meta" => {
meta_account_idx = idx;
tx = tx.add_rw_account(*account);
}
"program" => {
program_account_idx = idx;
tx = tx.add_rw_account(*account);
}
_ => unreachable!(),
}
}
let instruction_data = build_manager_header_instruction(
instruction_type,
meta_account_idx,
program_account_idx,
)?;
tx = tx.with_instructions(instruction_data);
Ok(tx)
}
pub fn build_manager_set_authority(
fee_payer: TnPubkey,
manager_program: TnPubkey,
meta_account: TnPubkey,
program_account: TnPubkey,
authority_candidate: [u8; 32],
fee: u64,
nonce: u64,
start_slot: u64,
) -> Result<Transaction> {
let mut tx = Transaction::new(fee_payer, manager_program, fee, nonce)
.with_start_slot(start_slot)
.with_expiry_after(10000)
.with_compute_units(100_000_000)
.with_memory_units(5000)
.with_state_units(5000);
let mut accounts = vec![(meta_account, "meta"), (program_account, "program")];
accounts.sort_by(|a, b| a.0.cmp(&b.0));
let mut meta_account_idx = 0u16;
let mut program_account_idx = 0u16;
for (i, (account, account_type)) in accounts.iter().enumerate() {
let idx = (i + 2) as u16;
match *account_type {
"meta" => {
meta_account_idx = idx;
tx = tx.add_rw_account(*account);
}
"program" => {
program_account_idx = idx;
tx = tx.add_rw_account(*account);
}
_ => unreachable!(),
}
}
let instruction_data = build_manager_set_authority_instruction(
meta_account_idx,
program_account_idx,
authority_candidate,
)?;
tx = tx.with_instructions(instruction_data);
Ok(tx)
}
pub fn build_test_uploader_create(
fee_payer: TnPubkey,
test_uploader_program: TnPubkey,
target_account: TnPubkey,
account_sz: u32,
seed: &[u8],
is_ephemeral: bool,
state_proof: Option<&[u8]>,
fee: u64,
nonce: u64,
start_slot: u64,
) -> Result<Transaction> {
let target_account_idx = 2u16;
let tx = Transaction::new(fee_payer, test_uploader_program, fee, nonce)
.with_start_slot(start_slot)
.with_expiry_after(100)
.with_compute_units(100_000 + account_sz)
.with_memory_units(10_000)
.with_state_units(10_000)
.add_rw_account(target_account);
let instruction_data = build_test_uploader_create_instruction(
target_account_idx,
account_sz,
seed,
is_ephemeral,
state_proof,
)?;
let tx = tx.with_instructions(instruction_data);
Ok(tx)
}
pub fn build_test_uploader_write(
fee_payer: TnPubkey,
test_uploader_program: TnPubkey,
target_account: TnPubkey,
offset: u32,
data: &[u8],
fee: u64,
nonce: u64,
start_slot: u64,
) -> Result<Transaction> {
let target_account_idx = 2u16;
let tx = Transaction::new(fee_payer, test_uploader_program, fee, nonce)
.with_start_slot(start_slot)
.with_expiry_after(10_000)
.with_compute_units(100_000 + 18 * data.len() as u32)
.with_memory_units(10_000)
.with_state_units(10_000)
.add_rw_account(target_account);
let instruction_data =
build_test_uploader_write_instruction(target_account_idx, offset, data)?;
let tx = tx.with_instructions(instruction_data);
Ok(tx)
}
pub fn build_decompress2(
fee_payer: TnPubkey,
program: TnPubkey,
target_account: TnPubkey,
meta_account: TnPubkey,
data_account: TnPubkey,
data_offset: u32,
state_proof: &[u8],
fee: u64,
nonce: u64,
start_slot: u64,
data_sz: u32,
) -> Result<Transaction> {
let mut tx = Transaction::new(fee_payer, program, fee, nonce)
.with_start_slot(start_slot)
.with_expiry_after(100)
.with_compute_units(10_000 + 2 * data_sz)
.with_memory_units(10_000)
.with_state_units(10);
let target_account_idx = 2u16;
tx = tx.add_rw_account(target_account);
let mut meta_account_idx = 0u16;
let mut data_account_idx = 0u16;
if meta_account == data_account {
let account_idx = 3u16;
meta_account_idx = account_idx;
data_account_idx = account_idx;
tx = tx.add_r_account(meta_account);
} else {
let mut read_accounts = vec![(meta_account, "meta"), (data_account, "data")];
read_accounts.sort_by(|a, b| a.0.cmp(&b.0));
for (i, (account, account_type)) in read_accounts.iter().enumerate() {
let idx = (3 + i) as u16; match *account_type {
"meta" => {
meta_account_idx = idx;
tx = tx.add_r_account(*account);
}
"data" => {
data_account_idx = idx;
tx = tx.add_r_account(*account);
}
_ => unreachable!(),
}
}
}
let instruction_data = build_decompress2_instruction(
target_account_idx,
meta_account_idx,
data_account_idx,
data_offset,
state_proof,
)?;
tx = tx.with_instructions(instruction_data);
Ok(tx)
}
}
fn build_uploader_create_instruction(
buffer_account_idx: u16,
meta_account_idx: u16,
authority_account_idx: u16,
buffer_size: u32,
expected_hash: [u8; 32],
seed: &[u8],
) -> Result<Vec<u8>> {
let mut instruction = Vec::new();
instruction.extend_from_slice(&TN_UPLOADER_PROGRAM_INSTRUCTION_CREATE.to_le_bytes());
let args = UploaderCreateArgs {
buffer_account_idx,
meta_account_idx,
authority_account_idx,
buffer_account_sz: buffer_size,
expected_account_hash: expected_hash,
seed_len: seed.len() as u32,
};
let args_bytes = unsafe {
std::slice::from_raw_parts(
&args as *const _ as *const u8,
std::mem::size_of::<UploaderCreateArgs>(),
)
};
instruction.extend_from_slice(args_bytes);
instruction.extend_from_slice(seed);
Ok(instruction)
}
fn build_uploader_write_instruction(
buffer_account_idx: u16,
meta_account_idx: u16,
data: &[u8],
offset: u32,
) -> Result<Vec<u8>> {
let mut instruction = Vec::new();
instruction.extend_from_slice(&TN_UPLOADER_PROGRAM_INSTRUCTION_WRITE.to_le_bytes());
let args = UploaderWriteArgs {
buffer_account_idx,
meta_account_idx,
data_len: data.len() as u32,
data_offset: offset,
};
let args_bytes = unsafe {
std::slice::from_raw_parts(
&args as *const _ as *const u8,
std::mem::size_of::<UploaderWriteArgs>(),
)
};
instruction.extend_from_slice(args_bytes);
instruction.extend_from_slice(data);
Ok(instruction)
}
fn build_uploader_finalize_instruction(
buffer_account_idx: u16,
meta_account_idx: u16,
expected_hash: [u8; 32],
) -> Result<Vec<u8>> {
let mut instruction = Vec::new();
instruction.extend_from_slice(&TN_UPLOADER_PROGRAM_INSTRUCTION_FINALIZE.to_le_bytes());
let args = UploaderFinalizeArgs {
buffer_account_idx,
meta_account_idx,
expected_account_hash: expected_hash,
};
let args_bytes = unsafe {
std::slice::from_raw_parts(
&args as *const _ as *const u8,
std::mem::size_of::<UploaderFinalizeArgs>(),
)
};
instruction.extend_from_slice(args_bytes);
Ok(instruction)
}
fn build_uploader_destroy_instruction(
buffer_account_idx: u16,
meta_account_idx: u16,
) -> Result<Vec<u8>> {
let mut instruction = Vec::new();
instruction.extend_from_slice(&TN_UPLOADER_PROGRAM_INSTRUCTION_DESTROY.to_le_bytes());
let args = UploaderDestroyArgs {
buffer_account_idx,
meta_account_idx,
};
let args_bytes = unsafe {
std::slice::from_raw_parts(
&args as *const _ as *const u8,
std::mem::size_of::<UploaderDestroyArgs>(),
)
};
instruction.extend_from_slice(args_bytes);
Ok(instruction)
}
fn build_manager_create_instruction(
discriminant: u8,
meta_account_idx: u16,
program_account_idx: u16,
srcbuf_account_idx: u16,
authority_account_idx: u16,
srcbuf_offset: u32,
srcbuf_size: u32,
seed: &[u8],
proof: Option<&[u8]>,
) -> Result<Vec<u8>> {
let mut instruction = Vec::new();
let args = ManagerCreateArgs {
discriminant,
meta_account_idx,
program_account_idx,
srcbuf_account_idx,
srcbuf_offset,
srcbuf_size,
authority_account_idx,
seed_len: seed.len() as u32,
};
let args_bytes = unsafe {
std::slice::from_raw_parts(
&args as *const ManagerCreateArgs as *const u8,
std::mem::size_of::<ManagerCreateArgs>(),
)
};
instruction.extend_from_slice(args_bytes);
instruction.extend_from_slice(seed);
if let Some(proof_bytes) = proof {
instruction.extend_from_slice(proof_bytes);
}
Ok(instruction)
}
fn build_abi_manager_create_meta_official_instruction(
discriminant: u8,
abi_meta_account_idx: u16,
program_meta_account_idx: u16,
authority_account_idx: u16,
proof: Option<&[u8]>,
) -> Result<Vec<u8>> {
let mut instruction = Vec::new();
instruction.push(discriminant);
let args = AbiManagerCreateMetaOfficialArgs {
abi_meta_account_idx,
program_meta_account_idx,
authority_account_idx,
};
let args_bytes = unsafe {
std::slice::from_raw_parts(
&args as *const AbiManagerCreateMetaOfficialArgs as *const u8,
std::mem::size_of::<AbiManagerCreateMetaOfficialArgs>(),
)
};
instruction.extend_from_slice(args_bytes);
if let Some(proof_bytes) = proof {
instruction.extend_from_slice(proof_bytes);
}
Ok(instruction)
}
fn build_abi_manager_create_meta_external_instruction(
discriminant: u8,
abi_meta_account_idx: u16,
authority_account_idx: u16,
target_program: TnPubkey,
seed: [u8; 32],
proof: Option<&[u8]>,
) -> Result<Vec<u8>> {
let mut instruction = Vec::new();
instruction.push(discriminant);
let args = AbiManagerCreateMetaExternalArgs {
abi_meta_account_idx,
authority_account_idx,
target_program,
seed,
};
let args_bytes = unsafe {
std::slice::from_raw_parts(
&args as *const AbiManagerCreateMetaExternalArgs as *const u8,
std::mem::size_of::<AbiManagerCreateMetaExternalArgs>(),
)
};
instruction.extend_from_slice(args_bytes);
if let Some(proof_bytes) = proof {
instruction.extend_from_slice(proof_bytes);
}
Ok(instruction)
}
fn build_abi_manager_create_abi_official_instruction(
discriminant: u8,
abi_meta_account_idx: u16,
program_meta_account_idx: u16,
abi_account_idx: u16,
srcbuf_account_idx: u16,
srcbuf_offset: u32,
srcbuf_size: u32,
authority_account_idx: u16,
proof: Option<&[u8]>,
) -> Result<Vec<u8>> {
let mut instruction = Vec::new();
instruction.push(discriminant);
let args = AbiManagerCreateAbiOfficialArgs {
abi_meta_account_idx,
program_meta_account_idx,
abi_account_idx,
srcbuf_account_idx,
srcbuf_offset,
srcbuf_size,
authority_account_idx,
};
let args_bytes = unsafe {
std::slice::from_raw_parts(
&args as *const AbiManagerCreateAbiOfficialArgs as *const u8,
std::mem::size_of::<AbiManagerCreateAbiOfficialArgs>(),
)
};
instruction.extend_from_slice(args_bytes);
if let Some(proof_bytes) = proof {
instruction.extend_from_slice(proof_bytes);
}
Ok(instruction)
}
fn build_abi_manager_create_abi_external_instruction(
discriminant: u8,
abi_meta_account_idx: u16,
abi_account_idx: u16,
srcbuf_account_idx: u16,
srcbuf_offset: u32,
srcbuf_size: u32,
authority_account_idx: u16,
proof: Option<&[u8]>,
) -> Result<Vec<u8>> {
let mut instruction = Vec::new();
instruction.push(discriminant);
let args = AbiManagerCreateAbiExternalArgs {
abi_meta_account_idx,
abi_account_idx,
srcbuf_account_idx,
srcbuf_offset,
srcbuf_size,
authority_account_idx,
};
let args_bytes = unsafe {
std::slice::from_raw_parts(
&args as *const AbiManagerCreateAbiExternalArgs as *const u8,
std::mem::size_of::<AbiManagerCreateAbiExternalArgs>(),
)
};
instruction.extend_from_slice(args_bytes);
if let Some(proof_bytes) = proof {
instruction.extend_from_slice(proof_bytes);
}
Ok(instruction)
}
fn build_abi_manager_upgrade_abi_official_instruction(
abi_meta_account_idx: u16,
program_meta_account_idx: u16,
abi_account_idx: u16,
srcbuf_account_idx: u16,
srcbuf_offset: u32,
srcbuf_size: u32,
authority_account_idx: u16,
) -> Result<Vec<u8>> {
let mut instruction = Vec::new();
instruction.push(ABI_MANAGER_INSTRUCTION_UPGRADE_ABI_OFFICIAL);
let args = AbiManagerUpgradeAbiOfficialArgs {
abi_meta_account_idx,
program_meta_account_idx,
abi_account_idx,
srcbuf_account_idx,
srcbuf_offset,
srcbuf_size,
authority_account_idx,
};
let args_bytes = unsafe {
std::slice::from_raw_parts(
&args as *const AbiManagerUpgradeAbiOfficialArgs as *const u8,
std::mem::size_of::<AbiManagerUpgradeAbiOfficialArgs>(),
)
};
instruction.extend_from_slice(args_bytes);
Ok(instruction)
}
fn build_abi_manager_upgrade_abi_external_instruction(
abi_meta_account_idx: u16,
abi_account_idx: u16,
srcbuf_account_idx: u16,
srcbuf_offset: u32,
srcbuf_size: u32,
authority_account_idx: u16,
) -> Result<Vec<u8>> {
let mut instruction = Vec::new();
instruction.push(ABI_MANAGER_INSTRUCTION_UPGRADE_ABI_EXTERNAL);
let args = AbiManagerUpgradeAbiExternalArgs {
abi_meta_account_idx,
abi_account_idx,
srcbuf_account_idx,
srcbuf_offset,
srcbuf_size,
authority_account_idx,
};
let args_bytes = unsafe {
std::slice::from_raw_parts(
&args as *const AbiManagerUpgradeAbiExternalArgs as *const u8,
std::mem::size_of::<AbiManagerUpgradeAbiExternalArgs>(),
)
};
instruction.extend_from_slice(args_bytes);
Ok(instruction)
}
fn build_abi_manager_finalize_abi_official_instruction(
abi_meta_account_idx: u16,
program_meta_account_idx: u16,
abi_account_idx: u16,
authority_account_idx: u16,
) -> Result<Vec<u8>> {
let mut instruction = Vec::new();
instruction.push(ABI_MANAGER_INSTRUCTION_FINALIZE_ABI_OFFICIAL);
let args = AbiManagerFinalizeAbiOfficialArgs {
abi_meta_account_idx,
program_meta_account_idx,
abi_account_idx,
authority_account_idx,
};
let args_bytes = unsafe {
std::slice::from_raw_parts(
&args as *const AbiManagerFinalizeAbiOfficialArgs as *const u8,
std::mem::size_of::<AbiManagerFinalizeAbiOfficialArgs>(),
)
};
instruction.extend_from_slice(args_bytes);
Ok(instruction)
}
fn build_abi_manager_finalize_abi_external_instruction(
abi_meta_account_idx: u16,
abi_account_idx: u16,
authority_account_idx: u16,
) -> Result<Vec<u8>> {
let mut instruction = Vec::new();
instruction.push(ABI_MANAGER_INSTRUCTION_FINALIZE_ABI_EXTERNAL);
let args = AbiManagerFinalizeAbiExternalArgs {
abi_meta_account_idx,
abi_account_idx,
authority_account_idx,
};
let args_bytes = unsafe {
std::slice::from_raw_parts(
&args as *const AbiManagerFinalizeAbiExternalArgs as *const u8,
std::mem::size_of::<AbiManagerFinalizeAbiExternalArgs>(),
)
};
instruction.extend_from_slice(args_bytes);
Ok(instruction)
}
fn build_abi_manager_close_abi_official_instruction(
abi_meta_account_idx: u16,
program_meta_account_idx: u16,
abi_account_idx: u16,
authority_account_idx: u16,
) -> Result<Vec<u8>> {
let mut instruction = Vec::new();
instruction.push(ABI_MANAGER_INSTRUCTION_CLOSE_ABI_OFFICIAL);
let args = AbiManagerCloseAbiOfficialArgs {
abi_meta_account_idx,
program_meta_account_idx,
abi_account_idx,
authority_account_idx,
};
let args_bytes = unsafe {
std::slice::from_raw_parts(
&args as *const AbiManagerCloseAbiOfficialArgs as *const u8,
std::mem::size_of::<AbiManagerCloseAbiOfficialArgs>(),
)
};
instruction.extend_from_slice(args_bytes);
Ok(instruction)
}
fn build_abi_manager_close_abi_external_instruction(
abi_meta_account_idx: u16,
abi_account_idx: u16,
authority_account_idx: u16,
) -> Result<Vec<u8>> {
let mut instruction = Vec::new();
instruction.push(ABI_MANAGER_INSTRUCTION_CLOSE_ABI_EXTERNAL);
let args = AbiManagerCloseAbiExternalArgs {
abi_meta_account_idx,
abi_account_idx,
authority_account_idx,
};
let args_bytes = unsafe {
std::slice::from_raw_parts(
&args as *const AbiManagerCloseAbiExternalArgs as *const u8,
std::mem::size_of::<AbiManagerCloseAbiExternalArgs>(),
)
};
instruction.extend_from_slice(args_bytes);
Ok(instruction)
}
fn build_manager_upgrade_instruction(
meta_account_idx: u16,
program_account_idx: u16,
srcbuf_account_idx: u16,
srcbuf_offset: u32,
srcbuf_size: u32,
) -> Result<Vec<u8>> {
let mut instruction = Vec::new();
let args = ManagerUpgradeArgs {
discriminant: MANAGER_INSTRUCTION_UPGRADE,
meta_account_idx,
program_account_idx,
srcbuf_account_idx,
srcbuf_offset,
srcbuf_size,
};
let args_bytes = unsafe {
std::slice::from_raw_parts(
&args as *const ManagerUpgradeArgs as *const u8,
std::mem::size_of::<ManagerUpgradeArgs>(),
)
};
instruction.extend_from_slice(args_bytes);
Ok(instruction)
}
fn build_manager_set_pause_instruction(
meta_account_idx: u16,
program_account_idx: u16,
is_paused: bool,
) -> Result<Vec<u8>> {
let mut instruction = Vec::new();
let args = ManagerSetPauseArgs {
discriminant: MANAGER_INSTRUCTION_SET_PAUSE,
meta_account_idx,
program_account_idx,
is_paused: if is_paused { 1 } else { 0 },
};
let args_bytes = unsafe {
std::slice::from_raw_parts(
&args as *const ManagerSetPauseArgs as *const u8,
std::mem::size_of::<ManagerSetPauseArgs>(),
)
};
instruction.extend_from_slice(args_bytes);
Ok(instruction)
}
fn build_manager_header_instruction(
discriminant: u8,
meta_account_idx: u16,
program_account_idx: u16,
) -> Result<Vec<u8>> {
let mut instruction = Vec::new();
let args = ManagerHeaderArgs {
discriminant,
meta_account_idx,
program_account_idx,
};
let args_bytes = unsafe {
std::slice::from_raw_parts(
&args as *const ManagerHeaderArgs as *const u8,
std::mem::size_of::<ManagerHeaderArgs>(),
)
};
instruction.extend_from_slice(args_bytes);
Ok(instruction)
}
fn build_manager_set_authority_instruction(
meta_account_idx: u16,
program_account_idx: u16,
authority_candidate: [u8; 32],
) -> Result<Vec<u8>> {
let mut instruction = Vec::new();
let args = ManagerSetAuthorityArgs {
discriminant: MANAGER_INSTRUCTION_SET_AUTHORITY,
meta_account_idx,
program_account_idx,
authority_candidate,
};
let args_bytes = unsafe {
std::slice::from_raw_parts(
&args as *const ManagerSetAuthorityArgs as *const u8,
std::mem::size_of::<ManagerSetAuthorityArgs>(),
)
};
instruction.extend_from_slice(args_bytes);
Ok(instruction)
}
fn build_test_uploader_create_instruction(
account_idx: u16,
account_sz: u32,
seed: &[u8],
is_ephemeral: bool,
state_proof: Option<&[u8]>,
) -> Result<Vec<u8>> {
let mut instruction = Vec::new();
instruction.push(TN_TEST_UPLOADER_PROGRAM_DISCRIMINANT_CREATE);
let args = TestUploaderCreateArgs {
account_idx,
is_ephemeral: if is_ephemeral { 1u8 } else { 0u8 },
account_sz,
seed_len: seed.len() as u32,
};
let args_bytes = unsafe {
std::slice::from_raw_parts(
&args as *const _ as *const u8,
std::mem::size_of::<TestUploaderCreateArgs>(),
)
};
instruction.extend_from_slice(args_bytes);
instruction.extend_from_slice(seed);
if let Some(proof) = state_proof {
instruction.extend_from_slice(proof);
}
Ok(instruction)
}
fn build_test_uploader_write_instruction(
target_account_idx: u16,
target_offset: u32,
data: &[u8],
) -> Result<Vec<u8>> {
let mut instruction = Vec::new();
instruction.push(TN_TEST_UPLOADER_PROGRAM_DISCRIMINANT_WRITE);
let args = TestUploaderWriteArgs {
target_account_idx,
target_offset,
data_len: data.len() as u32,
};
let args_bytes = unsafe {
std::slice::from_raw_parts(
&args as *const _ as *const u8,
std::mem::size_of::<TestUploaderWriteArgs>(),
)
};
instruction.extend_from_slice(args_bytes);
instruction.extend_from_slice(data);
Ok(instruction)
}
pub fn build_decompress2_instruction(
target_account_idx: u16,
meta_account_idx: u16,
data_account_idx: u16,
data_offset: u32,
state_proof: &[u8],
) -> Result<Vec<u8>> {
let mut instruction = Vec::new();
instruction.push(0x08);
let args = SystemProgramDecompress2Args {
target_account_idx,
meta_account_idx,
data_account_idx,
data_offset,
};
let args_bytes = unsafe {
std::slice::from_raw_parts(
&args as *const _ as *const u8,
std::mem::size_of::<SystemProgramDecompress2Args>(),
)
};
instruction.extend_from_slice(args_bytes);
instruction.extend_from_slice(state_proof);
Ok(instruction)
}
pub const TOKEN_INSTRUCTION_INITIALIZE_MINT: u8 = 0x00;
pub const TOKEN_INSTRUCTION_INITIALIZE_ACCOUNT: u8 = 0x01;
pub const TOKEN_INSTRUCTION_TRANSFER: u8 = 0x02;
pub const TOKEN_INSTRUCTION_MINT_TO: u8 = 0x03;
pub const TOKEN_INSTRUCTION_BURN: u8 = 0x04;
pub const TOKEN_INSTRUCTION_CLOSE_ACCOUNT: u8 = 0x05;
pub const TOKEN_INSTRUCTION_FREEZE_ACCOUNT: u8 = 0x06;
pub const TOKEN_INSTRUCTION_THAW_ACCOUNT: u8 = 0x07;
pub const TN_WTHRU_INSTRUCTION_INITIALIZE_MINT: u32 = 0;
pub const TN_WTHRU_INSTRUCTION_DEPOSIT: u32 = 1;
pub const TN_WTHRU_INSTRUCTION_WITHDRAW: u32 = 2;
pub const TN_NAME_SERVICE_INSTRUCTION_INITIALIZE_ROOT: u32 = 0;
pub const TN_NAME_SERVICE_INSTRUCTION_REGISTER_SUBDOMAIN: u32 = 1;
pub const TN_NAME_SERVICE_INSTRUCTION_APPEND_RECORD: u32 = 2;
pub const TN_NAME_SERVICE_INSTRUCTION_DELETE_RECORD: u32 = 3;
pub const TN_NAME_SERVICE_INSTRUCTION_UNREGISTER: u32 = 4;
pub const TN_NAME_SERVICE_PROOF_INLINE: u32 = 0;
pub const TN_NAME_SERVICE_MAX_DOMAIN_LENGTH: usize = 64;
pub const TN_NAME_SERVICE_MAX_KEY_LENGTH: usize = 32;
pub const TN_NAME_SERVICE_MAX_VALUE_LENGTH: usize = 256;
pub const TN_THRU_REGISTRAR_INSTRUCTION_INITIALIZE_REGISTRY: u32 = 0;
pub const TN_THRU_REGISTRAR_INSTRUCTION_PURCHASE_DOMAIN: u32 = 1;
pub const TN_THRU_REGISTRAR_INSTRUCTION_RENEW_LEASE: u32 = 2;
pub const TN_THRU_REGISTRAR_INSTRUCTION_CLAIM_EXPIRED_DOMAIN: u32 = 3;
fn add_sorted_accounts(tx: Transaction, accounts: &[(TnPubkey, bool)]) -> (Transaction, Vec<u16>) {
let mut rw_accounts: Vec<_> = accounts
.iter()
.enumerate()
.filter(|(_, (_, writable))| *writable)
.collect();
let mut ro_accounts: Vec<_> = accounts
.iter()
.enumerate()
.filter(|(_, (_, writable))| !*writable)
.collect();
rw_accounts.sort_by(|a, b| a.1.0.cmp(&b.1.0));
ro_accounts.sort_by(|a, b| a.1.0.cmp(&b.1.0));
let mut updated_tx = tx;
let mut indices = vec![0u16; accounts.len()];
let mut seen: HashMap<TnPubkey, u16> = HashMap::new();
seen.insert(updated_tx.fee_payer, 0u16);
seen.insert(updated_tx.program, 1u16);
let mut next_idx = 2u16;
for (i, (account, _)) in rw_accounts.iter() {
if let Some(idx) = seen.get(account) {
indices[*i] = *idx;
continue;
}
let account_idx = next_idx;
next_idx = next_idx.saturating_add(1);
seen.insert(*account, account_idx);
indices[*i] = account_idx;
updated_tx = updated_tx.add_rw_account(*account);
}
for (i, (account, _)) in ro_accounts.iter() {
if let Some(idx) = seen.get(account) {
indices[*i] = *idx;
continue;
}
let account_idx = next_idx;
next_idx = next_idx.saturating_add(1);
seen.insert(*account, account_idx);
indices[*i] = account_idx;
updated_tx = updated_tx.add_r_account(*account);
}
(updated_tx, indices)
}
fn add_sorted_rw_accounts(mut tx: Transaction, accounts: &[TnPubkey]) -> (Transaction, Vec<u16>) {
if accounts.is_empty() {
return (tx, Vec::new());
}
let mut sorted: Vec<(usize, TnPubkey)> = accounts.iter().cloned().enumerate().collect();
sorted.sort_by(|a, b| a.1.cmp(&b.1));
let mut indices = vec![0u16; accounts.len()];
for (pos, (orig_idx, account)) in sorted.into_iter().enumerate() {
let idx = (2 + pos) as u16;
indices[orig_idx] = idx;
tx = tx.add_rw_account(account);
}
(tx, indices)
}
fn add_sorted_ro_accounts(
mut tx: Transaction,
base_idx: u16,
accounts: &[TnPubkey],
) -> (Transaction, Vec<u16>) {
if accounts.is_empty() {
return (tx, Vec::new());
}
let mut sorted: Vec<(usize, TnPubkey)> = accounts.iter().cloned().enumerate().collect();
sorted.sort_by(|a, b| a.1.cmp(&b.1));
let mut indices = vec![0u16; accounts.len()];
for (pos, (orig_idx, account)) in sorted.into_iter().enumerate() {
let idx = base_idx + pos as u16;
indices[orig_idx] = idx;
tx = tx.add_r_account(account);
}
(tx, indices)
}
impl TransactionBuilder {
pub fn build_token_initialize_mint(
fee_payer: TnPubkey,
token_program: TnPubkey,
mint_account: TnPubkey,
creator: TnPubkey,
mint_authority: TnPubkey,
freeze_authority: Option<TnPubkey>,
decimals: u8,
ticker: &str,
seed: [u8; 32],
state_proof: Vec<u8>,
fee: u64,
nonce: u64,
start_slot: u64,
) -> Result<Transaction> {
let base_tx =
Transaction::new(fee_payer, token_program, fee, nonce).with_start_slot(start_slot);
let (tx, indices) = add_sorted_rw_accounts(base_tx, &[mint_account]);
let mint_account_idx = indices[0];
let instruction_data = build_token_initialize_mint_instruction(
mint_account_idx,
decimals,
creator,
mint_authority,
freeze_authority,
ticker,
seed,
state_proof,
)?;
let tx = tx
.with_instructions(instruction_data)
.with_expiry_after(100)
.with_compute_units(300_000)
.with_state_units(10_000)
.with_memory_units(10_000);
Ok(tx)
}
pub fn build_token_initialize_account(
fee_payer: TnPubkey,
token_program: TnPubkey,
token_account: TnPubkey,
mint_account: TnPubkey,
owner: TnPubkey,
seed: [u8; 32],
state_proof: Vec<u8>,
fee: u64,
nonce: u64,
start_slot: u64,
) -> Result<Transaction> {
let owner_is_fee_payer = owner == fee_payer;
let mut rw_accounts = vec![token_account];
rw_accounts.sort();
let mut ro_accounts = vec![mint_account];
if !owner_is_fee_payer {
ro_accounts.push(owner);
ro_accounts.sort();
}
let mut tx =
Transaction::new(fee_payer, token_program, fee, nonce).with_start_slot(start_slot);
let mut token_account_idx = 0u16;
for (i, account) in rw_accounts.iter().enumerate() {
let idx = (2 + i) as u16;
if *account == token_account {
token_account_idx = idx;
}
tx = tx.add_rw_account(*account);
}
let base_ro_idx = 2 + rw_accounts.len() as u16;
let mut mint_account_idx = 0u16;
let mut owner_account_idx = if owner_is_fee_payer { 0u16 } else { 0u16 };
for (i, account) in ro_accounts.iter().enumerate() {
let idx = base_ro_idx + i as u16;
if *account == mint_account {
mint_account_idx = idx;
} else if !owner_is_fee_payer && *account == owner {
owner_account_idx = idx;
}
tx = tx.add_r_account(*account);
}
let instruction_data = build_token_initialize_account_instruction(
token_account_idx,
mint_account_idx,
owner_account_idx,
seed,
state_proof,
)?;
let tx = tx
.with_instructions(instruction_data)
.with_expiry_after(100)
.with_compute_units(300_000)
.with_state_units(10_000)
.with_memory_units(10_000);
Ok(tx)
}
pub fn build_token_transfer(
fee_payer: TnPubkey,
token_program: TnPubkey,
source_account: TnPubkey,
dest_account: TnPubkey,
_authority: TnPubkey,
amount: u64,
fee: u64,
nonce: u64,
start_slot: u64,
) -> Result<Transaction> {
let mut tx = Transaction::new(fee_payer, token_program, fee, nonce)
.with_start_slot(start_slot)
.with_expiry_after(100)
.with_compute_units(300_000)
.with_state_units(10_000)
.with_memory_units(10_000);
let is_self_transfer = source_account == dest_account;
let (source_account_idx, dest_account_idx) = if is_self_transfer {
tx = tx.add_rw_account(source_account);
(2u16, 2u16)
} else {
let accounts = &[(source_account, true), (dest_account, true)];
let (updated_tx, indices) = add_sorted_accounts(tx, accounts);
tx = updated_tx;
(indices[0], indices[1])
};
let instruction_data =
build_token_transfer_instruction(source_account_idx, dest_account_idx, amount)?;
Ok(tx.with_instructions(instruction_data))
}
pub fn build_token_mint_to(
fee_payer: TnPubkey,
token_program: TnPubkey,
mint_account: TnPubkey,
dest_account: TnPubkey,
authority: TnPubkey,
amount: u64,
fee: u64,
nonce: u64,
start_slot: u64,
) -> Result<Transaction> {
let base_tx = Transaction::new(fee_payer, token_program, fee, nonce)
.with_start_slot(start_slot)
.with_expiry_after(100)
.with_compute_units(300_000)
.with_state_units(10_000)
.with_memory_units(10_000);
let (tx_after_rw, rw_indices) =
add_sorted_rw_accounts(base_tx, &[mint_account, dest_account]);
let mint_account_idx = rw_indices[0];
let dest_account_idx = rw_indices[1];
let mut tx = tx_after_rw;
let authority_account_idx = if authority == fee_payer {
0u16
} else {
let base_ro_idx = 2 + rw_indices.len() as u16;
let (tx_after_ro, ro_indices) = add_sorted_ro_accounts(tx, base_ro_idx, &[authority]);
tx = tx_after_ro;
ro_indices[0]
};
let instruction_data = build_token_mint_to_instruction(
mint_account_idx,
dest_account_idx,
authority_account_idx,
amount,
)?;
Ok(tx.with_instructions(instruction_data))
}
pub fn build_token_burn(
fee_payer: TnPubkey,
token_program: TnPubkey,
token_account: TnPubkey,
mint_account: TnPubkey,
authority: TnPubkey,
amount: u64,
fee: u64,
nonce: u64,
start_slot: u64,
) -> Result<Transaction> {
let base_tx = Transaction::new(fee_payer, token_program, fee, nonce)
.with_start_slot(start_slot)
.with_expiry_after(100)
.with_compute_units(300_000)
.with_state_units(10_000)
.with_memory_units(10_000);
let (tx_after_rw, rw_indices) =
add_sorted_rw_accounts(base_tx, &[token_account, mint_account]);
let token_account_idx = rw_indices[0];
let mint_account_idx = rw_indices[1];
let mut tx = tx_after_rw;
let authority_account_idx = if authority == fee_payer {
0u16
} else {
let base_ro_idx = 2 + rw_indices.len() as u16;
let (tx_after_ro, ro_indices) = add_sorted_ro_accounts(tx, base_ro_idx, &[authority]);
tx = tx_after_ro;
ro_indices[0]
};
let instruction_data = build_token_burn_instruction(
token_account_idx,
mint_account_idx,
authority_account_idx,
amount,
)?;
Ok(tx.with_instructions(instruction_data))
}
pub fn build_token_freeze_account(
fee_payer: TnPubkey,
token_program: TnPubkey,
token_account: TnPubkey,
mint_account: TnPubkey,
authority: TnPubkey,
fee: u64,
nonce: u64,
start_slot: u64,
) -> Result<Transaction> {
let base_tx = Transaction::new(fee_payer, token_program, fee, nonce)
.with_start_slot(start_slot)
.with_expiry_after(100)
.with_compute_units(300_000)
.with_state_units(10_000)
.with_memory_units(10_000);
let (tx_after_rw, rw_indices) =
add_sorted_rw_accounts(base_tx, &[token_account, mint_account]);
let token_account_idx = rw_indices[0];
let mint_account_idx = rw_indices[1];
let mut tx = tx_after_rw;
let authority_account_idx = if authority == fee_payer {
0u16
} else {
let base_ro_idx = 2 + rw_indices.len() as u16;
let (tx_after_ro, ro_indices) = add_sorted_ro_accounts(tx, base_ro_idx, &[authority]);
tx = tx_after_ro;
ro_indices[0]
};
let instruction_data = build_token_freeze_account_instruction(
token_account_idx,
mint_account_idx,
authority_account_idx,
)?;
Ok(tx.with_instructions(instruction_data))
}
pub fn build_token_thaw_account(
fee_payer: TnPubkey,
token_program: TnPubkey,
token_account: TnPubkey,
mint_account: TnPubkey,
authority: TnPubkey,
fee: u64,
nonce: u64,
start_slot: u64,
) -> Result<Transaction> {
let base_tx = Transaction::new(fee_payer, token_program, fee, nonce)
.with_start_slot(start_slot)
.with_expiry_after(100)
.with_compute_units(300_000)
.with_state_units(10_000)
.with_memory_units(10_000);
let (tx_after_rw, rw_indices) =
add_sorted_rw_accounts(base_tx, &[token_account, mint_account]);
let token_account_idx = rw_indices[0];
let mint_account_idx = rw_indices[1];
let mut tx = tx_after_rw;
let authority_account_idx = if authority == fee_payer {
0u16
} else {
let base_ro_idx = 2 + rw_indices.len() as u16;
let (tx_after_ro, ro_indices) = add_sorted_ro_accounts(tx, base_ro_idx, &[authority]);
tx = tx_after_ro;
ro_indices[0]
};
let instruction_data = build_token_thaw_account_instruction(
token_account_idx,
mint_account_idx,
authority_account_idx,
)?;
Ok(tx.with_instructions(instruction_data))
}
pub fn build_token_close_account(
fee_payer: TnPubkey,
token_program: TnPubkey,
token_account: TnPubkey,
destination: TnPubkey,
authority: TnPubkey,
fee: u64,
nonce: u64,
start_slot: u64,
) -> Result<Transaction> {
let base_tx = Transaction::new(fee_payer, token_program, fee, nonce)
.with_start_slot(start_slot)
.with_expiry_after(100)
.with_compute_units(300_000)
.with_state_units(10_000)
.with_memory_units(10_000);
let mut rw_accounts = vec![token_account];
let destination_in_accounts = destination != fee_payer;
if destination_in_accounts {
rw_accounts.push(destination);
}
let (tx_after_rw, rw_indices) = add_sorted_rw_accounts(base_tx, &rw_accounts);
let token_account_idx = rw_indices[0];
let destination_idx = if destination_in_accounts {
rw_indices[1]
} else {
0u16
};
let mut tx = tx_after_rw;
let authority_account_idx = if authority == fee_payer {
0u16
} else {
let base_ro_idx = 2 + rw_indices.len() as u16;
let (tx_after_ro, ro_indices) = add_sorted_ro_accounts(tx, base_ro_idx, &[authority]);
tx = tx_after_ro;
ro_indices[0]
};
let instruction_data = build_token_close_account_instruction(
token_account_idx,
destination_idx,
authority_account_idx,
)?;
Ok(tx.with_instructions(instruction_data))
}
pub fn build_wthru_initialize_mint(
fee_payer: TnPubkey,
wthru_program: TnPubkey,
token_program: TnPubkey,
mint_account: TnPubkey,
vault_account: TnPubkey,
decimals: u8,
mint_seed: [u8; 32],
mint_proof: Vec<u8>,
vault_proof: Vec<u8>,
fee: u64,
nonce: u64,
start_slot: u64,
) -> Result<Transaction> {
let mut tx = Transaction::new(fee_payer, wthru_program, fee, nonce)
.with_start_slot(start_slot)
.with_expiry_after(100)
.with_compute_units(500_000)
.with_state_units(10_000)
.with_memory_units(10_000);
let accounts = [
(mint_account, true),
(vault_account, true),
(token_program, false),
];
let (tx_with_accounts, indices) = add_sorted_accounts(tx, &accounts);
tx = tx_with_accounts;
let mint_account_idx = indices[0];
let vault_account_idx = indices[1];
let token_program_idx = indices[2];
let instruction_data = build_wthru_initialize_mint_instruction(
token_program_idx,
mint_account_idx,
vault_account_idx,
decimals,
mint_seed,
mint_proof,
vault_proof,
)?;
Ok(tx.with_instructions(instruction_data))
}
pub fn build_wthru_deposit(
fee_payer: TnPubkey,
wthru_program: TnPubkey,
token_program: TnPubkey,
mint_account: TnPubkey,
vault_account: TnPubkey,
dest_token_account: TnPubkey,
fee: u64,
nonce: u64,
start_slot: u64,
) -> Result<Transaction> {
let mut tx = Transaction::new(fee_payer, wthru_program, fee, nonce)
.with_start_slot(start_slot)
.with_expiry_after(100)
.with_compute_units(400_000)
.with_state_units(10_000)
.with_memory_units(10_000);
let accounts = [
(mint_account, true),
(vault_account, true),
(dest_token_account, true),
(token_program, false),
];
let (tx_with_accounts, indices) = add_sorted_accounts(tx, &accounts);
tx = tx_with_accounts;
let mint_account_idx = indices[0];
let vault_account_idx = indices[1];
let dest_account_idx = indices[2];
let token_program_idx = indices[3];
let instruction_data = build_wthru_deposit_instruction(
token_program_idx,
vault_account_idx,
mint_account_idx,
dest_account_idx,
)?;
Ok(tx.with_instructions(instruction_data))
}
pub fn build_wthru_withdraw(
fee_payer: TnPubkey,
wthru_program: TnPubkey,
token_program: TnPubkey,
mint_account: TnPubkey,
vault_account: TnPubkey,
wthru_token_account: TnPubkey,
recipient_account: TnPubkey,
amount: u64,
fee: u64,
nonce: u64,
start_slot: u64,
) -> Result<Transaction> {
let mut tx = Transaction::new(fee_payer, wthru_program, fee, nonce)
.with_start_slot(start_slot)
.with_expiry_after(100)
.with_compute_units(400_000)
.with_state_units(10_000)
.with_memory_units(10_000);
let accounts = [
(mint_account, true),
(vault_account, true),
(wthru_token_account, true),
(recipient_account, true),
(token_program, false),
];
let (tx_with_accounts, indices) = add_sorted_accounts(tx, &accounts);
tx = tx_with_accounts;
let mint_account_idx = indices[0];
let vault_account_idx = indices[1];
let token_account_idx = indices[2];
let recipient_account_idx = indices[3];
let token_program_idx = indices[4];
let owner_account_idx = 0u16;
let instruction_data = build_wthru_withdraw_instruction(
token_program_idx,
vault_account_idx,
mint_account_idx,
token_account_idx,
owner_account_idx,
recipient_account_idx,
amount,
)?;
Ok(tx.with_instructions(instruction_data))
}
pub fn build_faucet_deposit(
fee_payer: TnPubkey,
faucet_program: TnPubkey,
faucet_account: TnPubkey,
depositor_account: TnPubkey,
eoa_program: TnPubkey,
amount: u64,
fee: u64,
nonce: u64,
start_slot: u64,
) -> Result<Transaction> {
let tx = Transaction::new(fee_payer, faucet_program, fee, nonce)
.with_start_slot(start_slot)
.with_expiry_after(100)
.with_compute_units(300_000)
.with_state_units(10_000)
.with_memory_units(10_000);
let (tx, depositor_account_idx) = Self::ensure_rw_account(tx, depositor_account);
let (tx, faucet_account_idx) = Self::ensure_rw_account(tx, faucet_account);
let (tx, eoa_program_idx) = Self::ensure_ro_account(tx, eoa_program);
let instruction_data = build_faucet_deposit_instruction(
faucet_account_idx,
depositor_account_idx,
eoa_program_idx,
amount,
)?;
Ok(tx.with_instructions(instruction_data))
}
fn resolve_account_index(tx: &Transaction, target: &TnPubkey) -> Option<u16> {
if *target == tx.fee_payer {
return Some(0u16);
}
if *target == tx.program {
return Some(1u16);
}
if let Some(ref rw) = tx.rw_accs {
if let Some(pos) = rw.iter().position(|acc| acc == target) {
return Some(2u16 + pos as u16);
}
}
if let Some(ref ro) = tx.r_accs {
let base = 2u16 + tx.rw_accs.as_ref().map_or(0u16, |v| v.len() as u16);
if let Some(pos) = ro.iter().position(|acc| acc == target) {
return Some(base + pos as u16);
}
}
None
}
fn ensure_rw_account(mut tx: Transaction, account: TnPubkey) -> (Transaction, u16) {
if let Some(idx) = Self::resolve_account_index(&tx, &account) {
return (tx, idx);
}
tx = tx.add_rw_account(account);
let idx = Self::resolve_account_index(&tx, &account)
.expect("read-write account index should exist after insertion");
(tx, idx)
}
fn ensure_ro_account(mut tx: Transaction, account: TnPubkey) -> (Transaction, u16) {
if let Some(idx) = Self::resolve_account_index(&tx, &account) {
return (tx, idx);
}
tx = tx.add_r_account(account);
let idx = Self::resolve_account_index(&tx, &account)
.expect("read-only account index should exist after insertion");
(tx, idx)
}
pub fn build_faucet_withdraw(
fee_payer: TnPubkey,
faucet_program: TnPubkey,
faucet_account: TnPubkey,
recipient_account: TnPubkey,
amount: u64,
fee: u64,
nonce: u64,
start_slot: u64,
) -> Result<Transaction> {
let mut tx = Transaction::new(fee_payer, faucet_program, fee, nonce)
.with_start_slot(start_slot)
.with_expiry_after(100)
.with_compute_units(300_000)
.with_state_units(10_000)
.with_memory_units(10_000);
let faucet_is_fee_payer = faucet_account == fee_payer;
let recipient_is_fee_payer = recipient_account == fee_payer;
let recipient_is_faucet = recipient_account == faucet_account;
let (faucet_account_idx, recipient_account_idx) =
if faucet_is_fee_payer && recipient_is_fee_payer {
(0u16, 0u16)
} else if faucet_is_fee_payer {
tx = tx.add_rw_account(recipient_account);
(0u16, 2u16)
} else if recipient_is_fee_payer {
tx = tx.add_rw_account(faucet_account);
(2u16, 0u16)
} else if recipient_is_faucet {
tx = tx.add_rw_account(faucet_account);
(2u16, 2u16)
} else {
if faucet_account < recipient_account {
tx = tx.add_rw_account(faucet_account);
tx = tx.add_rw_account(recipient_account);
(2u16, 3u16)
} else {
tx = tx.add_rw_account(recipient_account);
tx = tx.add_rw_account(faucet_account);
(3u16, 2u16)
}
};
let instruction_data =
build_faucet_withdraw_instruction(faucet_account_idx, recipient_account_idx, amount)?;
Ok(tx.with_instructions(instruction_data))
}
pub fn build_activate(
fee_payer: TnPubkey,
source_token_account: TnPubkey,
bls_pk: [u8; 96],
claim_authority: TnPubkey,
token_amount: u64,
fee: u64,
nonce: u64,
start_slot: u64,
) -> Result<Transaction> {
Self::build_activate_with_accounts(
fee_payer,
ConsensusValidatorAccounts::default(),
source_token_account,
bls_pk,
claim_authority,
token_amount,
fee,
nonce,
start_slot,
)
}
pub fn build_activate_with_accounts(
fee_payer: TnPubkey,
accounts: ConsensusValidatorAccounts,
source_token_account: TnPubkey,
bls_pk: [u8; 96],
claim_authority: TnPubkey,
token_amount: u64,
fee: u64,
nonce: u64,
start_slot: u64,
) -> Result<Transaction> {
let tx = build_consensus_validator_tx(fee_payer, accounts.program, fee, nonce, start_slot);
let accounts = [
(accounts.attestor_table, true),
(source_token_account, true),
(accounts.converted_vault, true),
(accounts.token_program, false),
];
let (tx, indices) = add_sorted_accounts(tx, &accounts);
let attestor_table_idx = indices[0];
let source_token_account_idx = indices[1];
let converted_vault_idx = indices[2];
let token_program_idx = indices[3];
let identity_idx = 0u16;
let instruction_data = build_activate_instruction(
attestor_table_idx as u64,
token_program_idx,
source_token_account_idx,
converted_vault_idx,
identity_idx,
bls_pk,
claim_authority,
token_amount,
)?;
Ok(tx.with_instructions(instruction_data))
}
pub fn build_deactivate(
fee_payer: TnPubkey,
dest_token_account: TnPubkey,
fee: u64,
nonce: u64,
start_slot: u64,
) -> Result<Transaction> {
Self::build_deactivate_with_accounts(
fee_payer,
ConsensusValidatorAccounts::default(),
dest_token_account,
fee,
nonce,
start_slot,
)
}
pub fn build_deactivate_with_accounts(
fee_payer: TnPubkey,
accounts: ConsensusValidatorAccounts,
dest_token_account: TnPubkey,
fee: u64,
nonce: u64,
start_slot: u64,
) -> Result<Transaction> {
let tx = build_consensus_validator_tx(fee_payer, accounts.program, fee, nonce, start_slot);
let tx_accounts = [
(accounts.attestor_table, true),
(accounts.unclaimed_vault, true),
(dest_token_account, true),
(accounts.token_program, false),
];
let (tx, indices) = add_sorted_accounts(tx, &tx_accounts);
let attestor_table_idx = indices[0];
let unclaimed_vault_idx = indices[1];
let dest_token_account_idx = indices[2];
let token_program_idx = indices[3];
let instruction_data = build_deactivate_instruction(
attestor_table_idx as u64,
token_program_idx,
unclaimed_vault_idx,
dest_token_account_idx,
0u16,
)?;
Ok(tx.with_instructions(instruction_data))
}
pub fn build_convert_tokens(
fee_payer: TnPubkey,
source_token_account: TnPubkey,
token_amount: u64,
fee: u64,
nonce: u64,
start_slot: u64,
) -> Result<Transaction> {
Self::build_convert_tokens_with_accounts(
fee_payer,
ConsensusValidatorAccounts::default(),
source_token_account,
token_amount,
fee,
nonce,
start_slot,
)
}
pub fn build_convert_tokens_with_accounts(
fee_payer: TnPubkey,
accounts: ConsensusValidatorAccounts,
source_token_account: TnPubkey,
token_amount: u64,
fee: u64,
nonce: u64,
start_slot: u64,
) -> Result<Transaction> {
let tx = build_consensus_validator_tx(fee_payer, accounts.program, fee, nonce, start_slot);
let tx_accounts = [
(accounts.attestor_table, true),
(source_token_account, true),
(accounts.converted_vault, true),
(accounts.token_program, false),
];
let (tx, indices) = add_sorted_accounts(tx, &tx_accounts);
let attestor_table_idx = indices[0];
let source_token_account_idx = indices[1];
let converted_vault_idx = indices[2];
let token_program_idx = indices[3];
let instruction_data = build_convert_tokens_instruction(
attestor_table_idx as u64,
token_program_idx,
source_token_account_idx,
converted_vault_idx,
0u16,
token_amount,
)?;
Ok(tx.with_instructions(instruction_data))
}
pub fn build_claim(
fee_payer: TnPubkey,
subject_attestor: TnPubkey,
dest_token_account: TnPubkey,
fee: u64,
nonce: u64,
start_slot: u64,
) -> Result<Transaction> {
Self::build_claim_with_accounts(
fee_payer,
ConsensusValidatorAccounts::default(),
subject_attestor,
dest_token_account,
fee,
nonce,
start_slot,
)
}
pub fn build_claim_with_accounts(
fee_payer: TnPubkey,
accounts: ConsensusValidatorAccounts,
subject_attestor: TnPubkey,
dest_token_account: TnPubkey,
fee: u64,
nonce: u64,
start_slot: u64,
) -> Result<Transaction> {
let tx = build_consensus_validator_tx(fee_payer, accounts.program, fee, nonce, start_slot);
let tx_accounts = [
(accounts.attestor_table, true),
(accounts.unclaimed_vault, true),
(dest_token_account, true),
(accounts.token_program, false),
];
let (tx, indices) = add_sorted_accounts(tx, &tx_accounts);
let attestor_table_idx = indices[0];
let unclaimed_vault_idx = indices[1];
let dest_token_account_idx = indices[2];
let token_program_idx = indices[3];
let instruction_data = build_claim_instruction(
attestor_table_idx as u64,
token_program_idx,
unclaimed_vault_idx,
dest_token_account_idx,
0u16,
subject_attestor,
)?;
Ok(tx.with_instructions(instruction_data))
}
pub fn build_set_claim_authority(
fee_payer: TnPubkey,
subject_attestor: TnPubkey,
new_claim_authority: TnPubkey,
fee: u64,
nonce: u64,
start_slot: u64,
) -> Result<Transaction> {
Self::build_set_claim_authority_with_accounts(
fee_payer,
ConsensusValidatorAccounts::default(),
subject_attestor,
new_claim_authority,
fee,
nonce,
start_slot,
)
}
pub fn build_set_claim_authority_with_accounts(
fee_payer: TnPubkey,
accounts: ConsensusValidatorAccounts,
subject_attestor: TnPubkey,
new_claim_authority: TnPubkey,
fee: u64,
nonce: u64,
start_slot: u64,
) -> Result<Transaction> {
let tx = build_consensus_validator_tx(fee_payer, accounts.program, fee, nonce, start_slot);
let tx_accounts = [(accounts.attestor_table, true)];
let (tx, indices) = add_sorted_accounts(tx, &tx_accounts);
let attestor_table_idx = indices[0];
let instruction_data = build_set_claim_authority_instruction(
attestor_table_idx as u64,
0u16,
subject_attestor,
new_claim_authority,
)?;
Ok(tx.with_instructions(instruction_data))
}
pub fn build_name_service_initialize_root(
fee_payer: TnPubkey,
name_service_program: TnPubkey,
registrar_account: TnPubkey,
authority_account: TnPubkey,
root_name: &str,
state_proof: Vec<u8>,
fee: u64,
nonce: u64,
start_slot: u64,
) -> Result<Transaction> {
let mut tx = Transaction::new(fee_payer, name_service_program, fee, nonce)
.with_start_slot(start_slot)
.with_expiry_after(100)
.with_compute_units(500_000)
.with_state_units(10_000)
.with_memory_units(10_000);
let accounts = [(registrar_account, true), (authority_account, false)];
let (tx_with_accounts, indices) = add_sorted_accounts(tx, &accounts);
tx = tx_with_accounts;
let registrar_account_idx = indices[0];
let authority_account_idx = indices[1];
let instruction_data = build_name_service_initialize_root_instruction(
registrar_account_idx,
authority_account_idx,
root_name,
state_proof,
)?;
Ok(tx.with_instructions(instruction_data))
}
pub fn build_name_service_register_subdomain(
fee_payer: TnPubkey,
name_service_program: TnPubkey,
domain_account: TnPubkey,
parent_account: TnPubkey,
owner_account: TnPubkey,
authority_account: TnPubkey,
domain_name: &str,
state_proof: Vec<u8>,
fee: u64,
nonce: u64,
start_slot: u64,
) -> Result<Transaction> {
let mut tx = Transaction::new(fee_payer, name_service_program, fee, nonce)
.with_start_slot(start_slot)
.with_expiry_after(100)
.with_compute_units(500_000)
.with_state_units(10_000)
.with_memory_units(10_000);
let accounts = [
(domain_account, true),
(parent_account, true),
(owner_account, false),
(authority_account, false),
];
let (tx_with_accounts, indices) = add_sorted_accounts(tx, &accounts);
tx = tx_with_accounts;
let domain_account_idx = indices[0];
let parent_account_idx = indices[1];
let owner_account_idx = indices[2];
let authority_account_idx = indices[3];
let instruction_data = build_name_service_register_subdomain_instruction(
domain_account_idx,
parent_account_idx,
owner_account_idx,
authority_account_idx,
domain_name,
state_proof,
)?;
Ok(tx.with_instructions(instruction_data))
}
pub fn build_name_service_append_record(
fee_payer: TnPubkey,
name_service_program: TnPubkey,
domain_account: TnPubkey,
owner_account: TnPubkey,
key: &[u8],
value: &[u8],
fee: u64,
nonce: u64,
start_slot: u64,
) -> Result<Transaction> {
let mut tx = Transaction::new(fee_payer, name_service_program, fee, nonce)
.with_start_slot(start_slot)
.with_expiry_after(100)
.with_compute_units(250_000)
.with_state_units(10_000)
.with_memory_units(10_000);
let accounts = [(domain_account, true), (owner_account, false)];
let (tx_with_accounts, indices) = add_sorted_accounts(tx, &accounts);
tx = tx_with_accounts;
let domain_account_idx = indices[0];
let owner_account_idx = indices[1];
let instruction_data = build_name_service_append_record_instruction(
domain_account_idx,
owner_account_idx,
key,
value,
)?;
Ok(tx.with_instructions(instruction_data))
}
pub fn build_name_service_delete_record(
fee_payer: TnPubkey,
name_service_program: TnPubkey,
domain_account: TnPubkey,
owner_account: TnPubkey,
key: &[u8],
fee: u64,
nonce: u64,
start_slot: u64,
) -> Result<Transaction> {
let mut tx = Transaction::new(fee_payer, name_service_program, fee, nonce)
.with_start_slot(start_slot)
.with_expiry_after(100)
.with_compute_units(200_000)
.with_state_units(10_000)
.with_memory_units(10_000);
let accounts = [(domain_account, true), (owner_account, false)];
let (tx_with_accounts, indices) = add_sorted_accounts(tx, &accounts);
tx = tx_with_accounts;
let domain_account_idx = indices[0];
let owner_account_idx = indices[1];
let instruction_data = build_name_service_delete_record_instruction(
domain_account_idx,
owner_account_idx,
key,
)?;
Ok(tx.with_instructions(instruction_data))
}
pub fn build_name_service_unregister_subdomain(
fee_payer: TnPubkey,
name_service_program: TnPubkey,
domain_account: TnPubkey,
owner_account: TnPubkey,
fee: u64,
nonce: u64,
start_slot: u64,
) -> Result<Transaction> {
let mut tx = Transaction::new(fee_payer, name_service_program, fee, nonce)
.with_start_slot(start_slot)
.with_expiry_after(100)
.with_compute_units(200_000)
.with_state_units(10_000)
.with_memory_units(10_000);
let accounts = [(domain_account, true), (owner_account, false)];
let (tx_with_accounts, indices) = add_sorted_accounts(tx, &accounts);
tx = tx_with_accounts;
let domain_account_idx = indices[0];
let owner_account_idx = indices[1];
let instruction_data = build_name_service_unregister_subdomain_instruction(
domain_account_idx,
owner_account_idx,
)?;
Ok(tx.with_instructions(instruction_data))
}
pub fn build_thru_registrar_initialize_registry(
fee_payer: TnPubkey,
thru_registrar_program: TnPubkey,
config_account: TnPubkey,
name_service_program: TnPubkey,
root_registrar_account: TnPubkey,
treasurer_account: TnPubkey,
token_mint_account: TnPubkey,
token_program: TnPubkey,
root_domain_name: &str,
price_per_year: u64,
config_proof: Vec<u8>,
registrar_proof: Vec<u8>,
fee: u64,
nonce: u64,
start_slot: u64,
) -> Result<Transaction> {
let mut tx = Transaction::new(fee_payer, thru_registrar_program, fee, nonce)
.with_start_slot(start_slot)
.with_expiry_after(100)
.with_compute_units(500_000)
.with_state_units(10_000)
.with_memory_units(10_000);
let mut rw_accounts = vec![config_account, root_registrar_account];
rw_accounts.sort();
let mut ro_accounts = vec![
name_service_program,
treasurer_account,
token_mint_account,
token_program,
];
ro_accounts.sort();
let mut config_account_idx = 0u16;
let mut root_registrar_account_idx = 0u16;
for (i, account) in rw_accounts.iter().enumerate() {
let idx = (2 + i) as u16;
if *account == config_account {
config_account_idx = idx;
} else if *account == root_registrar_account {
root_registrar_account_idx = idx;
}
tx = tx.add_rw_account(*account);
}
let base_ro_idx = 2 + rw_accounts.len() as u16;
let mut name_service_program_idx = 0u16;
let mut treasurer_account_idx = 0u16;
let mut token_mint_account_idx = 0u16;
let mut token_program_idx = 0u16;
for (i, account) in ro_accounts.iter().enumerate() {
let idx = base_ro_idx + i as u16;
if *account == name_service_program {
name_service_program_idx = idx;
} else if *account == root_registrar_account {
root_registrar_account_idx = idx;
} else if *account == treasurer_account {
treasurer_account_idx = idx;
} else if *account == token_mint_account {
token_mint_account_idx = idx;
} else if *account == token_program {
token_program_idx = idx;
}
tx = tx.add_r_account(*account);
}
let instruction_data = build_thru_registrar_initialize_registry_instruction(
config_account_idx,
name_service_program_idx,
root_registrar_account_idx,
treasurer_account_idx,
token_mint_account_idx,
token_program_idx,
root_domain_name,
price_per_year,
config_proof,
registrar_proof,
)?;
Ok(tx.with_instructions(instruction_data))
}
pub fn build_thru_registrar_purchase_domain(
fee_payer: TnPubkey,
thru_registrar_program: TnPubkey,
config_account: TnPubkey,
lease_account: TnPubkey,
domain_account: TnPubkey,
name_service_program: TnPubkey,
root_registrar_account: TnPubkey,
treasurer_account: TnPubkey,
payer_token_account: TnPubkey,
token_mint_account: TnPubkey,
token_program: TnPubkey,
domain_name: &str,
years: u8,
lease_proof: Vec<u8>,
domain_proof: Vec<u8>,
fee: u64,
nonce: u64,
start_slot: u64,
) -> Result<Transaction> {
let mut tx = Transaction::new(fee_payer, thru_registrar_program, fee, nonce)
.with_start_slot(start_slot)
.with_expiry_after(100)
.with_compute_units(500_000)
.with_state_units(10_000)
.with_memory_units(10_000);
let mut rw_accounts = vec![
config_account,
lease_account,
domain_account,
treasurer_account,
payer_token_account,
root_registrar_account,
];
rw_accounts.sort();
let mut ro_accounts = vec![name_service_program, token_mint_account, token_program];
ro_accounts.sort();
let mut config_account_idx = 0u16;
let mut lease_account_idx = 0u16;
let mut domain_account_idx = 0u16;
let mut treasurer_account_idx = 0u16;
let mut payer_token_account_idx = 0u16;
let mut root_registrar_account_idx = 0u16;
for (i, account) in rw_accounts.iter().enumerate() {
let idx = (2 + i) as u16;
if *account == config_account {
config_account_idx = idx;
} else if *account == lease_account {
lease_account_idx = idx;
} else if *account == domain_account {
domain_account_idx = idx;
} else if *account == treasurer_account {
treasurer_account_idx = idx;
} else if *account == payer_token_account {
payer_token_account_idx = idx;
} else if *account == root_registrar_account {
root_registrar_account_idx = idx;
}
tx = tx.add_rw_account(*account);
}
let base_ro_idx = 2 + rw_accounts.len() as u16;
let mut name_service_program_idx = 0u16;
let mut token_mint_account_idx = 0u16;
let mut token_program_idx = 0u16;
for (i, account) in ro_accounts.iter().enumerate() {
let idx = base_ro_idx + i as u16;
if *account == config_account {
config_account_idx = idx; } else if *account == name_service_program {
name_service_program_idx = idx;
} else if *account == root_registrar_account {
root_registrar_account_idx = idx;
} else if *account == treasurer_account {
treasurer_account_idx = idx;
} else if *account == payer_token_account {
payer_token_account_idx = idx;
} else if *account == token_mint_account {
token_mint_account_idx = idx;
} else if *account == token_program {
token_program_idx = idx;
}
tx = tx.add_r_account(*account);
}
let instruction_data = build_thru_registrar_purchase_domain_instruction(
config_account_idx,
lease_account_idx,
domain_account_idx,
name_service_program_idx,
root_registrar_account_idx,
treasurer_account_idx,
payer_token_account_idx,
token_mint_account_idx,
token_program_idx,
domain_name,
years,
lease_proof,
domain_proof,
)?;
Ok(tx.with_instructions(instruction_data))
}
pub fn build_thru_registrar_renew_lease(
fee_payer: TnPubkey,
thru_registrar_program: TnPubkey,
config_account: TnPubkey,
lease_account: TnPubkey,
treasurer_account: TnPubkey,
payer_token_account: TnPubkey,
token_mint_account: TnPubkey,
token_program: TnPubkey,
years: u8,
fee: u64,
nonce: u64,
start_slot: u64,
) -> Result<Transaction> {
let mut tx = Transaction::new(fee_payer, thru_registrar_program, fee, nonce)
.with_start_slot(start_slot)
.with_expiry_after(100)
.with_compute_units(300_000)
.with_state_units(10_000)
.with_memory_units(10_000);
let mut rw_accounts = vec![lease_account, treasurer_account, payer_token_account];
rw_accounts.sort();
let mut ro_accounts = vec![config_account, token_mint_account, token_program];
ro_accounts.sort();
let mut lease_account_idx = 0u16;
let mut treasurer_account_idx = 0u16;
let mut payer_token_account_idx = 0u16;
for (i, account) in rw_accounts.iter().enumerate() {
let idx = (2 + i) as u16;
if *account == lease_account {
lease_account_idx = idx;
} else if *account == treasurer_account {
treasurer_account_idx = idx;
} else if *account == payer_token_account {
payer_token_account_idx = idx;
}
tx = tx.add_rw_account(*account);
}
let base_ro_idx = 2 + rw_accounts.len() as u16;
let mut config_account_idx = 0u16;
let mut token_mint_account_idx = 0u16;
let mut token_program_idx = 0u16;
for (i, account) in ro_accounts.iter().enumerate() {
let idx = base_ro_idx + i as u16;
if *account == config_account {
config_account_idx = idx;
} else if *account == token_mint_account {
token_mint_account_idx = idx;
} else if *account == token_program {
token_program_idx = idx;
}
tx = tx.add_r_account(*account);
}
let instruction_data = build_thru_registrar_renew_lease_instruction(
config_account_idx,
lease_account_idx,
treasurer_account_idx,
payer_token_account_idx,
token_mint_account_idx,
token_program_idx,
years,
)?;
Ok(tx.with_instructions(instruction_data))
}
pub fn build_thru_registrar_claim_expired_domain(
fee_payer: TnPubkey,
thru_registrar_program: TnPubkey,
config_account: TnPubkey,
lease_account: TnPubkey,
treasurer_account: TnPubkey,
payer_token_account: TnPubkey,
token_mint_account: TnPubkey,
token_program: TnPubkey,
years: u8,
fee: u64,
nonce: u64,
start_slot: u64,
) -> Result<Transaction> {
let mut tx = Transaction::new(fee_payer, thru_registrar_program, fee, nonce)
.with_start_slot(start_slot)
.with_expiry_after(100)
.with_compute_units(300_000)
.with_state_units(10_000)
.with_memory_units(10_000);
let mut rw_accounts = vec![lease_account, treasurer_account, payer_token_account];
rw_accounts.sort();
let mut ro_accounts = vec![config_account, token_mint_account, token_program];
ro_accounts.sort();
let mut lease_account_idx = 0u16;
let mut treasurer_account_idx = 0u16;
let mut payer_token_account_idx = 0u16;
for (i, account) in rw_accounts.iter().enumerate() {
let idx = (2 + i) as u16;
if *account == lease_account {
lease_account_idx = idx;
} else if *account == treasurer_account {
treasurer_account_idx = idx;
} else if *account == payer_token_account {
payer_token_account_idx = idx;
}
tx = tx.add_rw_account(*account);
}
let base_ro_idx = 2 + rw_accounts.len() as u16;
let mut config_account_idx = 0u16;
let mut token_mint_account_idx = 0u16;
let mut token_program_idx = 0u16;
for (i, account) in ro_accounts.iter().enumerate() {
let idx = base_ro_idx + i as u16;
if *account == config_account {
config_account_idx = idx;
} else if *account == token_mint_account {
token_mint_account_idx = idx;
} else if *account == token_program {
token_program_idx = idx;
}
tx = tx.add_r_account(*account);
}
let instruction_data = build_thru_registrar_claim_expired_domain_instruction(
config_account_idx,
lease_account_idx,
treasurer_account_idx,
payer_token_account_idx,
token_mint_account_idx,
token_program_idx,
years,
)?;
Ok(tx.with_instructions(instruction_data))
}
}
fn build_token_initialize_mint_instruction(
mint_account_idx: u16,
decimals: u8,
creator: TnPubkey,
mint_authority: TnPubkey,
freeze_authority: Option<TnPubkey>,
ticker: &str,
seed: [u8; 32],
state_proof: Vec<u8>,
) -> Result<Vec<u8>> {
let mut instruction_data = Vec::new();
instruction_data.push(TOKEN_INSTRUCTION_INITIALIZE_MINT);
instruction_data.extend_from_slice(&mint_account_idx.to_le_bytes());
instruction_data.push(decimals);
instruction_data.extend_from_slice(&creator);
instruction_data.extend_from_slice(&mint_authority);
let (freeze_auth, has_freeze_auth) = match freeze_authority {
Some(auth) => (auth, 1u8),
None => ([0u8; 32], 0u8),
};
instruction_data.extend_from_slice(&freeze_auth);
instruction_data.push(has_freeze_auth);
let ticker_bytes = ticker.as_bytes();
if ticker_bytes.len() > 8 {
return Err(anyhow::anyhow!("Ticker must be 8 characters or less"));
}
instruction_data.push(ticker_bytes.len() as u8);
let mut ticker_padded = [0u8; 8];
ticker_padded[..ticker_bytes.len()].copy_from_slice(ticker_bytes);
instruction_data.extend_from_slice(&ticker_padded);
instruction_data.extend_from_slice(&seed);
instruction_data.extend_from_slice(&state_proof);
Ok(instruction_data)
}
fn build_token_initialize_account_instruction(
token_account_idx: u16,
mint_account_idx: u16,
owner_account_idx: u16,
seed: [u8; 32],
state_proof: Vec<u8>,
) -> Result<Vec<u8>> {
let mut instruction_data = Vec::new();
instruction_data.push(TOKEN_INSTRUCTION_INITIALIZE_ACCOUNT);
instruction_data.extend_from_slice(&token_account_idx.to_le_bytes());
instruction_data.extend_from_slice(&mint_account_idx.to_le_bytes());
instruction_data.extend_from_slice(&owner_account_idx.to_le_bytes());
instruction_data.extend_from_slice(&seed);
instruction_data.extend_from_slice(&state_proof);
Ok(instruction_data)
}
fn build_token_transfer_instruction(
source_account_idx: u16,
dest_account_idx: u16,
amount: u64,
) -> Result<Vec<u8>> {
let mut instruction_data = Vec::new();
instruction_data.push(TOKEN_INSTRUCTION_TRANSFER);
instruction_data.extend_from_slice(&source_account_idx.to_le_bytes());
instruction_data.extend_from_slice(&dest_account_idx.to_le_bytes());
instruction_data.extend_from_slice(&amount.to_le_bytes());
Ok(instruction_data)
}
fn build_token_mint_to_instruction(
mint_account_idx: u16,
dest_account_idx: u16,
authority_idx: u16,
amount: u64,
) -> Result<Vec<u8>> {
let mut instruction_data = Vec::new();
instruction_data.push(TOKEN_INSTRUCTION_MINT_TO);
instruction_data.extend_from_slice(&mint_account_idx.to_le_bytes());
instruction_data.extend_from_slice(&dest_account_idx.to_le_bytes());
instruction_data.extend_from_slice(&authority_idx.to_le_bytes());
instruction_data.extend_from_slice(&amount.to_le_bytes());
Ok(instruction_data)
}
fn build_token_burn_instruction(
token_account_idx: u16,
mint_account_idx: u16,
authority_idx: u16,
amount: u64,
) -> Result<Vec<u8>> {
let mut instruction_data = Vec::new();
instruction_data.push(TOKEN_INSTRUCTION_BURN);
instruction_data.extend_from_slice(&token_account_idx.to_le_bytes());
instruction_data.extend_from_slice(&mint_account_idx.to_le_bytes());
instruction_data.extend_from_slice(&authority_idx.to_le_bytes());
instruction_data.extend_from_slice(&amount.to_le_bytes());
Ok(instruction_data)
}
fn build_token_freeze_account_instruction(
token_account_idx: u16,
mint_account_idx: u16,
authority_idx: u16,
) -> Result<Vec<u8>> {
let mut instruction_data = Vec::new();
instruction_data.push(TOKEN_INSTRUCTION_FREEZE_ACCOUNT);
instruction_data.extend_from_slice(&token_account_idx.to_le_bytes());
instruction_data.extend_from_slice(&mint_account_idx.to_le_bytes());
instruction_data.extend_from_slice(&authority_idx.to_le_bytes());
Ok(instruction_data)
}
fn build_token_thaw_account_instruction(
token_account_idx: u16,
mint_account_idx: u16,
authority_idx: u16,
) -> Result<Vec<u8>> {
let mut instruction_data = Vec::new();
instruction_data.push(TOKEN_INSTRUCTION_THAW_ACCOUNT);
instruction_data.extend_from_slice(&token_account_idx.to_le_bytes());
instruction_data.extend_from_slice(&mint_account_idx.to_le_bytes());
instruction_data.extend_from_slice(&authority_idx.to_le_bytes());
Ok(instruction_data)
}
fn build_token_close_account_instruction(
token_account_idx: u16,
destination_idx: u16,
authority_idx: u16,
) -> Result<Vec<u8>> {
let mut instruction_data = Vec::new();
instruction_data.push(TOKEN_INSTRUCTION_CLOSE_ACCOUNT);
instruction_data.extend_from_slice(&token_account_idx.to_le_bytes());
instruction_data.extend_from_slice(&destination_idx.to_le_bytes());
instruction_data.extend_from_slice(&authority_idx.to_le_bytes());
Ok(instruction_data)
}
fn build_wthru_initialize_mint_instruction(
token_program_idx: u16,
mint_account_idx: u16,
vault_account_idx: u16,
decimals: u8,
mint_seed: [u8; 32],
mint_proof: Vec<u8>,
vault_proof: Vec<u8>,
) -> Result<Vec<u8>> {
let mint_proof_len =
u64::try_from(mint_proof.len()).map_err(|_| anyhow::anyhow!("mint proof too large"))?;
let vault_proof_len =
u64::try_from(vault_proof.len()).map_err(|_| anyhow::anyhow!("vault proof too large"))?;
let mut instruction_data = Vec::new();
instruction_data.extend_from_slice(&TN_WTHRU_INSTRUCTION_INITIALIZE_MINT.to_le_bytes());
instruction_data.extend_from_slice(&token_program_idx.to_le_bytes());
instruction_data.extend_from_slice(&mint_account_idx.to_le_bytes());
instruction_data.extend_from_slice(&vault_account_idx.to_le_bytes());
instruction_data.push(decimals);
instruction_data.extend_from_slice(&mint_seed);
instruction_data.extend_from_slice(&mint_proof_len.to_le_bytes());
instruction_data.extend_from_slice(&vault_proof_len.to_le_bytes());
instruction_data.extend_from_slice(&mint_proof);
instruction_data.extend_from_slice(&vault_proof);
Ok(instruction_data)
}
fn build_wthru_deposit_instruction(
token_program_idx: u16,
vault_account_idx: u16,
mint_account_idx: u16,
dest_account_idx: u16,
) -> Result<Vec<u8>> {
let mut instruction_data = Vec::new();
instruction_data.extend_from_slice(&TN_WTHRU_INSTRUCTION_DEPOSIT.to_le_bytes());
instruction_data.extend_from_slice(&token_program_idx.to_le_bytes());
instruction_data.extend_from_slice(&vault_account_idx.to_le_bytes());
instruction_data.extend_from_slice(&mint_account_idx.to_le_bytes());
instruction_data.extend_from_slice(&dest_account_idx.to_le_bytes());
Ok(instruction_data)
}
fn build_wthru_withdraw_instruction(
token_program_idx: u16,
vault_account_idx: u16,
mint_account_idx: u16,
wthru_token_account_idx: u16,
owner_account_idx: u16,
recipient_account_idx: u16,
amount: u64,
) -> Result<Vec<u8>> {
let mut instruction_data = Vec::new();
instruction_data.extend_from_slice(&TN_WTHRU_INSTRUCTION_WITHDRAW.to_le_bytes());
instruction_data.extend_from_slice(&token_program_idx.to_le_bytes());
instruction_data.extend_from_slice(&vault_account_idx.to_le_bytes());
instruction_data.extend_from_slice(&mint_account_idx.to_le_bytes());
instruction_data.extend_from_slice(&wthru_token_account_idx.to_le_bytes());
instruction_data.extend_from_slice(&owner_account_idx.to_le_bytes());
instruction_data.extend_from_slice(&recipient_account_idx.to_le_bytes());
instruction_data.extend_from_slice(&amount.to_le_bytes());
Ok(instruction_data)
}
fn build_faucet_deposit_instruction(
faucet_account_idx: u16,
depositor_account_idx: u16,
eoa_program_idx: u16,
amount: u64,
) -> Result<Vec<u8>> {
let mut instruction_data = Vec::new();
instruction_data.extend_from_slice(&0u32.to_le_bytes());
instruction_data.extend_from_slice(&faucet_account_idx.to_le_bytes());
instruction_data.extend_from_slice(&depositor_account_idx.to_le_bytes());
instruction_data.extend_from_slice(&eoa_program_idx.to_le_bytes());
instruction_data.extend_from_slice(&amount.to_le_bytes());
Ok(instruction_data)
}
fn build_faucet_withdraw_instruction(
faucet_account_idx: u16,
recipient_account_idx: u16,
amount: u64,
) -> Result<Vec<u8>> {
let mut instruction_data = Vec::new();
instruction_data.extend_from_slice(&1u32.to_le_bytes());
instruction_data.extend_from_slice(&faucet_account_idx.to_le_bytes());
instruction_data.extend_from_slice(&recipient_account_idx.to_le_bytes());
instruction_data.extend_from_slice(&amount.to_le_bytes());
Ok(instruction_data)
}
fn build_activate_instruction(
attestor_table_idx: u64,
token_program_idx: u16,
source_token_account_idx: u16,
converted_vault_idx: u16,
identity_idx: u16,
bls_pk: [u8; 96],
claim_authority: [u8; 32],
token_amount: u64,
) -> Result<Vec<u8>> {
let mut instruction_data = Vec::new();
instruction_data.extend_from_slice(&1u32.to_le_bytes());
instruction_data.extend_from_slice(&attestor_table_idx.to_le_bytes());
instruction_data.extend_from_slice(&token_program_idx.to_le_bytes());
instruction_data.extend_from_slice(&source_token_account_idx.to_le_bytes());
instruction_data.extend_from_slice(&converted_vault_idx.to_le_bytes());
instruction_data.extend_from_slice(&identity_idx.to_le_bytes());
instruction_data.extend_from_slice(&bls_pk);
instruction_data.extend_from_slice(&claim_authority);
instruction_data.extend_from_slice(&token_amount.to_le_bytes());
Ok(instruction_data)
}
fn build_deactivate_instruction(
attestor_table_idx: u64,
token_program_idx: u16,
unclaimed_vault_idx: u16,
dest_token_account_idx: u16,
identity_idx: u16,
) -> Result<Vec<u8>> {
let mut instruction_data = Vec::new();
instruction_data.extend_from_slice(&2u32.to_le_bytes());
instruction_data.extend_from_slice(&attestor_table_idx.to_le_bytes());
instruction_data.extend_from_slice(&token_program_idx.to_le_bytes());
instruction_data.extend_from_slice(&unclaimed_vault_idx.to_le_bytes());
instruction_data.extend_from_slice(&dest_token_account_idx.to_le_bytes());
instruction_data.extend_from_slice(&identity_idx.to_le_bytes());
Ok(instruction_data)
}
fn build_convert_tokens_instruction(
attestor_table_idx: u64,
token_program_idx: u16,
source_token_account_idx: u16,
converted_vault_idx: u16,
identity_idx: u16,
token_amount: u64,
) -> Result<Vec<u8>> {
let mut instruction_data = Vec::new();
instruction_data.extend_from_slice(&3u32.to_le_bytes());
instruction_data.extend_from_slice(&attestor_table_idx.to_le_bytes());
instruction_data.extend_from_slice(&token_program_idx.to_le_bytes());
instruction_data.extend_from_slice(&source_token_account_idx.to_le_bytes());
instruction_data.extend_from_slice(&converted_vault_idx.to_le_bytes());
instruction_data.extend_from_slice(&identity_idx.to_le_bytes());
instruction_data.extend_from_slice(&token_amount.to_le_bytes());
Ok(instruction_data)
}
fn build_claim_instruction(
attestor_table_idx: u64,
token_program_idx: u16,
unclaimed_vault_idx: u16,
dest_token_account_idx: u16,
claim_authority_idx: u16,
subject_attestor: TnPubkey,
) -> Result<Vec<u8>> {
let mut instruction_data = Vec::new();
instruction_data.extend_from_slice(&4u32.to_le_bytes());
instruction_data.extend_from_slice(&attestor_table_idx.to_le_bytes());
instruction_data.extend_from_slice(&token_program_idx.to_le_bytes());
instruction_data.extend_from_slice(&unclaimed_vault_idx.to_le_bytes());
instruction_data.extend_from_slice(&dest_token_account_idx.to_le_bytes());
instruction_data.extend_from_slice(&claim_authority_idx.to_le_bytes());
instruction_data.extend_from_slice(&subject_attestor);
Ok(instruction_data)
}
fn build_set_claim_authority_instruction(
attestor_table_idx: u64,
current_claim_auth_idx: u16,
subject_attestor: TnPubkey,
new_claim_authority: TnPubkey,
) -> Result<Vec<u8>> {
let mut instruction_data = Vec::new();
instruction_data.extend_from_slice(&5u32.to_le_bytes());
instruction_data.extend_from_slice(&attestor_table_idx.to_le_bytes());
instruction_data.extend_from_slice(¤t_claim_auth_idx.to_le_bytes());
instruction_data.extend_from_slice(&subject_attestor);
instruction_data.extend_from_slice(&new_claim_authority);
Ok(instruction_data)
}
#[repr(C, packed)]
struct NameServiceInitializeRootArgs {
registrar_account_idx: u16,
authority_account_idx: u16,
root_name: [u8; TN_NAME_SERVICE_MAX_DOMAIN_LENGTH],
root_name_length: u32,
}
#[repr(C, packed)]
struct NameServiceRegisterSubdomainArgs {
domain_account_idx: u16,
parent_account_idx: u16,
owner_account_idx: u16,
authority_account_idx: u16,
name: [u8; TN_NAME_SERVICE_MAX_DOMAIN_LENGTH],
name_length: u32,
}
#[repr(C, packed)]
struct NameServiceAppendRecordArgs {
domain_account_idx: u16,
owner_account_idx: u16,
key_length: u32,
key: [u8; TN_NAME_SERVICE_MAX_KEY_LENGTH],
value_length: u32,
value: [u8; TN_NAME_SERVICE_MAX_VALUE_LENGTH],
}
#[repr(C, packed)]
struct NameServiceDeleteRecordArgs {
domain_account_idx: u16,
owner_account_idx: u16,
key_length: u32,
key: [u8; TN_NAME_SERVICE_MAX_KEY_LENGTH],
}
#[repr(C, packed)]
struct NameServiceUnregisterSubdomainArgs {
domain_account_idx: u16,
owner_account_idx: u16,
}
fn build_name_service_initialize_root_instruction(
registrar_account_idx: u16,
authority_account_idx: u16,
root_name: &str,
state_proof: Vec<u8>,
) -> Result<Vec<u8>> {
let root_name_bytes = root_name.as_bytes();
if root_name_bytes.is_empty() || root_name_bytes.len() > TN_NAME_SERVICE_MAX_DOMAIN_LENGTH {
return Err(anyhow::anyhow!(
"Root name length must be between 1 and {}",
TN_NAME_SERVICE_MAX_DOMAIN_LENGTH
));
}
let mut args = NameServiceInitializeRootArgs {
registrar_account_idx,
authority_account_idx,
root_name: [0u8; TN_NAME_SERVICE_MAX_DOMAIN_LENGTH],
root_name_length: root_name_bytes.len() as u32,
};
args.root_name[..root_name_bytes.len()].copy_from_slice(root_name_bytes);
let mut instruction_data = Vec::new();
instruction_data.extend_from_slice(&TN_NAME_SERVICE_INSTRUCTION_INITIALIZE_ROOT.to_le_bytes());
let args_bytes = unsafe {
std::slice::from_raw_parts(
&args as *const _ as *const u8,
std::mem::size_of::<NameServiceInitializeRootArgs>(),
)
};
instruction_data.extend_from_slice(args_bytes);
instruction_data.extend_from_slice(&TN_NAME_SERVICE_PROOF_INLINE.to_le_bytes());
instruction_data.extend_from_slice(&state_proof);
Ok(instruction_data)
}
fn build_name_service_register_subdomain_instruction(
domain_account_idx: u16,
parent_account_idx: u16,
owner_account_idx: u16,
authority_account_idx: u16,
domain_name: &str,
state_proof: Vec<u8>,
) -> Result<Vec<u8>> {
let domain_bytes = domain_name.as_bytes();
if domain_bytes.is_empty() || domain_bytes.len() > TN_NAME_SERVICE_MAX_DOMAIN_LENGTH {
return Err(anyhow::anyhow!(
"Domain name length must be between 1 and {}",
TN_NAME_SERVICE_MAX_DOMAIN_LENGTH
));
}
let mut args = NameServiceRegisterSubdomainArgs {
domain_account_idx,
parent_account_idx,
owner_account_idx,
authority_account_idx,
name: [0u8; TN_NAME_SERVICE_MAX_DOMAIN_LENGTH],
name_length: domain_bytes.len() as u32,
};
args.name[..domain_bytes.len()].copy_from_slice(domain_bytes);
let mut instruction_data = Vec::new();
instruction_data
.extend_from_slice(&TN_NAME_SERVICE_INSTRUCTION_REGISTER_SUBDOMAIN.to_le_bytes());
let args_bytes = unsafe {
std::slice::from_raw_parts(
&args as *const _ as *const u8,
std::mem::size_of::<NameServiceRegisterSubdomainArgs>(),
)
};
instruction_data.extend_from_slice(args_bytes);
instruction_data.extend_from_slice(&TN_NAME_SERVICE_PROOF_INLINE.to_le_bytes());
instruction_data.extend_from_slice(&state_proof);
Ok(instruction_data)
}
fn build_name_service_append_record_instruction(
domain_account_idx: u16,
owner_account_idx: u16,
key: &[u8],
value: &[u8],
) -> Result<Vec<u8>> {
if key.is_empty() || key.len() > TN_NAME_SERVICE_MAX_KEY_LENGTH {
return Err(anyhow::anyhow!(
"Key length must be between 1 and {} bytes",
TN_NAME_SERVICE_MAX_KEY_LENGTH
));
}
if value.len() > TN_NAME_SERVICE_MAX_VALUE_LENGTH {
return Err(anyhow::anyhow!(
"Value length must be <= {} bytes",
TN_NAME_SERVICE_MAX_VALUE_LENGTH
));
}
let mut args = NameServiceAppendRecordArgs {
domain_account_idx,
owner_account_idx,
key_length: key.len() as u32,
key: [0u8; TN_NAME_SERVICE_MAX_KEY_LENGTH],
value_length: value.len() as u32,
value: [0u8; TN_NAME_SERVICE_MAX_VALUE_LENGTH],
};
args.key[..key.len()].copy_from_slice(key);
args.value[..value.len()].copy_from_slice(value);
let mut instruction_data = Vec::new();
instruction_data.extend_from_slice(&TN_NAME_SERVICE_INSTRUCTION_APPEND_RECORD.to_le_bytes());
let args_bytes = unsafe {
std::slice::from_raw_parts(
&args as *const _ as *const u8,
std::mem::size_of::<NameServiceAppendRecordArgs>(),
)
};
instruction_data.extend_from_slice(args_bytes);
Ok(instruction_data)
}
fn build_name_service_delete_record_instruction(
domain_account_idx: u16,
owner_account_idx: u16,
key: &[u8],
) -> Result<Vec<u8>> {
if key.is_empty() || key.len() > TN_NAME_SERVICE_MAX_KEY_LENGTH {
return Err(anyhow::anyhow!(
"Key length must be between 1 and {} bytes",
TN_NAME_SERVICE_MAX_KEY_LENGTH
));
}
let mut args = NameServiceDeleteRecordArgs {
domain_account_idx,
owner_account_idx,
key_length: key.len() as u32,
key: [0u8; TN_NAME_SERVICE_MAX_KEY_LENGTH],
};
args.key[..key.len()].copy_from_slice(key);
let mut instruction_data = Vec::new();
instruction_data.extend_from_slice(&TN_NAME_SERVICE_INSTRUCTION_DELETE_RECORD.to_le_bytes());
let args_bytes = unsafe {
std::slice::from_raw_parts(
&args as *const _ as *const u8,
std::mem::size_of::<NameServiceDeleteRecordArgs>(),
)
};
instruction_data.extend_from_slice(args_bytes);
Ok(instruction_data)
}
fn build_name_service_unregister_subdomain_instruction(
domain_account_idx: u16,
owner_account_idx: u16,
) -> Result<Vec<u8>> {
let args = NameServiceUnregisterSubdomainArgs {
domain_account_idx,
owner_account_idx,
};
let mut instruction_data = Vec::new();
instruction_data.extend_from_slice(&TN_NAME_SERVICE_INSTRUCTION_UNREGISTER.to_le_bytes());
let args_bytes = unsafe {
std::slice::from_raw_parts(
&args as *const _ as *const u8,
std::mem::size_of::<NameServiceUnregisterSubdomainArgs>(),
)
};
instruction_data.extend_from_slice(args_bytes);
Ok(instruction_data)
}
fn build_thru_registrar_initialize_registry_instruction(
config_account_idx: u16,
name_service_program_idx: u16,
root_registrar_account_idx: u16,
treasurer_account_idx: u16,
token_mint_account_idx: u16,
token_program_idx: u16,
root_domain_name: &str,
price_per_year: u64,
config_proof: Vec<u8>,
registrar_proof: Vec<u8>,
) -> Result<Vec<u8>> {
let mut instruction_data = Vec::new();
instruction_data
.extend_from_slice(&TN_THRU_REGISTRAR_INSTRUCTION_INITIALIZE_REGISTRY.to_le_bytes());
instruction_data.extend_from_slice(&config_account_idx.to_le_bytes());
instruction_data.extend_from_slice(&name_service_program_idx.to_le_bytes());
instruction_data.extend_from_slice(&root_registrar_account_idx.to_le_bytes());
instruction_data.extend_from_slice(&treasurer_account_idx.to_le_bytes());
instruction_data.extend_from_slice(&token_mint_account_idx.to_le_bytes());
instruction_data.extend_from_slice(&token_program_idx.to_le_bytes());
let domain_bytes = root_domain_name.as_bytes();
if domain_bytes.len() > 64 {
return Err(anyhow::anyhow!(
"Root domain name must be 64 characters or less"
));
}
let mut domain_padded = [0u8; 64];
domain_padded[..domain_bytes.len()].copy_from_slice(domain_bytes);
instruction_data.extend_from_slice(&domain_padded);
instruction_data.extend_from_slice(&(domain_bytes.len() as u32).to_le_bytes());
instruction_data.extend_from_slice(&price_per_year.to_le_bytes());
instruction_data.extend_from_slice(&config_proof);
instruction_data.extend_from_slice(®istrar_proof);
Ok(instruction_data)
}
fn build_thru_registrar_purchase_domain_instruction(
config_account_idx: u16,
lease_account_idx: u16,
domain_account_idx: u16,
name_service_program_idx: u16,
root_registrar_account_idx: u16,
treasurer_account_idx: u16,
payer_token_account_idx: u16,
token_mint_account_idx: u16,
token_program_idx: u16,
domain_name: &str,
years: u8,
lease_proof: Vec<u8>,
domain_proof: Vec<u8>,
) -> Result<Vec<u8>> {
let mut instruction_data = Vec::new();
instruction_data
.extend_from_slice(&TN_THRU_REGISTRAR_INSTRUCTION_PURCHASE_DOMAIN.to_le_bytes());
instruction_data.extend_from_slice(&config_account_idx.to_le_bytes());
instruction_data.extend_from_slice(&lease_account_idx.to_le_bytes());
instruction_data.extend_from_slice(&domain_account_idx.to_le_bytes());
instruction_data.extend_from_slice(&name_service_program_idx.to_le_bytes());
instruction_data.extend_from_slice(&root_registrar_account_idx.to_le_bytes());
instruction_data.extend_from_slice(&treasurer_account_idx.to_le_bytes());
instruction_data.extend_from_slice(&payer_token_account_idx.to_le_bytes());
instruction_data.extend_from_slice(&token_mint_account_idx.to_le_bytes());
instruction_data.extend_from_slice(&token_program_idx.to_le_bytes());
let domain_bytes = domain_name.as_bytes();
if domain_bytes.len() > 64 {
return Err(anyhow::anyhow!("Domain name must be 64 characters or less"));
}
let mut domain_padded = [0u8; 64];
domain_padded[..domain_bytes.len()].copy_from_slice(domain_bytes);
instruction_data.extend_from_slice(&domain_padded);
instruction_data.extend_from_slice(&(domain_bytes.len() as u32).to_le_bytes());
instruction_data.push(years);
instruction_data.extend_from_slice(&lease_proof);
instruction_data.extend_from_slice(&domain_proof);
Ok(instruction_data)
}
fn build_thru_registrar_renew_lease_instruction(
config_account_idx: u16,
lease_account_idx: u16,
treasurer_account_idx: u16,
payer_token_account_idx: u16,
token_mint_account_idx: u16,
token_program_idx: u16,
years: u8,
) -> Result<Vec<u8>> {
let mut instruction_data = Vec::new();
instruction_data.extend_from_slice(&TN_THRU_REGISTRAR_INSTRUCTION_RENEW_LEASE.to_le_bytes());
instruction_data.extend_from_slice(&config_account_idx.to_le_bytes());
instruction_data.extend_from_slice(&lease_account_idx.to_le_bytes());
instruction_data.extend_from_slice(&treasurer_account_idx.to_le_bytes());
instruction_data.extend_from_slice(&payer_token_account_idx.to_le_bytes());
instruction_data.extend_from_slice(&token_mint_account_idx.to_le_bytes());
instruction_data.extend_from_slice(&token_program_idx.to_le_bytes());
instruction_data.push(years);
Ok(instruction_data)
}
fn build_thru_registrar_claim_expired_domain_instruction(
config_account_idx: u16,
lease_account_idx: u16,
treasurer_account_idx: u16,
payer_token_account_idx: u16,
token_mint_account_idx: u16,
token_program_idx: u16,
years: u8,
) -> Result<Vec<u8>> {
let mut instruction_data = Vec::new();
instruction_data
.extend_from_slice(&TN_THRU_REGISTRAR_INSTRUCTION_CLAIM_EXPIRED_DOMAIN.to_le_bytes());
instruction_data.extend_from_slice(&config_account_idx.to_le_bytes());
instruction_data.extend_from_slice(&lease_account_idx.to_le_bytes());
instruction_data.extend_from_slice(&treasurer_account_idx.to_le_bytes());
instruction_data.extend_from_slice(&payer_token_account_idx.to_le_bytes());
instruction_data.extend_from_slice(&token_mint_account_idx.to_le_bytes());
instruction_data.extend_from_slice(&token_program_idx.to_le_bytes());
instruction_data.push(years);
Ok(instruction_data)
}