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: CrossAccountId, at: Option<BlockHash>) -> Result<u128>;251252 253 #[method(name = "unique_totalStakedPerBlock")]254 fn total_staked_per_block(255 &self,256 staker: CrossAccountId,257 at: Option<BlockHash>,258 ) -> Result<Vec<(BlockNumber, u128)>>;259260 261 #[method(name = "unique_totalStakingLocked")]262 fn total_staking_locked(&self, staker: CrossAccountId, at: Option<BlockHash>) -> Result<u128>;263}264265mod rmrk_unique_rpc {266 use super::*;267268 #[rpc(server)]269 #[async_trait]270 pub trait RmrkApi<271 BlockHash,272 AccountId,273 CollectionInfo,274 NftInfo,275 ResourceInfo,276 PropertyInfo,277 BaseInfo,278 PartType,279 Theme,280 >281 {282 283 #[method(name = "rmrk_lastCollectionIdx")]284 fn last_collection_idx(&self, at: Option<BlockHash>) -> Result<RmrkCollectionId>;285286 287 #[method(name = "rmrk_collectionById")]288 fn collection_by_id(289 &self,290 id: RmrkCollectionId,291 at: Option<BlockHash>,292 ) -> Result<Option<CollectionInfo>>;293294 295 #[method(name = "rmrk_nftById")]296 fn nft_by_id(297 &self,298 collection_id: RmrkCollectionId,299 nft_id: RmrkNftId,300 at: Option<BlockHash>,301 ) -> Result<Option<NftInfo>>;302303 304 #[method(name = "rmrk_accountTokens")]305 fn account_tokens(306 &self,307 account_id: AccountId,308 collection_id: RmrkCollectionId,309 at: Option<BlockHash>,310 ) -> Result<Vec<RmrkNftId>>;311312 313 #[method(name = "rmrk_nftChildren")]314 fn nft_children(315 &self,316 collection_id: RmrkCollectionId,317 nft_id: RmrkNftId,318 at: Option<BlockHash>,319 ) -> Result<Vec<RmrkNftChild>>;320321 322 #[method(name = "rmrk_collectionProperties")]323 fn collection_properties(324 &self,325 collection_id: RmrkCollectionId,326 filter_keys: Option<Vec<String>>,327 at: Option<BlockHash>,328 ) -> Result<Vec<PropertyInfo>>;329330 331 #[method(name = "rmrk_nftProperties")]332 fn nft_properties(333 &self,334 collection_id: RmrkCollectionId,335 nft_id: RmrkNftId,336 filter_keys: Option<Vec<String>>,337 at: Option<BlockHash>,338 ) -> Result<Vec<PropertyInfo>>;339340 341 #[method(name = "rmrk_nftResources")]342 fn nft_resources(343 &self,344 collection_id: RmrkCollectionId,345 nft_id: RmrkNftId,346 at: Option<BlockHash>,347 ) -> Result<Vec<ResourceInfo>>;348349 350 #[method(name = "rmrk_nftResourcePriority")]351 fn nft_resource_priority(352 &self,353 collection_id: RmrkCollectionId,354 nft_id: RmrkNftId,355 resource_id: RmrkResourceId,356 at: Option<BlockHash>,357 ) -> Result<Option<u32>>;358359 360 #[method(name = "rmrk_base")]361 fn base(&self, base_id: RmrkBaseId, at: Option<BlockHash>) -> Result<Option<BaseInfo>>;362363 364 #[method(name = "rmrk_baseParts")]365 fn base_parts(&self, base_id: RmrkBaseId, at: Option<BlockHash>) -> Result<Vec<PartType>>;366367 368 #[method(name = "rmrk_themeNames")]369 fn theme_names(370 &self,371 base_id: RmrkBaseId,372 at: Option<BlockHash>,373 ) -> Result<Vec<RmrkThemeName>>;374375 376 #[method(name = "rmrk_themes")]377 fn theme(378 &self,379 base_id: RmrkBaseId,380 theme_name: String,381 filter_keys: Option<Vec<String>>,382 at: Option<BlockHash>,383 ) -> Result<Option<Theme>>;384 }385}386387pub struct Unique<C, P> {388 client: Arc<C>,389 _marker: std::marker::PhantomData<P>,390}391392impl<C, P> Unique<C, P> {393 pub fn new(client: Arc<C>) -> Self {394 Self {395 client,396 _marker: Default::default(),397 }398 }399}400401pub struct Rmrk<C, P> {402 client: Arc<C>,403 _marker: std::marker::PhantomData<P>,404}405406impl<C, P> Rmrk<C, P> {407 pub fn new(client: Arc<C>) -> Self {408 Self {409 client,410 _marker: Default::default(),411 }412 }413}414415macro_rules! pass_method {416 (417 $method_name:ident(418 $($(#[map(|$map_arg:ident| $map:expr)])? $name:ident: $ty:ty),* $(,)?419 ) -> $result:ty $(=> $mapper:expr)?,420 421 $runtime_api_macro:ident422 $(; changed_in $ver:expr, $changed_method_name:ident ($($changed_name:expr), * $(,)?) => $fixer:expr)*423 ) => {424 fn $method_name(425 &self,426 $(427 $name: $ty,428 )*429 at: Option<<Block as BlockT>::Hash>,430 ) -> Result<$result> {431 let api = self.client.runtime_api();432 let at = BlockId::hash(at.unwrap_or_else(|| self.client.info().best_hash));433 let _api_version = if let Ok(Some(api_version)) =434 api.api_version::<$runtime_api_macro!()>(&at)435 {436 api_version437 } else {438 439 return Err(anyhow!("api is not available").into())440 };441442 let result = $(if _api_version < $ver {443 api.$changed_method_name(&at, $($changed_name),*).map(|r| r.map($fixer))444 } else)*445 { api.$method_name(&at, $($((|$map_arg: $ty| $map))? ($name)),*) };446447 Ok(result448 .map_err(|e| anyhow!("unable to query: {e}"))?449 .map_err(|e| anyhow!("runtime error: {e:?}"))$(.map($mapper))??)450 }451 };452}453454macro_rules! unique_api {455 () => {456 dyn UniqueRuntimeApi<Block, BlockNumber, CrossAccountId, AccountId>457 };458}459460macro_rules! rmrk_api {461 () => {462 dyn RmrkRuntimeApi<Block, AccountId, CollectionInfo, NftInfo, ResourceInfo, PropertyInfo, BaseInfo, PartType, Theme>463 };464}465466#[allow(deprecated)]467impl<C, Block, BlockNumber, CrossAccountId, AccountId>468 UniqueApiServer<<Block as BlockT>::Hash, BlockNumber, CrossAccountId, AccountId>469 for Unique<C, Block>470where471 Block: BlockT,472 BlockNumber: Decode + Member + AtLeast32BitUnsigned,473 AccountId: Decode,474 C: 'static + ProvideRuntimeApi<Block> + HeaderBackend<Block>,475 C::Api: UniqueRuntimeApi<Block, BlockNumber, CrossAccountId, AccountId>,476 CrossAccountId: pallet_evm::account::CrossAccountId<AccountId>,477{478 pass_method!(479 account_tokens(collection: CollectionId, account: CrossAccountId) -> Vec<TokenId>, unique_api480 );481 pass_method!(482 collection_tokens(collection: CollectionId) -> Vec<TokenId>, unique_api483 );484 pass_method!(485 token_exists(collection: CollectionId, token: TokenId) -> bool, unique_api486 );487 pass_method!(488 token_owner(collection: CollectionId, token: TokenId) -> Option<CrossAccountId>, unique_api489 );490 pass_method!(491 topmost_token_owner(collection: CollectionId, token: TokenId) -> Option<CrossAccountId>, unique_api492 );493 pass_method!(token_children(collection: CollectionId, token: TokenId) -> Vec<TokenChild>, unique_api);494 pass_method!(total_supply(collection: CollectionId) -> u32, unique_api);495 pass_method!(account_balance(collection: CollectionId, account: CrossAccountId) -> u32, unique_api);496 pass_method!(balance(collection: CollectionId, account: CrossAccountId, token: TokenId) -> String => |v| v.to_string(), unique_api);497 pass_method!(498 allowance(collection: CollectionId, sender: CrossAccountId, spender: CrossAccountId, token: TokenId) -> String => |v| v.to_string(),499 unique_api500 );501502 pass_method!(collection_properties(503 collection: CollectionId,504505 #[map(|keys| string_keys_to_bytes_keys(keys))]506 keys: Option<Vec<String>>507 ) -> Vec<Property>, unique_api);508509 pass_method!(token_properties(510 collection: CollectionId,511 token_id: TokenId,512513 #[map(|keys| string_keys_to_bytes_keys(keys))]514 keys: Option<Vec<String>>515 ) -> Vec<Property>, unique_api);516517 pass_method!(property_permissions(518 collection: CollectionId,519520 #[map(|keys| string_keys_to_bytes_keys(keys))]521 keys: Option<Vec<String>>522 ) -> Vec<PropertyKeyPermission>, unique_api);523524 pass_method!(token_data(525 collection: CollectionId,526 token_id: TokenId,527528 #[map(|keys| string_keys_to_bytes_keys(keys))]529 keys: Option<Vec<String>>,530 ) -> TokenData<CrossAccountId>, unique_api);531532 pass_method!(adminlist(collection: CollectionId) -> Vec<CrossAccountId>, unique_api);533 pass_method!(allowlist(collection: CollectionId) -> Vec<CrossAccountId>, unique_api);534 pass_method!(allowed(collection: CollectionId, user: CrossAccountId) -> bool, unique_api);535 pass_method!(last_token_id(collection: CollectionId) -> TokenId, unique_api);536 pass_method!(collection_by_id(collection: CollectionId) -> Option<RpcCollection<AccountId>>, unique_api);537 pass_method!(collection_stats() -> CollectionStats, unique_api);538 pass_method!(next_sponsored(collection: CollectionId, account: CrossAccountId, token: TokenId) -> Option<u64>, unique_api);539 pass_method!(effective_collection_limits(collection_id: CollectionId) -> Option<CollectionLimits>, unique_api);540 pass_method!(total_pieces(collection_id: CollectionId, token_id: TokenId) -> Option<String> => |o| o.map(|number| number.to_string()) , unique_api);541 pass_method!(token_owners(collection: CollectionId, token: TokenId) -> Vec<CrossAccountId>, unique_api);542 pass_method!(total_staked(staker: CrossAccountId) -> u128, unique_api);543 pass_method!(total_staked_per_block(staker: CrossAccountId) -> Vec<(BlockNumber, u128)>, unique_api);544 pass_method!(total_staking_locked(staker: CrossAccountId) -> u128, unique_api);545}546547#[allow(deprecated)]548impl<549 C,550 Block,551 AccountId,552 CollectionInfo,553 NftInfo,554 ResourceInfo,555 PropertyInfo,556 BaseInfo,557 PartType,558 Theme,559 >560 rmrk_unique_rpc::RmrkApiServer<561 <Block as BlockT>::Hash,562 AccountId,563 CollectionInfo,564 NftInfo,565 ResourceInfo,566 PropertyInfo,567 BaseInfo,568 PartType,569 Theme,570 > for Rmrk<C, Block>571where572 C: Send + Sync + 'static + ProvideRuntimeApi<Block> + HeaderBackend<Block>,573 C::Api: RmrkRuntimeApi<574 Block,575 AccountId,576 CollectionInfo,577 NftInfo,578 ResourceInfo,579 PropertyInfo,580 BaseInfo,581 PartType,582 Theme,583 >,584 AccountId: Decode + Encode,585 CollectionInfo: Decode,586 NftInfo: Decode,587 ResourceInfo: Decode,588 PropertyInfo: Decode,589 BaseInfo: Decode,590 PartType: Decode,591 Theme: Decode,592 Block: BlockT,593{594 pass_method!(last_collection_idx() -> RmrkCollectionId, rmrk_api);595 pass_method!(collection_by_id(id: RmrkCollectionId) -> Option<CollectionInfo>, rmrk_api);596 pass_method!(nft_by_id(collection_id: RmrkCollectionId, nft_id: RmrkNftId) -> Option<NftInfo>, rmrk_api);597 pass_method!(account_tokens(account_id: AccountId, collection_id: RmrkCollectionId) -> Vec<RmrkNftId>, rmrk_api);598 pass_method!(nft_children(collection_id: RmrkCollectionId, nft_id: RmrkNftId) -> Vec<RmrkNftChild>, rmrk_api);599 pass_method!(600 collection_properties(601 collection_id: RmrkCollectionId,602603 #[map(|keys| string_keys_to_bytes_keys(keys))]604 filter_keys: Option<Vec<String>>605 ) -> Vec<PropertyInfo>,606 rmrk_api607 );608 pass_method!(609 nft_properties(610 collection_id: RmrkCollectionId,611 nft_id: RmrkNftId,612613 #[map(|keys| string_keys_to_bytes_keys(keys))]614 filter_keys: Option<Vec<String>>615 ) -> Vec<PropertyInfo>,616 rmrk_api617 );618 pass_method!(nft_resources(collection_id: RmrkCollectionId, nft_id: RmrkNftId) -> Vec<ResourceInfo>, rmrk_api);619 pass_method!(nft_resource_priority(collection_id: RmrkCollectionId, nft_id: RmrkNftId, resource_id: RmrkResourceId) -> Option<u32>, rmrk_api);620 pass_method!(base(base_id: RmrkBaseId) -> Option<BaseInfo>, rmrk_api);621 pass_method!(base_parts(base_id: RmrkBaseId) -> Vec<PartType>, rmrk_api);622 pass_method!(theme_names(base_id: RmrkBaseId) -> Vec<RmrkThemeName>, rmrk_api);623 pass_method!(624 theme(625 base_id: RmrkBaseId,626627 #[map(|n| n.into_bytes())]628 theme_name: String,629630 #[map(|keys| string_keys_to_bytes_keys(keys))]631 filter_keys: Option<Vec<String>>632 ) -> Option<Theme>, rmrk_api);633}634635fn string_keys_to_bytes_keys(keys: Option<Vec<String>>) -> Option<Vec<Vec<u8>>> {636 keys.map(|keys| keys.into_iter().map(|key| key.into_bytes()).collect())637}