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>;265}266267mod rmrk_unique_rpc {268 use super::*;269270 #[rpc(server)]271 #[async_trait]272 pub trait RmrkApi<273 BlockHash,274 AccountId,275 CollectionInfo,276 NftInfo,277 ResourceInfo,278 PropertyInfo,279 BaseInfo,280 PartType,281 Theme,282 >283 {284 285 #[method(name = "rmrk_lastCollectionIdx")]286 fn last_collection_idx(&self, at: Option<BlockHash>) -> Result<RmrkCollectionId>;287288 289 #[method(name = "rmrk_collectionById")]290 fn collection_by_id(291 &self,292 id: RmrkCollectionId,293 at: Option<BlockHash>,294 ) -> Result<Option<CollectionInfo>>;295296 297 #[method(name = "rmrk_nftById")]298 fn nft_by_id(299 &self,300 collection_id: RmrkCollectionId,301 nft_id: RmrkNftId,302 at: Option<BlockHash>,303 ) -> Result<Option<NftInfo>>;304305 306 #[method(name = "rmrk_accountTokens")]307 fn account_tokens(308 &self,309 account_id: AccountId,310 collection_id: RmrkCollectionId,311 at: Option<BlockHash>,312 ) -> Result<Vec<RmrkNftId>>;313314 315 #[method(name = "rmrk_nftChildren")]316 fn nft_children(317 &self,318 collection_id: RmrkCollectionId,319 nft_id: RmrkNftId,320 at: Option<BlockHash>,321 ) -> Result<Vec<RmrkNftChild>>;322323 324 #[method(name = "rmrk_collectionProperties")]325 fn collection_properties(326 &self,327 collection_id: RmrkCollectionId,328 filter_keys: Option<Vec<String>>,329 at: Option<BlockHash>,330 ) -> Result<Vec<PropertyInfo>>;331332 333 #[method(name = "rmrk_nftProperties")]334 fn nft_properties(335 &self,336 collection_id: RmrkCollectionId,337 nft_id: RmrkNftId,338 filter_keys: Option<Vec<String>>,339 at: Option<BlockHash>,340 ) -> Result<Vec<PropertyInfo>>;341342 343 #[method(name = "rmrk_nftResources")]344 fn nft_resources(345 &self,346 collection_id: RmrkCollectionId,347 nft_id: RmrkNftId,348 at: Option<BlockHash>,349 ) -> Result<Vec<ResourceInfo>>;350351 352 #[method(name = "rmrk_nftResourcePriority")]353 fn nft_resource_priority(354 &self,355 collection_id: RmrkCollectionId,356 nft_id: RmrkNftId,357 resource_id: RmrkResourceId,358 at: Option<BlockHash>,359 ) -> Result<Option<u32>>;360361 362 #[method(name = "rmrk_base")]363 fn base(&self, base_id: RmrkBaseId, at: Option<BlockHash>) -> Result<Option<BaseInfo>>;364365 366 #[method(name = "rmrk_baseParts")]367 fn base_parts(&self, base_id: RmrkBaseId, at: Option<BlockHash>) -> Result<Vec<PartType>>;368369 370 #[method(name = "rmrk_themeNames")]371 fn theme_names(372 &self,373 base_id: RmrkBaseId,374 at: Option<BlockHash>,375 ) -> Result<Vec<RmrkThemeName>>;376377 378 #[method(name = "rmrk_themes")]379 fn theme(380 &self,381 base_id: RmrkBaseId,382 theme_name: String,383 filter_keys: Option<Vec<String>>,384 at: Option<BlockHash>,385 ) -> Result<Option<Theme>>;386 }387}388389pub struct Unique<C, P> {390 client: Arc<C>,391 _marker: std::marker::PhantomData<P>,392}393394impl<C, P> Unique<C, P> {395 pub fn new(client: Arc<C>) -> Self {396 Self {397 client,398 _marker: Default::default(),399 }400 }401}402403pub struct Rmrk<C, P> {404 client: Arc<C>,405 _marker: std::marker::PhantomData<P>,406}407408impl<C, P> Rmrk<C, P> {409 pub fn new(client: Arc<C>) -> Self {410 Self {411 client,412 _marker: Default::default(),413 }414 }415}416417macro_rules! pass_method {418 (419 $method_name:ident(420 $($(#[map(|$map_arg:ident| $map:expr)])? $name:ident: $ty:ty),* $(,)?421 ) -> $result:ty $(=> $mapper:expr)?,422 423 $runtime_api_macro:ident424 $(; changed_in $ver:expr, $changed_method_name:ident ($($changed_name:expr), * $(,)?) => $fixer:expr)*425 ) => {426 fn $method_name(427 &self,428 $(429 $name: $ty,430 )*431 at: Option<<Block as BlockT>::Hash>,432 ) -> Result<$result> {433 let api = self.client.runtime_api();434 let at = BlockId::hash(at.unwrap_or_else(|| self.client.info().best_hash));435 let _api_version = if let Ok(Some(api_version)) =436 api.api_version::<$runtime_api_macro!()>(&at)437 {438 api_version439 } else {440 441 return Err(anyhow!("api is not available").into())442 };443444 let result = $(if _api_version < $ver {445 api.$changed_method_name(&at, $($changed_name),*).map(|r| r.map($fixer))446 } else)*447 { api.$method_name(&at, $($((|$map_arg: $ty| $map))? ($name)),*) };448449 Ok(result450 .map_err(|e| anyhow!("unable to query: {e}"))?451 .map_err(|e| anyhow!("runtime error: {e:?}"))$(.map($mapper))??)452 }453 };454}455456macro_rules! unique_api {457 () => {458 dyn UniqueRuntimeApi<Block, BlockNumber, CrossAccountId, AccountId>459 };460}461462macro_rules! rmrk_api {463 () => {464 dyn RmrkRuntimeApi<Block, AccountId, CollectionInfo, NftInfo, ResourceInfo, PropertyInfo, BaseInfo, PartType, Theme>465 };466}467468#[allow(deprecated)]469impl<C, Block, BlockNumber, CrossAccountId, AccountId>470 UniqueApiServer<<Block as BlockT>::Hash, BlockNumber, CrossAccountId, AccountId>471 for Unique<C, Block>472where473 Block: BlockT,474 BlockNumber: Decode + Member + AtLeast32BitUnsigned,475 AccountId: Decode,476 C: 'static + ProvideRuntimeApi<Block> + HeaderBackend<Block>,477 C::Api: UniqueRuntimeApi<Block, BlockNumber, CrossAccountId, AccountId>,478 CrossAccountId: pallet_evm::account::CrossAccountId<AccountId>,479{480 pass_method!(481 account_tokens(collection: CollectionId, account: CrossAccountId) -> Vec<TokenId>, unique_api482 );483 pass_method!(484 collection_tokens(collection: CollectionId) -> Vec<TokenId>, unique_api485 );486 pass_method!(487 token_exists(collection: CollectionId, token: TokenId) -> bool, unique_api488 );489 pass_method!(490 token_owner(collection: CollectionId, token: TokenId) -> Option<CrossAccountId>, unique_api491 );492 pass_method!(493 topmost_token_owner(collection: CollectionId, token: TokenId) -> Option<CrossAccountId>, unique_api494 );495 pass_method!(token_children(collection: CollectionId, token: TokenId) -> Vec<TokenChild>, unique_api);496 pass_method!(total_supply(collection: CollectionId) -> u32, unique_api);497 pass_method!(account_balance(collection: CollectionId, account: CrossAccountId) -> u32, unique_api);498 pass_method!(balance(collection: CollectionId, account: CrossAccountId, token: TokenId) -> String => |v| v.to_string(), unique_api);499 pass_method!(500 allowance(collection: CollectionId, sender: CrossAccountId, spender: CrossAccountId, token: TokenId) -> String => |v| v.to_string(),501 unique_api502 );503504 pass_method!(collection_properties(505 collection: CollectionId,506507 #[map(|keys| string_keys_to_bytes_keys(keys))]508 keys: Option<Vec<String>>509 ) -> Vec<Property>, unique_api);510511 pass_method!(token_properties(512 collection: CollectionId,513 token_id: TokenId,514515 #[map(|keys| string_keys_to_bytes_keys(keys))]516 keys: Option<Vec<String>>517 ) -> Vec<Property>, unique_api);518519 pass_method!(property_permissions(520 collection: CollectionId,521522 #[map(|keys| string_keys_to_bytes_keys(keys))]523 keys: Option<Vec<String>>524 ) -> Vec<PropertyKeyPermission>, unique_api);525526 pass_method!(token_data(527 collection: CollectionId,528 token_id: TokenId,529530 #[map(|keys| string_keys_to_bytes_keys(keys))]531 keys: Option<Vec<String>>,532 ) -> TokenData<CrossAccountId>, unique_api);533534 pass_method!(adminlist(collection: CollectionId) -> Vec<CrossAccountId>, unique_api);535 pass_method!(allowlist(collection: CollectionId) -> Vec<CrossAccountId>, unique_api);536 pass_method!(allowed(collection: CollectionId, user: CrossAccountId) -> bool, unique_api);537 pass_method!(last_token_id(collection: CollectionId) -> TokenId, unique_api);538 pass_method!(collection_by_id(collection: CollectionId) -> Option<RpcCollection<AccountId>>, unique_api);539 pass_method!(collection_stats() -> CollectionStats, unique_api);540 pass_method!(next_sponsored(collection: CollectionId, account: CrossAccountId, token: TokenId) -> Option<u64>, unique_api);541 pass_method!(effective_collection_limits(collection_id: CollectionId) -> Option<CollectionLimits>, unique_api);542 pass_method!(total_pieces(collection_id: CollectionId, token_id: TokenId) -> Option<String> => |o| o.map(|number| number.to_string()) , unique_api);543 pass_method!(token_owners(collection: CollectionId, token: TokenId) -> Vec<CrossAccountId>, unique_api);544 pass_method!(total_staked(staker: Option<CrossAccountId>) -> String => |v| v.to_string(), unique_api);545 pass_method!(total_staked_per_block(staker: CrossAccountId) -> Vec<(BlockNumber, String)> =>546 |v| v547 .into_iter()548 .map(|(b, a)| (b, a.to_string()))549 .collect::<Vec<_>>(), unique_api);550 pass_method!(total_staking_locked(staker: CrossAccountId) -> String => |v| v.to_string(), unique_api);551}552553#[allow(deprecated)]554impl<555 C,556 Block,557 AccountId,558 CollectionInfo,559 NftInfo,560 ResourceInfo,561 PropertyInfo,562 BaseInfo,563 PartType,564 Theme,565 >566 rmrk_unique_rpc::RmrkApiServer<567 <Block as BlockT>::Hash,568 AccountId,569 CollectionInfo,570 NftInfo,571 ResourceInfo,572 PropertyInfo,573 BaseInfo,574 PartType,575 Theme,576 > for Rmrk<C, Block>577where578 C: Send + Sync + 'static + ProvideRuntimeApi<Block> + HeaderBackend<Block>,579 C::Api: RmrkRuntimeApi<580 Block,581 AccountId,582 CollectionInfo,583 NftInfo,584 ResourceInfo,585 PropertyInfo,586 BaseInfo,587 PartType,588 Theme,589 >,590 AccountId: Decode + Encode,591 CollectionInfo: Decode,592 NftInfo: Decode,593 ResourceInfo: Decode,594 PropertyInfo: Decode,595 BaseInfo: Decode,596 PartType: Decode,597 Theme: Decode,598 Block: BlockT,599{600 pass_method!(last_collection_idx() -> RmrkCollectionId, rmrk_api);601 pass_method!(collection_by_id(id: RmrkCollectionId) -> Option<CollectionInfo>, rmrk_api);602 pass_method!(nft_by_id(collection_id: RmrkCollectionId, nft_id: RmrkNftId) -> Option<NftInfo>, rmrk_api);603 pass_method!(account_tokens(account_id: AccountId, collection_id: RmrkCollectionId) -> Vec<RmrkNftId>, rmrk_api);604 pass_method!(nft_children(collection_id: RmrkCollectionId, nft_id: RmrkNftId) -> Vec<RmrkNftChild>, rmrk_api);605 pass_method!(606 collection_properties(607 collection_id: RmrkCollectionId,608609 #[map(|keys| string_keys_to_bytes_keys(keys))]610 filter_keys: Option<Vec<String>>611 ) -> Vec<PropertyInfo>,612 rmrk_api613 );614 pass_method!(615 nft_properties(616 collection_id: RmrkCollectionId,617 nft_id: RmrkNftId,618619 #[map(|keys| string_keys_to_bytes_keys(keys))]620 filter_keys: Option<Vec<String>>621 ) -> Vec<PropertyInfo>,622 rmrk_api623 );624 pass_method!(nft_resources(collection_id: RmrkCollectionId, nft_id: RmrkNftId) -> Vec<ResourceInfo>, rmrk_api);625 pass_method!(nft_resource_priority(collection_id: RmrkCollectionId, nft_id: RmrkNftId, resource_id: RmrkResourceId) -> Option<u32>, rmrk_api);626 pass_method!(base(base_id: RmrkBaseId) -> Option<BaseInfo>, rmrk_api);627 pass_method!(base_parts(base_id: RmrkBaseId) -> Vec<PartType>, rmrk_api);628 pass_method!(theme_names(base_id: RmrkBaseId) -> Vec<RmrkThemeName>, rmrk_api);629 pass_method!(630 theme(631 base_id: RmrkBaseId,632633 #[map(|n| n.into_bytes())]634 theme_name: String,635636 #[map(|keys| string_keys_to_bytes_keys(keys))]637 filter_keys: Option<Vec<String>>638 ) -> Option<Theme>, rmrk_api);639}640641fn string_keys_to_bytes_keys(keys: Option<Vec<String>>) -> Option<Vec<Vec<u8>>> {642 keys.map(|keys| keys.into_iter().map(|key| key.into_bytes()).collect())643}