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;34use app_promotion_rpc::AppPromotionApi as AppPromotionRuntimeApi;353637use rmrk_rpc::RmrkApi as RmrkRuntimeApi;38use up_data_structs::{39 RmrkCollectionId, RmrkNftId, RmrkBaseId, RmrkNftChild, RmrkThemeName, RmrkResourceId,40};4142pub use app_promotion_unique_rpc::AppPromotionApiServer;43pub use rmrk_unique_rpc::RmrkApiServer;4445#[cfg(feature = "pov-estimate")]46pub mod pov_estimate;4748#[rpc(server)]49#[async_trait]50pub trait UniqueApi<BlockHash, CrossAccountId, AccountId> {51 52 #[method(name = "unique_accountTokens")]53 fn account_tokens(54 &self,55 collection: CollectionId,56 account: CrossAccountId,57 at: Option<BlockHash>,58 ) -> Result<Vec<TokenId>>;5960 61 #[method(name = "unique_collectionTokens")]62 fn collection_tokens(63 &self,64 collection: CollectionId,65 at: Option<BlockHash>,66 ) -> Result<Vec<TokenId>>;6768 69 #[method(name = "unique_tokenExists")]70 fn token_exists(71 &self,72 collection: CollectionId,73 token: TokenId,74 at: Option<BlockHash>,75 ) -> Result<bool>;7677 78 #[method(name = "unique_tokenOwner")]79 fn token_owner(80 &self,81 collection: CollectionId,82 token: TokenId,83 at: Option<BlockHash>,84 ) -> Result<Option<CrossAccountId>>;8586 87 #[method(name = "unique_tokenOwners")]88 fn token_owners(89 &self,90 collection: CollectionId,91 token: TokenId,92 at: Option<BlockHash>,93 ) -> Result<Vec<CrossAccountId>>;9495 96 #[method(name = "unique_topmostTokenOwner")]97 fn topmost_token_owner(98 &self,99 collection: CollectionId,100 token: TokenId,101 at: Option<BlockHash>,102 ) -> Result<Option<CrossAccountId>>;103104 105 #[method(name = "unique_tokenChildren")]106 fn token_children(107 &self,108 collection: CollectionId,109 token: TokenId,110 at: Option<BlockHash>,111 ) -> Result<Vec<TokenChild>>;112113 114 #[method(name = "unique_collectionProperties")]115 fn collection_properties(116 &self,117 collection: CollectionId,118 keys: Option<Vec<String>>,119 at: Option<BlockHash>,120 ) -> Result<Vec<Property>>;121122 123 #[method(name = "unique_tokenProperties")]124 fn token_properties(125 &self,126 collection: CollectionId,127 token_id: TokenId,128 keys: Option<Vec<String>>,129 at: Option<BlockHash>,130 ) -> Result<Vec<Property>>;131132 133 #[method(name = "unique_propertyPermissions")]134 fn property_permissions(135 &self,136 collection: CollectionId,137 keys: Option<Vec<String>>,138 at: Option<BlockHash>,139 ) -> Result<Vec<PropertyKeyPermission>>;140141 142 #[method(name = "unique_tokenData")]143 fn token_data(144 &self,145 collection: CollectionId,146 token_id: TokenId,147 keys: Option<Vec<String>>,148 at: Option<BlockHash>,149 ) -> Result<TokenData<CrossAccountId>>;150151 152 #[method(name = "unique_totalSupply")]153 fn total_supply(&self, collection: CollectionId, at: Option<BlockHash>) -> Result<u32>;154155 156 #[method(name = "unique_accountBalance")]157 fn account_balance(158 &self,159 collection: CollectionId,160 account: CrossAccountId,161 at: Option<BlockHash>,162 ) -> Result<u32>;163164 165 #[method(name = "unique_balance")]166 fn balance(167 &self,168 collection: CollectionId,169 account: CrossAccountId,170 token: TokenId,171 at: Option<BlockHash>,172 ) -> Result<String>;173174 175 #[method(name = "unique_allowance")]176 fn allowance(177 &self,178 collection: CollectionId,179 sender: CrossAccountId,180 spender: CrossAccountId,181 token: TokenId,182 at: Option<BlockHash>,183 ) -> Result<String>;184185 186 #[method(name = "unique_adminlist")]187 fn adminlist(188 &self,189 collection: CollectionId,190 at: Option<BlockHash>,191 ) -> Result<Vec<CrossAccountId>>;192193 194 #[method(name = "unique_allowlist")]195 fn allowlist(196 &self,197 collection: CollectionId,198 at: Option<BlockHash>,199 ) -> Result<Vec<CrossAccountId>>;200201 202 #[method(name = "unique_allowed")]203 fn allowed(204 &self,205 collection: CollectionId,206 user: CrossAccountId,207 at: Option<BlockHash>,208 ) -> Result<bool>;209210 211 #[method(name = "unique_lastTokenId")]212 fn last_token_id(&self, collection: CollectionId, at: Option<BlockHash>) -> Result<TokenId>;213214 215 #[method(name = "unique_collectionById")]216 fn collection_by_id(217 &self,218 collection: CollectionId,219 at: Option<BlockHash>,220 ) -> Result<Option<RpcCollection<AccountId>>>;221222 223 #[method(name = "unique_collectionStats")]224 fn collection_stats(&self, at: Option<BlockHash>) -> Result<CollectionStats>;225226 227 #[method(name = "unique_nextSponsored")]228 fn next_sponsored(229 &self,230 collection: CollectionId,231 account: CrossAccountId,232 token: TokenId,233 at: Option<BlockHash>,234 ) -> Result<Option<u64>>;235236 237 #[method(name = "unique_effectiveCollectionLimits")]238 fn effective_collection_limits(239 &self,240 collection_id: CollectionId,241 at: Option<BlockHash>,242 ) -> Result<Option<CollectionLimits>>;243244 245 #[method(name = "unique_totalPieces")]246 fn total_pieces(247 &self,248 collection_id: CollectionId,249 token_id: TokenId,250 at: Option<BlockHash>,251 ) -> Result<Option<String>>;252253 254 #[method(name = "unique_allowanceForAll")]255 fn allowance_for_all(256 &self,257 collection: CollectionId,258 owner: CrossAccountId,259 operator: CrossAccountId,260 at: Option<BlockHash>,261 ) -> Result<bool>;262}263264mod app_promotion_unique_rpc {265 use super::*;266267 #[rpc(server)]268 #[async_trait]269 pub trait AppPromotionApi<BlockHash, BlockNumber, CrossAccountId, AccountId> {270 271 #[method(name = "appPromotion_totalStaked")]272 fn total_staked(273 &self,274 staker: Option<CrossAccountId>,275 at: Option<BlockHash>,276 ) -> Result<String>;277278 279 #[method(name = "appPromotion_totalStakedPerBlock")]280 fn total_staked_per_block(281 &self,282 staker: CrossAccountId,283 at: Option<BlockHash>,284 ) -> Result<Vec<(BlockNumber, String)>>;285286 287 #[method(name = "appPromotion_pendingUnstake")]288 fn pending_unstake(289 &self,290 staker: Option<CrossAccountId>,291 at: Option<BlockHash>,292 ) -> Result<String>;293294 295 #[method(name = "appPromotion_pendingUnstakePerBlock")]296 fn pending_unstake_per_block(297 &self,298 staker: CrossAccountId,299 at: Option<BlockHash>,300 ) -> Result<Vec<(BlockNumber, String)>>;301 }302}303304mod rmrk_unique_rpc {305 use super::*;306307 #[rpc(server)]308 #[async_trait]309 pub trait RmrkApi<310 BlockHash,311 AccountId,312 CollectionInfo,313 NftInfo,314 ResourceInfo,315 PropertyInfo,316 BaseInfo,317 PartType,318 Theme,319 >320 {321 322 #[method(name = "rmrk_lastCollectionIdx")]323 fn last_collection_idx(&self, at: Option<BlockHash>) -> Result<RmrkCollectionId>;324325 326 #[method(name = "rmrk_collectionById")]327 fn collection_by_id(328 &self,329 id: RmrkCollectionId,330 at: Option<BlockHash>,331 ) -> Result<Option<CollectionInfo>>;332333 334 #[method(name = "rmrk_nftById")]335 fn nft_by_id(336 &self,337 collection_id: RmrkCollectionId,338 nft_id: RmrkNftId,339 at: Option<BlockHash>,340 ) -> Result<Option<NftInfo>>;341342 343 #[method(name = "rmrk_accountTokens")]344 fn account_tokens(345 &self,346 account_id: AccountId,347 collection_id: RmrkCollectionId,348 at: Option<BlockHash>,349 ) -> Result<Vec<RmrkNftId>>;350351 352 #[method(name = "rmrk_nftChildren")]353 fn nft_children(354 &self,355 collection_id: RmrkCollectionId,356 nft_id: RmrkNftId,357 at: Option<BlockHash>,358 ) -> Result<Vec<RmrkNftChild>>;359360 361 #[method(name = "rmrk_collectionProperties")]362 fn collection_properties(363 &self,364 collection_id: RmrkCollectionId,365 filter_keys: Option<Vec<String>>,366 at: Option<BlockHash>,367 ) -> Result<Vec<PropertyInfo>>;368369 370 #[method(name = "rmrk_nftProperties")]371 fn nft_properties(372 &self,373 collection_id: RmrkCollectionId,374 nft_id: RmrkNftId,375 filter_keys: Option<Vec<String>>,376 at: Option<BlockHash>,377 ) -> Result<Vec<PropertyInfo>>;378379 380 #[method(name = "rmrk_nftResources")]381 fn nft_resources(382 &self,383 collection_id: RmrkCollectionId,384 nft_id: RmrkNftId,385 at: Option<BlockHash>,386 ) -> Result<Vec<ResourceInfo>>;387388 389 #[method(name = "rmrk_nftResourcePriority")]390 fn nft_resource_priority(391 &self,392 collection_id: RmrkCollectionId,393 nft_id: RmrkNftId,394 resource_id: RmrkResourceId,395 at: Option<BlockHash>,396 ) -> Result<Option<u32>>;397398 399 #[method(name = "rmrk_base")]400 fn base(&self, base_id: RmrkBaseId, at: Option<BlockHash>) -> Result<Option<BaseInfo>>;401402 403 #[method(name = "rmrk_baseParts")]404 fn base_parts(&self, base_id: RmrkBaseId, at: Option<BlockHash>) -> Result<Vec<PartType>>;405406 407 #[method(name = "rmrk_themeNames")]408 fn theme_names(409 &self,410 base_id: RmrkBaseId,411 at: Option<BlockHash>,412 ) -> Result<Vec<RmrkThemeName>>;413414 415 #[method(name = "rmrk_themes")]416 fn theme(417 &self,418 base_id: RmrkBaseId,419 theme_name: String,420 filter_keys: Option<Vec<String>>,421 at: Option<BlockHash>,422 ) -> Result<Option<Theme>>;423 }424}425426#[macro_export]427macro_rules! define_struct_for_server_api {428 ($name:ident { $($arg:ident: $arg_ty:ty),+ $(,)? }) => {429 pub struct $name<Client, Block: BlockT> {430 $($arg: $arg_ty),+,431 _marker: std::marker::PhantomData<Block>,432 }433434 impl<Client, Block: BlockT> $name<Client, Block> {435 pub fn new($($arg: $arg_ty),+) -> Self {436 Self {437 $($arg),+,438 _marker: Default::default(),439 }440 }441 }442 };443}444445define_struct_for_server_api! {446 Unique {447 client: Arc<Client>448 }449}450451define_struct_for_server_api! {452 AppPromotion {453 client: Arc<Client>454 }455}456457define_struct_for_server_api! {458 Rmrk {459 client: Arc<Client>460 }461}462463macro_rules! pass_method {464 (465 $method_name:ident(466 $($(#[map(|$map_arg:ident| $map:expr)])? $name:ident: $ty:ty),* $(,)?467 ) -> $result:ty $(=> $mapper:expr)?,468 469 $runtime_api_macro:ident470 $(; changed_in $ver:expr, $changed_method_name:ident ($($changed_name:expr), * $(,)?) => $fixer:expr)*471 ) => {472 fn $method_name(473 &self,474 $(475 $name: $ty,476 )*477 at: Option<<Block as BlockT>::Hash>,478 ) -> Result<$result> {479 let api = self.client.runtime_api();480 let at = BlockId::hash(at.unwrap_or_else(|| self.client.info().best_hash));481 let _api_version = if let Ok(Some(api_version)) =482 api.api_version::<$runtime_api_macro!()>(&at)483 {484 api_version485 } else {486 487 return Err(anyhow!("api is not available").into())488 };489490 let result = $(if _api_version < $ver {491 api.$changed_method_name(&at, $($changed_name),*).map(|r| r.map($fixer))492 } else)*493 { api.$method_name(&at, $($((|$map_arg: $ty| $map))? ($name)),*) };494495 Ok(result496 .map_err(|e| anyhow!("unable to query: {e}"))?497 .map_err(|e| anyhow!("runtime error: {e:?}"))$(.map($mapper))??)498 }499 };500}501502macro_rules! unique_api {503 () => {504 dyn UniqueRuntimeApi<Block, CrossAccountId, AccountId>505 };506}507508macro_rules! app_promotion_api {509 () => {510 dyn AppPromotionRuntimeApi<Block, BlockNumber, CrossAccountId, AccountId>511 };512}513514macro_rules! rmrk_api {515 () => {516 dyn RmrkRuntimeApi<Block, AccountId, CollectionInfo, NftInfo, ResourceInfo, PropertyInfo, BaseInfo, PartType, Theme>517 };518}519520#[allow(deprecated)]521impl<C, Block, CrossAccountId, AccountId>522 UniqueApiServer<<Block as BlockT>::Hash, CrossAccountId, AccountId> for Unique<C, Block>523where524 Block: BlockT,525 AccountId: Decode,526 C: 'static + ProvideRuntimeApi<Block> + HeaderBackend<Block>,527 C::Api: UniqueRuntimeApi<Block, CrossAccountId, AccountId>,528 CrossAccountId: pallet_evm::account::CrossAccountId<AccountId>,529{530 pass_method!(531 account_tokens(collection: CollectionId, account: CrossAccountId) -> Vec<TokenId>, unique_api532 );533 pass_method!(534 collection_tokens(collection: CollectionId) -> Vec<TokenId>, unique_api535 );536 pass_method!(537 token_exists(collection: CollectionId, token: TokenId) -> bool, unique_api538 );539 pass_method!(540 token_owner(collection: CollectionId, token: TokenId) -> Option<CrossAccountId>, unique_api541 );542 pass_method!(543 topmost_token_owner(collection: CollectionId, token: TokenId) -> Option<CrossAccountId>, unique_api544 );545 pass_method!(token_children(collection: CollectionId, token: TokenId) -> Vec<TokenChild>, unique_api);546 pass_method!(total_supply(collection: CollectionId) -> u32, unique_api);547 pass_method!(account_balance(collection: CollectionId, account: CrossAccountId) -> u32, unique_api);548 pass_method!(balance(collection: CollectionId, account: CrossAccountId, token: TokenId) -> String => |v| v.to_string(), unique_api);549 pass_method!(550 allowance(collection: CollectionId, sender: CrossAccountId, spender: CrossAccountId, token: TokenId) -> String => |v| v.to_string(),551 unique_api552 );553554 pass_method!(collection_properties(555 collection: CollectionId,556557 #[map(|keys| string_keys_to_bytes_keys(keys))]558 keys: Option<Vec<String>>559 ) -> Vec<Property>, unique_api);560561 pass_method!(token_properties(562 collection: CollectionId,563 token_id: TokenId,564565 #[map(|keys| string_keys_to_bytes_keys(keys))]566 keys: Option<Vec<String>>567 ) -> Vec<Property>, unique_api);568569 pass_method!(property_permissions(570 collection: CollectionId,571572 #[map(|keys| string_keys_to_bytes_keys(keys))]573 keys: Option<Vec<String>>574 ) -> Vec<PropertyKeyPermission>, unique_api);575576 pass_method!(577 token_data(578 collection: CollectionId,579 token_id: TokenId,580581 #[map(|keys| string_keys_to_bytes_keys(keys))]582 keys: Option<Vec<String>>,583 ) -> TokenData<CrossAccountId>, unique_api;584 changed_in 3, token_data_before_version_3(collection, token_id, string_keys_to_bytes_keys(keys)) => |value| value.into()585 );586587 pass_method!(adminlist(collection: CollectionId) -> Vec<CrossAccountId>, unique_api);588 pass_method!(allowlist(collection: CollectionId) -> Vec<CrossAccountId>, unique_api);589 pass_method!(allowed(collection: CollectionId, user: CrossAccountId) -> bool, unique_api);590 pass_method!(last_token_id(collection: CollectionId) -> TokenId, unique_api);591 pass_method!(592 collection_by_id(collection: CollectionId) -> Option<RpcCollection<AccountId>>, unique_api;593 changed_in 3, collection_by_id_before_version_3(collection) => |value| value.map(|coll| coll.into())594 );595 pass_method!(collection_stats() -> CollectionStats, unique_api);596 pass_method!(next_sponsored(collection: CollectionId, account: CrossAccountId, token: TokenId) -> Option<u64>, unique_api);597 pass_method!(effective_collection_limits(collection_id: CollectionId) -> Option<CollectionLimits>, unique_api);598 pass_method!(total_pieces(collection_id: CollectionId, token_id: TokenId) -> Option<String> => |o| o.map(|number| number.to_string()) , unique_api);599 pass_method!(token_owners(collection: CollectionId, token: TokenId) -> Vec<CrossAccountId>, unique_api);600 pass_method!(allowance_for_all(collection: CollectionId, owner: CrossAccountId, operator: CrossAccountId) -> bool, unique_api);601}602603impl<C, Block, BlockNumber, CrossAccountId, AccountId>604 app_promotion_unique_rpc::AppPromotionApiServer<605 <Block as BlockT>::Hash,606 BlockNumber,607 CrossAccountId,608 AccountId,609 > for AppPromotion<C, Block>610where611 Block: BlockT,612 BlockNumber: Decode + Member + AtLeast32BitUnsigned,613 AccountId: Decode,614 C: 'static + ProvideRuntimeApi<Block> + HeaderBackend<Block>,615 CrossAccountId: pallet_evm::account::CrossAccountId<AccountId>,616 C::Api: AppPromotionRuntimeApi<Block, BlockNumber, CrossAccountId, AccountId>,617{618 pass_method!(total_staked(staker: Option<CrossAccountId>) -> String => |v| v.to_string(), app_promotion_api);619 pass_method!(total_staked_per_block(staker: CrossAccountId) -> Vec<(BlockNumber, String)> =>620 |v| v621 .into_iter()622 .map(|(b, a)| (b, a.to_string()))623 .collect::<Vec<_>>(), app_promotion_api);624 pass_method!(pending_unstake(staker: Option<CrossAccountId>) -> String => |v| v.to_string(), app_promotion_api);625 pass_method!(pending_unstake_per_block(staker: CrossAccountId) -> Vec<(BlockNumber, String)> =>626 |v| v627 .into_iter()628 .map(|(b, a)| (b, a.to_string()))629 .collect::<Vec<_>>(), app_promotion_api);630}631632#[allow(deprecated)]633impl<634 C,635 Block,636 AccountId,637 CollectionInfo,638 NftInfo,639 ResourceInfo,640 PropertyInfo,641 BaseInfo,642 PartType,643 Theme,644 >645 rmrk_unique_rpc::RmrkApiServer<646 <Block as BlockT>::Hash,647 AccountId,648 CollectionInfo,649 NftInfo,650 ResourceInfo,651 PropertyInfo,652 BaseInfo,653 PartType,654 Theme,655 > for Rmrk<C, Block>656where657 C: Send + Sync + 'static + ProvideRuntimeApi<Block> + HeaderBackend<Block>,658 C::Api: RmrkRuntimeApi<659 Block,660 AccountId,661 CollectionInfo,662 NftInfo,663 ResourceInfo,664 PropertyInfo,665 BaseInfo,666 PartType,667 Theme,668 >,669 AccountId: Decode + Encode,670 CollectionInfo: Decode,671 NftInfo: Decode,672 ResourceInfo: Decode,673 PropertyInfo: Decode,674 BaseInfo: Decode,675 PartType: Decode,676 Theme: Decode,677 Block: BlockT,678{679 pass_method!(last_collection_idx() -> RmrkCollectionId, rmrk_api);680 pass_method!(collection_by_id(id: RmrkCollectionId) -> Option<CollectionInfo>, rmrk_api);681 pass_method!(nft_by_id(collection_id: RmrkCollectionId, nft_id: RmrkNftId) -> Option<NftInfo>, rmrk_api);682 pass_method!(account_tokens(account_id: AccountId, collection_id: RmrkCollectionId) -> Vec<RmrkNftId>, rmrk_api);683 pass_method!(nft_children(collection_id: RmrkCollectionId, nft_id: RmrkNftId) -> Vec<RmrkNftChild>, rmrk_api);684 pass_method!(685 collection_properties(686 collection_id: RmrkCollectionId,687688 #[map(|keys| string_keys_to_bytes_keys(keys))]689 filter_keys: Option<Vec<String>>690 ) -> Vec<PropertyInfo>,691 rmrk_api692 );693 pass_method!(694 nft_properties(695 collection_id: RmrkCollectionId,696 nft_id: RmrkNftId,697698 #[map(|keys| string_keys_to_bytes_keys(keys))]699 filter_keys: Option<Vec<String>>700 ) -> Vec<PropertyInfo>,701 rmrk_api702 );703 pass_method!(nft_resources(collection_id: RmrkCollectionId, nft_id: RmrkNftId) -> Vec<ResourceInfo>, rmrk_api);704 pass_method!(nft_resource_priority(collection_id: RmrkCollectionId, nft_id: RmrkNftId, resource_id: RmrkResourceId) -> Option<u32>, rmrk_api);705 pass_method!(base(base_id: RmrkBaseId) -> Option<BaseInfo>, rmrk_api);706 pass_method!(base_parts(base_id: RmrkBaseId) -> Vec<PartType>, rmrk_api);707 pass_method!(theme_names(base_id: RmrkBaseId) -> Vec<RmrkThemeName>, rmrk_api);708 pass_method!(709 theme(710 base_id: RmrkBaseId,711712 #[map(|n| n.into_bytes())]713 theme_name: String,714715 #[map(|keys| string_keys_to_bytes_keys(keys))]716 filter_keys: Option<Vec<String>>717 ) -> Option<Theme>, rmrk_api);718}719720fn string_keys_to_bytes_keys(keys: Option<Vec<String>>) -> Option<Vec<Vec<u8>>> {721 keys.map(|keys| keys.into_iter().map(|key| key.into_bytes()).collect())722}