use crate::{
Api, BlockNumber, GearGasNode, GearGasNodeId, GearPages, IntoAccountId32, IntoSubstrate,
gear::{
self,
runtime_types::{
frame_system::AccountInfo,
gear_common::storage::primitives::Interval,
gear_core::{
pages::Page,
program::{ActiveProgram, Program},
},
pallet_balances::types::AccountData,
pallet_gear_bank::pallet::BankAccount,
vara_runtime::RuntimeEvent,
},
},
result::{Error, FailedPage, Result},
};
use futures::prelude::*;
use gear_core::{
code::{CodeMetadata, InstrumentedCode},
ids::{ActorId, CodeId, MessageId},
message::UserStoredMessage,
pages::GearPage,
program::MemoryInfix,
};
use gsdk_codegen::at_block;
use sp_core::crypto::AccountId32;
use subxt::{
error::MetadataError,
ext::subxt_core::storage::address::StorageHashers,
metadata::types::StorageEntryType,
storage::{Address, StaticStorageKey, StorageKey},
utils::{H256, Yes},
};
impl Api {
#[at_block]
pub async fn storage_fetch_at<'a, Addr>(
&self,
address: &'a Addr,
block_hash: Option<H256>,
) -> Result<Addr::Target>
where
Addr: Address<IsFetchable = Yes> + 'a,
{
self.storage_at(block_hash)
.await?
.fetch(address)
.await?
.ok_or(Error::StorageEntryNotFound)
}
}
impl Api {
#[at_block]
pub async fn account_info_at(
&self,
address: impl IntoAccountId32,
block_hash: Option<H256>,
) -> Result<AccountInfo<u32, AccountData<u128>>> {
self.storage_fetch_at(
&gear::storage().system().account(address.into_account_id()),
block_hash,
)
.await
}
#[at_block]
pub async fn account_data_at(
&self,
address: impl IntoAccountId32,
block_hash: Option<H256>,
) -> Result<AccountData<u128>> {
self.account_info_at(address, block_hash)
.map_ok(|info| info.data)
.await
}
pub async fn number(&self) -> Result<u32> {
self.storage_fetch(&gear::storage().system().number()).await
}
#[at_block]
pub async fn free_balance_at(
&self,
account_id: impl IntoAccountId32,
block_hash: Option<H256>,
) -> Result<u128> {
Ok(self.account_data_at(account_id, block_hash).await?.free)
}
#[at_block]
pub async fn reserved_balance_at(
&self,
account_id: impl IntoAccountId32,
block_hash: Option<H256>,
) -> Result<u128> {
Ok(self.account_data_at(account_id, block_hash).await?.reserved)
}
#[at_block]
pub async fn total_balance_at(
&self,
account_id: impl IntoAccountId32,
block_hash: Option<H256>,
) -> Result<u128> {
let data = self.account_data_at(account_id, block_hash).await?;
data.free
.checked_add(data.reserved)
.ok_or(Error::BalanceOverflow)
}
#[at_block]
pub async fn block_events_at(&self, block_hash: Option<H256>) -> Result<Vec<RuntimeEvent>> {
let addr = gear::storage().system().events();
let evs = self.storage_fetch_at(&addr, block_hash).await?;
Ok(evs.into_iter().map(|ev| ev.event).collect())
}
}
impl Api {
pub async fn block_timestamp(&self, block_hash: Option<H256>) -> Result<u64> {
self.storage_fetch_at(&gear::storage().timestamp().now(), block_hash)
.await
}
}
impl Api {
pub async fn validators(&self) -> Result<Vec<AccountId32>> {
Ok(self
.storage_fetch(&gear::storage().session().validators())
.await?
.into_iter()
.map(|id| id.into_substrate())
.collect())
}
}
impl Api {
#[at_block]
pub async fn total_issuance_at(&self, block_hash: Option<H256>) -> Result<u64> {
self.storage_fetch_at(&gear::storage().gear_gas().total_issuance(), block_hash)
.await
}
#[at_block]
pub async fn gas_nodes_at(
&self,
gas_node_ids: impl IntoIterator<Item = GearGasNodeId>,
block_hash: Option<H256>,
) -> Result<Vec<(GearGasNodeId, GearGasNode)>> {
stream::iter(gas_node_ids)
.then(|gas_node_id| async move {
let addr = gear::storage().gear_gas().gas_nodes(gas_node_id.clone());
let gas_node = self.storage_fetch_at(&addr, block_hash).await?;
Ok((gas_node_id.clone(), gas_node))
})
.try_collect()
.await
}
}
impl Api {
#[at_block]
pub async fn bank_info_at(
&self,
account_id: impl IntoAccountId32,
block_hash: Option<H256>,
) -> Result<BankAccount<u128>> {
self.storage_fetch_at(
&gear::storage()
.gear_bank()
.bank(account_id.into_account_id()),
block_hash,
)
.await
}
pub async fn bank_address(&self) -> Result<AccountId32> {
Ok(self
.storage_fetch(&gear::storage().gear_bank().bank_address())
.await?
.into_substrate())
}
}
impl Api {
pub async fn execute_inherent(&self) -> Result<bool> {
Ok(self
.storage()
.at_latest()
.await?
.fetch_or_default(&gear::storage().gear().execute_inherent())
.await?)
}
pub async fn gear_block_number(&self, block_hash: Option<H256>) -> Result<BlockNumber> {
Ok(self
.storage_at(block_hash)
.await?
.fetch_or_default(&gear::storage().gear().block_number())
.await?)
}
}
impl Api {
#[at_block]
pub async fn original_code_at(
&self,
code_id: CodeId,
block_hash: Option<H256>,
) -> Result<Vec<u8>> {
self.storage_fetch_at(
&gear::storage()
.gear_program()
.original_code_storage(code_id),
block_hash,
)
.await
}
#[at_block]
pub async fn instrumented_code_storage_at(
&self,
code_id: CodeId,
block_hash: Option<H256>,
) -> Result<InstrumentedCode> {
self.storage_fetch_at(
&gear::storage()
.gear_program()
.instrumented_code_storage(code_id),
block_hash,
)
.await
}
#[at_block]
pub async fn code_metadata_storage_at(
&self,
code_id: CodeId,
block_hash: Option<H256>,
) -> Result<CodeMetadata> {
self.storage_fetch_at(
&gear::storage()
.gear_program()
.code_metadata_storage(code_id),
block_hash,
)
.await
}
#[at_block]
pub async fn active_program_at(
&self,
program_id: ActorId,
block_hash: Option<H256>,
) -> Result<ActiveProgram<BlockNumber>> {
match self.program_at(program_id, block_hash).await? {
Program::Active(p) => Ok(p),
_ => Err(Error::ProgramTerminated),
}
}
#[at_block]
pub async fn program_pages_at(
&self,
program_id: ActorId,
block_hash: Option<H256>,
) -> Result<GearPages> {
let address = gear::storage()
.gear_program()
.memory_pages_iter1(program_id);
let metadata = self.metadata();
let hashers = subxt::ext::subxt_core::storage::lookup_storage_entry_details(
address.pallet_name(),
address.entry_name(),
&metadata,
)
.and_then(|(_, entry)| StorageHashers::new(entry.entry_type(), metadata.types()))
.map_err(subxt::Error::from)?;
let pages = self
.storage_at(block_hash)
.await?
.iter(address)
.try_flatten_stream()
.map_err(Error::from)
.and_then(|pair| {
std::future::ready({
<(
StaticStorageKey<ActorId>,
StaticStorageKey<MemoryInfix>,
StaticStorageKey<Page>,
) as StorageKey>::decode_storage_key(
&mut &pair.key_bytes[32..],
&mut hashers.iter(),
metadata.types(),
)
.map_err(subxt::Error::from)
.map_err(Error::from)
.and_then(|(_, _, page_index)| {
Ok((page_index.into_key().0.try_into()?, pair.value))
})
})
})
.try_collect()
.await?;
Ok(pages)
}
#[at_block]
pub async fn inheritor_of_at(
&self,
program_id: ActorId,
block_hash: Option<H256>,
) -> Result<Option<ActorId>> {
Ok(match self.program_at(program_id, block_hash).await? {
Program::Exited(p) => Some(p),
_ => None,
})
}
#[at_block]
pub async fn specified_program_pages_at(
&self,
program_id: ActorId,
page_numbers: impl IntoIterator<Item = GearPage>,
block_hash: Option<H256>,
) -> Result<GearPages> {
futures::stream::iter(page_numbers)
.then(|page| async move {
let addr = gear::storage().gear_program().memory_pages(
program_id,
MemoryInfix::default(),
page.into(),
);
let page_buf = self
.storage_at(block_hash)
.await?
.fetch(&addr)
.await?
.ok_or_else(|| FailedPage::new(page, program_id).not_found())?;
Ok((page, page_buf))
})
.try_collect()
.await
}
#[at_block]
pub async fn program_at(
&self,
program_id: ActorId,
block_hash: Option<H256>,
) -> Result<Program<BlockNumber>> {
self.storage_fetch_at(
&gear::storage().gear_program().program_storage(program_id),
block_hash,
)
.await
}
}
impl Api {
#[at_block]
pub async fn mailbox_account_message_at(
&self,
account_id: impl IntoAccountId32,
message_id: MessageId,
block_hash: Option<H256>,
) -> Result<Option<(UserStoredMessage, Interval<u32>)>> {
Ok(self
.storage_fetch_at(
&gear::storage()
.gear_messenger()
.mailbox(account_id.into_account_id(), message_id),
block_hash,
)
.await
.ok())
}
#[at_block]
pub async fn mailbox_messages_at(
&self,
account_id: impl IntoAccountId32,
count: usize,
block_hash: Option<H256>,
) -> Result<Vec<(UserStoredMessage, Interval<u32>)>> {
self.storage_at(block_hash)
.await?
.iter(
gear::storage()
.gear_messenger()
.mailbox_iter1(account_id.into_account_id()),
)
.await?
.map_ok(|pair| pair.value)
.take(count)
.try_collect()
.await
.map_err(Error::from)
}
}
pub(crate) fn storage_type_id(
metadata: &subxt::Metadata,
address: &impl Address,
) -> Result<u32, MetadataError> {
let storage_type = metadata
.pallet_by_name_err(address.pallet_name())?
.storage()
.ok_or_else(|| MetadataError::StorageNotFoundInPallet(address.pallet_name().to_owned()))?
.entry_by_name(address.entry_name())
.ok_or_else(|| MetadataError::StorageEntryNotFound(address.entry_name().to_owned()))?
.entry_type();
let storage_type_id = *match storage_type {
StorageEntryType::Plain(id) => id,
StorageEntryType::Map { value_ty, .. } => value_ty,
};
Ok(storage_type_id)
}