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 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>>;7576 #[rpc(name = "unique_collectionProperties")]77 fn collection_properties(78 &self,79 collection: CollectionId,80 keys: Option<Vec<String>>,81 at: Option<BlockHash>,82 ) -> Result<Vec<Property>>;8384 #[rpc(name = "unique_tokenProperties")]85 fn token_properties(86 &self,87 collection: CollectionId,88 token_id: TokenId,89 keys: Option<Vec<String>>,90 at: Option<BlockHash>,91 ) -> Result<Vec<Property>>;9293 #[rpc(name = "unique_propertyPermissions")]94 fn property_permissions(95 &self,96 collection: CollectionId,97 keys: Option<Vec<String>>,98 at: Option<BlockHash>,99 ) -> Result<Vec<PropertyKeyPermission>>;100101 #[rpc(name = "unique_tokenData")]102 fn token_data(103 &self,104 collection: CollectionId,105 token_id: TokenId,106 keys: Option<Vec<String>>,107 at: Option<BlockHash>,108 ) -> Result<TokenData<CrossAccountId>>;109110 #[rpc(name = "unique_totalSupply")]111 fn total_supply(&self, collection: CollectionId, at: Option<BlockHash>) -> Result<u32>;112 #[rpc(name = "unique_accountBalance")]113 fn account_balance(114 &self,115 collection: CollectionId,116 account: CrossAccountId,117 at: Option<BlockHash>,118 ) -> Result<u32>;119 #[rpc(name = "unique_balance")]120 fn balance(121 &self,122 collection: CollectionId,123 account: CrossAccountId,124 token: TokenId,125 at: Option<BlockHash>,126 ) -> Result<String>;127 #[rpc(name = "unique_allowance")]128 fn allowance(129 &self,130 collection: CollectionId,131 sender: CrossAccountId,132 spender: CrossAccountId,133 token: TokenId,134 at: Option<BlockHash>,135 ) -> Result<String>;136137 #[rpc(name = "unique_adminlist")]138 fn adminlist(139 &self,140 collection: CollectionId,141 at: Option<BlockHash>,142 ) -> Result<Vec<CrossAccountId>>;143 #[rpc(name = "unique_allowlist")]144 fn allowlist(145 &self,146 collection: CollectionId,147 at: Option<BlockHash>,148 ) -> Result<Vec<CrossAccountId>>;149 #[rpc(name = "unique_allowed")]150 fn allowed(151 &self,152 collection: CollectionId,153 user: CrossAccountId,154 at: Option<BlockHash>,155 ) -> Result<bool>;156 #[rpc(name = "unique_lastTokenId")]157 fn last_token_id(&self, collection: CollectionId, at: Option<BlockHash>) -> Result<TokenId>;158 #[rpc(name = "unique_collectionById")]159 fn collection_by_id(160 &self,161 collection: CollectionId,162 at: Option<BlockHash>,163 ) -> Result<Option<RpcCollection<AccountId>>>;164 #[rpc(name = "unique_collectionStats")]165 fn collection_stats(&self, at: Option<BlockHash>) -> Result<CollectionStats>;166167 #[rpc(name = "unique_nextSponsored")]168 fn next_sponsored(169 &self,170 collection: CollectionId,171 account: CrossAccountId,172 token: TokenId,173 at: Option<BlockHash>,174 ) -> Result<Option<u64>>;175 #[rpc(name = "unique_effectiveCollectionLimits")]176 fn effective_collection_limits(177 &self,178 collection_id: CollectionId,179 at: Option<BlockHash>,180 ) -> Result<Option<CollectionLimits>>;181}182183mod rmrk_unique_rpc {184 use super::*;185186 #[rpc(server)]187 pub trait RmrkApi<188 BlockHash,189 AccountId,190 CollectionInfo,191 NftInfo,192 ResourceInfo,193 PropertyInfo,194 BaseInfo,195 PartType,196 Theme,197 >198 {199 #[rpc(name = "rmrk_lastCollectionIdx")]200 201 fn last_collection_idx(&self, at: Option<BlockHash>) -> Result<RmrkCollectionId>;202203 #[rpc(name = "rmrk_collectionById")]204 205 fn collection_by_id(206 &self,207 id: RmrkCollectionId,208 at: Option<BlockHash>,209 ) -> Result<Option<CollectionInfo>>;210211 #[rpc(name = "rmrk_nftById")]212 213 fn nft_by_id(214 &self,215 collection_id: RmrkCollectionId,216 nft_id: RmrkNftId,217 at: Option<BlockHash>,218 ) -> Result<Option<NftInfo>>;219220 #[rpc(name = "rmrk_accountTokens")]221 222 fn account_tokens(223 &self,224 account_id: AccountId,225 collection_id: RmrkCollectionId,226 at: Option<BlockHash>,227 ) -> Result<Vec<RmrkNftId>>;228229 #[rpc(name = "rmrk_nftChildren")]230 231 fn nft_children(232 &self,233 collection_id: RmrkCollectionId,234 nft_id: RmrkNftId,235 at: Option<BlockHash>,236 ) -> Result<Vec<RmrkNftChild>>;237238 #[rpc(name = "rmrk_collectionProperties")]239 240 fn collection_properties(241 &self,242 collection_id: RmrkCollectionId,243 filter_keys: Option<Vec<String>>,244 at: Option<BlockHash>,245 ) -> Result<Vec<PropertyInfo>>;246247 #[rpc(name = "rmrk_nftProperties")]248 249 fn nft_properties(250 &self,251 collection_id: RmrkCollectionId,252 nft_id: RmrkNftId,253 filter_keys: Option<Vec<String>>,254 at: Option<BlockHash>,255 ) -> Result<Vec<PropertyInfo>>;256257 #[rpc(name = "rmrk_nftResources")]258 259 fn nft_resources(260 &self,261 collection_id: RmrkCollectionId,262 nft_id: RmrkNftId,263 at: Option<BlockHash>,264 ) -> Result<Vec<ResourceInfo>>;265266 #[rpc(name = "rmrk_nftResourcePriorities")]267 268 fn nft_resource_priorities(269 &self,270 collection_id: RmrkCollectionId,271 nft_id: RmrkNftId,272 at: Option<BlockHash>,273 ) -> Result<Vec<RmrkResourceId>>;274275 #[rpc(name = "rmrk_base")]276 277 fn base(&self, base_id: RmrkBaseId, at: Option<BlockHash>) -> Result<Option<BaseInfo>>;278279 #[rpc(name = "rmrk_baseParts")]280 281 fn base_parts(&self, base_id: RmrkBaseId, at: Option<BlockHash>) -> Result<Vec<PartType>>;282283 #[rpc(name = "rmrk_themeNames")]284 fn theme_names(285 &self,286 base_id: RmrkBaseId,287 at: Option<BlockHash>,288 ) -> Result<Vec<RmrkThemeName>>;289290 #[rpc(name = "rmrk_themes")]291 fn theme(292 &self,293 base_id: RmrkBaseId,294 theme_name: String,295 filter_keys: Option<Vec<String>>,296 at: Option<BlockHash>,297 ) -> Result<Option<Theme>>;298 }299}300301pub struct Unique<C, P> {302 client: Arc<C>,303 _marker: std::marker::PhantomData<P>,304}305306impl<C, P> Unique<C, P> {307 pub fn new(client: Arc<C>) -> Self {308 Self {309 client,310 _marker: Default::default(),311 }312 }313}314315pub enum Error {316 RuntimeError,317}318319impl From<Error> for i64 {320 fn from(e: Error) -> i64 {321 match e {322 Error::RuntimeError => 1,323 }324 }325}326327macro_rules! pass_method {328 (329 $method_name:ident(330 $($(#[map(|$map_arg:ident| $map:expr)])? $name:ident: $ty:ty),* $(,)?331 ) -> $result:ty $(=> $mapper:expr)?,332 333 $runtime_api_macro:ident334 $(; changed_in $ver:expr, $changed_method_name:ident ($($changed_name:expr), * $(,)?) => $fixer:expr)*335 ) => {336 fn $method_name(337 &self,338 $(339 $name: $ty,340 )*341 at: Option<<Block as BlockT>::Hash>,342 ) -> Result<$result> {343 let api = self.client.runtime_api();344 let at = BlockId::hash(at.unwrap_or_else(|| self.client.info().best_hash));345 let _api_version = if let Ok(Some(api_version)) =346 api.api_version::<$runtime_api_macro!()>(&at)347 {348 api_version349 } else {350 351 return Err(RpcError {352 code: ErrorCode::InvalidParams,353 message: "Api is not available".into(),354 data: None,355 })356 };357358 let result = $(if _api_version < $ver {359 api.$changed_method_name(&at, $($changed_name),*).map(|r| r.map($fixer))360 } else)*361 { api.$method_name(&at, $($((|$map_arg: $ty| $map))? ($name)),*) };362363 let result = result.map_err(|e| RpcError {364 code: ErrorCode::ServerError(Error::RuntimeError.into()),365 message: "Unable to query".into(),366 data: Some(format!("{:?}", e).into()),367 })?;368 result.map_err(|e| RpcError {369 code: ErrorCode::InvalidParams,370 message: "Runtime returned error".into(),371 data: Some(format!("{:?}", e).into()),372 })$(.map($mapper))?373 }374 };375}376377macro_rules! unique_api {378 () => {379 dyn UniqueRuntimeApi<Block, CrossAccountId, AccountId>380 };381}382383macro_rules! rmrk_api {384 () => {385 dyn RmrkRuntimeApi<Block, AccountId, CollectionInfo, NftInfo, ResourceInfo, PropertyInfo, BaseInfo, PartType, Theme>386 };387}388389#[allow(deprecated)]390impl<C, Block, CrossAccountId, AccountId>391 UniqueApi<<Block as BlockT>::Hash, CrossAccountId, AccountId> for Unique<C, Block>392where393 Block: BlockT,394 AccountId: Decode,395 C: 'static + ProvideRuntimeApi<Block> + HeaderBackend<Block>,396 C::Api: UniqueRuntimeApi<Block, CrossAccountId, AccountId>,397 CrossAccountId: pallet_evm::account::CrossAccountId<AccountId>,398{399 pass_method!(400 account_tokens(collection: CollectionId, account: CrossAccountId) -> Vec<TokenId>, unique_api401 );402 pass_method!(403 collection_tokens(collection: CollectionId) -> Vec<TokenId>, unique_api404 );405 pass_method!(406 token_exists(collection: CollectionId, token: TokenId) -> bool, unique_api407 );408 pass_method!(409 token_owner(collection: CollectionId, token: TokenId) -> Option<CrossAccountId>, unique_api410 );411 pass_method!(412 topmost_token_owner(collection: CollectionId, token: TokenId) -> Option<CrossAccountId>, unique_api413 );414 pass_method!(total_supply(collection: CollectionId) -> u32, unique_api);415 pass_method!(account_balance(collection: CollectionId, account: CrossAccountId) -> u32, unique_api);416 pass_method!(balance(collection: CollectionId, account: CrossAccountId, token: TokenId) -> String => |v| v.to_string(), unique_api);417 pass_method!(418 allowance(collection: CollectionId, sender: CrossAccountId, spender: CrossAccountId, token: TokenId) -> String => |v| v.to_string(),419 unique_api420 );421422 pass_method!(collection_properties(423 collection: CollectionId,424425 #[map(|keys| string_keys_to_bytes_keys(keys))]426 keys: Option<Vec<String>>427 ) -> Vec<Property>, unique_api);428429 pass_method!(token_properties(430 collection: CollectionId,431 token_id: TokenId,432433 #[map(|keys| string_keys_to_bytes_keys(keys))]434 keys: Option<Vec<String>>435 ) -> Vec<Property>, unique_api);436437 pass_method!(property_permissions(438 collection: CollectionId,439440 #[map(|keys| string_keys_to_bytes_keys(keys))]441 keys: Option<Vec<String>>442 ) -> Vec<PropertyKeyPermission>, unique_api);443444 pass_method!(token_data(445 collection: CollectionId,446 token_id: TokenId,447448 #[map(|keys| string_keys_to_bytes_keys(keys))]449 keys: Option<Vec<String>>,450 ) -> TokenData<CrossAccountId>, unique_api);451452 pass_method!(adminlist(collection: CollectionId) -> Vec<CrossAccountId>, unique_api);453 pass_method!(allowlist(collection: CollectionId) -> Vec<CrossAccountId>, unique_api);454 pass_method!(allowed(collection: CollectionId, user: CrossAccountId) -> bool, unique_api);455 pass_method!(last_token_id(collection: CollectionId) -> TokenId, unique_api);456 pass_method!(collection_by_id(collection: CollectionId) -> Option<RpcCollection<AccountId>>, unique_api);457 pass_method!(collection_stats() -> CollectionStats, unique_api);458 pass_method!(next_sponsored(collection: CollectionId, account: CrossAccountId, token: TokenId) -> Option<u64>, unique_api);459 pass_method!(effective_collection_limits(collection_id: CollectionId) -> Option<CollectionLimits>, unique_api);460}461462#[allow(deprecated)]463impl<464 C,465 Block,466 AccountId,467 CollectionInfo,468 NftInfo,469 ResourceInfo,470 PropertyInfo,471 BaseInfo,472 PartType,473 Theme,474 >475 rmrk_unique_rpc::RmrkApi<476 <Block as BlockT>::Hash,477 AccountId,478 CollectionInfo,479 NftInfo,480 ResourceInfo,481 PropertyInfo,482 BaseInfo,483 PartType,484 Theme,485 > for Unique<C, Block>486where487 C: Send + Sync + 'static + ProvideRuntimeApi<Block> + HeaderBackend<Block>,488 C::Api: RmrkRuntimeApi<489 Block,490 AccountId,491 CollectionInfo,492 NftInfo,493 ResourceInfo,494 PropertyInfo,495 BaseInfo,496 PartType,497 Theme,498 >,499 AccountId: Decode + Encode,500 CollectionInfo: Decode,501 NftInfo: Decode,502 ResourceInfo: Decode,503 PropertyInfo: Decode,504 BaseInfo: Decode,505 PartType: Decode,506 Theme: Decode,507 Block: BlockT,508{509 pass_method!(last_collection_idx() -> RmrkCollectionId, rmrk_api);510 pass_method!(collection_by_id(id: RmrkCollectionId) -> Option<CollectionInfo>, rmrk_api);511 pass_method!(nft_by_id(collection_id: RmrkCollectionId, nft_id: RmrkNftId) -> Option<NftInfo>, rmrk_api);512 pass_method!(account_tokens(account_id: AccountId, collection_id: RmrkCollectionId) -> Vec<RmrkNftId>, rmrk_api);513 pass_method!(nft_children(collection_id: RmrkCollectionId, nft_id: RmrkNftId) -> Vec<RmrkNftChild>, rmrk_api);514 pass_method!(515 collection_properties(516 collection_id: RmrkCollectionId,517518 #[map(|keys| string_keys_to_bytes_keys(keys))]519 filter_keys: Option<Vec<String>>520 ) -> Vec<PropertyInfo>,521 rmrk_api522 );523 pass_method!(524 nft_properties(525 collection_id: RmrkCollectionId,526 nft_id: RmrkNftId,527528 #[map(|keys| string_keys_to_bytes_keys(keys))]529 filter_keys: Option<Vec<String>>530 ) -> Vec<PropertyInfo>,531 rmrk_api532 );533 pass_method!(nft_resources(collection_id: RmrkCollectionId, nft_id: RmrkNftId) -> Vec<ResourceInfo>, rmrk_api);534 pass_method!(nft_resource_priorities(collection_id: RmrkCollectionId, nft_id: RmrkNftId) -> Vec<RmrkResourceId>, rmrk_api);535 pass_method!(base(base_id: RmrkBaseId) -> Option<BaseInfo>, rmrk_api);536 pass_method!(base_parts(base_id: RmrkBaseId) -> Vec<PartType>, rmrk_api);537 pass_method!(theme_names(base_id: RmrkBaseId) -> Vec<RmrkThemeName>, rmrk_api);538 pass_method!(539 theme(540 base_id: RmrkBaseId,541542 #[map(|n| n.into_bytes())]543 theme_name: String,544545 #[map(|keys| string_keys_to_bytes_keys(keys))]546 filter_keys: Option<Vec<String>>547 ) -> Option<Theme>, rmrk_api);548}549550fn string_keys_to_bytes_keys(keys: Option<Vec<String>>) -> Option<Vec<Vec<u8>>> {551 keys.map(|keys| keys.into_iter().map(|key| key.into_bytes()).collect())552}