Remove runtime dependency from solana-transaction-status (#26930)
* Move RewardType out of runtime * Move collect_token_balances to solana-ledger * Remove solana-runtime dependency
This commit is contained in:
parent
6c30ff2033
commit
2dca239480
|
@ -5485,6 +5485,8 @@ dependencies = [
|
|||
"solana-storage-proto",
|
||||
"solana-transaction-status",
|
||||
"solana-vote-program",
|
||||
"spl-token",
|
||||
"spl-token-2022",
|
||||
"static_assertions",
|
||||
"tempfile",
|
||||
"thiserror",
|
||||
|
@ -6431,7 +6433,6 @@ dependencies = [
|
|||
"solana-account-decoder",
|
||||
"solana-measure",
|
||||
"solana-metrics",
|
||||
"solana-runtime",
|
||||
"solana-sdk 1.11.6",
|
||||
"solana-vote-program",
|
||||
"spl-associated-token-account",
|
||||
|
|
|
@ -23,7 +23,9 @@ use {
|
|||
solana_client::{connection_cache::ConnectionCache, tpu_connection::TpuConnection},
|
||||
solana_entry::entry::hash_transactions,
|
||||
solana_gossip::{cluster_info::ClusterInfo, contact_info::ContactInfo},
|
||||
solana_ledger::blockstore_processor::TransactionStatusSender,
|
||||
solana_ledger::{
|
||||
blockstore_processor::TransactionStatusSender, token_balances::collect_token_balances,
|
||||
},
|
||||
solana_measure::{measure, measure::Measure},
|
||||
solana_metrics::inc_new_counter_info,
|
||||
solana_perf::{
|
||||
|
@ -57,9 +59,7 @@ use {
|
|||
transport::TransportError,
|
||||
},
|
||||
solana_streamer::sendmmsg::batch_send,
|
||||
solana_transaction_status::token_balances::{
|
||||
collect_token_balances, TransactionTokenBalancesSet,
|
||||
},
|
||||
solana_transaction_status::token_balances::TransactionTokenBalancesSet,
|
||||
std::{
|
||||
cmp,
|
||||
collections::HashMap,
|
||||
|
|
|
@ -34,6 +34,7 @@ reed-solomon-erasure = { version = "5.0.3", features = ["simd-accel"] }
|
|||
serde = "1.0.138"
|
||||
serde_bytes = "0.11.6"
|
||||
sha2 = "0.10.2"
|
||||
solana-account-decoder = { path = "../account-decoder", version = "=1.11.6" }
|
||||
solana-bpf-loader-program = { path = "../programs/bpf_loader", version = "=1.11.6" }
|
||||
solana-entry = { path = "../entry", version = "=1.11.6" }
|
||||
solana-frozen-abi = { path = "../frozen-abi", version = "=1.11.6" }
|
||||
|
@ -50,6 +51,8 @@ solana-storage-bigtable = { path = "../storage-bigtable", version = "=1.11.6" }
|
|||
solana-storage-proto = { path = "../storage-proto", version = "=1.11.6" }
|
||||
solana-transaction-status = { path = "../transaction-status", version = "=1.11.6" }
|
||||
solana-vote-program = { path = "../programs/vote", version = "=1.11.6" }
|
||||
spl-token = { version = "=3.3.1", features = ["no-entrypoint"] }
|
||||
spl-token-2022 = { version = "=0.4.2", features = ["no-entrypoint"] }
|
||||
static_assertions = "1.1.0"
|
||||
tempfile = "3.3.0"
|
||||
thiserror = "1.0"
|
||||
|
|
|
@ -2,6 +2,7 @@ use {
|
|||
crate::{
|
||||
block_error::BlockError, blockstore::Blockstore, blockstore_db::BlockstoreError,
|
||||
blockstore_meta::SlotMeta, leader_schedule_cache::LeaderScheduleCache,
|
||||
token_balances::collect_token_balances,
|
||||
},
|
||||
chrono_humanize::{Accuracy, HumanTime, Tense},
|
||||
crossbeam_channel::Sender,
|
||||
|
@ -49,9 +50,7 @@ use {
|
|||
VersionedTransaction,
|
||||
},
|
||||
},
|
||||
solana_transaction_status::token_balances::{
|
||||
collect_token_balances, TransactionTokenBalancesSet,
|
||||
},
|
||||
solana_transaction_status::token_balances::TransactionTokenBalancesSet,
|
||||
std::{
|
||||
borrow::Cow,
|
||||
collections::{HashMap, HashSet},
|
||||
|
|
|
@ -28,6 +28,7 @@ mod shredder;
|
|||
pub mod sigverify_shreds;
|
||||
pub mod slot_stats;
|
||||
mod staking_utils;
|
||||
pub mod token_balances;
|
||||
|
||||
#[macro_use]
|
||||
extern crate solana_metrics;
|
||||
|
|
|
@ -0,0 +1,477 @@
|
|||
use {
|
||||
solana_account_decoder::parse_token::{
|
||||
is_known_spl_token_id, pubkey_from_spl_token, spl_token_native_mint,
|
||||
token_amount_to_ui_amount, UiTokenAmount,
|
||||
},
|
||||
solana_measure::measure::Measure,
|
||||
solana_metrics::datapoint_debug,
|
||||
solana_runtime::{bank::Bank, transaction_batch::TransactionBatch},
|
||||
solana_sdk::{account::ReadableAccount, pubkey::Pubkey},
|
||||
solana_transaction_status::{
|
||||
token_balances::TransactionTokenBalances, TransactionTokenBalance,
|
||||
},
|
||||
spl_token_2022::{
|
||||
extension::StateWithExtensions,
|
||||
state::{Account as TokenAccount, Mint},
|
||||
},
|
||||
std::collections::HashMap,
|
||||
};
|
||||
|
||||
fn get_mint_decimals(bank: &Bank, mint: &Pubkey) -> Option<u8> {
|
||||
if mint == &spl_token_native_mint() {
|
||||
Some(spl_token::native_mint::DECIMALS)
|
||||
} else {
|
||||
let mint_account = bank.get_account(mint)?;
|
||||
|
||||
if !is_known_spl_token_id(mint_account.owner()) {
|
||||
return None;
|
||||
}
|
||||
|
||||
let decimals = StateWithExtensions::<Mint>::unpack(mint_account.data())
|
||||
.map(|mint| mint.base.decimals)
|
||||
.ok()?;
|
||||
|
||||
Some(decimals)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn collect_token_balances(
|
||||
bank: &Bank,
|
||||
batch: &TransactionBatch,
|
||||
mint_decimals: &mut HashMap<Pubkey, u8>,
|
||||
) -> TransactionTokenBalances {
|
||||
let mut balances: TransactionTokenBalances = vec![];
|
||||
let mut collect_time = Measure::start("collect_token_balances");
|
||||
|
||||
for transaction in batch.sanitized_transactions() {
|
||||
let account_keys = transaction.message().account_keys();
|
||||
let has_token_program = account_keys.iter().any(is_known_spl_token_id);
|
||||
|
||||
let mut transaction_balances: Vec<TransactionTokenBalance> = vec![];
|
||||
if has_token_program {
|
||||
for (index, account_id) in account_keys.iter().enumerate() {
|
||||
if transaction.message().is_invoked(index) || is_known_spl_token_id(account_id) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if let Some(TokenBalanceData {
|
||||
mint,
|
||||
ui_token_amount,
|
||||
owner,
|
||||
program_id,
|
||||
}) = collect_token_balance_from_account(bank, account_id, mint_decimals)
|
||||
{
|
||||
transaction_balances.push(TransactionTokenBalance {
|
||||
account_index: index as u8,
|
||||
mint,
|
||||
ui_token_amount,
|
||||
owner,
|
||||
program_id,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
balances.push(transaction_balances);
|
||||
}
|
||||
collect_time.stop();
|
||||
datapoint_debug!(
|
||||
"collect_token_balances",
|
||||
("collect_time_us", collect_time.as_us(), i64),
|
||||
);
|
||||
balances
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
struct TokenBalanceData {
|
||||
mint: String,
|
||||
owner: String,
|
||||
ui_token_amount: UiTokenAmount,
|
||||
program_id: String,
|
||||
}
|
||||
|
||||
fn collect_token_balance_from_account(
|
||||
bank: &Bank,
|
||||
account_id: &Pubkey,
|
||||
mint_decimals: &mut HashMap<Pubkey, u8>,
|
||||
) -> Option<TokenBalanceData> {
|
||||
let account = bank.get_account(account_id)?;
|
||||
|
||||
if !is_known_spl_token_id(account.owner()) {
|
||||
return None;
|
||||
}
|
||||
|
||||
let token_account = StateWithExtensions::<TokenAccount>::unpack(account.data()).ok()?;
|
||||
let mint = pubkey_from_spl_token(&token_account.base.mint);
|
||||
|
||||
let decimals = mint_decimals.get(&mint).cloned().or_else(|| {
|
||||
let decimals = get_mint_decimals(bank, &mint)?;
|
||||
mint_decimals.insert(mint, decimals);
|
||||
Some(decimals)
|
||||
})?;
|
||||
|
||||
Some(TokenBalanceData {
|
||||
mint: token_account.base.mint.to_string(),
|
||||
owner: token_account.base.owner.to_string(),
|
||||
ui_token_amount: token_amount_to_ui_amount(token_account.base.amount, decimals),
|
||||
program_id: account.owner().to_string(),
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use {
|
||||
super::*,
|
||||
solana_account_decoder::parse_token::{pubkey_from_spl_token, spl_token_pubkey},
|
||||
solana_sdk::{account::Account, genesis_config::create_genesis_config},
|
||||
spl_token_2022::{
|
||||
extension::{
|
||||
immutable_owner::ImmutableOwner, memo_transfer::MemoTransfer,
|
||||
mint_close_authority::MintCloseAuthority, ExtensionType, StateWithExtensionsMut,
|
||||
},
|
||||
pod::OptionalNonZeroPubkey,
|
||||
solana_program::{program_option::COption, program_pack::Pack},
|
||||
},
|
||||
std::collections::BTreeMap,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn test_collect_token_balance_from_account() {
|
||||
let (mut genesis_config, _mint_keypair) = create_genesis_config(500);
|
||||
|
||||
// Add a variety of accounts, token and not
|
||||
let account = Account::new(42, 55, &Pubkey::new_unique());
|
||||
|
||||
let mint_data = Mint {
|
||||
mint_authority: COption::None,
|
||||
supply: 4242,
|
||||
decimals: 2,
|
||||
is_initialized: true,
|
||||
freeze_authority: COption::None,
|
||||
};
|
||||
let mut data = [0; Mint::LEN];
|
||||
Mint::pack(mint_data, &mut data).unwrap();
|
||||
let mint_pubkey = Pubkey::new_unique();
|
||||
let mint = Account {
|
||||
lamports: 100,
|
||||
data: data.to_vec(),
|
||||
owner: pubkey_from_spl_token(&spl_token::id()),
|
||||
executable: false,
|
||||
rent_epoch: 0,
|
||||
};
|
||||
let other_mint_pubkey = Pubkey::new_unique();
|
||||
let other_mint = Account {
|
||||
lamports: 100,
|
||||
data: data.to_vec(),
|
||||
owner: Pubkey::new_unique(), // !is_known_spl_token_id
|
||||
executable: false,
|
||||
rent_epoch: 0,
|
||||
};
|
||||
|
||||
let token_owner = Pubkey::new_unique();
|
||||
let token_data = TokenAccount {
|
||||
mint: spl_token_pubkey(&mint_pubkey),
|
||||
owner: spl_token_pubkey(&token_owner),
|
||||
amount: 42,
|
||||
delegate: COption::None,
|
||||
state: spl_token_2022::state::AccountState::Initialized,
|
||||
is_native: COption::Some(100),
|
||||
delegated_amount: 0,
|
||||
close_authority: COption::None,
|
||||
};
|
||||
let mut data = [0; TokenAccount::LEN];
|
||||
TokenAccount::pack(token_data, &mut data).unwrap();
|
||||
|
||||
let spl_token_account = Account {
|
||||
lamports: 100,
|
||||
data: data.to_vec(),
|
||||
owner: pubkey_from_spl_token(&spl_token::id()),
|
||||
executable: false,
|
||||
rent_epoch: 0,
|
||||
};
|
||||
let other_account = Account {
|
||||
lamports: 100,
|
||||
data: data.to_vec(),
|
||||
owner: Pubkey::new_unique(), // !is_known_spl_token_id
|
||||
executable: false,
|
||||
rent_epoch: 0,
|
||||
};
|
||||
|
||||
let other_mint_data = TokenAccount {
|
||||
mint: spl_token_pubkey(&other_mint_pubkey),
|
||||
owner: spl_token_pubkey(&token_owner),
|
||||
amount: 42,
|
||||
delegate: COption::None,
|
||||
state: spl_token_2022::state::AccountState::Initialized,
|
||||
is_native: COption::Some(100),
|
||||
delegated_amount: 0,
|
||||
close_authority: COption::None,
|
||||
};
|
||||
let mut data = [0; TokenAccount::LEN];
|
||||
TokenAccount::pack(other_mint_data, &mut data).unwrap();
|
||||
|
||||
let other_mint_token_account = Account {
|
||||
lamports: 100,
|
||||
data: data.to_vec(),
|
||||
owner: pubkey_from_spl_token(&spl_token::id()),
|
||||
executable: false,
|
||||
rent_epoch: 0,
|
||||
};
|
||||
|
||||
let mut accounts = BTreeMap::new();
|
||||
|
||||
let account_pubkey = Pubkey::new_unique();
|
||||
accounts.insert(account_pubkey, account);
|
||||
accounts.insert(mint_pubkey, mint);
|
||||
accounts.insert(other_mint_pubkey, other_mint);
|
||||
let spl_token_account_pubkey = Pubkey::new_unique();
|
||||
accounts.insert(spl_token_account_pubkey, spl_token_account);
|
||||
let other_account_pubkey = Pubkey::new_unique();
|
||||
accounts.insert(other_account_pubkey, other_account);
|
||||
let other_mint_account_pubkey = Pubkey::new_unique();
|
||||
accounts.insert(other_mint_account_pubkey, other_mint_token_account);
|
||||
|
||||
genesis_config.accounts = accounts;
|
||||
|
||||
let bank = Bank::new_for_tests(&genesis_config);
|
||||
let mut mint_decimals = HashMap::new();
|
||||
|
||||
// Account is not owned by spl_token (nor does it have TokenAccount state)
|
||||
assert_eq!(
|
||||
collect_token_balance_from_account(&bank, &account_pubkey, &mut mint_decimals),
|
||||
None
|
||||
);
|
||||
|
||||
// Mint does not have TokenAccount state
|
||||
assert_eq!(
|
||||
collect_token_balance_from_account(&bank, &mint_pubkey, &mut mint_decimals),
|
||||
None
|
||||
);
|
||||
|
||||
// TokenAccount owned by spl_token::id() works
|
||||
assert_eq!(
|
||||
collect_token_balance_from_account(
|
||||
&bank,
|
||||
&spl_token_account_pubkey,
|
||||
&mut mint_decimals
|
||||
),
|
||||
Some(TokenBalanceData {
|
||||
mint: mint_pubkey.to_string(),
|
||||
owner: token_owner.to_string(),
|
||||
ui_token_amount: UiTokenAmount {
|
||||
ui_amount: Some(0.42),
|
||||
decimals: 2,
|
||||
amount: "42".to_string(),
|
||||
ui_amount_string: "0.42".to_string(),
|
||||
},
|
||||
program_id: spl_token::id().to_string(),
|
||||
})
|
||||
);
|
||||
|
||||
// TokenAccount is not owned by known spl-token program_id
|
||||
assert_eq!(
|
||||
collect_token_balance_from_account(&bank, &other_account_pubkey, &mut mint_decimals),
|
||||
None
|
||||
);
|
||||
|
||||
// TokenAccount's mint is not owned by known spl-token program_id
|
||||
assert_eq!(
|
||||
collect_token_balance_from_account(
|
||||
&bank,
|
||||
&other_mint_account_pubkey,
|
||||
&mut mint_decimals
|
||||
),
|
||||
None
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_collect_token_balance_from_spl_token_2022_account() {
|
||||
let (mut genesis_config, _mint_keypair) = create_genesis_config(500);
|
||||
|
||||
// Add a variety of accounts, token and not
|
||||
let account = Account::new(42, 55, &Pubkey::new_unique());
|
||||
|
||||
let mint_authority = Pubkey::new_unique();
|
||||
let mint_size =
|
||||
ExtensionType::get_account_len::<Mint>(&[ExtensionType::MintCloseAuthority]);
|
||||
let mint_base = Mint {
|
||||
mint_authority: COption::None,
|
||||
supply: 4242,
|
||||
decimals: 2,
|
||||
is_initialized: true,
|
||||
freeze_authority: COption::None,
|
||||
};
|
||||
let mut mint_data = vec![0; mint_size];
|
||||
let mut mint_state =
|
||||
StateWithExtensionsMut::<Mint>::unpack_uninitialized(&mut mint_data).unwrap();
|
||||
mint_state.base = mint_base;
|
||||
mint_state.pack_base();
|
||||
mint_state.init_account_type().unwrap();
|
||||
let mut mint_close_authority = mint_state
|
||||
.init_extension::<MintCloseAuthority>(true)
|
||||
.unwrap();
|
||||
mint_close_authority.close_authority =
|
||||
OptionalNonZeroPubkey::try_from(Some(spl_token_pubkey(&mint_authority))).unwrap();
|
||||
|
||||
let mint_pubkey = Pubkey::new_unique();
|
||||
let mint = Account {
|
||||
lamports: 100,
|
||||
data: mint_data.to_vec(),
|
||||
owner: pubkey_from_spl_token(&spl_token_2022::id()),
|
||||
executable: false,
|
||||
rent_epoch: 0,
|
||||
};
|
||||
let other_mint_pubkey = Pubkey::new_unique();
|
||||
let other_mint = Account {
|
||||
lamports: 100,
|
||||
data: mint_data.to_vec(),
|
||||
owner: Pubkey::new_unique(),
|
||||
executable: false,
|
||||
rent_epoch: 0,
|
||||
};
|
||||
|
||||
let token_owner = Pubkey::new_unique();
|
||||
let token_base = TokenAccount {
|
||||
mint: spl_token_pubkey(&mint_pubkey),
|
||||
owner: spl_token_pubkey(&token_owner),
|
||||
amount: 42,
|
||||
delegate: COption::None,
|
||||
state: spl_token_2022::state::AccountState::Initialized,
|
||||
is_native: COption::Some(100),
|
||||
delegated_amount: 0,
|
||||
close_authority: COption::None,
|
||||
};
|
||||
let account_size = ExtensionType::get_account_len::<TokenAccount>(&[
|
||||
ExtensionType::ImmutableOwner,
|
||||
ExtensionType::MemoTransfer,
|
||||
]);
|
||||
let mut account_data = vec![0; account_size];
|
||||
let mut account_state =
|
||||
StateWithExtensionsMut::<TokenAccount>::unpack_uninitialized(&mut account_data)
|
||||
.unwrap();
|
||||
account_state.base = token_base;
|
||||
account_state.pack_base();
|
||||
account_state.init_account_type().unwrap();
|
||||
account_state
|
||||
.init_extension::<ImmutableOwner>(true)
|
||||
.unwrap();
|
||||
let mut memo_transfer = account_state.init_extension::<MemoTransfer>(true).unwrap();
|
||||
memo_transfer.require_incoming_transfer_memos = true.into();
|
||||
|
||||
let spl_token_account = Account {
|
||||
lamports: 100,
|
||||
data: account_data.to_vec(),
|
||||
owner: pubkey_from_spl_token(&spl_token_2022::id()),
|
||||
executable: false,
|
||||
rent_epoch: 0,
|
||||
};
|
||||
let other_account = Account {
|
||||
lamports: 100,
|
||||
data: account_data.to_vec(),
|
||||
owner: Pubkey::new_unique(),
|
||||
executable: false,
|
||||
rent_epoch: 0,
|
||||
};
|
||||
|
||||
let other_mint_token_base = TokenAccount {
|
||||
mint: spl_token_pubkey(&other_mint_pubkey),
|
||||
owner: spl_token_pubkey(&token_owner),
|
||||
amount: 42,
|
||||
delegate: COption::None,
|
||||
state: spl_token_2022::state::AccountState::Initialized,
|
||||
is_native: COption::Some(100),
|
||||
delegated_amount: 0,
|
||||
close_authority: COption::None,
|
||||
};
|
||||
let account_size = ExtensionType::get_account_len::<TokenAccount>(&[
|
||||
ExtensionType::ImmutableOwner,
|
||||
ExtensionType::MemoTransfer,
|
||||
]);
|
||||
let mut account_data = vec![0; account_size];
|
||||
let mut account_state =
|
||||
StateWithExtensionsMut::<TokenAccount>::unpack_uninitialized(&mut account_data)
|
||||
.unwrap();
|
||||
account_state.base = other_mint_token_base;
|
||||
account_state.pack_base();
|
||||
account_state.init_account_type().unwrap();
|
||||
account_state
|
||||
.init_extension::<ImmutableOwner>(true)
|
||||
.unwrap();
|
||||
let mut memo_transfer = account_state.init_extension::<MemoTransfer>(true).unwrap();
|
||||
memo_transfer.require_incoming_transfer_memos = true.into();
|
||||
|
||||
let other_mint_token_account = Account {
|
||||
lamports: 100,
|
||||
data: account_data.to_vec(),
|
||||
owner: pubkey_from_spl_token(&spl_token_2022::id()),
|
||||
executable: false,
|
||||
rent_epoch: 0,
|
||||
};
|
||||
|
||||
let mut accounts = BTreeMap::new();
|
||||
|
||||
let account_pubkey = Pubkey::new_unique();
|
||||
accounts.insert(account_pubkey, account);
|
||||
accounts.insert(mint_pubkey, mint);
|
||||
accounts.insert(other_mint_pubkey, other_mint);
|
||||
let spl_token_account_pubkey = Pubkey::new_unique();
|
||||
accounts.insert(spl_token_account_pubkey, spl_token_account);
|
||||
let other_account_pubkey = Pubkey::new_unique();
|
||||
accounts.insert(other_account_pubkey, other_account);
|
||||
let other_mint_account_pubkey = Pubkey::new_unique();
|
||||
accounts.insert(other_mint_account_pubkey, other_mint_token_account);
|
||||
|
||||
genesis_config.accounts = accounts;
|
||||
|
||||
let bank = Bank::new_for_tests(&genesis_config);
|
||||
let mut mint_decimals = HashMap::new();
|
||||
|
||||
// Account is not owned by spl_token (nor does it have TokenAccount state)
|
||||
assert_eq!(
|
||||
collect_token_balance_from_account(&bank, &account_pubkey, &mut mint_decimals),
|
||||
None
|
||||
);
|
||||
|
||||
// Mint does not have TokenAccount state
|
||||
assert_eq!(
|
||||
collect_token_balance_from_account(&bank, &mint_pubkey, &mut mint_decimals),
|
||||
None
|
||||
);
|
||||
|
||||
// TokenAccount owned by spl_token_2022::id() works
|
||||
assert_eq!(
|
||||
collect_token_balance_from_account(
|
||||
&bank,
|
||||
&spl_token_account_pubkey,
|
||||
&mut mint_decimals
|
||||
),
|
||||
Some(TokenBalanceData {
|
||||
mint: mint_pubkey.to_string(),
|
||||
owner: token_owner.to_string(),
|
||||
ui_token_amount: UiTokenAmount {
|
||||
ui_amount: Some(0.42),
|
||||
decimals: 2,
|
||||
amount: "42".to_string(),
|
||||
ui_amount_string: "0.42".to_string(),
|
||||
},
|
||||
program_id: spl_token_2022::id().to_string(),
|
||||
})
|
||||
);
|
||||
|
||||
// TokenAccount is not owned by known spl-token program_id
|
||||
assert_eq!(
|
||||
collect_token_balance_from_account(&bank, &other_account_pubkey, &mut mint_decimals),
|
||||
None
|
||||
);
|
||||
|
||||
// TokenAccount's mint is not owned by known spl-token program_id
|
||||
assert_eq!(
|
||||
collect_token_balance_from_account(
|
||||
&bank,
|
||||
&other_mint_account_pubkey,
|
||||
&mut mint_decimals
|
||||
),
|
||||
None
|
||||
);
|
||||
}
|
||||
}
|
|
@ -4147,6 +4147,7 @@ dependencies = [
|
|||
"solana-bpf-rust-realloc",
|
||||
"solana-bpf-rust-realloc-invoke",
|
||||
"solana-cli-output",
|
||||
"solana-ledger",
|
||||
"solana-logger 1.11.6",
|
||||
"solana-measure",
|
||||
"solana-program-runtime",
|
||||
|
@ -4955,6 +4956,7 @@ dependencies = [
|
|||
"serde",
|
||||
"serde_bytes",
|
||||
"sha2 0.10.2",
|
||||
"solana-account-decoder",
|
||||
"solana-bpf-loader-program",
|
||||
"solana-entry",
|
||||
"solana-frozen-abi 1.11.6",
|
||||
|
@ -4971,6 +4973,8 @@ dependencies = [
|
|||
"solana-storage-proto",
|
||||
"solana-transaction-status",
|
||||
"solana-vote-program",
|
||||
"spl-token",
|
||||
"spl-token-2022",
|
||||
"static_assertions",
|
||||
"tempfile",
|
||||
"thiserror",
|
||||
|
@ -5648,7 +5652,6 @@ dependencies = [
|
|||
"solana-account-decoder",
|
||||
"solana-measure",
|
||||
"solana-metrics",
|
||||
"solana-runtime",
|
||||
"solana-sdk 1.11.6",
|
||||
"solana-vote-program",
|
||||
"spl-associated-token-account",
|
||||
|
|
|
@ -40,6 +40,9 @@ solana-sdk = { path = "../../sdk", version = "=1.11.6" }
|
|||
solana-transaction-status = { path = "../../transaction-status", version = "=1.11.6" }
|
||||
solana_rbpf = "=0.2.31"
|
||||
|
||||
[dev-dependencies]
|
||||
solana-ledger = { path = "../../ledger", version = "=1.11.6" }
|
||||
|
||||
[[bench]]
|
||||
name = "bpf_loader"
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@ use {
|
|||
solana_bpf_rust_invoke::instructions::*,
|
||||
solana_bpf_rust_realloc::instructions::*,
|
||||
solana_bpf_rust_realloc_invoke::instructions::*,
|
||||
solana_ledger::token_balances::collect_token_balances,
|
||||
solana_program_runtime::{
|
||||
compute_budget::ComputeBudget, invoke_context::with_mock_invoke_context,
|
||||
timings::ExecuteTimings,
|
||||
|
@ -64,9 +65,8 @@ use {
|
|||
transaction::{SanitizedTransaction, Transaction, TransactionError, VersionedTransaction},
|
||||
},
|
||||
solana_transaction_status::{
|
||||
token_balances::collect_token_balances, ConfirmedTransactionWithStatusMeta,
|
||||
InnerInstructions, TransactionStatusMeta, TransactionWithStatusMeta,
|
||||
VersionedTransactionWithStatusMeta,
|
||||
ConfirmedTransactionWithStatusMeta, InnerInstructions, TransactionStatusMeta,
|
||||
TransactionWithStatusMeta, VersionedTransactionWithStatusMeta,
|
||||
},
|
||||
std::{collections::HashMap, env, fs::File, io::Read, path::PathBuf, str::FromStr, sync::Arc},
|
||||
};
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
//! already been signed and verified.
|
||||
#[allow(deprecated)]
|
||||
use solana_sdk::recent_blockhashes_account;
|
||||
pub use solana_sdk::reward_type::RewardType;
|
||||
use {
|
||||
crate::{
|
||||
account_overrides::AccountOverrides,
|
||||
|
@ -1119,14 +1120,6 @@ impl PartialEq for Bank {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, AbiExample, AbiEnumVisitor, Clone, Copy)]
|
||||
pub enum RewardType {
|
||||
Fee,
|
||||
Rent,
|
||||
Staking,
|
||||
Voting,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum RewardCalculationEvent<'a, 'b> {
|
||||
Staking(&'a Pubkey, &'b InflationPointCalculationEvent),
|
||||
|
@ -1136,21 +1129,6 @@ fn null_tracer() -> Option<impl Fn(&RewardCalculationEvent) + Send + Sync> {
|
|||
None::<fn(&RewardCalculationEvent)>
|
||||
}
|
||||
|
||||
impl fmt::Display for RewardType {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{}",
|
||||
match self {
|
||||
RewardType::Fee => "fee",
|
||||
RewardType::Rent => "rent",
|
||||
RewardType::Staking => "staking",
|
||||
RewardType::Voting => "voting",
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait DropCallback: fmt::Debug {
|
||||
fn callback(&self, b: &Bank);
|
||||
fn clone_box(&self) -> Box<dyn DropCallback + Send + Sync>;
|
||||
|
|
|
@ -41,6 +41,7 @@ pub mod program_utils;
|
|||
pub mod pubkey;
|
||||
pub mod quic;
|
||||
pub mod recent_blockhashes_account;
|
||||
pub mod reward_type;
|
||||
pub mod rpc_port;
|
||||
pub mod secp256k1_instruction;
|
||||
pub mod shred_version;
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
use std::fmt;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, AbiExample, AbiEnumVisitor, Clone, Copy)]
|
||||
pub enum RewardType {
|
||||
Fee,
|
||||
Rent,
|
||||
Staking,
|
||||
Voting,
|
||||
}
|
||||
|
||||
impl fmt::Display for RewardType {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{}",
|
||||
match self {
|
||||
RewardType::Fee => "fee",
|
||||
RewardType::Rent => "rent",
|
||||
RewardType::Staking => "staking",
|
||||
RewardType::Voting => "voting",
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
|
@ -23,7 +23,6 @@ serde_json = "1.0.81"
|
|||
solana-account-decoder = { path = "../account-decoder", version = "=1.11.6" }
|
||||
solana-measure = { path = "../measure", version = "=1.11.6" }
|
||||
solana-metrics = { path = "../metrics", version = "=1.11.6" }
|
||||
solana-runtime = { path = "../runtime", version = "=1.11.6" }
|
||||
solana-sdk = { path = "../sdk", version = "=1.11.6" }
|
||||
solana-vote-program = { path = "../programs/vote", version = "=1.11.6" }
|
||||
spl-associated-token-account = { version = "=1.1.1", features = ["no-entrypoint"] }
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#![allow(clippy::integer_arithmetic)]
|
||||
|
||||
pub use {crate::extract_memos::extract_and_fmt_memos, solana_runtime::bank::RewardType};
|
||||
pub use {crate::extract_memos::extract_and_fmt_memos, solana_sdk::reward_type::RewardType};
|
||||
use {
|
||||
crate::{
|
||||
parse_accounts::{parse_accounts, parse_static_accounts, ParsedAccount},
|
||||
|
|
|
@ -1,19 +1,4 @@
|
|||
use {
|
||||
crate::TransactionTokenBalance,
|
||||
solana_account_decoder::parse_token::{
|
||||
is_known_spl_token_id, pubkey_from_spl_token, spl_token_native_mint,
|
||||
token_amount_to_ui_amount, UiTokenAmount,
|
||||
},
|
||||
solana_measure::measure::Measure,
|
||||
solana_metrics::datapoint_debug,
|
||||
solana_runtime::{bank::Bank, transaction_batch::TransactionBatch},
|
||||
solana_sdk::{account::ReadableAccount, pubkey::Pubkey},
|
||||
spl_token_2022::{
|
||||
extension::StateWithExtensions,
|
||||
state::{Account as TokenAccount, Mint},
|
||||
},
|
||||
std::collections::HashMap,
|
||||
};
|
||||
use crate::TransactionTokenBalance;
|
||||
|
||||
pub type TransactionTokenBalances = Vec<Vec<TransactionTokenBalance>>;
|
||||
|
||||
|
@ -34,462 +19,3 @@ impl TransactionTokenBalancesSet {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_mint_decimals(bank: &Bank, mint: &Pubkey) -> Option<u8> {
|
||||
if mint == &spl_token_native_mint() {
|
||||
Some(spl_token::native_mint::DECIMALS)
|
||||
} else {
|
||||
let mint_account = bank.get_account(mint)?;
|
||||
|
||||
if !is_known_spl_token_id(mint_account.owner()) {
|
||||
return None;
|
||||
}
|
||||
|
||||
let decimals = StateWithExtensions::<Mint>::unpack(mint_account.data())
|
||||
.map(|mint| mint.base.decimals)
|
||||
.ok()?;
|
||||
|
||||
Some(decimals)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn collect_token_balances(
|
||||
bank: &Bank,
|
||||
batch: &TransactionBatch,
|
||||
mint_decimals: &mut HashMap<Pubkey, u8>,
|
||||
) -> TransactionTokenBalances {
|
||||
let mut balances: TransactionTokenBalances = vec![];
|
||||
let mut collect_time = Measure::start("collect_token_balances");
|
||||
|
||||
for transaction in batch.sanitized_transactions() {
|
||||
let account_keys = transaction.message().account_keys();
|
||||
let has_token_program = account_keys.iter().any(is_known_spl_token_id);
|
||||
|
||||
let mut transaction_balances: Vec<TransactionTokenBalance> = vec![];
|
||||
if has_token_program {
|
||||
for (index, account_id) in account_keys.iter().enumerate() {
|
||||
if transaction.message().is_invoked(index) || is_known_spl_token_id(account_id) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if let Some(TokenBalanceData {
|
||||
mint,
|
||||
ui_token_amount,
|
||||
owner,
|
||||
program_id,
|
||||
}) = collect_token_balance_from_account(bank, account_id, mint_decimals)
|
||||
{
|
||||
transaction_balances.push(TransactionTokenBalance {
|
||||
account_index: index as u8,
|
||||
mint,
|
||||
ui_token_amount,
|
||||
owner,
|
||||
program_id,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
balances.push(transaction_balances);
|
||||
}
|
||||
collect_time.stop();
|
||||
datapoint_debug!(
|
||||
"collect_token_balances",
|
||||
("collect_time_us", collect_time.as_us(), i64),
|
||||
);
|
||||
balances
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
struct TokenBalanceData {
|
||||
mint: String,
|
||||
owner: String,
|
||||
ui_token_amount: UiTokenAmount,
|
||||
program_id: String,
|
||||
}
|
||||
|
||||
fn collect_token_balance_from_account(
|
||||
bank: &Bank,
|
||||
account_id: &Pubkey,
|
||||
mint_decimals: &mut HashMap<Pubkey, u8>,
|
||||
) -> Option<TokenBalanceData> {
|
||||
let account = bank.get_account(account_id)?;
|
||||
|
||||
if !is_known_spl_token_id(account.owner()) {
|
||||
return None;
|
||||
}
|
||||
|
||||
let token_account = StateWithExtensions::<TokenAccount>::unpack(account.data()).ok()?;
|
||||
let mint = pubkey_from_spl_token(&token_account.base.mint);
|
||||
|
||||
let decimals = mint_decimals.get(&mint).cloned().or_else(|| {
|
||||
let decimals = get_mint_decimals(bank, &mint)?;
|
||||
mint_decimals.insert(mint, decimals);
|
||||
Some(decimals)
|
||||
})?;
|
||||
|
||||
Some(TokenBalanceData {
|
||||
mint: token_account.base.mint.to_string(),
|
||||
owner: token_account.base.owner.to_string(),
|
||||
ui_token_amount: token_amount_to_ui_amount(token_account.base.amount, decimals),
|
||||
program_id: account.owner().to_string(),
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use {
|
||||
super::*,
|
||||
solana_account_decoder::parse_token::{pubkey_from_spl_token, spl_token_pubkey},
|
||||
solana_sdk::{account::Account, genesis_config::create_genesis_config},
|
||||
spl_token_2022::{
|
||||
extension::{
|
||||
immutable_owner::ImmutableOwner, memo_transfer::MemoTransfer,
|
||||
mint_close_authority::MintCloseAuthority, ExtensionType, StateWithExtensionsMut,
|
||||
},
|
||||
pod::OptionalNonZeroPubkey,
|
||||
solana_program::{program_option::COption, program_pack::Pack},
|
||||
},
|
||||
std::collections::BTreeMap,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn test_collect_token_balance_from_account() {
|
||||
let (mut genesis_config, _mint_keypair) = create_genesis_config(500);
|
||||
|
||||
// Add a variety of accounts, token and not
|
||||
let account = Account::new(42, 55, &Pubkey::new_unique());
|
||||
|
||||
let mint_data = Mint {
|
||||
mint_authority: COption::None,
|
||||
supply: 4242,
|
||||
decimals: 2,
|
||||
is_initialized: true,
|
||||
freeze_authority: COption::None,
|
||||
};
|
||||
let mut data = [0; Mint::LEN];
|
||||
Mint::pack(mint_data, &mut data).unwrap();
|
||||
let mint_pubkey = Pubkey::new_unique();
|
||||
let mint = Account {
|
||||
lamports: 100,
|
||||
data: data.to_vec(),
|
||||
owner: pubkey_from_spl_token(&spl_token::id()),
|
||||
executable: false,
|
||||
rent_epoch: 0,
|
||||
};
|
||||
let other_mint_pubkey = Pubkey::new_unique();
|
||||
let other_mint = Account {
|
||||
lamports: 100,
|
||||
data: data.to_vec(),
|
||||
owner: Pubkey::new_unique(), // !is_known_spl_token_id
|
||||
executable: false,
|
||||
rent_epoch: 0,
|
||||
};
|
||||
|
||||
let token_owner = Pubkey::new_unique();
|
||||
let token_data = TokenAccount {
|
||||
mint: spl_token_pubkey(&mint_pubkey),
|
||||
owner: spl_token_pubkey(&token_owner),
|
||||
amount: 42,
|
||||
delegate: COption::None,
|
||||
state: spl_token_2022::state::AccountState::Initialized,
|
||||
is_native: COption::Some(100),
|
||||
delegated_amount: 0,
|
||||
close_authority: COption::None,
|
||||
};
|
||||
let mut data = [0; TokenAccount::LEN];
|
||||
TokenAccount::pack(token_data, &mut data).unwrap();
|
||||
|
||||
let spl_token_account = Account {
|
||||
lamports: 100,
|
||||
data: data.to_vec(),
|
||||
owner: pubkey_from_spl_token(&spl_token::id()),
|
||||
executable: false,
|
||||
rent_epoch: 0,
|
||||
};
|
||||
let other_account = Account {
|
||||
lamports: 100,
|
||||
data: data.to_vec(),
|
||||
owner: Pubkey::new_unique(), // !is_known_spl_token_id
|
||||
executable: false,
|
||||
rent_epoch: 0,
|
||||
};
|
||||
|
||||
let other_mint_data = TokenAccount {
|
||||
mint: spl_token_pubkey(&other_mint_pubkey),
|
||||
owner: spl_token_pubkey(&token_owner),
|
||||
amount: 42,
|
||||
delegate: COption::None,
|
||||
state: spl_token_2022::state::AccountState::Initialized,
|
||||
is_native: COption::Some(100),
|
||||
delegated_amount: 0,
|
||||
close_authority: COption::None,
|
||||
};
|
||||
let mut data = [0; TokenAccount::LEN];
|
||||
TokenAccount::pack(other_mint_data, &mut data).unwrap();
|
||||
|
||||
let other_mint_token_account = Account {
|
||||
lamports: 100,
|
||||
data: data.to_vec(),
|
||||
owner: pubkey_from_spl_token(&spl_token::id()),
|
||||
executable: false,
|
||||
rent_epoch: 0,
|
||||
};
|
||||
|
||||
let mut accounts = BTreeMap::new();
|
||||
|
||||
let account_pubkey = Pubkey::new_unique();
|
||||
accounts.insert(account_pubkey, account);
|
||||
accounts.insert(mint_pubkey, mint);
|
||||
accounts.insert(other_mint_pubkey, other_mint);
|
||||
let spl_token_account_pubkey = Pubkey::new_unique();
|
||||
accounts.insert(spl_token_account_pubkey, spl_token_account);
|
||||
let other_account_pubkey = Pubkey::new_unique();
|
||||
accounts.insert(other_account_pubkey, other_account);
|
||||
let other_mint_account_pubkey = Pubkey::new_unique();
|
||||
accounts.insert(other_mint_account_pubkey, other_mint_token_account);
|
||||
|
||||
genesis_config.accounts = accounts;
|
||||
|
||||
let bank = Bank::new_for_tests(&genesis_config);
|
||||
let mut mint_decimals = HashMap::new();
|
||||
|
||||
// Account is not owned by spl_token (nor does it have TokenAccount state)
|
||||
assert_eq!(
|
||||
collect_token_balance_from_account(&bank, &account_pubkey, &mut mint_decimals),
|
||||
None
|
||||
);
|
||||
|
||||
// Mint does not have TokenAccount state
|
||||
assert_eq!(
|
||||
collect_token_balance_from_account(&bank, &mint_pubkey, &mut mint_decimals),
|
||||
None
|
||||
);
|
||||
|
||||
// TokenAccount owned by spl_token::id() works
|
||||
assert_eq!(
|
||||
collect_token_balance_from_account(
|
||||
&bank,
|
||||
&spl_token_account_pubkey,
|
||||
&mut mint_decimals
|
||||
),
|
||||
Some(TokenBalanceData {
|
||||
mint: mint_pubkey.to_string(),
|
||||
owner: token_owner.to_string(),
|
||||
ui_token_amount: UiTokenAmount {
|
||||
ui_amount: Some(0.42),
|
||||
decimals: 2,
|
||||
amount: "42".to_string(),
|
||||
ui_amount_string: "0.42".to_string(),
|
||||
},
|
||||
program_id: spl_token::id().to_string(),
|
||||
})
|
||||
);
|
||||
|
||||
// TokenAccount is not owned by known spl-token program_id
|
||||
assert_eq!(
|
||||
collect_token_balance_from_account(&bank, &other_account_pubkey, &mut mint_decimals),
|
||||
None
|
||||
);
|
||||
|
||||
// TokenAccount's mint is not owned by known spl-token program_id
|
||||
assert_eq!(
|
||||
collect_token_balance_from_account(
|
||||
&bank,
|
||||
&other_mint_account_pubkey,
|
||||
&mut mint_decimals
|
||||
),
|
||||
None
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_collect_token_balance_from_spl_token_2022_account() {
|
||||
let (mut genesis_config, _mint_keypair) = create_genesis_config(500);
|
||||
|
||||
// Add a variety of accounts, token and not
|
||||
let account = Account::new(42, 55, &Pubkey::new_unique());
|
||||
|
||||
let mint_authority = Pubkey::new_unique();
|
||||
let mint_size =
|
||||
ExtensionType::get_account_len::<Mint>(&[ExtensionType::MintCloseAuthority]);
|
||||
let mint_base = Mint {
|
||||
mint_authority: COption::None,
|
||||
supply: 4242,
|
||||
decimals: 2,
|
||||
is_initialized: true,
|
||||
freeze_authority: COption::None,
|
||||
};
|
||||
let mut mint_data = vec![0; mint_size];
|
||||
let mut mint_state =
|
||||
StateWithExtensionsMut::<Mint>::unpack_uninitialized(&mut mint_data).unwrap();
|
||||
mint_state.base = mint_base;
|
||||
mint_state.pack_base();
|
||||
mint_state.init_account_type().unwrap();
|
||||
let mut mint_close_authority = mint_state
|
||||
.init_extension::<MintCloseAuthority>(true)
|
||||
.unwrap();
|
||||
mint_close_authority.close_authority =
|
||||
OptionalNonZeroPubkey::try_from(Some(spl_token_pubkey(&mint_authority))).unwrap();
|
||||
|
||||
let mint_pubkey = Pubkey::new_unique();
|
||||
let mint = Account {
|
||||
lamports: 100,
|
||||
data: mint_data.to_vec(),
|
||||
owner: pubkey_from_spl_token(&spl_token_2022::id()),
|
||||
executable: false,
|
||||
rent_epoch: 0,
|
||||
};
|
||||
let other_mint_pubkey = Pubkey::new_unique();
|
||||
let other_mint = Account {
|
||||
lamports: 100,
|
||||
data: mint_data.to_vec(),
|
||||
owner: Pubkey::new_unique(),
|
||||
executable: false,
|
||||
rent_epoch: 0,
|
||||
};
|
||||
|
||||
let token_owner = Pubkey::new_unique();
|
||||
let token_base = TokenAccount {
|
||||
mint: spl_token_pubkey(&mint_pubkey),
|
||||
owner: spl_token_pubkey(&token_owner),
|
||||
amount: 42,
|
||||
delegate: COption::None,
|
||||
state: spl_token_2022::state::AccountState::Initialized,
|
||||
is_native: COption::Some(100),
|
||||
delegated_amount: 0,
|
||||
close_authority: COption::None,
|
||||
};
|
||||
let account_size = ExtensionType::get_account_len::<TokenAccount>(&[
|
||||
ExtensionType::ImmutableOwner,
|
||||
ExtensionType::MemoTransfer,
|
||||
]);
|
||||
let mut account_data = vec![0; account_size];
|
||||
let mut account_state =
|
||||
StateWithExtensionsMut::<TokenAccount>::unpack_uninitialized(&mut account_data)
|
||||
.unwrap();
|
||||
account_state.base = token_base;
|
||||
account_state.pack_base();
|
||||
account_state.init_account_type().unwrap();
|
||||
account_state
|
||||
.init_extension::<ImmutableOwner>(true)
|
||||
.unwrap();
|
||||
let mut memo_transfer = account_state.init_extension::<MemoTransfer>(true).unwrap();
|
||||
memo_transfer.require_incoming_transfer_memos = true.into();
|
||||
|
||||
let spl_token_account = Account {
|
||||
lamports: 100,
|
||||
data: account_data.to_vec(),
|
||||
owner: pubkey_from_spl_token(&spl_token_2022::id()),
|
||||
executable: false,
|
||||
rent_epoch: 0,
|
||||
};
|
||||
let other_account = Account {
|
||||
lamports: 100,
|
||||
data: account_data.to_vec(),
|
||||
owner: Pubkey::new_unique(),
|
||||
executable: false,
|
||||
rent_epoch: 0,
|
||||
};
|
||||
|
||||
let other_mint_token_base = TokenAccount {
|
||||
mint: spl_token_pubkey(&other_mint_pubkey),
|
||||
owner: spl_token_pubkey(&token_owner),
|
||||
amount: 42,
|
||||
delegate: COption::None,
|
||||
state: spl_token_2022::state::AccountState::Initialized,
|
||||
is_native: COption::Some(100),
|
||||
delegated_amount: 0,
|
||||
close_authority: COption::None,
|
||||
};
|
||||
let account_size = ExtensionType::get_account_len::<TokenAccount>(&[
|
||||
ExtensionType::ImmutableOwner,
|
||||
ExtensionType::MemoTransfer,
|
||||
]);
|
||||
let mut account_data = vec![0; account_size];
|
||||
let mut account_state =
|
||||
StateWithExtensionsMut::<TokenAccount>::unpack_uninitialized(&mut account_data)
|
||||
.unwrap();
|
||||
account_state.base = other_mint_token_base;
|
||||
account_state.pack_base();
|
||||
account_state.init_account_type().unwrap();
|
||||
account_state
|
||||
.init_extension::<ImmutableOwner>(true)
|
||||
.unwrap();
|
||||
let mut memo_transfer = account_state.init_extension::<MemoTransfer>(true).unwrap();
|
||||
memo_transfer.require_incoming_transfer_memos = true.into();
|
||||
|
||||
let other_mint_token_account = Account {
|
||||
lamports: 100,
|
||||
data: account_data.to_vec(),
|
||||
owner: pubkey_from_spl_token(&spl_token_2022::id()),
|
||||
executable: false,
|
||||
rent_epoch: 0,
|
||||
};
|
||||
|
||||
let mut accounts = BTreeMap::new();
|
||||
|
||||
let account_pubkey = Pubkey::new_unique();
|
||||
accounts.insert(account_pubkey, account);
|
||||
accounts.insert(mint_pubkey, mint);
|
||||
accounts.insert(other_mint_pubkey, other_mint);
|
||||
let spl_token_account_pubkey = Pubkey::new_unique();
|
||||
accounts.insert(spl_token_account_pubkey, spl_token_account);
|
||||
let other_account_pubkey = Pubkey::new_unique();
|
||||
accounts.insert(other_account_pubkey, other_account);
|
||||
let other_mint_account_pubkey = Pubkey::new_unique();
|
||||
accounts.insert(other_mint_account_pubkey, other_mint_token_account);
|
||||
|
||||
genesis_config.accounts = accounts;
|
||||
|
||||
let bank = Bank::new_for_tests(&genesis_config);
|
||||
let mut mint_decimals = HashMap::new();
|
||||
|
||||
// Account is not owned by spl_token (nor does it have TokenAccount state)
|
||||
assert_eq!(
|
||||
collect_token_balance_from_account(&bank, &account_pubkey, &mut mint_decimals),
|
||||
None
|
||||
);
|
||||
|
||||
// Mint does not have TokenAccount state
|
||||
assert_eq!(
|
||||
collect_token_balance_from_account(&bank, &mint_pubkey, &mut mint_decimals),
|
||||
None
|
||||
);
|
||||
|
||||
// TokenAccount owned by spl_token_2022::id() works
|
||||
assert_eq!(
|
||||
collect_token_balance_from_account(
|
||||
&bank,
|
||||
&spl_token_account_pubkey,
|
||||
&mut mint_decimals
|
||||
),
|
||||
Some(TokenBalanceData {
|
||||
mint: mint_pubkey.to_string(),
|
||||
owner: token_owner.to_string(),
|
||||
ui_token_amount: UiTokenAmount {
|
||||
ui_amount: Some(0.42),
|
||||
decimals: 2,
|
||||
amount: "42".to_string(),
|
||||
ui_amount_string: "0.42".to_string(),
|
||||
},
|
||||
program_id: spl_token_2022::id().to_string(),
|
||||
})
|
||||
);
|
||||
|
||||
// TokenAccount is not owned by known spl-token program_id
|
||||
assert_eq!(
|
||||
collect_token_balance_from_account(&bank, &other_account_pubkey, &mut mint_decimals),
|
||||
None
|
||||
);
|
||||
|
||||
// TokenAccount's mint is not owned by known spl-token program_id
|
||||
assert_eq!(
|
||||
collect_token_balance_from_account(
|
||||
&bank,
|
||||
&other_mint_account_pubkey,
|
||||
&mut mint_decimals
|
||||
),
|
||||
None
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue