1234567891011121314151617use std::sync::Arc;1819use codec::{Decode, Encode};20use jsonrpc_core::{Error as RpcError, ErrorCode, Result};21use jsonrpc_derive::rpc;22use up_data_structs::{23 RpcCollection, CollectionId, CollectionStats, CollectionLimits, TokenId, Property,24 PropertyKeyPermission, TokenData, TokenChild,25};26use sp_api::{BlockId, BlockT, ProvideRuntimeApi, ApiExt};27use sp_blockchain::HeaderBackend;28use up_rpc::UniqueApi as UniqueRuntimeApi;293031use rmrk_rpc::RmrkApi as RmrkRuntimeApi;32use up_data_structs::{33 RmrkCollectionId, RmrkNftId, RmrkBaseId, RmrkNftChild, RmrkThemeName, RmrkResourceId,34};3536pub use rmrk_unique_rpc::RmrkApi;3738#[rpc]39pub trait UniqueApi<BlockHash, CrossAccountId, AccountId> {40 #[rpc(name = "unique_accountTokens")]41 fn account_tokens(42 &self,43 collection: CollectionId,44 account: CrossAccountId,45 at: Option<BlockHash>,46 ) -> Result<Vec<TokenId>>;47 #[rpc(name = "unique_collectionTokens")]48 fn collection_tokens(49 &self,50 collection: CollectionId,51 at: Option<BlockHash>,52 ) -> Result<Vec<TokenId>>;53 #[rpc(name = "unique_tokenExists")]54 fn token_exists(55 &self,56 collection: CollectionId,57 token: TokenId,58 at: Option<BlockHash>,59 ) -> Result<bool>;6061 #[rpc(name = "unique_tokenOwner")]62 fn token_owner(63 &self,64 collection: CollectionId,65 token: TokenId,66 at: Option<BlockHash>,67 ) -> Result<Option<CrossAccountId>>;68 #[rpc(name = "unique_topmostTokenOwner")]69 fn topmost_token_owner(70 &self,71 collection: CollectionId,72 token: TokenId,73 at: Option<BlockHash>,74 ) -> Result<Option<CrossAccountId>>;75 #[rpc(name = "unique_tokenChildren")]76 fn token_children(77 &self,78 collection: CollectionId,79 token: TokenId,80 at: Option<BlockHash>,81 ) -> Result<Vec<TokenChild>>;8283 #[rpc(name = "unique_collectionProperties")]84 fn collection_properties(85 &self,86 collection: CollectionId,87 keys: Option<Vec<String>>,88 at: Option<BlockHash>,89 ) -> Result<Vec<Property>>;9091 #[rpc(name = "unique_tokenProperties")]92 fn token_properties(93 &self,94 collection: CollectionId,95 token_id: TokenId,96 keys: Option<Vec<String>>,97 at: Option<BlockHash>,98 ) -> Result<Vec<Property>>;99100 #[rpc(name = "unique_propertyPermissions")]101 fn property_permissions(102 &self,103 collection: CollectionId,104 keys: Option<Vec<String>>,105 at: Option<BlockHash>,106 ) -> Result<Vec<PropertyKeyPermission>>;107108 #[rpc(name = "unique_tokenData")]109 fn token_data(110 &self,111 collection: CollectionId,112 token_id: TokenId,113 keys: Option<Vec<String>>,114 at: Option<BlockHash>,115 ) -> Result<TokenData<CrossAccountId>>;116117 #[rpc(name = "unique_totalSupply")]118 fn total_supply(&self, collection: CollectionId, at: Option<BlockHash>) -> Result<u32>;119 #[rpc(name = "unique_accountBalance")]120 fn account_balance(121 &self,122 collection: CollectionId,123 account: CrossAccountId,124 at: Option<BlockHash>,125 ) -> Result<u32>;126 #[rpc(name = "unique_balance")]127 fn balance(128 &self,129 collection: CollectionId,130 account: CrossAccountId,131 token: TokenId,132 at: Option<BlockHash>,133 ) -> Result<String>;134 #[rpc(name = "unique_allowance")]135 fn allowance(136 &self,137 collection: CollectionId,138 sender: CrossAccountId,139 spender: CrossAccountId,140 token: TokenId,141 at: Option<BlockHash>,142 ) -> Result<String>;143144 #[rpc(name = "unique_adminlist")]145 fn adminlist(146 &self,147 collection: CollectionId,148 at: Option<BlockHash>,149 ) -> Result<Vec<CrossAccountId>>;150 #[rpc(name = "unique_allowlist")]151 fn allowlist(152 &self,153 collection: CollectionId,154 at: Option<BlockHash>,155 ) -> Result<Vec<CrossAccountId>>;156 #[rpc(name = "unique_allowed")]157 fn allowed(158 &self,159 collection: CollectionId,160 user: CrossAccountId,161 at: Option<BlockHash>,162 ) -> Result<bool>;163 #[rpc(name = "unique_lastTokenId")]164 fn last_token_id(&self, collection: CollectionId, at: Option<BlockHash>) -> Result<TokenId>;165 #[rpc(name = "unique_collectionById")]166 fn collection_by_id(167 &self,168 collection: CollectionId,169 at: Option<BlockHash>,170 ) -> Result<Option<RpcCollection<AccountId>>>;171 #[rpc(name = "unique_collectionStats")]172 fn collection_stats(&self, at: Option<BlockHash>) -> Result<CollectionStats>;173174 #[rpc(name = "unique_nextSponsored")]175 fn next_sponsored(176 &self,177 collection: CollectionId,178 account: CrossAccountId,179 token: TokenId,180 at: Option<BlockHash>,181 ) -> Result<Option<u64>>;182 #[rpc(name = "unique_effectiveCollectionLimits")]183 fn effective_collection_limits(184 &self,185 collection_id: CollectionId,186 at: Option<BlockHash>,187 ) -> Result<Option<CollectionLimits>>;188}189190mod rmrk_unique_rpc {191 use super::*;192193 #[rpc(server)]194 pub trait RmrkApi<195 BlockHash,196 AccountId,197 CollectionInfo,198 NftInfo,199 ResourceInfo,200 PropertyInfo,201 BaseInfo,202 PartType,203 Theme,204 >205 {206 #[rpc(name = "rmrk_lastCollectionIdx")]207 208 fn last_collection_idx(&self, at: Option<BlockHash>) -> Result<RmrkCollectionId>;209210 #[rpc(name = "rmrk_collectionById")]211 212 fn collection_by_id(213 &self,214 id: RmrkCollectionId,215 at: Option<BlockHash>,216 ) -> Result<Option<CollectionInfo>>;217218 #[rpc(name = "rmrk_nftById")]219 220 fn nft_by_id(221 &self,222 collection_id: RmrkCollectionId,223 nft_id: RmrkNftId,224 at: Option<BlockHash>,225 ) -> Result<Option<NftInfo>>;226227 #[rpc(name = "rmrk_accountTokens")]228 229 fn account_tokens(230 &self,231 account_id: AccountId,232 collection_id: RmrkCollectionId,233 at: Option<BlockHash>,234 ) -> Result<Vec<RmrkNftId>>;235236 #[rpc(name = "rmrk_nftChildren")]237 238 fn nft_children(239 &self,240 collection_id: RmrkCollectionId,241 nft_id: RmrkNftId,242 at: Option<BlockHash>,243 ) -> Result<Vec<RmrkNftChild>>;244245 #[rpc(name = "rmrk_collectionProperties")]246 247 fn collection_properties(248 &self,249 collection_id: RmrkCollectionId,250 filter_keys: Option<Vec<String>>,251 at: Option<BlockHash>,252 ) -> Result<Vec<PropertyInfo>>;253254 #[rpc(name = "rmrk_nftProperties")]255 256 fn nft_properties(257 &self,258 collection_id: RmrkCollectionId,259 nft_id: RmrkNftId,260 filter_keys: Option<Vec<String>>,261 at: Option<BlockHash>,262 ) -> Result<Vec<PropertyInfo>>;263264 #[rpc(name = "rmrk_nftResources")]265 266 fn nft_resources(267 &self,268 collection_id: RmrkCollectionId,269 nft_id: RmrkNftId,270 at: Option<BlockHash>,271 ) -> Result<Vec<ResourceInfo>>;272273 #[rpc(name = "rmrk_nftResourcePriorities")]274 275 fn nft_resource_priorities(276 &self,277 collection_id: RmrkCollectionId,278 nft_id: RmrkNftId,279 at: Option<BlockHash>,280 ) -> Result<Vec<RmrkResourceId>>;281282 #[rpc(name = "rmrk_base")]283 284 fn base(&self, base_id: RmrkBaseId, at: Option<BlockHash>) -> Result<Option<BaseInfo>>;285286 #[rpc(name = "rmrk_baseParts")]287 288 fn base_parts(&self, base_id: RmrkBaseId, at: Option<BlockHash>) -> Result<Vec<PartType>>;289290 #[rpc(name = "rmrk_themeNames")]291 fn theme_names(292 &self,293 base_id: RmrkBaseId,294 at: Option<BlockHash>,295 ) -> Result<Vec<RmrkThemeName>>;296297 #[rpc(name = "rmrk_themes")]298 fn theme(299 &self,300 base_id: RmrkBaseId,301 theme_name: String,302 filter_keys: Option<Vec<String>>,303 at: Option<BlockHash>,304 ) -> Result<Option<Theme>>;305 }306}307308pub struct Unique<C, P> {309 client: Arc<C>,310 _marker: std::marker::PhantomData<P>,311}312313impl<C, P> Unique<C, P> {314 pub fn new(client: Arc<C>) -> Self {315 Self {316 client,317 _marker: Default::default(),318 }319 }320}321322pub enum Error {323 RuntimeError,324}325326impl From<Error> for i64 {327 fn from(e: Error) -> i64 {328 match e {329 Error::RuntimeError => 1,330 }331 }332}333334macro_rules! pass_method {335 (336 $method_name:ident(337 $($(#[map(|$map_arg:ident| $map:expr)])? $name:ident: $ty:ty),* $(,)?338 ) -> $result:ty $(=> $mapper:expr)?,339 340 $runtime_api_macro:ident341 $(; changed_in $ver:expr, $changed_method_name:ident ($($changed_name:expr), * $(,)?) => $fixer:expr)*342 ) => {343 fn $method_name(344 &self,345 $(346 $name: $ty,347 )*348 at: Option<<Block as BlockT>::Hash>,349 ) -> Result<$result> {350 let api = self.client.runtime_api();351 let at = BlockId::hash(at.unwrap_or_else(|| self.client.info().best_hash));352 let _api_version = if let Ok(Some(api_version)) =353 api.api_version::<$runtime_api_macro!()>(&at)354 {355 api_version356 } else {357 358 return Err(RpcError {359 code: ErrorCode::InvalidParams,360 message: "Api is not available".into(),361 data: None,362 })363 };364365 let result = $(if _api_version < $ver {366 api.$changed_method_name(&at, $($changed_name),*).map(|r| r.map($fixer))367 } else)*368 { api.$method_name(&at, $($((|$map_arg: $ty| $map))? ($name)),*) };369370 let result = result.map_err(|e| RpcError {371 code: ErrorCode::ServerError(Error::RuntimeError.into()),372 message: "Unable to query".into(),373 data: Some(format!("{:?}", e).into()),374 })?;375 result.map_err(|e| RpcError {376 code: ErrorCode::InvalidParams,377 message: "Runtime returned error".into(),378 data: Some(format!("{:?}", e).into()),379 })$(.map($mapper))?380 }381 };382}383384macro_rules! unique_api {385 () => {386 dyn UniqueRuntimeApi<Block, CrossAccountId, AccountId>387 };388}389390macro_rules! rmrk_api {391 () => {392 dyn RmrkRuntimeApi<Block, AccountId, CollectionInfo, NftInfo, ResourceInfo, PropertyInfo, BaseInfo, PartType, Theme>393 };394}395396#[allow(deprecated)]397impl<C, Block, CrossAccountId, AccountId>398 UniqueApi<<Block as BlockT>::Hash, CrossAccountId, AccountId> for Unique<C, Block>399where400 Block: BlockT,401 AccountId: Decode,402 C: 'static + ProvideRuntimeApi<Block> + HeaderBackend<Block>,403 C::Api: UniqueRuntimeApi<Block, CrossAccountId, AccountId>,404 CrossAccountId: pallet_evm::account::CrossAccountId<AccountId>,405{406 pass_method!(407 account_tokens(collection: CollectionId, account: CrossAccountId) -> Vec<TokenId>, unique_api408 );409 pass_method!(410 collection_tokens(collection: CollectionId) -> Vec<TokenId>, unique_api411 );412 pass_method!(413 token_exists(collection: CollectionId, token: TokenId) -> bool, unique_api414 );415 pass_method!(416 token_owner(collection: CollectionId, token: TokenId) -> Option<CrossAccountId>, unique_api417 );418 pass_method!(419 topmost_token_owner(collection: CollectionId, token: TokenId) -> Option<CrossAccountId>, unique_api420 );421 pass_method!(token_children(collection: CollectionId, token: TokenId) -> Vec<TokenChild>, unique_api);422 pass_method!(total_supply(collection: CollectionId) -> u32, unique_api);423 pass_method!(account_balance(collection: CollectionId, account: CrossAccountId) -> u32, unique_api);424 pass_method!(balance(collection: CollectionId, account: CrossAccountId, token: TokenId) -> String => |v| v.to_string(), unique_api);425 pass_method!(426 allowance(collection: CollectionId, sender: CrossAccountId, spender: CrossAccountId, token: TokenId) -> String => |v| v.to_string(),427 unique_api428 );429430 pass_method!(collection_properties(431 collection: CollectionId,432433 #[map(|keys| string_keys_to_bytes_keys(keys))]434 keys: Option<Vec<String>>435 ) -> Vec<Property>, unique_api);436437 pass_method!(token_properties(438 collection: CollectionId,439 token_id: TokenId,440441 #[map(|keys| string_keys_to_bytes_keys(keys))]442 keys: Option<Vec<String>>443 ) -> Vec<Property>, unique_api);444445 pass_method!(property_permissions(446 collection: CollectionId,447448 #[map(|keys| string_keys_to_bytes_keys(keys))]449 keys: Option<Vec<String>>450 ) -> Vec<PropertyKeyPermission>, unique_api);451452 pass_method!(token_data(453 collection: CollectionId,454 token_id: TokenId,455456 #[map(|keys| string_keys_to_bytes_keys(keys))]457 keys: Option<Vec<String>>,458 ) -> TokenData<CrossAccountId>, unique_api);459460 pass_method!(adminlist(collection: CollectionId) -> Vec<CrossAccountId>, unique_api);461 pass_method!(allowlist(collection: CollectionId) -> Vec<CrossAccountId>, unique_api);462 pass_method!(allowed(collection: CollectionId, user: CrossAccountId) -> bool, unique_api);463 pass_method!(last_token_id(collection: CollectionId) -> TokenId, unique_api);464 pass_method!(collection_by_id(collection: CollectionId) -> Option<RpcCollection<AccountId>>, unique_api);465 pass_method!(collection_stats() -> CollectionStats, unique_api);466 pass_method!(next_sponsored(collection: CollectionId, account: CrossAccountId, token: TokenId) -> Option<u64>, unique_api);467 pass_method!(effective_collection_limits(collection_id: CollectionId) -> Option<CollectionLimits>, unique_api);468}469470#[allow(deprecated)]471impl<472 C,473 Block,474 AccountId,475 CollectionInfo,476 NftInfo,477 ResourceInfo,478 PropertyInfo,479 BaseInfo,480 PartType,481 Theme,482 >483 rmrk_unique_rpc::RmrkApi<484 <Block as BlockT>::Hash,485 AccountId,486 CollectionInfo,487 NftInfo,488 ResourceInfo,489 PropertyInfo,490 BaseInfo,491 PartType,492 Theme,493 > for Unique<C, Block>494where495 C: Send + Sync + 'static + ProvideRuntimeApi<Block> + HeaderBackend<Block>,496 C::Api: RmrkRuntimeApi<497 Block,498 AccountId,499 CollectionInfo,500 NftInfo,501 ResourceInfo,502 PropertyInfo,503 BaseInfo,504 PartType,505 Theme,506 >,507 AccountId: Decode + Encode,508 CollectionInfo: Decode,509 NftInfo: Decode,510 ResourceInfo: Decode,511 PropertyInfo: Decode,512 BaseInfo: Decode,513 PartType: Decode,514 Theme: Decode,515 Block: BlockT,516{517 pass_method!(last_collection_idx() -> RmrkCollectionId, rmrk_api);518 pass_method!(collection_by_id(id: RmrkCollectionId) -> Option<CollectionInfo>, rmrk_api);519 pass_method!(nft_by_id(collection_id: RmrkCollectionId, nft_id: RmrkNftId) -> Option<NftInfo>, rmrk_api);520 pass_method!(account_tokens(account_id: AccountId, collection_id: RmrkCollectionId) -> Vec<RmrkNftId>, rmrk_api);521 pass_method!(nft_children(collection_id: RmrkCollectionId, nft_id: RmrkNftId) -> Vec<RmrkNftChild>, rmrk_api);522 pass_method!(523 collection_properties(524 collection_id: RmrkCollectionId,525526 #[map(|keys| string_keys_to_bytes_keys(keys))]527 filter_keys: Option<Vec<String>>528 ) -> Vec<PropertyInfo>,529 rmrk_api530 );531 pass_method!(532 nft_properties(533 collection_id: RmrkCollectionId,534 nft_id: RmrkNftId,535536 #[map(|keys| string_keys_to_bytes_keys(keys))]537 filter_keys: Option<Vec<String>>538 ) -> Vec<PropertyInfo>,539 rmrk_api540 );541 pass_method!(nft_resources(collection_id: RmrkCollectionId, nft_id: RmrkNftId) -> Vec<ResourceInfo>, rmrk_api);542 pass_method!(nft_resource_priorities(collection_id: RmrkCollectionId, nft_id: RmrkNftId) -> Vec<RmrkResourceId>, rmrk_api);543 pass_method!(base(base_id: RmrkBaseId) -> Option<BaseInfo>, rmrk_api);544 pass_method!(base_parts(base_id: RmrkBaseId) -> Vec<PartType>, rmrk_api);545 pass_method!(theme_names(base_id: RmrkBaseId) -> Vec<RmrkThemeName>, rmrk_api);546 pass_method!(547 theme(548 base_id: RmrkBaseId,549550 #[map(|n| n.into_bytes())]551 theme_name: String,552553 #[map(|keys| string_keys_to_bytes_keys(keys))]554 filter_keys: Option<Vec<String>>555 ) -> Option<Theme>, rmrk_api);556}557558fn string_keys_to_bytes_keys(keys: Option<Vec<String>>) -> Option<Vec<Vec<u8>>> {559 keys.map(|keys| keys.into_iter().map(|key| key.into_bytes()).collect())560}