Skip to content

Constructing Instructions#

Trident allows you to construct instructions similarly to tests written using JavaScript and Anchor framework tests.

The following source code shows an example of how a transaction can be constructed:

// Obtain addresses from address storages
let author = self.fuzz_accounts.author.insert(&mut self.trident, None);

let hello_world = self.fuzz_accounts.hello_world_account.insert(
    &mut self.trident,
    Some(PdaSeeds {
        seeds: &[b"hello_world_seed"],
        program_id: hello_world::program_id(),
    }),
);

// Obtain random input
let input = self.trident.random_from_range(0..u8::MAX);

// Construct instruction
let ix = InitializeFnInstruction::data(InitializeFnInstructionData::new(input))
    .accounts(InitializeFnInstructionAccounts::new(author, hello_world))
    .instruction();

// Execute transaction
let res = self.trident.process_transaction(&[ix], Some("Initialize"));

Available Types

All types used for instruction construction are generated automatically by Trident and stored within the types.rs file.

Constructing instructions consists of two steps:

Setting Up Data#

The first step is to set up the data for the instruction using the data method. In the example above, we use the random_from_range method to generate a random number between 0 and u8::MAX, then create the instruction data using the InitializeFnInstructionData::new method.

let input = self.trident.random_from_range(0..u8::MAX);

let ix = InitializeFnInstruction::data(InitializeFnInstructionData::new(input))

Setting Up Accounts#

The second step is to set up the accounts for the instruction using the accounts method. In the example above, we use the InitializeFnInstructionAccounts::new method to create the instruction accounts.

Address Storages#

Address storages are important containers for managing addresses used in your tests:

  • When you call insert() without seeds, it adds a random address to the storage
  • When you call insert() with specified seeds, it inserts a Program Derived Address (PDA) to the storage
  • When you call get() method, it returns a random address from the storage
  • Storages allow you to create test scenarios where some addresses are correct and others are incorrect, enabling comprehensive access pattern testing
// Obtain addresses from address storages
let author = self.fuzz_accounts.author.insert(&mut self.trident, None);

let hello_world = self.fuzz_accounts.hello_world_account.insert(
    &mut self.trident,
    Some(PdaSeeds {
        seeds: &[b"hello_world_seed"],
        program_id: hello_world::program_id(),
    }),
);

// Construct instruction with accounts
let ix = InitializeFnInstruction::data(InitializeFnInstructionData::new(input))
    .accounts(InitializeFnInstructionAccounts::new(author, hello_world))
    .instruction();

Setting Up Remaining Accounts#

Remaining accounts are accounts that are not specified in the instruction accounts. You can specify remaining accounts using the remaining_accounts method.

// Obtain remaining accounts
let remaining_accounts = vec![
    AccountMeta::new(pubkey!("11111111111111111111111111111111"), false),
];

let ix = InitializeFnInstruction::data(InitializeFnInstructionData::new(input))
    .accounts(InitializeFnInstructionAccounts::new(author, hello_world))
    .remaining_accounts(remaining_accounts)
    .instruction();