use std::fmt;
use std::path::Path;
use near_primitives::views::AccountView;
use crate::error::ErrorKind;
use crate::rpc::query::{
Query, ViewAccessKey, ViewAccessKeyList, ViewAccount, ViewCode, ViewFunction, ViewState,
};
use crate::types::{AccountId, Balance, InMemorySigner, PublicKey, SecretKey};
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<std::path::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.client(),
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_near(
&self,
receiver_id: &AccountId,
amount: Balance,
) -> Result<ExecutionFinalResult> {
self.worker
.transfer_near(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.as_ref().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.client(),
self.signer().clone(),
contract_id.clone(),
)
}
pub async fn store_credentials(&self, save_dir: impl AsRef<Path>) -> Result<()> {
let savepath = save_dir.as_ref().to_path_buf();
std::fs::create_dir_all(save_dir).map_err(|e| ErrorKind::Io.custom(e))?;
let mut savepath = savepath.join(self.id().to_string());
savepath.set_extension("json");
crate::rpc::tool::write_cred_to_file(&savepath, self.id(), &self.secret_key().0);
Ok(())
}
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, Eq, PartialEq)]
#[non_exhaustive]
pub struct AccountDetails {
pub balance: Balance,
pub locked: Balance,
pub code_hash: CryptoHash,
pub storage_usage: u64,
pub(crate) storage_paid_at: BlockHeight,
}
impl AccountDetails {
pub(crate) fn into_near_account(self) -> near_primitives::account::Account {
AccountView {
amount: self.balance,
locked: self.locked,
code_hash: near_primitives::hash::CryptoHash(self.code_hash.0),
storage_usage: self.storage_usage,
storage_paid_at: self.storage_paid_at,
}
.into()
}
}
impl From<AccountView> for AccountDetails {
fn from(account: AccountView) -> Self {
Self {
balance: account.amount,
locked: account.locked,
code_hash: CryptoHash(account.code_hash.0),
storage_usage: account.storage_usage,
storage_paid_at: account.storage_paid_at,
}
}
}