Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support Balance #10

Merged
merged 12 commits into from
Aug 4, 2023
Merged
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
# will have compiled files and executables
debug/
target/
# TODO: Remove this later
metadata.yml

# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
Expand Down
32 changes: 19 additions & 13 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,24 @@ version = "0.0.1"

[dependencies]
# crate.io
jsonrpsee = { version = "0.18.2", features = ["http-client", "async-client", "client-ws-transport-native-tls"] }
tokio = { version = "1.29.0", features = ["rt", "macros", "rt-multi-thread"] }
rustyline = { version = "12.0.0" }
clap = { version = "4.3.8", features = ["derive"] }
dirs = { version = "5.0.1" }
colored = { version = "2.0.0" }
figlet-rs = { version = "0.1.5" }
shell-words = { version = "1.1.0" }
async-trait = { version = "0.1.69" }
serde_json = { version = "1.0.100" }
serde = { version = "1.0.166" }
jsonrpsee = { version = "0.18.2", features = ["http-client", "async-client", "client-ws-transport-native-tls"] }
tokio = { version = "1.29.0", features = ["rt", "macros", "rt-multi-thread"] }
rustyline = { version = "12.0.0" }
clap = { version = "4.3.8", features = ["derive"] }
dirs = { version = "5.0.1" }
colored = { version = "2.0.0" }
figlet-rs = { version = "0.1.5" }
shell-words = { version = "1.1.0" }
async-trait = { version = "0.1.69" }
serde_json = { version = "1.0.100" }
serde = { version = "1.0.166" }
clap_complete_command = { version = "0.5.1" }
frame-metadata = { version = "16.0.0", features = ["decode"] }
# substrate
sp-core = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v1.0.0" }
sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v1.0.0" }
sp-core = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v1.0.0" }
sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v1.0.0" }
sp-storage = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v1.0.0" }
sp-version = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v1.0.0" }
pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v1.0.0" }
frame-system = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v1.0.0" }
fp-account = { git = "https://github.com/boundless-forest/frontier", branch = "bear-polkadot-v1.0.0" }
29 changes: 27 additions & 2 deletions src/app/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,21 @@ pub enum AppCommand {
/// RPC interfaces
#[command(subcommand)]
Rpc(RpcCommand),
/// Chain interfaces
#[command(subcommand)]
Chain(ChainCommand),
/// Chain state interfaces
#[command(subcommand)]
State(StateCommand),
/// Account interfaces
#[command(subcommand)]
AccountInfo(AccountInfoCommand),
// TODO: ADD HELP COMMAND
}

#[derive(Subcommand, Clone, Debug)]
#[command(name = "rpc")]
pub enum RpcCommand {
/// Get RPC methods
// RpcMethods,
/// Get the node name
SysName,
/// Get System Properties
Expand Down Expand Up @@ -61,3 +66,23 @@ pub enum ChainCommand {
hash: String,
},
}

#[derive(Subcommand, Clone, Debug)]
#[command(name = "state")]
pub enum StateCommand {
RuntimeVersion {
#[arg(long)]
hash: Option<String>,
},
}

#[derive(Subcommand, Clone, Debug)]
#[command(name = "account-info")]
pub enum AccountInfoCommand {
Balances {
#[arg(name = "account-id", long)]
account_id: String,
#[arg(name = "at-block", long)]
at_block: Option<String>,
},
}
2 changes: 1 addition & 1 deletion src/app/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
mod command;
mod helper;

pub use command::{AppCommand, ChainCommand, RpcCommand};
pub use command::{AccountInfoCommand, AppCommand, ChainCommand, RpcCommand, StateCommand};
pub use helper::{create_editor, history_path, print_welcome_message, Config, EditorHelper};
2 changes: 2 additions & 0 deletions src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ pub enum RpcError {
WsHandshakeError,
JsonRpseeError(Error),
InvalidCommandParams,
DecodeError,
StorageKeyFailed,
}

