123456789101112131415161718use std::sync::Arc;1920use codec::{Decode, Encode};21use jsonrpsee::{22 core::{RpcResult as Result},23 proc_macros::rpc,24};25use anyhow::anyhow;26use sp_runtime::traits::{AtLeast32BitUnsigned, Member};27use up_data_structs::{28 RpcCollection, CollectionId, CollectionStats, CollectionLimits, TokenId, Property,29 PropertyKeyPermission, TokenData, TokenChild,30};31use sp_api::{BlockId, BlockT, ProvideRuntimeApi, ApiExt};32use sp_blockchain::HeaderBackend;33use up_rpc::UniqueApi as UniqueRuntimeApi;343536use rmrk_rpc::RmrkApi as RmrkRuntimeApi;37use up_data_structs::{38 RmrkCollectionId, RmrkNftId, RmrkBaseId, RmrkNftChild, RmrkThemeName, RmrkResourceId,39};4041pub use rmrk_unique_rpc::RmrkApiServer;4243#[rpc(server)]44#[async_trait]45pub trait UniqueApi<BlockHash, BlockNumber, CrossAccountId, AccountId> {46 47 #[method(name = "unique_accountTokens")]48 fn account_tokens(49 &self,50 collection: CollectionId,51 account: CrossAccountId,52 at: Option<BlockHash>,53 ) -> Result<Vec<TokenId>>;5455 56 #[method(name = "unique_collectionTokens")]57 fn collection_tokens(58 &self,59 collection: CollectionId,60 at: Option<BlockHash>,61 ) -> Result<Vec<TokenId>>;6263 64 #[method(name = "unique_tokenExists")]65 fn token_exists(66 &self,67 collection: CollectionId,68 token: TokenId,69 at: Option<BlockHash>,70 ) -> Result<bool>;7172 73 #[method(name = "unique_tokenOwner")]74 fn token_owner(75 &self,76 collection: CollectionId,77 token: TokenId,78 at: Option<BlockHash>,79 ) -> Result<Option<CrossAccountId>>;8081 82 #[method(name = "unique_tokenOwners")]83 fn token_owners(84 &self,85 collection: CollectionId,86 token: TokenId,87 at: Option<BlockHash>,88 ) -> Result<Vec<CrossAccountId>>;8990 91 #[method(name = "unique_topmostTokenOwner")]92 fn topmost_token_owner(93 &self,94 collection: CollectionId,95 token: TokenId,96 at: Option<BlockHash>,97 ) -> Result<Option<CrossAccountId>>;9899 100 #[method(name = "unique_tokenChildren")]101 fn token_children(102 &self,103 collection: CollectionId,104 token: TokenId,105 at: Option<BlockHash>,106 ) -> Result<Vec<TokenChild>>;107108 109 #[method(name = "unique_collectionProperties")]110 fn collection_properties(111 &self,112 collection: CollectionId,113 keys: Option<Vec<String>>,114 at: Option<BlockHash>,115 ) -> Result<Vec<Property>>;116117 118 #[method(name = "unique_tokenProperties")]119 fn token_properties(120 &self,121 collection: CollectionId,122 token_id: TokenId,123 keys: Option<Vec<String>>,124 at: Option<BlockHash>,125 ) -> Result<Vec<Property>>;126127 128 #[method(name = "unique_propertyPermissions")]129 fn property_permissions(130 &self,131 collection: CollectionId,132 keys: Option<Vec<String>>,133 at: Option<BlockHash>,134 ) -> Result<Vec<PropertyKeyPermission>>;135136 137 #[method(name = "unique_tokenData")]138 fn token_data(139 &self,140 collection: CollectionId,141 token_id: TokenId,142 keys: Option<Vec<String>>,143 at: Option<BlockHash>,144 ) -> Result<TokenData<CrossAccountId>>;145146 147 #[method(name = "unique_totalSupply")]148 fn total_supply(&self, collection: CollectionId, at: Option<BlockHash>) -> Result<u32>;149150 151 #[method(name = "unique_accountBalance")]152 fn account_balance(153 &self,154 collection: CollectionId,155 account: CrossAccountId,156 at: Option<BlockHash>,157 ) -> Result<u32>;158159 160 #[method(name = "unique_balance")]161 fn balance(162 &self,163 collection: CollectionId,164 account: CrossAccountId,165 token: TokenId,166 at: Option<BlockHash>,167 ) -> Result<String>;168169 170 #[method(name = "unique_allowance")]171 fn allowance(172 &self,173 collection: CollectionId,174 sender: CrossAccountId,175 spender: CrossAccountId,176 token: TokenId,177 at: Option<BlockHash>,178 ) -> Result<String>;179180 181 #[method(name = "unique_adminlist")]182 fn adminlist(183 &self,184 collection: CollectionId,185 at: Option<BlockHash>,186 ) -> Result<Vec<CrossAccountId>>;187188 189 #[method(name = "unique_allowlist")]190 fn allowlist(191 &self,192 collection: CollectionId,193 at: Option<BlockHash>,194 ) -> Result<Vec<CrossAccountId>>;195196 197 #[method(name = "unique_allowed")]198 fn allowed(199 &self,200 collection: CollectionId,201 user: CrossAccountId,202 at: Option<BlockHash>,203 ) -> Result<bool>;204205 206 #[method(name = "unique_lastTokenId")]207 fn last_token_id(&self, collection: CollectionId, at: Option<BlockHash>) -> Result<TokenId>;208209 210 #[method(name = "unique_collectionById")]211 fn collection_by_id(212 &self,213 collection: CollectionId,214 at: Option<BlockHash>,215 ) -> Result<Option<RpcCollection<AccountId>>>;216217 218 #[method(name = "unique_collectionStats")]219 fn collection_stats(&self, at: Option<BlockHash>) -> Result<CollectionStats>;220221 222 #[method(name = "unique_nextSponsored")]223 fn next_sponsored(224 &self,225 collection: CollectionId,226 account: CrossAccountId,227 token: TokenId,228 at: Option<BlockHash>,229 ) -> Result<Option<u64>>;230231 232 #[method(name = "unique_effectiveCollectionLimits")]233 fn effective_collection_limits(234 &self,235 collection_id: CollectionId,236 at: Option<BlockHash>,237 ) -> Result<Option<CollectionLimits>>;238239 240 #[method(name = "unique_totalPieces")]241 fn total_pieces(242 &self,243 collection_id: CollectionId,244 token_id: TokenId,245 at: Option<BlockHash>,246 ) -> Result<Option<String>>;247248 249 #[method(name = "unique_totalStaked")]250 fn total_staked(&self, staker: Option<CrossAccountId>, at: Option<BlockHash>)251 -> Result<String>;252253 254 #[method(name = "unique_totalStakedPerBlock")]255 fn total_staked_per_block(256 &self,257 staker: CrossAccountId,258 at: Option<BlockHash>,259 ) -> Result<Vec<(BlockNumber, String)>>;260261 262 #[method(name = "unique_totalStakingLocked")]263 fn total_staking_locked(&self, staker: CrossAccountId, at: Option<BlockHash>)264 -> Result<String>;265266 267 #[method(name = "unique_pendingUnstake")]268 fn pending_unstake(269 &self,270 staker: Option<CrossAccountId>,271 at: Option<BlockHash>,272 ) -> Result<String>;273 274 #[method(name = "unique_pendingUnstakePerBlock")]275 fn pending_unstake_per_block(276 &self,277 staker: CrossAccountId,278 at: Option<BlockHash>,279 ) -> Result<Vec<(BlockNumber, String)>>;280}281282mod rmrk_unique_rpc {283 use super::*;284285 #[rpc(server)]286 #[async_trait]287 pub trait RmrkApi<288 BlockHash,289 AccountId,290 CollectionInfo,291 NftInfo,292 ResourceInfo,293 PropertyInfo,294 BaseInfo,295 PartType,296 Theme,297 >298 {299 300 #[method(name = "rmrk_lastCollectionIdx")]301 fn last_collection_idx(&self, at: Option<BlockHash>) -> Result<RmrkCollectionId>;302303 304 #[method(name = "rmrk_collectionById")]305 fn collection_by_id(306 &self,307 id: RmrkCollectionId,308 at: Option<BlockHash>,309 ) -> Result<Option<CollectionInfo>>;310311 312 #[method(name = "rmrk_nftById")]313 fn nft_by_id(314 &self,315 collection_id: RmrkCollectionId,316 nft_id: RmrkNftId,317 at: Option<BlockHash>,318 ) -> Result<Option<NftInfo>>;319320 321 #[method(name = "rmrk_accountTokens")]322 fn account_tokens(323 &self,324 account_id: AccountId,325 collection_id: RmrkCollectionId,326 at: Option<BlockHash>,327 ) -> Result<Vec<RmrkNftId>>;328329 330 #[method(name = "rmrk_nftChildren")]331 fn nft_children(332 &self,333 collection_id: RmrkCollectionId,334 nft_id: RmrkNftId,335 at: Option<BlockHash>,336 ) -> Result<Vec<RmrkNftChild>>;337338 339 #[method(name = "rmrk_collectionProperties")]340 fn collection_properties(341 &self,342 collection_id: RmrkCollectionId,343 filter_keys: Option<Vec<String>>,344 at: Option<BlockHash>,345 ) -> Result<Vec<PropertyInfo>>;346347 348 #[method(name = "rmrk_nftProperties")]349 fn nft_properties(350 &self,351 collection_id: RmrkCollectionId,352 nft_id: RmrkNftId,353 filter_keys: Option<Vec<String>>,354 at: Option<BlockHash>,355 ) -> Result<Vec<PropertyInfo>>;356357 358 #[method(name = "rmrk_nftResources")]359 fn nft_resources(360 &self,361 collection_id: RmrkCollectionId,362 nft_id: RmrkNftId,363 at: Option<BlockHash>,364 ) -> Result<Vec<ResourceInfo>>;365366 367 #[method(name = "rmrk_nftResourcePriority")]368 fn nft_resource_priority(369 &self,370 collection_id: RmrkCollectionId,371 nft_id: RmrkNftId,372 resource_id: RmrkResourceId,373 at: Option<BlockHash>,374 ) -> Result<Option<u32>>;375376 377 #[method(name = "rmrk_base")]378 fn base(&self, base_id: RmrkBaseId, at: Option<BlockHash>) -> Result<Option<BaseInfo>>;379380 381 #[method(name = "rmrk_baseParts")]382 fn base_parts(&self, base_id: RmrkBaseId, at: Option<BlockHash>) -> Result<Vec<PartType>>;383384 385 #[method(name = "rmrk_themeNames")]386 fn theme_names(387 &self,388 base_id: RmrkBaseId,389 at: Option<BlockHash>,390 ) -> Result<Vec<RmrkThemeName>>;391392 393 #[method(name = "rmrk_themes")]394 fn theme(395 &self,396 base_id: RmrkBaseId,397 theme_name: String,398 filter_keys: Option<Vec<String>>,399 at: Option<BlockHash>,400 ) -> Result<Option<Theme>>;401 }402}403404pub struct Unique<C, P> {405 client: Arc<C>,406 _marker: std::marker::PhantomData<P>,407}408409impl<C, P> Unique<C, P> {410 pub fn new(client: Arc<C>) -> Self {411 Self {412 client,413 _marker: Default::default(),414 }415 }416}417418pub struct Rmrk<C, P> {419 client: Arc<C>,420 _marker: std::marker::PhantomData<P>,421}422423impl<C, P> Rmrk<C, P> {424 pub fn new(client: Arc<C>) -> Self {425 Self {426 client,427 _marker: Default::default(),428 }429 }430}431432macro_rules! pass_method {433 (434 $method_name:ident(435 $($(#[map(|$map_arg:ident| $map:expr)])? $name:ident: $ty:ty),* $(,)?436 ) -> $result:ty $(=> $mapper:expr)?,437 438 $runtime_api_macro:ident439 $(; changed_in $ver:expr, $changed_method_name:ident ($($changed_name:expr), * $(,)?) => $fixer:expr)*440 ) => {441 fn $method_name(442 &self,443 $(444 $name: $ty,445 )*446 at: Option<<Block as BlockT>::Hash>,447 ) -> Result<$result> {448 let api = self.client.runtime_api();449 let at = BlockId::hash(at.unwrap_or_else(|| self.client.info().best_hash));450 let _api_version = if let Ok(Some(api_version)) =451 api.api_version::<$runtime_api_macro!()>(&at)452 {453 api_version454 } else {455 456 return Err(anyhow!("api is not available").into())457 };458459 let result = $(if _api_version < $ver {460 api.$changed_method_name(&at, $($changed_name),*).map(|r| r.map($fixer))461 } else)*462 { api.$method_name(&at, $($((|$map_arg: $ty| $map))? ($name)),*) };463464 Ok(result465 .map_err(|e| anyhow!("unable to query: {e}"))?466 .map_err(|e| anyhow!("runtime error: {e:?}"))$(.map($mapper))??)467 }468 };469}470471macro_rules! unique_api {472 () => {473 dyn UniqueRuntimeApi<Block, BlockNumber, CrossAccountId, AccountId>474 };475}476477macro_rules! rmrk_api {478 () => {479 dyn RmrkRuntimeApi<Block, AccountId, CollectionInfo, NftInfo, ResourceInfo, PropertyInfo, BaseInfo, PartType, Theme>480 };481}482483#[allow(deprecated)]484impl<C, Block, BlockNumber, CrossAccountId, AccountId>485 UniqueApiServer<<Block as BlockT>::Hash, BlockNumber, CrossAccountId, AccountId>486 for Unique<C, Block>487where488 Block: BlockT,489 BlockNumber: Decode + Member + AtLeast32BitUnsigned,490 AccountId: Decode,491 C: 'static + ProvideRuntimeApi<Block> + HeaderBackend<Block>,492 C::Api: UniqueRuntimeApi<Block, BlockNumber, CrossAccountId, AccountId>,493 CrossAccountId: pallet_evm::account::CrossAccountId<AccountId>,494{495 pass_method!(496 account_tokens(collection: CollectionId, account: CrossAccountId) -> Vec<TokenId>, unique_api497 );498 pass_method!(499 collection_tokens(collection: CollectionId) -> Vec<TokenId>, unique_api500 );501 pass_method!(502 token_exists(collection: CollectionId, token: TokenId) -> bool, unique_api503 );504 pass_method!(505 token_owner(collection: CollectionId, token: TokenId) -> Option<CrossAccountId>, unique_api506 );507 pass_method!(508 topmost_token_owner(collection: CollectionId, token: TokenId) -> Option<CrossAccountId>, unique_api509 );510 pass_method!(token_children(collection: CollectionId, token: TokenId) -> Vec<TokenChild>, unique_api);511 pass_method!(total_supply(collection: CollectionId) -> u32, unique_api);512 pass_method!(account_balance(collection: CollectionId, account: CrossAccountId) -> u32, unique_api);513 pass_method!(balance(collection: CollectionId, account: CrossAccountId, token: TokenId) -> String => |v| v.to_string(), unique_api);514 pass_method!(515 allowance(collection: CollectionId, sender: CrossAccountId, spender: CrossAccountId, token: TokenId) -> String => |v| v.to_string(),516 unique_api517 );518519 pass_method!(collection_properties(520 collection: CollectionId,521522 #[map(|keys| string_keys_to_bytes_keys(keys))]523 keys: Option<Vec<String>>524 ) -> Vec<Property>, unique_api);525526 pass_method!(token_properties(527 collection: CollectionId,528 token_id: TokenId,529530 #[map(|keys| string_keys_to_bytes_keys(keys))]531 keys: Option<Vec<String>>532 ) -> Vec<Property>, unique_api);533534 pass_method!(property_permissions(535 collection: CollectionId,536537 #[map(|keys| string_keys_to_bytes_keys(keys))]538 keys: Option<Vec<String>>539 ) -> Vec<PropertyKeyPermission>, unique_api);540541 pass_method!(token_data(542 collection: CollectionId,543 token_id: TokenId,544545 #[map(|keys| string_keys_to_bytes_keys(keys))]546 keys: Option<Vec<String>>,547 ) -> TokenData<CrossAccountId>, unique_api);548549 pass_method!(adminlist(collection: CollectionId) -> Vec<CrossAccountId>, unique_api);550 pass_method!(allowlist(collection: CollectionId) -> Vec<CrossAccountId>, unique_api);551 pass_method!(allowed(collection: CollectionId, user: CrossAccountId) -> bool, unique_api);552 pass_method!(last_token_id(collection: CollectionId) -> TokenId, unique_api);553 pass_method!(collection_by_id(collection: CollectionId) -> Option<RpcCollection<AccountId>>, unique_api);554 pass_method!(collection_stats() -> CollectionStats, unique_api);555 pass_method!(next_sponsored(collection: CollectionId, account: CrossAccountId, token: TokenId) -> Option<u64>, unique_api);556 pass_method!(effective_collection_limits(collection_id: CollectionId) -> Option<CollectionLimits>, unique_api);557 pass_method!(total_pieces(collection_id: CollectionId, token_id: TokenId) -> Option<String> => |o| o.map(|number| number.to_string()) , unique_api);558 pass_method!(token_owners(collection: CollectionId, token: TokenId) -> Vec<CrossAccountId>, unique_api);559 pass_method!(total_staked(staker: Option<CrossAccountId>) -> String => |v| v.to_string(), unique_api);560 pass_method!(total_staked_per_block(staker: CrossAccountId) -> Vec<(BlockNumber, String)> =>561 |v| v562 .into_iter()563 .map(|(b, a)| (b, a.to_string()))564 .collect::<Vec<_>>(), unique_api);565 pass_method!(total_staking_locked(staker: CrossAccountId) -> String => |v| v.to_string(), unique_api);566 pass_method!(pending_unstake(staker: Option<CrossAccountId>) -> String => |v| v.to_string(), unique_api);567 pass_method!(pending_unstake_per_block(staker: CrossAccountId) -> Vec<(BlockNumber, String)> =>568 |v| v569 .into_iter()570 .map(|(b, a)| (b, a.to_string()))571 .collect::<Vec<_>>(), unique_api);572}573574#[allow(deprecated)]575impl<576 C,577 Block,578 AccountId,579 CollectionInfo,580 NftInfo,581 ResourceInfo,582 PropertyInfo,583 BaseInfo,584 PartType,585 Theme,586 >587 rmrk_unique_rpc::RmrkApiServer<588 <Block as BlockT>::Hash,589 AccountId,590 CollectionInfo,591 NftInfo,592 ResourceInfo,593 PropertyInfo,594 BaseInfo,595 PartType,596 Theme,597 > for Rmrk<C, Block>598where599 C: Send + Sync + 'static + ProvideRuntimeApi<Block> + HeaderBackend<Block>,600 C::Api: RmrkRuntimeApi<601 Block,602 AccountId,603 CollectionInfo,604 NftInfo,605 ResourceInfo,606 PropertyInfo,607 BaseInfo,608 PartType,609 Theme,610 >,611 AccountId: Decode + Encode,612 CollectionInfo: Decode,613 NftInfo: Decode,614 ResourceInfo: Decode,615 PropertyInfo: Decode,616 BaseInfo: Decode,617 PartType: Decode,618 Theme: Decode,619 Block: BlockT,620{621 pass_method!(last_collection_idx() -> RmrkCollectionId, rmrk_api);622 pass_method!(collection_by_id(id: RmrkCollectionId) -> Option<CollectionInfo>, rmrk_api);623 pass_method!(nft_by_id(collection_id: RmrkCollectionId, nft_id: RmrkNftId) -> Option<NftInfo>, rmrk_api);624 pass_method!(account_tokens(account_id: AccountId, collection_id: RmrkCollectionId) -> Vec<RmrkNftId>, rmrk_api);625 pass_method!(nft_children(collection_id: RmrkCollectionId, nft_id: RmrkNftId) -> Vec<RmrkNftChild>, rmrk_api);626 pass_method!(627 collection_properties(628 collection_id: RmrkCollectionId,629630 #[map(|keys| string_keys_to_bytes_keys(keys))]631 filter_keys: Option<Vec<String>>632 ) -> Vec<PropertyInfo>,633 rmrk_api634 );635 pass_method!(636 nft_properties(637 collection_id: RmrkCollectionId,638 nft_id: RmrkNftId,639640 #[map(|keys| string_keys_to_bytes_keys(keys))]641 filter_keys: Option<Vec<String>>642 ) -> Vec<PropertyInfo>,643 rmrk_api644 );645 pass_method!(nft_resources(collection_id: RmrkCollectionId, nft_id: RmrkNftId) -> Vec<ResourceInfo>, rmrk_api);646 pass_method!(nft_resource_priority(collection_id: RmrkCollectionId, nft_id: RmrkNftId, resource_id: RmrkResourceId) -> Option<u32>, rmrk_api);647 pass_method!(base(base_id: RmrkBaseId) -> Option<BaseInfo>, rmrk_api);648 pass_method!(base_parts(base_id: RmrkBaseId) -> Vec<PartType>, rmrk_api);649 pass_method!(theme_names(base_id: RmrkBaseId) -> Vec<RmrkThemeName>, rmrk_api);650 pass_method!(651 theme(652 base_id: RmrkBaseId,653654 #[map(|n| n.into_bytes())]655 theme_name: String,656657 #[map(|keys| string_keys_to_bytes_keys(keys))]658 filter_keys: Option<Vec<String>>659 ) -> Option<Theme>, rmrk_api);660}661662fn string_keys_to_bytes_keys(keys: Option<Vec<String>>) -> Option<Vec<Vec<u8>>> {663 keys.map(|keys| keys.into_iter().map(|key| key.into_bytes()).collect())664}