use std::fmt;
use std::path::Path;
use unc_primitives::views::AccountView;
use crate::error::ErrorKind;
use crate::rpc::query::{
Query, ViewAccessKey, ViewAccessKeyList, ViewAccount, ViewCode, ViewFunction, ViewState,
};
use crate::types::{AccountId, InMemorySigner, PublicKey, SecretKey, UncToken};
use crate::{BlockHeight, CryptoHash, Network, Worker};
use crate::operations::{CallTransaction, CreateAccountTransaction, Transaction};
use crate::result::{Execution, ExecutionFinalResult, Result};
#[derive(Clone)]
pub struct Account {
signer: InMemorySigner,
worker: Worker<dyn Network>,
}
impl fmt::Debug for Account {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Account")
.field("id", &self.signer.account_id)
.finish()
}
}
impl Account {
pub fn from_file(
path: impl AsRef<Path>,
worker: &Worker<impl Network + 'static>,
) -> Result<Self> {
let signer = InMemorySigner::from_file(path.as_ref())?;
Ok(Self::new(signer, worker.clone().coerce()))
}
pub fn from_secret_key(
id: AccountId,
sk: SecretKey,
worker: &Worker<impl Network + 'static>,
) -> Self {
Self {
signer: InMemorySigner::from_secret_key(id, sk),
worker: worker.clone().coerce(),
}
}
pub(crate) fn new(signer: InMemorySigner, worker: Worker<dyn Network>) -> Self {
Self { signer, worker }
}
pub fn id(&self) -> &AccountId {
&self.signer.account_id
}
pub fn signer(&self) -> &InMemorySigner {
&self.signer
}
pub fn call(&self, contract_id: &AccountId, function: &str) -> CallTransaction {
CallTransaction::new(
self.worker.clone(),
contract_id.to_owned(),
self.signer.clone(),
function,
)
}
pub fn view(&self, contract_id: &AccountId, function: &str) -> Query<'_, ViewFunction> {
self.worker.view(contract_id, function)
}
pub async fn transfer_unc(
&self,
receiver_id: &AccountId,
amount: UncToken,
) -> Result<ExecutionFinalResult> {
self.worker
.transfer_unc(self.signer(), receiver_id, amount)
.await
}
pub async fn delete_account(self, beneficiary_id: &AccountId) -> Result<ExecutionFinalResult> {
self.worker
.delete_account(self.id(), &self.signer, beneficiary_id)
.await
}
pub fn view_account(&self) -> Query<'_, ViewAccount> {
self.worker.view_account(self.id())
}
pub fn view_access_key(&self, pk: &PublicKey) -> Query<'_, ViewAccessKey> {
Query::new(
self.worker.client(),
ViewAccessKey {
account_id: self.id().clone(),
public_key: pk.clone(),
},
)
}
pub fn view_access_keys(&self) -> Query<'_, ViewAccessKeyList> {
Query::new(
self.worker.client(),
ViewAccessKeyList {
account_id: self.id().clone(),
},
)
}
pub fn create_subaccount<'a, 'b>(
&'a self,
new_account_id: &'b str,
) -> CreateAccountTransaction<'a, 'b> {
CreateAccountTransaction::new(
&self.worker,
self.signer.clone(),
self.id().clone(),
new_account_id,
)
}
pub async fn deploy(&self, wasm: &[u8]) -> Result<Execution<Contract>> {
let outcome = self
.worker
.client()
.deploy(&self.signer, self.id(), wasm.into())
.await?;
Ok(Execution {
result: Contract::new(self.signer().clone(), self.worker.clone()),
details: ExecutionFinalResult::from_view(outcome),
})
}
pub fn batch(&self, contract_id: &AccountId) -> Transaction {
Transaction::new(
self.worker.clone(),
self.signer().clone(),
contract_id.clone(),
)
}
pub async fn store_credentials(&self, save_dir: impl AsRef<Path> + Send) -> Result<()> {
let savepath = save_dir.as_ref();
std::fs::create_dir_all(&save_dir).map_err(|e| ErrorKind::Io.custom(e))?;
let savepath = savepath.join(format!("{}.json", self.id()));
crate::rpc::tool::write_cred_to_file(&savepath, self.id(), &self.secret_key().0)
}
pub fn secret_key(&self) -> &SecretKey {
&self.signer.secret_key
}
pub fn set_secret_key(&mut self, sk: SecretKey) {
self.signer.secret_key = sk;
}
}
#[derive(Clone)]
pub struct Contract {
pub(crate) account: Account,
}
impl fmt::Debug for Contract {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Contract")
.field("id", self.account.id())
.finish()
}
}
impl Contract {
pub fn from_secret_key(
id: AccountId,
sk: SecretKey,
worker: &Worker<impl Network + 'static>,
) -> Self {
Self::account(Account::from_secret_key(id, sk, worker))
}
pub(crate) fn new(signer: InMemorySigner, worker: Worker<dyn Network>) -> Self {
Self {
account: Account::new(signer, worker),
}
}
pub(crate) fn account(account: Account) -> Self {
Self { account }
}
pub fn id(&self) -> &AccountId {
self.account.id()
}
pub fn as_account(&self) -> &Account {
&self.account
}
pub fn as_account_mut(&mut self) -> &mut Account {
&mut self.account
}
pub fn signer(&self) -> &InMemorySigner {
self.account.signer()
}
pub fn call(&self, function: &str) -> CallTransaction {
self.account.call(self.id(), function)
}
pub fn view(&self, function: &str) -> Query<'_, ViewFunction> {
self.account.view(self.id(), function)
}
pub fn view_code(&self) -> Query<'_, ViewCode> {
self.account.worker.view_code(self.id())
}
pub fn view_state(&self) -> Query<'_, ViewState> {
self.account.worker.view_state(self.id())
}
pub fn view_account(&self) -> Query<'_, ViewAccount> {
self.account.worker.view_account(self.id())
}
pub fn view_access_key(&self, pk: &PublicKey) -> Query<'_, ViewAccessKey> {
self.account.view_access_key(pk)
}
pub fn view_access_keys(&self) -> Query<'_, ViewAccessKeyList> {
self.account.view_access_keys()
}
pub async fn delete_contract(self, beneficiary_id: &AccountId) -> Result<ExecutionFinalResult> {
self.account.delete_account(beneficiary_id).await
}
pub fn batch(&self) -> Transaction {
self.account.batch(self.id())
}
}
#[derive(Debug, Default, Eq, PartialEq)]
#[non_exhaustive]
pub struct AccountDetailsPatch {
pub balance: Option<UncToken>,
pub pledging: Option<UncToken>,
pub power: Option<u128>,
pub code_hash: Option<CryptoHash>,
pub storage_usage: Option<u64>,
pub(crate) storage_paid_at: Option<BlockHeight>,
}
impl AccountDetailsPatch {
pub fn reduce(&mut self, acc: Self) {
if let Some(balance) = acc.balance {
self.balance = Some(balance);
}
if let Some(pledging) = acc.pledging {
self.pledging = Some(pledging);
}
if let Some(power) = acc.power {
self.power = Some(power);
}
if let Some(code_hash) = acc.code_hash {
self.code_hash = Some(code_hash);
}
if let Some(storage) = acc.storage_usage {
self.storage_usage = Some(storage);
}
if let Some(storage_paid_at) = acc.storage_paid_at {
self.storage_paid_at = Some(storage_paid_at);
}
}
pub fn balance(mut self, balance: UncToken) -> Self {
self.balance = Some(balance);
self
}
pub fn pledging(mut self, pledging: UncToken) -> Self {
self.pledging = Some(pledging);
self
}
pub fn power(mut self, power: u128) -> Self {
self.power = Some(power);
self
}
pub fn code_hash(mut self, code_hash: CryptoHash) -> Self {
self.code_hash = Some(code_hash);
self
}
pub fn storage_usage(mut self, storage_usage: u64) -> Self {
self.storage_usage = Some(storage_usage);
self
}
}
impl From<AccountDetails> for AccountDetailsPatch {
fn from(account: AccountDetails) -> Self {
Self {
balance: Some(account.balance),
pledging: Some(account.pledging),
power: Some(account.power),
code_hash: Some(account.code_hash),
storage_usage: Some(account.storage_usage),
storage_paid_at: Some(account.storage_paid_at),
}
}
}
#[derive(Debug, Eq, PartialEq)]
#[non_exhaustive]
pub struct AccountDetails {
pub balance: UncToken,
pub pledging: UncToken,
pub power: u128,
pub code_hash: CryptoHash,
pub storage_usage: u64,
pub(crate) storage_paid_at: BlockHeight,
}
impl AccountDetails {
pub fn new() -> Self {
Self {
balance: UncToken::from_unc(0),
pledging: UncToken::from_unc(0),
power: 0,
code_hash: CryptoHash::default(),
storage_usage: 0,
storage_paid_at: 0,
}
}
pub(crate) fn into_unc_account(self) -> unc_primitives::account::Account {
unc_primitives::account::Account::new(
self.balance.as_attounc(),
self.pledging.as_attounc(),
self.power,
unc_primitives::hash::CryptoHash(self.code_hash.0),
self.storage_usage,
)
}
}
impl Default for AccountDetails {
fn default() -> Self {
Self::new()
}
}
impl From<AccountView> for AccountDetails {
fn from(account: AccountView) -> Self {
Self {
balance: UncToken::from_attounc(account.amount),
pledging: UncToken::from_attounc(account.pledging),
power: account.power,
code_hash: CryptoHash(account.code_hash.0),
storage_usage: account.storage_usage,
storage_paid_at: account.storage_paid_at,
}
}
}
impl From<AccountDetailsPatch> for AccountDetails {
fn from(value: AccountDetailsPatch) -> Self {
Self {
balance: value.balance.unwrap_or_default(),
pledging: value.pledging.unwrap_or_default(),
power: value.power.unwrap_or_default(),
code_hash: value.code_hash.unwrap_or_default(),
storage_usage: value.storage_usage.unwrap_or_default(),
storage_paid_at: value.storage_paid_at.unwrap_or_default(),
}
}
}