impl From<RpcError> for AppError {
Expand Down
44 changes: 42 additions & 2 deletions src/handler.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
// std
use std::str::FromStr;
// crates.io
use frame_system::AccountInfo;
use pallet_balances::AccountData;
// this crate
use crate::{
app::{AppCommand, ChainCommand, RpcCommand},
app::{AccountInfoCommand, AppCommand, ChainCommand, RpcCommand, StateCommand},
errors::{AppError, RpcError},
networks::{ChainInfo, Network},
rpc::{print_format_json, ChainApi, RpcClient, SystemApi},
rpc::{
print_format_json, single_map_storage_key, AccountBalances, ChainApi, RpcClient, StateApi,
SystemApi,
},
};

/// The APP's command execution result.
Expand Down Expand Up @@ -83,6 +89,40 @@ pub async fn handle_commands<CI: ChainInfo>(
print_format_json(res);
},
},
AppCommand::State(sub_command) => match sub_command {
StateCommand::RuntimeVersion { hash } => {
let hash = if let Some(hash) = hash {
<CI as ChainInfo>::Hash::from_str(hash.as_str())
.map_err(|_| RpcError::InvalidCommandParams)?
} else {
client.get_finalized_head().await?.expect("Failed to get finalized head")
};

let res = client.runtime_version(hash).await?;
print_format_json(res);
},
},
AppCommand::AccountInfo(sub_command) => match sub_command {
AccountInfoCommand::Balances { account_id, at_block } => {
let metadata = client.runtime_metadata().await?;
let hash = at_block.and_then(|s| <CI as ChainInfo>::Hash::from_str(&s).ok());

let key = <CI as ChainInfo>::AccountId::from_str(account_id.as_str())
.map_err(|_| RpcError::InvalidCommandParams)?;
let storage_key = single_map_storage_key(&metadata, "System", "Account", key)
.map_err(|_| RpcError::StorageKeyFailed)?;

let account: Option<AccountInfo<CI::Nonce, AccountData<CI::Balance>>> =
client.get_storage(storage_key, hash).await?;
if let Some(a) = account {
print_format_json(AccountBalances {
free: a.data.free,
reserved: a.data.reserved,
frozen: a.data.frozen,
});
}
},
},
}

