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,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 rmrk_traits::{33 primitives::{34 CollectionId as RmrkCollectionId,35 NftId as RmrkNftId,36 BaseId as RmrkBaseId,37 38 },39 NftChild as RmrkNftChild,40};41pub use unique_runtime_common::types::{RmrkThemeName, RmrkPropertyKey, RmrkResourceId};4243pub use rmrk::RmrkApi;4445#[rpc]46pub trait UniqueApi<BlockHash, CrossAccountId, AccountId> {47 #[rpc(name = "unique_accountTokens")]48 fn account_tokens(49 &self,50 collection: CollectionId,51 account: CrossAccountId,52 at: Option<BlockHash>,53 ) -> Result<Vec<TokenId>>;54 #[rpc(name = "unique_collectionTokens")]55 fn collection_tokens(56 &self,57 collection: CollectionId,58 at: Option<BlockHash>,59 ) -> Result<Vec<TokenId>>;60 #[rpc(name = "unique_tokenExists")]61 fn token_exists(62 &self,63 collection: CollectionId,64 token: TokenId,65 at: Option<BlockHash>,66 ) -> Result<bool>;6768 #[rpc(name = "unique_tokenOwner")]69 fn token_owner(70 &self,71 collection: CollectionId,72 token: TokenId,73 at: Option<BlockHash>,74 ) -> Result<Option<CrossAccountId>>;75 #[rpc(name = "unique_topmostTokenOwner")]76 fn topmost_token_owner(77 &self,78 collection: CollectionId,79 token: TokenId,80 at: Option<BlockHash>,81 ) -> Result<Option<CrossAccountId>>;82 #[rpc(name = "unique_constMetadata")]83 fn const_metadata(84 &self,85 collection: CollectionId,86 token: TokenId,87 at: Option<BlockHash>,88 ) -> Result<Vec<u8>>;89 #[rpc(name = "unique_variableMetadata")]90 fn variable_metadata(91 &self,92 collection: CollectionId,93 token: TokenId,94 at: Option<BlockHash>,95 ) -> Result<Vec<u8>>;9697 #[rpc(name = "unique_collectionProperties")]98 fn collection_properties(99 &self,100 collection: CollectionId,101 keys: Vec<String>,102 at: Option<BlockHash>,103 ) -> Result<Vec<Property>>;104105 #[rpc(name = "unique_tokenProperties")]106 fn token_properties(107 &self,108 collection: CollectionId,109 token_id: TokenId,110 properties: Vec<String>,111 at: Option<BlockHash>,112 ) -> Result<Vec<Property>>;113114 #[rpc(name = "unique_propertyPermissions")]115 fn property_permissions(116 &self,117 collection: CollectionId,118 keys: Vec<String>,119 at: Option<BlockHash>,120 ) -> Result<Vec<PropertyKeyPermission>>;121122 #[rpc(name = "unique_tokenData")]123 fn token_data(124 &self,125 collection: CollectionId,126 token_id: TokenId,127 keys: Vec<String>,128 at: Option<BlockHash>,129 ) -> Result<TokenData<CrossAccountId>>;130131 #[rpc(name = "unique_totalSupply")]132 fn total_supply(&self, collection: CollectionId, at: Option<BlockHash>) -> Result<u32>;133 #[rpc(name = "unique_accountBalance")]134 fn account_balance(135 &self,136 collection: CollectionId,137 account: CrossAccountId,138 at: Option<BlockHash>,139 ) -> Result<u32>;140 #[rpc(name = "unique_balance")]141 fn balance(142 &self,143 collection: CollectionId,144 account: CrossAccountId,145 token: TokenId,146 at: Option<BlockHash>,147 ) -> Result<String>;148 #[rpc(name = "unique_allowance")]149 fn allowance(150 &self,151 collection: CollectionId,152 sender: CrossAccountId,153 spender: CrossAccountId,154 token: TokenId,155 at: Option<BlockHash>,156 ) -> Result<String>;157158 #[rpc(name = "unique_adminlist")]159 fn adminlist(160 &self,161 collection: CollectionId,162 at: Option<BlockHash>,163 ) -> Result<Vec<CrossAccountId>>;164 #[rpc(name = "unique_allowlist")]165 fn allowlist(166 &self,167 collection: CollectionId,168 at: Option<BlockHash>,169 ) -> Result<Vec<CrossAccountId>>;170 #[rpc(name = "unique_allowed")]171 fn allowed(172 &self,173 collection: CollectionId,174 user: CrossAccountId,175 at: Option<BlockHash>,176 ) -> Result<bool>;177 #[rpc(name = "unique_lastTokenId")]178 fn last_token_id(&self, collection: CollectionId, at: Option<BlockHash>) -> Result<TokenId>;179 #[rpc(name = "unique_collectionById")]180 fn collection_by_id(181 &self,182 collection: CollectionId,183 at: Option<BlockHash>,184 ) -> Result<Option<RpcCollection<AccountId>>>;185 #[rpc(name = "unique_collectionStats")]186 fn collection_stats(&self, at: Option<BlockHash>) -> Result<CollectionStats>;187188 #[rpc(name = "unique_nextSponsored")]189 fn next_sponsored(190 &self,191 collection: CollectionId,192 account: CrossAccountId,193 token: TokenId,194 at: Option<BlockHash>,195 ) -> Result<Option<u64>>;196 #[rpc(name = "unique_effectiveCollectionLimits")]197 fn effective_collection_limits(198 &self,199 collection_id: CollectionId,200 at: Option<BlockHash>,201 ) -> Result<Option<CollectionLimits>>;202}203204mod rmrk {205 use super::*;206207 #[rpc]208 pub trait RmrkApi<209 BlockHash,210 AccountId,211 CollectionInfo,212 NftInfo,213 ResourceInfo,214 PropertyInfo,215 BaseInfo,216 PartType,217 Theme,218 >219 {220 #[rpc(name = "rmrk_lastCollectionIdx")]221 222 fn last_collection_idx(&self, at: Option<BlockHash>) -> Result<RmrkCollectionId>;223224 #[rpc(name = "rmrk_collectionById")]225 226 fn collection_by_id(227 &self,228 id: RmrkCollectionId,229 at: Option<BlockHash>,230 ) -> Result<Option<CollectionInfo>>;231232 #[rpc(name = "rmrk_nftById")]233 234 fn nft_by_id(235 &self,236 collection_id: RmrkCollectionId,237 nft_id: RmrkNftId,238 at: Option<BlockHash>,239 ) -> Result<Option<NftInfo>>;240241 #[rpc(name = "rmrk_accountTokens")]242 243 fn account_tokens(244 &self,245 account_id: AccountId,246 collection_id: RmrkCollectionId,247 at: Option<BlockHash>,248 ) -> Result<Vec<RmrkNftId>>;249250 #[rpc(name = "rmrk_nftChildren")]251 252 fn nft_children(253 &self,254 collection_id: RmrkCollectionId,255 nft_id: RmrkNftId,256 at: Option<BlockHash>,257 ) -> Result<Vec<RmrkNftChild>>;258259 #[rpc(name = "rmrk_collectionProperties")]260 261 fn collection_properties(262 &self,263 collection_id: RmrkCollectionId,264 filter_keys: Option<Vec<RmrkPropertyKey>>, 265 at: Option<BlockHash>,266 ) -> Result<Vec<PropertyInfo>>;267268 #[rpc(name = "rmrk_nftProperties")]269 270 fn nft_properties(271 &self,272 collection_id: RmrkCollectionId,273 nft_id: RmrkNftId,274 filter_keys: Option<Vec<RmrkPropertyKey>>,275 at: Option<BlockHash>,276 ) -> Result<Vec<PropertyInfo>>;277278 #[rpc(name = "rmrk_nftResources")]279 280 fn nft_resources(281 &self,282 collection_id: RmrkCollectionId,283 nft_id: RmrkNftId,284 at: Option<BlockHash>,285 ) -> Result<Vec<ResourceInfo>>;286287 #[rpc(name = "rmrk_nftResourcePriorities")]288 289 fn nft_resource_priorities(290 &self,291 collection_id: RmrkCollectionId,292 nft_id: RmrkNftId,293 at: Option<BlockHash>,294 ) -> Result<Vec<RmrkResourceId>>;295296 #[rpc(name = "rmrk_base")]297 298 fn base(&self, base_id: RmrkBaseId, at: Option<BlockHash>) -> Result<Option<BaseInfo>>;299300 #[rpc(name = "rmrk_baseParts")]301 302 fn base_parts(&self, base_id: RmrkBaseId, at: Option<BlockHash>) -> Result<Vec<PartType>>;303304 #[rpc(name = "rmrk_themeNames")]305 fn theme_names(306 &self,307 base_id: RmrkBaseId,308 at: Option<BlockHash>,309 ) -> Result<Vec<RmrkThemeName>>;310311 #[rpc(name = "rmrk_themes")]312 fn theme(313 &self,314 base_id: RmrkBaseId,315 theme_name: RmrkThemeName, 316 filter_keys: Option<Vec<RmrkPropertyKey>>,317 at: Option<BlockHash>,318 ) -> Result<Option<Theme>>;319 }320}321322323pub struct Unique<C, P> {324 client: Arc<C>,325 _marker: std::marker::PhantomData<P>,326}327328impl<C, P> Unique<C, P> {329 pub fn new(client: Arc<C>) -> Self {330 Self {331 client,332 _marker: Default::default(),333 }334 }335}336337pub enum Error {338 RuntimeError,339}340341impl From<Error> for i64 {342 fn from(e: Error) -> i64 {343 match e {344 Error::RuntimeError => 1,345 }346 }347}348349macro_rules! pass_method {350 (351 $method_name:ident(352 $($(#[map(|$map_arg:ident| $map:expr)])? $name:ident: $ty:ty),* $(,)?353 ) -> $result:ty $(=> $mapper:expr)?,354 355 $runtime_api_macro:ident356 $(; changed_in $ver:expr, $changed_method_name:ident ($($changed_name:expr), * $(,)?) => $fixer:expr)*357 ) => {358 fn $method_name(359 &self,360 $(361 $name: $ty,362 )*363 at: Option<<Block as BlockT>::Hash>,364 ) -> Result<$result> {365 let api = self.client.runtime_api();366 let at = BlockId::hash(at.unwrap_or_else(|| self.client.info().best_hash));367 let _api_version = if let Ok(Some(api_version)) =368 api.api_version::<$runtime_api_macro!()>(&at)369 {370 api_version371 } else {372 373 return Err(RpcError {374 code: ErrorCode::InvalidParams,375 message: "Api is not available".into(),376 data: None,377 })378 };379380 let result = $(if _api_version < $ver {381 api.$changed_method_name(&at, $($changed_name),*).map(|r| r.map($fixer))382 } else)*383 { api.$method_name(&at, $($((|$map_arg: $ty| $map))? ($name)),*) };384385 let result = result.map_err(|e| RpcError {386 code: ErrorCode::ServerError(Error::RuntimeError.into()),387 message: "Unable to query".into(),388 data: Some(format!("{:?}", e).into()),389 })?;390 result.map_err(|e| RpcError {391 code: ErrorCode::InvalidParams,392 message: "Runtime returned error".into(),393 data: Some(format!("{:?}", e).into()),394 })$(.map($mapper))?395 }396 };397}398399macro_rules! unique_api {400 () => {401 dyn UniqueRuntimeApi<Block, CrossAccountId, AccountId>402 };403}404405macro_rules! rmrk_api {406 () => {407 dyn RmrkRuntimeApi<Block, AccountId, CollectionInfo, NftInfo, ResourceInfo, PropertyInfo, BaseInfo, PartType, Theme>408 };409}410411#[allow(deprecated)]412impl<C, Block, CrossAccountId, AccountId>413 UniqueApi<<Block as BlockT>::Hash, CrossAccountId, AccountId> for Unique<C, Block>414where415 Block: BlockT,416 AccountId: Decode,417 C: 'static + ProvideRuntimeApi<Block> + HeaderBackend<Block>,418 C::Api: UniqueRuntimeApi<Block, CrossAccountId, AccountId>, 419 CrossAccountId: pallet_evm::account::CrossAccountId<AccountId>,420{421 pass_method!(422 account_tokens(collection: CollectionId, account: CrossAccountId) -> Vec<TokenId>, unique_api423 );424 pass_method!(425 collection_tokens(collection: CollectionId) -> Vec<TokenId>, unique_api426 );427 pass_method!(428 token_exists(collection: CollectionId, token: TokenId) -> bool, unique_api429 );430 pass_method!(431 token_owner(collection: CollectionId, token: TokenId) -> Option<CrossAccountId>, unique_api432 );433 pass_method!(434 topmost_token_owner(collection: CollectionId, token: TokenId) -> Option<CrossAccountId>, unique_api435 );436 437438439440441 pass_method!(442 const_metadata(collection: CollectionId, token: TokenId) -> Vec<u8>, unique_api443 );444 pass_method!(445 variable_metadata(collection: CollectionId, token: TokenId) -> Vec<u8>,446 unique_api447 );448 pass_method!(total_supply(collection: CollectionId) -> u32, unique_api);449 pass_method!(account_balance(collection: CollectionId, account: CrossAccountId) -> u32, unique_api);450 pass_method!(balance(collection: CollectionId, account: CrossAccountId, token: TokenId) -> String => |v| v.to_string(), unique_api);451 pass_method!(452 allowance(collection: CollectionId, sender: CrossAccountId, spender: CrossAccountId, token: TokenId) -> String => |v| v.to_string(),453 unique_api454 );455 pass_method!(topmost_token_owner(collection: CollectionId, token: TokenId) -> Option<CrossAccountId>, unique_api);456 pass_method!(const_metadata(collection: CollectionId, token: TokenId) -> Vec<u8>, unique_api);457 pass_method!(variable_metadata(collection: CollectionId, token: TokenId) -> Vec<u8>, unique_api);458459 pass_method!(collection_properties(460 collection: CollectionId,461462 #[map(|keys| string_keys_to_bytes_keys(keys))]463 keys: Vec<String>464 ) -> Vec<Property>, unique_api);465466 pass_method!(token_properties(467 collection: CollectionId,468 token_id: TokenId,469470 #[map(|keys| string_keys_to_bytes_keys(keys))]471 properties: Vec<String>472 ) -> Vec<Property>, unique_api);473474 pass_method!(property_permissions(475 collection: CollectionId,476477 #[map(|keys| string_keys_to_bytes_keys(keys))]478 keys: Vec<String>479 ) -> Vec<PropertyKeyPermission>, unique_api);480481 pass_method!(token_data(482 collection: CollectionId,483 token_id: TokenId,484485 #[map(|keys| string_keys_to_bytes_keys(keys))]486 keys: Vec<String>,487 ) -> TokenData<CrossAccountId>, unique_api);488489 pass_method!(total_supply(collection: CollectionId) -> u32, unique_api);490 pass_method!(account_balance(collection: CollectionId, account: CrossAccountId) -> u32, unique_api);491 pass_method!(balance(collection: CollectionId, account: CrossAccountId, token: TokenId) -> String => |v| v.to_string(), unique_api);492 pass_method!(493 allowance(collection: CollectionId, sender: CrossAccountId, spender: CrossAccountId, token: TokenId) -> String => |v| v.to_string(), 494 unique_api495 ); 496497 pass_method!(adminlist(collection: CollectionId) -> Vec<CrossAccountId>, unique_api);498 pass_method!(allowlist(collection: CollectionId) -> Vec<CrossAccountId>, unique_api);499 pass_method!(allowed(collection: CollectionId, user: CrossAccountId) -> bool, unique_api);500 pass_method!(last_token_id(collection: CollectionId) -> TokenId, unique_api);501 pass_method!(collection_by_id(collection: CollectionId) -> Option<RpcCollection<AccountId>>, unique_api);502 pass_method!(collection_stats() -> CollectionStats, unique_api);503 pass_method!(next_sponsored(collection: CollectionId, account: CrossAccountId, token: TokenId) -> Option<u64>, unique_api);504 pass_method!(effective_collection_limits(collection_id: CollectionId) -> Option<CollectionLimits>, unique_api);505}506507#[allow(deprecated)]508impl<509 C,510 Block,511 AccountId,512 CollectionInfo,513 NftInfo,514 ResourceInfo,515 PropertyInfo,516 BaseInfo,517 PartType,518 Theme,519 >520 rmrk::RmrkApi<521 <Block as BlockT>::Hash,522 AccountId,523 CollectionInfo,524 NftInfo,525 ResourceInfo,526 PropertyInfo,527 BaseInfo,528 PartType,529 Theme,530 > for Unique<C, Block>531where532 C: Send + Sync + 'static + ProvideRuntimeApi<Block> + HeaderBackend<Block>,533 C::Api: RmrkRuntimeApi<534 Block,535 AccountId,536 CollectionInfo,537 NftInfo,538 ResourceInfo,539 PropertyInfo,540 BaseInfo,541 PartType,542 Theme,543 >,544 AccountId: Decode + Encode,545 CollectionInfo: Decode,546 NftInfo: Decode,547 ResourceInfo: Decode,548 PropertyInfo: Decode,549 BaseInfo: Decode,550 PartType: Decode,551 Theme: Decode,552 Block: BlockT,553{554 pass_method!(last_collection_idx() -> RmrkCollectionId, rmrk_api);555 pass_method!(collection_by_id(id: RmrkCollectionId) -> Option<CollectionInfo>, rmrk_api);556 pass_method!(nft_by_id(collection_id: RmrkCollectionId, nft_id: RmrkNftId) -> Option<NftInfo>, rmrk_api);557 pass_method!(account_tokens(account_id: AccountId, collection_id: RmrkCollectionId) -> Vec<RmrkNftId>, rmrk_api);558 pass_method!(nft_children(collection_id: RmrkCollectionId, nft_id: RmrkNftId) -> Vec<RmrkNftChild>, rmrk_api);559 pass_method!(560 collection_properties(collection_id: RmrkCollectionId, filter_keys: Option<Vec<RmrkPropertyKey>>) -> Vec<PropertyInfo>,561 rmrk_api562 );563 pass_method!(564 nft_properties(collection_id: RmrkCollectionId, nft_id: RmrkNftId, filter_keys: Option<Vec<RmrkPropertyKey>>) -> Vec<PropertyInfo>,565 rmrk_api566 );567 pass_method!(nft_resources(collection_id: RmrkCollectionId, nft_id: RmrkNftId) -> Vec<ResourceInfo>, rmrk_api);568 pass_method!(nft_resource_priorities(collection_id: RmrkCollectionId, nft_id: RmrkNftId) -> Vec<RmrkResourceId>, rmrk_api);569 pass_method!(base(base_id: RmrkBaseId) -> Option<BaseInfo>, rmrk_api);570 pass_method!(base_parts(base_id: RmrkBaseId) -> Vec<PartType>, rmrk_api);571 pass_method!(theme_names(base_id: RmrkBaseId) -> Vec<RmrkThemeName>, rmrk_api);572 573574575576577578579 pass_method!(theme(base_id: RmrkBaseId, theme_name: RmrkThemeName, filter_keys: Option<Vec<RmrkPropertyKey>>) -> Option<Theme>, rmrk_api);580}581582fn string_keys_to_bytes_keys(keys: Vec<String>) -> Vec<Vec<u8>> {583 keys.into_iter().map(|key| key.into_bytes()).collect()584}