--- a/Cargo.toml +++ b/Cargo.toml @@ -3,6 +3,7 @@ members = [ 'node/*', 'pallets/*', + 'client/*', 'primitives/*', 'runtime', 'crates/*', --- /dev/null +++ b/client/rpc/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "uc-rpc" +version = "0.1.0" +edition = "2018" + +[dependencies] +pallet-common = { default-features = false, path = '../../pallets/common' } +nft-data-structs = { default-features = false, path = '../../primitives/nft' } +up-rpc = { path = "../../primitives/rpc" } +codec = { package = "parity-scale-codec", version = "2.0.0" } +jsonrpc-core = "15.1.0" +jsonrpc-core-client = "15.1.0" +jsonrpc-derive = "15.1.0" + +sp-api = { default-features = false, version = '4.0.0-dev', git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.10" } +sp-blockchain = { default-features = false, version = '4.0.0-dev', git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.10" } +sp-core = { default-features = false, version = '4.0.0-dev', git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.10" } +sp-rpc = { default-features = false, version = '4.0.0-dev', git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.10" } +sp-runtime = { default-features = false, version = '4.0.0-dev', git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.10" } --- /dev/null +++ b/client/rpc/src/lib.rs @@ -0,0 +1,141 @@ +use std::sync::Arc; + +use jsonrpc_core::{Error as RpcError, ErrorCode, Result}; +use jsonrpc_derive::rpc; +use nft_data_structs::{CollectionId, TokenId}; +use sp_api::{BlockId, BlockT, ProvideRuntimeApi}; +use sp_blockchain::HeaderBackend; +use up_rpc::NftApi as NftRuntimeApi; + +#[rpc] +pub trait NftApi { + #[rpc(name = "nft_accountTokens")] + fn account_tokens( + &self, + collection: CollectionId, + account: CrossAccountId, + at: Option, + ) -> Result>; + #[rpc(name = "nft_tokenExists")] + fn token_exists( + &self, + collection: CollectionId, + token: TokenId, + at: Option, + ) -> Result; + + #[rpc(name = "nft_tokenOwner")] + fn token_owner( + &self, + collection: CollectionId, + token: TokenId, + at: Option, + ) -> Result; + #[rpc(name = "nft_constMetadata")] + fn const_metadata( + &self, + collection: CollectionId, + token: TokenId, + at: Option, + ) -> Result>; + #[rpc(name = "nft_variableMetadata")] + fn variable_metadata( + &self, + collection: CollectionId, + token: TokenId, + at: Option, + ) -> Result>; + + #[rpc(name = "nft_collectionTokens")] + fn collection_tokens(&self, collection: CollectionId, at: Option) -> Result; + #[rpc(name = "nft_accountBalance")] + fn account_balance( + &self, + collection: CollectionId, + account: CrossAccountId, + at: Option, + ) -> Result; + #[rpc(name = "nft_balance")] + fn balance( + &self, + collection: CollectionId, + account: CrossAccountId, + token: TokenId, + at: Option, + ) -> Result; + #[rpc(name = "nft_allowance")] + fn allowance( + &self, + collection: CollectionId, + sender: CrossAccountId, + spender: CrossAccountId, + token: TokenId, + at: Option, + ) -> Result; +} + +pub struct Nft { + client: Arc, + _marker: std::marker::PhantomData

, +} + +impl Nft { + pub fn new(client: Arc) -> Self { + Self { + client, + _marker: Default::default(), + } + } +} + +pub enum Error { + RuntimeError, +} + +impl From for i64 { + fn from(e: Error) -> i64 { + match e { + Error::RuntimeError => 1, + } + } +} + +macro_rules! pass_method { + ($method_name:ident($($name:ident: $ty:ty),* $(,)?) -> $result:ty) => { + fn $method_name( + &self, + $( + $name: $ty, + )* + at: Option<::Hash>, + ) -> Result<$result> { + let api = self.client.runtime_api(); + let at = BlockId::hash(at.unwrap_or_else(|| self.client.info().best_hash)); + + api.$method_name(&at, $($name),*).map_err(|e| RpcError { + code: ErrorCode::ServerError(Error::RuntimeError.into()), + message: "Unable to query".into(), + data: Some(format!("{:?}", e).into()), + }) + } + }; +} + +impl NftApi<::Hash, CrossAccountId, AccountId> + for Nft +where + Block: BlockT, + C: 'static + ProvideRuntimeApi + HeaderBackend, + C::Api: NftRuntimeApi, + CrossAccountId: pallet_common::account::CrossAccountId, +{ + pass_method!(account_tokens(collection: CollectionId, account: CrossAccountId) -> Vec); + pass_method!(token_exists(collection: CollectionId, token: TokenId) -> bool); + pass_method!(token_owner(collection: CollectionId, token: TokenId) -> CrossAccountId); + pass_method!(const_metadata(collection: CollectionId, token: TokenId) -> Vec); + pass_method!(variable_metadata(collection: CollectionId, token: TokenId) -> Vec); + pass_method!(collection_tokens(collection: CollectionId) -> u32); + pass_method!(account_balance(collection: CollectionId, account: CrossAccountId) -> u32); + pass_method!(balance(collection: CollectionId, account: CrossAccountId, token: TokenId) -> u128); + pass_method!(allowance(collection: CollectionId, sender: CrossAccountId, spender: CrossAccountId, token: TokenId) -> u128); +} --- a/node/rpc/Cargo.toml +++ b/node/rpc/Cargo.toml @@ -13,7 +13,7 @@ futures = { version = "0.3.17", features = ["compat"] } jsonrpc-core = "15.1.0" jsonrpc-pubsub = "15.1.0" -# pallet-contracts-rpc = { version = "4.0.0-dev", git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.9' } +# pallet-contracts-rpc = { version = "4.0.0-dev", git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.10' } pallet-transaction-payment-rpc = { version = "4.0.0-dev", git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.10' } pallet-transaction-payment-rpc-runtime-api = { version = "4.0.0-dev", git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.10' } sc-client-api = { version = "4.0.0-dev", git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.10' } @@ -49,6 +49,8 @@ fc-mapping-sync = { default-features = false, git = "https://github.com/uniquenetwork/frontier.git", branch = "unique-polkadot-v0.9.10" } pallet-nft = { path = "../../pallets/nft" } +uc-rpc = { path = "../../client/rpc" } +up-rpc = { path = "../../primitives/rpc" } nft-runtime = { path = "../../runtime" } [features] --- a/node/rpc/src/lib.rs +++ b/node/rpc/src/lib.rs @@ -1,4 +1,4 @@ -use nft_runtime::{Hash, AccountId, Index, opaque::Block, BlockNumber, Balance}; +use nft_runtime::{AccountId, Balance, BlockNumber, CrossAccountId, Hash, Index, opaque::Block}; use fc_rpc::{OverrideHandle, RuntimeApiStorageOverride, SchemaV1Override, StorageOverride}; use fc_rpc_core::types::{FilterPool, PendingTransactions}; use jsonrpc_pubsub::manager::SubscriptionManager; @@ -7,7 +7,6 @@ backend::{AuxStore, StorageProvider}, client::BlockchainEvents, }; -use pallet_nft::NftApi; use sc_finality_grandpa::{ FinalityProofProvider, GrandpaJustificationStream, SharedAuthoritySet, SharedVoterState, }; @@ -98,9 +97,10 @@ where Block: sp_api::BlockT, C: ProvideRuntimeApi, - C::Api: pallet_nft::NftApi, + C::Api: up_rpc::NftApi, { fn code(&self, block: &sp_api::BlockId, account: sp_core::H160) -> Option> { + use up_rpc::NftApi; self.client .runtime_api() .eth_contract_code(block, account) @@ -124,7 +124,7 @@ // C::Api: pallet_contracts_rpc::ContractsRuntimeApi, C::Api: pallet_transaction_payment_rpc::TransactionPaymentRuntimeApi, C::Api: fp_rpc::EthereumRuntimeRPCApi, - C::Api: pallet_nft::NftApi, + C::Api: up_rpc::NftApi, P: TransactionPool + 'static, SC: SelectChain + 'static, B: sc_client_api::Backend + Send + Sync + 'static, @@ -135,6 +135,7 @@ EthPubSubApiServer, EthSigner, HexEncodedIdProvider, NetApi, NetApiServer, Web3Api, Web3ApiServer, }; + use uc_rpc::{NftApi, Nft}; // use pallet_contracts_rpc::{Contracts, ContractsApi}; use pallet_transaction_payment_rpc::{TransactionPayment, TransactionPaymentApi}; use substrate_frame_rpc_system::{FullSystem, SystemApi}; @@ -196,6 +197,7 @@ is_authority, max_past_logs, ))); + io.extend_with(NftApi::to_delegate(Nft::new(client.clone()))); if let Some(filter_pool) = filter_pool { io.extend_with(EthFilterApiServer::to_delegate(EthFilterApi::new( --- /dev/null +++ b/primitives/rpc/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "up-rpc" +version = "0.1.0" +edition = "2018" + +[dependencies] +pallet-common = { default-features = false, path = '../../pallets/common' } +nft-data-structs = { default-features = false, path = '../nft' } +codec = { package = "parity-scale-codec", version = "2.0.0", default-features = false, features = ["derive"] } +sp-core = { default-features = false, version = "4.0.0-dev", git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.10' } +sp-std = { default-features = false, version = "4.0.0-dev", git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.10' } +sp-api = { default-features = false, version = "4.0.0-dev", git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.10' } +sp-runtime = { default-features = false, version = '4.0.0-dev', git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.10' } + +[features] +default = ["std"] +std = [ + "codec/std", + "sp-core/std", + "sp-std/std", + "sp-api/std", + "sp-runtime/std", + "pallet-common/std", + "nft-data-structs/std", +] --- /dev/null +++ b/primitives/rpc/src/lib.rs @@ -0,0 +1,31 @@ +#![cfg_attr(not(feature = "std"), no_std)] + +use nft_data_structs::{CollectionId, TokenId}; +use sp_std::vec::Vec; +use sp_core::H160; + +sp_api::decl_runtime_apis! { + pub trait NftApi where + CrossAccountId: pallet_common::account::CrossAccountId, + { + fn account_tokens(collection: CollectionId, account: CrossAccountId) -> Vec; + fn token_exists(collection: CollectionId, token: TokenId) -> bool; + + fn token_owner(collection: CollectionId, token: TokenId) -> CrossAccountId; + fn const_metadata(collection: CollectionId, token: TokenId) -> Vec; + fn variable_metadata(collection: CollectionId, token: TokenId) -> Vec; + + fn collection_tokens(collection: CollectionId) -> u32; + fn account_balance(collection: CollectionId, account: CrossAccountId) -> u32; + fn balance(collection: CollectionId, account: CrossAccountId, token: TokenId) -> u128; + fn allowance( + collection: CollectionId, + sender: CrossAccountId, + spender: CrossAccountId, + token: TokenId, + ) -> u128; + + /// Used for ethereum integration + fn eth_contract_code(account: H160) -> Option>; + } +}