Ok(ExecutionResult::Success)
Expand Down
16 changes: 16 additions & 0 deletions src/networks/darwinia.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,21 @@ use sp_runtime::{
// this crate
use super::{ChainInfo, Network};

type Signature = fp_account::EthereumSignature;
type AccountPublic = <Signature as sp_runtime::traits::Verify>::Signer;
pub type AccountId = <AccountPublic as sp_runtime::traits::IdentifyAccount>::AccountId;

/// Pangolin Chain information
pub struct PangolinChain;

impl ChainInfo for PangolinChain {
type AccountId = AccountId;
type Balance = u128;
type Block = Block<Self::Header, UncheckedExtrinsic>;
type BlockNumber = u32;
type Hash = H256;
type Header = Header<Self::BlockNumber, BlakeTwo256>;
type Nonce = u32;

const NET_WORK: Network = Network::Pangolin;
const WS_PORT: &'static str = "wss://pangolin-rpc.darwinia.network:443";
Expand All @@ -25,10 +32,13 @@ impl ChainInfo for PangolinChain {
pub struct PangoroChain;

impl ChainInfo for PangoroChain {
type AccountId = AccountId;
type Balance = u128;
type Block = Block<Self::Header, UncheckedExtrinsic>;
type BlockNumber = u32;
type Hash = H256;
type Header = Header<Self::BlockNumber, BlakeTwo256>;
type Nonce = u32;

const NET_WORK: Network = Network::Pangoro;
const WS_PORT: &'static str = "wss://pangoro-rpc.darwinia.network:443";
Expand All @@ -38,10 +48,13 @@ impl ChainInfo for PangoroChain {
pub struct CrabChain;

impl ChainInfo for CrabChain {
type AccountId = AccountId;
type Balance = u128;
type Block = Block<Self::Header, UncheckedExtrinsic>;
type BlockNumber = u32;
type Hash = H256;
type Header = Header<Self::BlockNumber, BlakeTwo256>;
type Nonce = u32;

const NET_WORK: Network = Network::Crab;
const WS_PORT: &'static str = "wss://crab-rpc.darwinia.network:443";
Expand All @@ -51,10 +64,13 @@ impl ChainInfo for CrabChain {
pub struct DarwiniaChain;

impl ChainInfo for DarwiniaChain {
type AccountId = AccountId;
type Balance = u128;
type Block = Block<Self::Header, UncheckedExtrinsic>;
type BlockNumber = u32;
type Hash = H256;
type Header = Header<Self::BlockNumber, BlakeTwo256>;
type Nonce = u32;

const NET_WORK: Network = Network::Darwinia;
const WS_PORT: &'static str = "wss://rpc.darwinia.network:443";
Expand Down
17 changes: 12 additions & 5 deletions src/networks/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@ pub use node_template::NoteTemplate;
pub use polkadot::{KusamaChain, PolkadotChain};

// std
use std::{marker::Sync, str::FromStr};
use std::{fmt::Debug, marker::Sync, str::FromStr};
// crates.io
use clap::Subcommand;
use serde::{Deserialize, Serialize};
use sp_core::{Decode, Encode};
use sp_runtime::DeserializeOwned;

/// The ChainInfo API
Expand All @@ -20,14 +21,20 @@ pub trait ChainInfo: Sync + Send {
/// The network name of this chain
const NET_WORK: Network;

/// The hash type of the chain
type Hash: Serialize + DeserializeOwned + Send + FromStr;
/// The account id type of the chain
type AccountId: Serialize + DeserializeOwned + Encode + FromStr + AsRef<[u8]>;
/// The balance type of the chain
type Balance: Serialize + DeserializeOwned + Decode + Debug;
/// The block type of the chain
type Block: Serialize + DeserializeOwned;
/// The block number type of the chain
type BlockNumber: Serialize + DeserializeOwned + Send + From<u32>;
/// The hash type of the chain
type Hash: Serialize + DeserializeOwned + Send + FromStr;
/// The header type of the chain
type Header: Serialize + DeserializeOwned;
/// The block type of the chain
type Block: Serialize + DeserializeOwned;
/// The nonce type of the chain
type Nonce: Serialize + DeserializeOwned + Decode + Debug;
}

#[derive(Subcommand, Clone, Debug, Serialize, Deserialize, Default)]
Expand Down
11 changes: 9 additions & 2 deletions src/networks/node_template.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,26 @@
use sp_core::H256;
use sp_runtime::{
generic::{Block, Header},
traits::BlakeTwo256,
OpaqueExtrinsic as UncheckedExtrinsic,
traits::{BlakeTwo256, IdentifyAccount, Verify},
MultiSignature, OpaqueExtrinsic as UncheckedExtrinsic,
};
// this crate
use super::{ChainInfo, Network};

pub type Signature = MultiSignature;
pub type AccountPublic = <Signature as Verify>::Signer;
pub type AccountId = <AccountPublic as IdentifyAccount>::AccountId;

pub struct NoteTemplate;

impl ChainInfo for NoteTemplate {
type AccountId = AccountId;
type Balance = u128;
type Block = Block<Self::Header, UncheckedExtrinsic>;
type BlockNumber = u32;
type Hash = H256;
type Header = Header<Self::BlockNumber, BlakeTwo256>;
type Nonce = u32;

const NET_WORK: Network = Network::Local;
const WS_PORT: &'static str = "ws://127.0.0.1:9944";
Expand Down
14 changes: 12 additions & 2 deletions src/networks/polkadot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,27 @@
use sp_core::H256;
use sp_runtime::{
generic::{Block, Header},
traits::BlakeTwo256,
OpaqueExtrinsic as UncheckedExtrinsic,
traits::{BlakeTwo256, IdentifyAccount, Verify},
MultiSignature, OpaqueExtrinsic as UncheckedExtrinsic,
};
// this crate
use super::{ChainInfo, Network};

type Signature = MultiSignature;
type AccountPublic = <Signature as Verify>::Signer;
pub type AccountId = <AccountPublic as IdentifyAccount>::AccountId;

/// Polkadot Chain information
pub struct PolkadotChain;

impl ChainInfo for PolkadotChain {
type AccountId = AccountId;
type Balance = u128;
type Block = Block<Self::Header, UncheckedExtrinsic>;
type BlockNumber = u32;
type Hash = H256;
type Header = Header<Self::BlockNumber, BlakeTwo256>;
type Nonce = u32;

const NET_WORK: Network = Network::Polkadot;
const WS_PORT: &'static str = "wss://rpc.polkadot.io:443";
Expand All @@ -25,10 +32,13 @@ impl ChainInfo for PolkadotChain {
pub struct KusamaChain;

impl ChainInfo for KusamaChain {
type AccountId = AccountId;
type Balance = u128;
type Block = Block<Self::Header, UncheckedExtrinsic>;
type BlockNumber = u32;
type Hash = H256;
type Header = Header<Self::BlockNumber, BlakeTwo256>;
type Nonce = u32;

const NET_WORK: Network = Network::Kusama;
const WS_PORT: &'static str = "wss://kusama-rpc.polkadot.io:443";
Expand Down
27 changes: 27 additions & 0 deletions src/rpc/api.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
// crates.io
use async_trait::async_trait;
use frame_metadata::RuntimeMetadataPrefixed;
use sp_core::Decode;
use sp_runtime::generic::SignedBlock;
use sp_storage::StorageKey;
use sp_version::RuntimeVersion;
// this crate
use super::{
client::RpcResult,
Expand Down Expand Up @@ -65,3 +69,26 @@ pub trait ChainApi {
hash: HashForChain<Self::ChainInfo>,
) -> RpcResult<HeaderForChain<Self::ChainInfo>>;
}

/// The State API provides access to chain state and storage.
#[async_trait]
pub trait StateApi {
/// The chain info type
type ChainInfo: ChainInfo;

/// Get the runtime version
async fn runtime_version(
&self,
hash: HashForChain<Self::ChainInfo>,
) -> RpcResult<RuntimeVersion>;

/// Get the runtime metadata
async fn runtime_metadata(&self) -> RpcResult<RuntimeMetadataPrefixed>;

/// Retrieves the storage for a key
async fn get_storage<R: Decode>(
&self,
storage_key: StorageKey,
at_block: Option<HashForChain<Self::ChainInfo>>,
) -> RpcResult<Option<R>>;
}
Loading