1234567891011121314151617use std::sync::Arc;1819use codec::Decode;20use jsonrpc_core::{Error as RpcError, ErrorCode, Result};21use jsonrpc_derive::rpc;22use up_data_structs::{Collection, CollectionId, CollectionStats, TokenId};23use sp_api::{BlockId, BlockT, ProvideRuntimeApi, ApiExt};24use sp_blockchain::HeaderBackend;25use up_rpc::UniqueApi as UniqueRuntimeApi;2627#[rpc]28pub trait UniqueApi<BlockHash, CrossAccountId, AccountId> {29 #[rpc(name = "unique_accountTokens")]30 fn account_tokens(31 &self,32 collection: CollectionId,33 account: CrossAccountId,34 at: Option<BlockHash>,35 ) -> Result<Vec<TokenId>>;36 #[rpc(name = "unique_tokenExists")]37 fn token_exists(38 &self,39 collection: CollectionId,40 token: TokenId,41 at: Option<BlockHash>,42 ) -> Result<bool>;4344 #[rpc(name = "unique_tokenOwner")]45 fn token_owner(46 &self,47 collection: CollectionId,48 token: TokenId,49 at: Option<BlockHash>,50 ) -> Result<Option<CrossAccountId>>;51 #[rpc(name = "unique_constMetadata")]52 fn const_metadata(53 &self,54 collection: CollectionId,55 token: TokenId,56 at: Option<BlockHash>,57 ) -> Result<Vec<u8>>;58 #[rpc(name = "unique_variableMetadata")]59 fn variable_metadata(60 &self,61 collection: CollectionId,62 token: TokenId,63 at: Option<BlockHash>,64 ) -> Result<Vec<u8>>;6566 #[rpc(name = "unique_collectionTokens")]67 fn collection_tokens(&self, collection: CollectionId, at: Option<BlockHash>) -> Result<u32>;68 #[rpc(name = "unique_accountBalance")]69 fn account_balance(70 &self,71 collection: CollectionId,72 account: CrossAccountId,73 at: Option<BlockHash>,74 ) -> Result<u32>;75 #[rpc(name = "unique_balance")]76 fn balance(77 &self,78 collection: CollectionId,79 account: CrossAccountId,80 token: TokenId,81 at: Option<BlockHash>,82 ) -> Result<String>;83 #[rpc(name = "unique_allowance")]84 fn allowance(85 &self,86 collection: CollectionId,87 sender: CrossAccountId,88 spender: CrossAccountId,89 token: TokenId,90 at: Option<BlockHash>,91 ) -> Result<String>;9293 #[rpc(name = "unique_adminlist")]94 fn adminlist(95 &self,96 collection: CollectionId,97 at: Option<BlockHash>,98 ) -> Result<Vec<CrossAccountId>>;99 #[rpc(name = "unique_allowlist")]100 fn allowlist(101 &self,102 collection: CollectionId,103 at: Option<BlockHash>,104 ) -> Result<Vec<CrossAccountId>>;105 #[rpc(name = "unique_allowed")]106 fn allowed(107 &self,108 collection: CollectionId,109 user: CrossAccountId,110 at: Option<BlockHash>,111 ) -> Result<bool>;112 #[rpc(name = "unique_lastTokenId")]113 fn last_token_id(&self, collection: CollectionId, at: Option<BlockHash>) -> Result<TokenId>;114 #[rpc(name = "unique_collectionById")]115 fn collection_by_id(116 &self,117 collection: CollectionId,118 at: Option<BlockHash>,119 ) -> Result<Option<Collection<AccountId>>>;120 #[rpc(name = "unique_collectionStats")]121 fn collection_stats(&self, at: Option<BlockHash>) -> Result<CollectionStats>;122123 #[rpc(name = "unique_nextSponsored")]124 fn next_sponsored(125 &self,126 collection: CollectionId,127 account: CrossAccountId,128 token: TokenId,129 at: Option<BlockHash>,130 ) -> Result<Option<u64>>;131}132133pub struct Unique<C, P> {134 client: Arc<C>,135 _marker: std::marker::PhantomData<P>,136}137138impl<C, P> Unique<C, P> {139 pub fn new(client: Arc<C>) -> Self {140 Self {141 client,142 _marker: Default::default(),143 }144 }145}146147pub enum Error {148 RuntimeError,149}150151impl From<Error> for i64 {152 fn from(e: Error) -> i64 {153 match e {154 Error::RuntimeError => 1,155 }156 }157}158159macro_rules! pass_method {160 (161 $method_name:ident($($name:ident: $ty:ty),* $(,)?) -> $result:ty $(=> $mapper:expr)?162 $(; changed_in $ver:expr, $changed_method_name:ident ($($changed_name:expr), * $(,)?) => $fixer:expr)*163 ) => {164 fn $method_name(165 &self,166 $(167 $name: $ty,168 )*169 at: Option<<Block as BlockT>::Hash>,170 ) -> Result<$result> {171 let api = self.client.runtime_api();172 let at = BlockId::hash(at.unwrap_or_else(|| self.client.info().best_hash));173 let _api_version = if let Ok(Some(api_version)) =174 api.api_version::<dyn UniqueRuntimeApi<Block, CrossAccountId, AccountId>>(&at)175 {176 api_version177 } else {178 179 return Err(RpcError {180 code: ErrorCode::InvalidParams,181 message: "Api is not available".into(),182 data: None,183 })184 };185186 let result = $(if _api_version < $ver {187 api.$changed_method_name(&at, $($changed_name),*).map(|r| r.map($fixer))188 } else)*189 { api.$method_name(&at, $($name),*) };190191 let result = result.map_err(|e| RpcError {192 code: ErrorCode::ServerError(Error::RuntimeError.into()),193 message: "Unable to query".into(),194 data: Some(format!("{:?}", e).into()),195 })?;196 result.map_err(|e| RpcError {197 code: ErrorCode::InvalidParams,198 message: "Runtime returned error".into(),199 data: Some(format!("{:?}", e).into()),200 })$(.map($mapper))?201 }202 };203}204205#[allow(deprecated)]206impl<C, Block, CrossAccountId, AccountId>207 UniqueApi<<Block as BlockT>::Hash, CrossAccountId, AccountId> for Unique<C, Block>208where209 Block: BlockT,210 AccountId: Decode,211 C: 'static + ProvideRuntimeApi<Block> + HeaderBackend<Block>,212 C::Api: UniqueRuntimeApi<Block, CrossAccountId, AccountId>,213 CrossAccountId: pallet_common::account::CrossAccountId<AccountId>,214{215 pass_method!(account_tokens(collection: CollectionId, account: CrossAccountId) -> Vec<TokenId>);216 pass_method!(token_exists(collection: CollectionId, token: TokenId) -> bool);217 pass_method!(218 token_owner(collection: CollectionId, token: TokenId) -> Option<CrossAccountId>;219 changed_in 2, token_owner_before_version_2(collection, token) => |u| Some(u)220 );221 pass_method!(const_metadata(collection: CollectionId, token: TokenId) -> Vec<u8>);222 pass_method!(variable_metadata(collection: CollectionId, token: TokenId) -> Vec<u8>);223 pass_method!(collection_tokens(collection: CollectionId) -> u32);224 pass_method!(account_balance(collection: CollectionId, account: CrossAccountId) -> u32);225 pass_method!(balance(collection: CollectionId, account: CrossAccountId, token: TokenId) -> String => |v| v.to_string());226 pass_method!(allowance(collection: CollectionId, sender: CrossAccountId, spender: CrossAccountId, token: TokenId) -> String => |v| v.to_string());227228 pass_method!(adminlist(collection: CollectionId) -> Vec<CrossAccountId>);229 pass_method!(allowlist(collection: CollectionId) -> Vec<CrossAccountId>);230 pass_method!(allowed(collection: CollectionId, user: CrossAccountId) -> bool);231 pass_method!(last_token_id(collection: CollectionId) -> TokenId);232 pass_method!(collection_by_id(collection: CollectionId) -> Option<Collection<AccountId>>);233 pass_method!(collection_stats() -> CollectionStats);234 pass_method!(next_sponsored(collection: CollectionId, account: CrossAccountId, token: TokenId) -> Option<u64>);235}