use futures::prelude::*;
use crate::{
Api, BlockNumber, GearGasNode, GearGasNodeId, GearPages, IntoSubstrate, IntoSubxt,
gear::{
self,
runtime_types::{
frame_system::{AccountInfo, EventRecord},
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 gear_core::{
code::{CodeMetadata, InstrumentedCode},
ids::{ActorId, CodeId, MessageId},
message::UserStoredMessage,
program::MemoryInfix,
};
use gsdk_codegen::storage_fetch;
use sp_core::crypto::{AccountId32, Ss58Codec};
use subxt::{
error::MetadataError,
ext::subxt_core::storage::address::StorageHashers,
metadata::types::StorageEntryType,
storage::{Address, StaticStorageKey, StorageKey},
utils::{H256, Yes},
};
impl Api {
#[storage_fetch]
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)
}
pub async fn program_pages(&self, program_id: ActorId) -> Result<GearPages> {
self.gpages(program_id, None).await
}
}
impl Api {
#[storage_fetch]
pub async fn info_at(
&self,
address: &str,
block_hash: Option<H256>,
) -> Result<AccountInfo<u32, AccountData<u128>>> {
let dest = AccountId32::from_ss58check(address)?;
let addr = gear::storage().system().account(dest.into_subxt());
self.storage_fetch_at(&addr, block_hash).await
}
pub async fn number(&self) -> Result<u32> {
self.storage_fetch(&gear::storage().system().number()).await
}
pub async fn get_balance(&self, address: &str) -> Result<u128> {
Ok(self.info(address).await?.data.free)
}
#[storage_fetch]
pub async fn get_events_at(&self, block_hash: Option<H256>) -> Result<Vec<RuntimeEvent>> {
let addr = gear::storage().system().events();
let evs: Vec<EventRecord<RuntimeEvent, H256>> =
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 {
#[storage_fetch]
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
}
#[storage_fetch]
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 {
#[storage_fetch]
pub async fn bank_info_at(
&self,
account_id: AccountId32,
block_hash: Option<H256>,
) -> Result<BankAccount<u128>> {
self.storage_fetch_at(
&gear::storage().gear_bank().bank(account_id.into_subxt()),
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 {
#[storage_fetch]
pub async fn original_code_storage_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
}
#[storage_fetch]
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
}
#[storage_fetch]
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
}
#[storage_fetch]
pub async fn gprog_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),
}
}
#[storage_fetch]
pub async fn gpages_at(
&self,
program_id: ActorId,
memory_infix: Option<MemoryInfix>,
block_hash: Option<H256>,
) -> Result<GearPages> {
let memory_infix = match memory_infix {
Some(infix) => infix,
None => self.gprog_at(program_id, block_hash).await?.memory_infix,
};
let address = gear::storage()
.gear_program()
.memory_pages_iter2(program_id, memory_infix);
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()
.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(|(_, _, page_index)| (page_index.into_key().0, pair.value))
.map_err(subxt::Error::from)
})
})
.try_collect()
.await?;
Ok(pages)
}
#[storage_fetch]
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,
})
}
#[storage_fetch]
pub async fn specified_gpages_at(
&self,
program_id: ActorId,
memory_infix: Option<MemoryInfix>,
page_numbers: impl IntoIterator<Item = u32>,
block_hash: Option<H256>,
) -> Result<GearPages> {
let memory_infix = match memory_infix {
Some(infix) => infix,
None => self.gprog_at(program_id, block_hash).await?.memory_infix,
};
futures::stream::iter(page_numbers)
.then(|page| async move {
let addr = gear::storage().gear_program().memory_pages(
program_id,
memory_infix,
Page(page),
);
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
}
#[storage_fetch]
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 {
pub async fn get_mailbox_account_message(
&self,
account_id: AccountId32,
message_id: MessageId,
) -> Result<Option<(UserStoredMessage, Interval<u32>)>> {
Ok(self
.storage_fetch(
&gear::storage()
.gear_messenger()
.mailbox(account_id.into_subxt(), message_id),
)
.await
.ok())
}
pub async fn mailbox(
&self,
account_id: Option<AccountId32>,
count: usize,
) -> Result<Vec<(UserStoredMessage, Interval<u32>)>> {
let storage = self.storage().at_latest().await?;
if let Some(account_id) = account_id {
let query_key = gear::storage()
.gear_messenger()
.mailbox_iter1(account_id.into_subxt());
storage
.iter(query_key)
.await?
.map_ok(|pair| pair.value)
.boxed()
} else {
let query_key = gear::storage().gear_messenger().mailbox_iter();
storage
.iter(query_key)
.await?
.map_ok(|pair| pair.value)
.boxed()
}
.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)
}