Skip to content

Commit

Permalink
feat: Add LoadedAccounts event to trace account changes (#123)
Browse files Browse the repository at this point in the history
* feat: Add LoadedAccounts event to trace account changes

* refactor: Leverage inject_runtime_type for pallet_solana RuntimeEvent
  • Loading branch information
code0xff authored Jan 15, 2025
1 parent 340515f commit c681ad6
Show file tree
Hide file tree
Showing 2 changed files with 117 additions and 1 deletion.
18 changes: 18 additions & 0 deletions frame/solana/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,9 @@ pub mod pallet {

#[pallet::config(with_default)]
pub trait Config: frame_system::Config + pallet_timestamp::Config {
#[pallet::no_default_bounds]
type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;

#[pallet::no_default]
type AccountIdConversion: ConvertBack<Pubkey, Self::AccountId>;

Expand Down Expand Up @@ -204,6 +207,8 @@ pub mod pallet {

#[frame_support::register_default_impl(TestDefaultConfig)]
impl DefaultConfig for TestDefaultConfig {
#[inject_runtime_type]
type RuntimeEvent = ();
type DecimalMultiplier = ConstU64<1>;
/// Hashes older than 2 minutes (20 blocks) will be dropped from the blockhash queue.
type BlockhashQueueMaxAge = ConstU64<20>;
Expand All @@ -225,6 +230,12 @@ pub mod pallet {
#[pallet::origin]
pub type Origin = RawOrigin;

#[pallet::event]
#[pallet::generate_deposit(pub(super) fn deposit_event)]
pub enum Event<T: Config> {
LoadedAccounts(Vec<Pubkey>),
}

#[pallet::error]
pub enum Error<T> {
/// Failed to reallocate account data of this length
Expand Down Expand Up @@ -399,6 +410,13 @@ pub mod pallet {

if bank.load_execute_and_commit_sanitized_transaction(&sanitized_tx).is_ok() {
Self::update_transaction_cache(&sanitized_tx)?;
let account_keys = sanitized_tx
.message()
.account_keys()
.iter()
.map(Clone::clone)
.collect::<Vec<Pubkey>>();
Self::deposit_event(Event::LoadedAccounts(account_keys));
}

Ok(().into())
Expand Down
100 changes: 99 additions & 1 deletion frame/solana/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,7 @@ fn spl_token_program_should_work() {
let state = spl_token::state::Account::unpack_from_slice(&<AccountData<Test>>::get(
&account.account_id(),
))
.expect("token acccount state");
.expect("token account state");
assert_eq!(state.mint, mint.pubkey());
assert_eq!(state.owner, owner.pubkey());
assert_eq!(state.amount, sol_into_lamports(1_000));
Expand Down Expand Up @@ -299,3 +299,101 @@ fn filter_duplicated_transaction() {
assert!(Pallet::<Test>::check_transaction(&versioned_tx).is_ok());
});
}

#[test]
fn trace_executed_transaction_events() {
new_test_ext().execute_with(|| {
before_each();
let bank = mock_bank();

let authority = Keypair::alice();
let owner = Keypair::bob();
let mint = Keypair::get("Mint");

let token_program_id = Pubkey::parse("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA");
let program_data =
std::fs::read("tests/example-programs/token/token_program.so").expect("program data");

mock_deploy_program(&token_program_id, program_data);

let create_account = system_instruction::create_account(
&authority.pubkey(),
&mint.pubkey(),
sol_into_lamports(1),
82,
&token_program_id,
);
let initialize_mint = spl_token::instruction::initialize_mint2(
&token_program_id,
&mint.pubkey(),
&authority.pubkey(),
None,
9,
)
.expect("initialize_mint2 instruction");
let mut tx = Transaction::new_with_payer(
&[create_account, initialize_mint],
Some(&authority.pubkey()),
);
tx.sign(&[&authority, &mint], Hash::default());

let origin = RawOrigin::SolanaTransaction(authority.pubkey());
let versioned_tx: VersionedTransaction = tx.into();

assert!(Pallet::<Test>::transact(origin.into(), versioned_tx.clone()).is_ok());

let account = Keypair::get("Account");
let create_account = system_instruction::create_account(
&owner.pubkey(),
&account.pubkey(),
sol_into_lamports(1),
165,
&token_program_id,
);
let initialize_account = spl_token::instruction::initialize_account(
&token_program_id,
&account.pubkey(),
&mint.pubkey(),
&owner.pubkey(),
)
.expect("initialize_account instruction");
let mint_to = spl_token::instruction::mint_to(
&token_program_id,
&mint.pubkey(),
&account.pubkey(),
&authority.pubkey(),
&[],
sol_into_lamports(1_000),
)
.expect("mint_to instruction");
let mut tx = Transaction::new_with_payer(
&[create_account, initialize_account, mint_to],
Some(&owner.pubkey()),
);
tx.sign(&[&authority, &account, &owner], Hash::default());
let origin = RawOrigin::SolanaTransaction(authority.pubkey());
let versioned_tx: VersionedTransaction = tx.into();

assert!(Pallet::<Test>::transact(origin.into(), versioned_tx.clone()).is_ok());

let account_keys: Vec<Pubkey> = System::events()
.into_iter()
.filter_map(|record| match record.event {
RuntimeEvent::Solana(Event::LoadedAccounts(keys)) => Some(keys),
_ => None,
})
.flatten()
.collect();

let token_account = account_keys
.into_iter()
.filter_map(|account_key| Pallet::<Test>::get_account_info(account_key))
.filter(|account| account.owner == spl_token::id())
.filter_map(|account| spl_token::state::Account::unpack(&account.data).ok())
.next()
.unwrap();

assert!(token_account.owner == owner.pubkey());
assert!(token_account.mint == mint.pubkey());
});
}

0 comments on commit c681ad6

Please sign in to comment.