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<u128>>;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}274275mod rmrk_unique_rpc {276 use super::*;277278 #[rpc(server)]279 #[async_trait]280 pub trait RmrkApi<281 BlockHash,282 AccountId,283 CollectionInfo,284 NftInfo,285 ResourceInfo,286 PropertyInfo,287 BaseInfo,288 PartType,289 Theme,290 >291 {292 293 #[method(name = "rmrk_lastCollectionIdx")]294 fn last_collection_idx(&self, at: Option<BlockHash>) -> Result<RmrkCollectionId>;295296 297 #[method(name = "rmrk_collectionById")]298 fn collection_by_id(299 &self,300 id: RmrkCollectionId,301 at: Option<BlockHash>,302 ) -> Result<Option<CollectionInfo>>;303304 305 #[method(name = "rmrk_nftById")]306 fn nft_by_id(307 &self,308 collection_id: RmrkCollectionId,309 nft_id: RmrkNftId,310 at: Option<BlockHash>,311 ) -> Result<Option<NftInfo>>;312313 314 #[method(name = "rmrk_accountTokens")]315 fn account_tokens(316 &self,317 account_id: AccountId,318 collection_id: RmrkCollectionId,319 at: Option<BlockHash>,320 ) -> Result<Vec<RmrkNftId>>;321322 323 #[method(name = "rmrk_nftChildren")]324 fn nft_children(325 &self,326 collection_id: RmrkCollectionId,327 nft_id: RmrkNftId,328 at: Option<BlockHash>,329 ) -> Result<Vec<RmrkNftChild>>;330331 332 #[method(name = "rmrk_collectionProperties")]333 fn collection_properties(334 &self,335 collection_id: RmrkCollectionId,336 filter_keys: Option<Vec<String>>,337 at: Option<BlockHash>,338 ) -> Result<Vec<PropertyInfo>>;339340 341 #[method(name = "rmrk_nftProperties")]342 fn nft_properties(343 &self,344 collection_id: RmrkCollectionId,345 nft_id: RmrkNftId,346 filter_keys: Option<Vec<String>>,347 at: Option<BlockHash>,348 ) -> Result<Vec<PropertyInfo>>;349350 351 #[method(name = "rmrk_nftResources")]352 fn nft_resources(353 &self,354 collection_id: RmrkCollectionId,355 nft_id: RmrkNftId,356 at: Option<BlockHash>,357 ) -> Result<Vec<ResourceInfo>>;358359 360 #[method(name = "rmrk_nftResourcePriority")]361 fn nft_resource_priority(362 &self,363 collection_id: RmrkCollectionId,364 nft_id: RmrkNftId,365 resource_id: RmrkResourceId,366 at: Option<BlockHash>,367 ) -> Result<Option<u32>>;368369 370 #[method(name = "rmrk_base")]371 fn base(&self, base_id: RmrkBaseId, at: Option<BlockHash>) -> Result<Option<BaseInfo>>;372373 374 #[method(name = "rmrk_baseParts")]375 fn base_parts(&self, base_id: RmrkBaseId, at: Option<BlockHash>) -> Result<Vec<PartType>>;376377 378 #[method(name = "rmrk_themeNames")]379 fn theme_names(380 &self,381 base_id: RmrkBaseId,382 at: Option<BlockHash>,383 ) -> Result<Vec<RmrkThemeName>>;384385 386 #[method(name = "rmrk_themes")]387 fn theme(388 &self,389 base_id: RmrkBaseId,390 theme_name: String,391 filter_keys: Option<Vec<String>>,392 at: Option<BlockHash>,393 ) -> Result<Option<Theme>>;394 }395}396397pub struct Unique<C, P> {398 client: Arc<C>,399 _marker: std::marker::PhantomData<P>,400}401402impl<C, P> Unique<C, P> {403 pub fn new(client: Arc<C>) -> Self {404 Self {405 client,406 _marker: Default::default(),407 }408 }409}410411pub struct Rmrk<C, P> {412 client: Arc<C>,413 _marker: std::marker::PhantomData<P>,414}415416impl<C, P> Rmrk<C, P> {417 pub fn new(client: Arc<C>) -> Self {418 Self {419 client,420 _marker: Default::default(),421 }422 }423}424425macro_rules! pass_method {426 (427 $method_name:ident(428 $($(#[map(|$map_arg:ident| $map:expr)])? $name:ident: $ty:ty),* $(,)?429 ) -> $result:ty $(=> $mapper:expr)?,430 431 $runtime_api_macro:ident432 $(; changed_in $ver:expr, $changed_method_name:ident ($($changed_name:expr), * $(,)?) => $fixer:expr)*433 ) => {434 fn $method_name(435 &self,436 $(437 $name: $ty,438 )*439 at: Option<<Block as BlockT>::Hash>,440 ) -> Result<$result> {441 let api = self.client.runtime_api();442 let at = BlockId::hash(at.unwrap_or_else(|| self.client.info().best_hash));443 let _api_version = if let Ok(Some(api_version)) =444 api.api_version::<$runtime_api_macro!()>(&at)445 {446 api_version447 } else {448 449 return Err(anyhow!("api is not available").into())450 };451452 let result = $(if _api_version < $ver {453 api.$changed_method_name(&at, $($changed_name),*).map(|r| r.map($fixer))454 } else)*455 { api.$method_name(&at, $($((|$map_arg: $ty| $map))? ($name)),*) };456457 Ok(result458 .map_err(|e| anyhow!("unable to query: {e}"))?459 .map_err(|e| anyhow!("runtime error: {e:?}"))$(.map($mapper))??)460 }461 };462}463464macro_rules! unique_api {465 () => {466 dyn UniqueRuntimeApi<Block, BlockNumber, CrossAccountId, AccountId>467 };468}469470macro_rules! rmrk_api {471 () => {472 dyn RmrkRuntimeApi<Block, AccountId, CollectionInfo, NftInfo, ResourceInfo, PropertyInfo, BaseInfo, PartType, Theme>473 };474}475476#[allow(deprecated)]477impl<C, Block, BlockNumber, CrossAccountId, AccountId>478 UniqueApiServer<<Block as BlockT>::Hash, BlockNumber, CrossAccountId, AccountId>479 for Unique<C, Block>480where481 Block: BlockT,482 BlockNumber: Decode + Member + AtLeast32BitUnsigned,483 AccountId: Decode,484 C: 'static + ProvideRuntimeApi<Block> + HeaderBackend<Block>,485 C::Api: UniqueRuntimeApi<Block, BlockNumber, CrossAccountId, AccountId>,486 CrossAccountId: pallet_evm::account::CrossAccountId<AccountId>,487{488 pass_method!(489 account_tokens(collection: CollectionId, account: CrossAccountId) -> Vec<TokenId>, unique_api490 );491 pass_method!(492 collection_tokens(collection: CollectionId) -> Vec<TokenId>, unique_api493 );494 pass_method!(495 token_exists(collection: CollectionId, token: TokenId) -> bool, unique_api496 );497 pass_method!(498 token_owner(collection: CollectionId, token: TokenId) -> Option<CrossAccountId>, unique_api499 );500 pass_method!(501 topmost_token_owner(collection: CollectionId, token: TokenId) -> Option<CrossAccountId>, unique_api502 );503 pass_method!(token_children(collection: CollectionId, token: TokenId) -> Vec<TokenChild>, unique_api);504 pass_method!(total_supply(collection: CollectionId) -> u32, unique_api);505 pass_method!(account_balance(collection: CollectionId, account: CrossAccountId) -> u32, unique_api);506 pass_method!(balance(collection: CollectionId, account: CrossAccountId, token: TokenId) -> String => |v| v.to_string(), unique_api);507 pass_method!(508 allowance(collection: CollectionId, sender: CrossAccountId, spender: CrossAccountId, token: TokenId) -> String => |v| v.to_string(),509 unique_api510 );511512 pass_method!(collection_properties(513 collection: CollectionId,514515 #[map(|keys| string_keys_to_bytes_keys(keys))]516 keys: Option<Vec<String>>517 ) -> Vec<Property>, unique_api);518519 pass_method!(token_properties(520 collection: CollectionId,521 token_id: TokenId,522523 #[map(|keys| string_keys_to_bytes_keys(keys))]524 keys: Option<Vec<String>>525 ) -> Vec<Property>, unique_api);526527 pass_method!(property_permissions(528 collection: CollectionId,529530 #[map(|keys| string_keys_to_bytes_keys(keys))]531 keys: Option<Vec<String>>532 ) -> Vec<PropertyKeyPermission>, unique_api);533534 pass_method!(token_data(535 collection: CollectionId,536 token_id: TokenId,537538 #[map(|keys| string_keys_to_bytes_keys(keys))]539 keys: Option<Vec<String>>,540 ) -> TokenData<CrossAccountId>, unique_api);541542 pass_method!(adminlist(collection: CollectionId) -> Vec<CrossAccountId>, unique_api);543 pass_method!(allowlist(collection: CollectionId) -> Vec<CrossAccountId>, unique_api);544 pass_method!(allowed(collection: CollectionId, user: CrossAccountId) -> bool, unique_api);545 pass_method!(last_token_id(collection: CollectionId) -> TokenId, unique_api);546 pass_method!(collection_by_id(collection: CollectionId) -> Option<RpcCollection<AccountId>>, unique_api);547 pass_method!(collection_stats() -> CollectionStats, unique_api);548 pass_method!(next_sponsored(collection: CollectionId, account: CrossAccountId, token: TokenId) -> Option<u64>, unique_api);549 pass_method!(effective_collection_limits(collection_id: CollectionId) -> Option<CollectionLimits>, unique_api);550 pass_method!(total_pieces(collection_id: CollectionId, token_id: TokenId) -> Option<String> => |o| o.map(|number| number.to_string()) , unique_api);551 pass_method!(token_owners(collection: CollectionId, token: TokenId) -> Vec<CrossAccountId>, unique_api);552 pass_method!(total_staked(staker: Option<CrossAccountId>) -> String => |v| v.to_string(), unique_api);553 pass_method!(total_staked_per_block(staker: CrossAccountId) -> Vec<(BlockNumber, String)> =>554 |v| v555 .into_iter()556 .map(|(b, a)| (b, a.to_string()))557 .collect::<Vec<_>>(), unique_api);558 pass_method!(total_staking_locked(staker: CrossAccountId) -> String => |v| v.to_string(), unique_api);559 pass_method!(pending_unstake(staker: Option<CrossAccountId>) -> String => |v| v.to_string(), unique_api);560}561562#[allow(deprecated)]563impl<564 C,565 Block,566 AccountId,567 CollectionInfo,568 NftInfo,569 ResourceInfo,570 PropertyInfo,571 BaseInfo,572 PartType,573 Theme,574 >575 rmrk_unique_rpc::RmrkApiServer<576 <Block as BlockT>::Hash,577 AccountId,578 CollectionInfo,579 NftInfo,580 ResourceInfo,581 PropertyInfo,582 BaseInfo,583 PartType,584 Theme,585 > for Rmrk<C, Block>586where587 C: Send + Sync + 'static + ProvideRuntimeApi<Block> + HeaderBackend<Block>,588 C::Api: RmrkRuntimeApi<589 Block,590 AccountId,591 CollectionInfo,592 NftInfo,593 ResourceInfo,594 PropertyInfo,595 BaseInfo,596 PartType,597 Theme,598 >,599 AccountId: Decode + Encode,600 CollectionInfo: Decode,601 NftInfo: Decode,602 ResourceInfo: Decode,603 PropertyInfo: Decode,604 BaseInfo: Decode,605 PartType: Decode,606 Theme: Decode,607 Block: BlockT,608{609 pass_method!(last_collection_idx() -> RmrkCollectionId, rmrk_api);610 pass_method!(collection_by_id(id: RmrkCollectionId) -> Option<CollectionInfo>, rmrk_api);611 pass_method!(nft_by_id(collection_id: RmrkCollectionId, nft_id: RmrkNftId) -> Option<NftInfo>, rmrk_api);612 pass_method!(account_tokens(account_id: AccountId, collection_id: RmrkCollectionId) -> Vec<RmrkNftId>, rmrk_api);613 pass_method!(nft_children(collection_id: RmrkCollectionId, nft_id: RmrkNftId) -> Vec<RmrkNftChild>, rmrk_api);614 pass_method!(615 collection_properties(616 collection_id: RmrkCollectionId,617618 #[map(|keys| string_keys_to_bytes_keys(keys))]619 filter_keys: Option<Vec<String>>620 ) -> Vec<PropertyInfo>,621 rmrk_api622 );623 pass_method!(624 nft_properties(625 collection_id: RmrkCollectionId,626 nft_id: RmrkNftId,627628 #[map(|keys| string_keys_to_bytes_keys(keys))]629 filter_keys: Option<Vec<String>>630 ) -> Vec<PropertyInfo>,631 rmrk_api632 );633 pass_method!(nft_resources(collection_id: RmrkCollectionId, nft_id: RmrkNftId) -> Vec<ResourceInfo>, rmrk_api);634 pass_method!(nft_resource_priority(collection_id: RmrkCollectionId, nft_id: RmrkNftId, resource_id: RmrkResourceId) -> Option<u32>, rmrk_api);635 pass_method!(base(base_id: RmrkBaseId) -> Option<BaseInfo>, rmrk_api);636 pass_method!(base_parts(base_id: RmrkBaseId) -> Vec<PartType>, rmrk_api);637 pass_method!(theme_names(base_id: RmrkBaseId) -> Vec<RmrkThemeName>, rmrk_api);638 pass_method!(639 theme(640 base_id: RmrkBaseId,641642 #[map(|n| n.into_bytes())]643 theme_name: String,644645 #[map(|keys| string_keys_to_bytes_keys(keys))]646 filter_keys: Option<Vec<String>>647 ) -> Option<Theme>, rmrk_api);648}649650fn string_keys_to_bytes_keys(keys: Option<Vec<String>>) -> Option<Vec<Vec<u8>>> {651 keys.map(|keys| keys.into_iter().map(|key| key.into_bytes()).collect())652}