123456789101112131415161718use std::sync::Arc;1920use codec::{Decode, Encode};21use jsonrpsee::{22 core::{RpcResult as Result},23 proc_macros::rpc,24};25use anyhow::anyhow;26use up_data_structs::{27 RpcCollection, CollectionId, CollectionStats, CollectionLimits, TokenId, Property,28 PropertyKeyPermission, TokenData,29};30use sp_api::{BlockId, BlockT, ProvideRuntimeApi, ApiExt};31use sp_blockchain::HeaderBackend;32use up_rpc::UniqueApi as UniqueRuntimeApi;333435use rmrk_rpc::RmrkApi as RmrkRuntimeApi;36use up_data_structs::{37 RmrkCollectionId, RmrkNftId, RmrkBaseId, RmrkNftChild, RmrkThemeName, RmrkResourceId,38};3940pub use rmrk_unique_rpc::RmrkApiServer;4142#[rpc(server)]43#[async_trait]44pub trait UniqueApi<BlockHash, CrossAccountId, AccountId> {45 #[method(name = "unique_accountTokens")]46 fn account_tokens(47 &self,48 collection: CollectionId,49 account: CrossAccountId,50 at: Option<BlockHash>,51 ) -> Result<Vec<TokenId>>;52 #[method(name = "unique_collectionTokens")]53 fn collection_tokens(54 &self,55 collection: CollectionId,56 at: Option<BlockHash>,57 ) -> Result<Vec<TokenId>>;58 #[method(name = "unique_tokenExists")]59 fn token_exists(60 &self,61 collection: CollectionId,62 token: TokenId,63 at: Option<BlockHash>,64 ) -> Result<bool>;6566 #[method(name = "unique_tokenOwner")]67 fn token_owner(68 &self,69 collection: CollectionId,70 token: TokenId,71 at: Option<BlockHash>,72 ) -> Result<Option<CrossAccountId>>;73 #[method(name = "unique_topmostTokenOwner")]74 fn topmost_token_owner(75 &self,76 collection: CollectionId,77 token: TokenId,78 at: Option<BlockHash>,79 ) -> Result<Option<CrossAccountId>>;8081 #[method(name = "unique_collectionProperties")]82 fn collection_properties(83 &self,84 collection: CollectionId,85 keys: Option<Vec<String>>,86 at: Option<BlockHash>,87 ) -> Result<Vec<Property>>;8889 #[method(name = "unique_tokenProperties")]90 fn token_properties(91 &self,92 collection: CollectionId,93 token_id: TokenId,94 keys: Option<Vec<String>>,95 at: Option<BlockHash>,96 ) -> Result<Vec<Property>>;9798 #[method(name = "unique_propertyPermissions")]99 fn property_permissions(100 &self,101 collection: CollectionId,102 keys: Option<Vec<String>>,103 at: Option<BlockHash>,104 ) -> Result<Vec<PropertyKeyPermission>>;105106 #[method(name = "unique_tokenData")]107 fn token_data(108 &self,109 collection: CollectionId,110 token_id: TokenId,111 keys: Option<Vec<String>>,112 at: Option<BlockHash>,113 ) -> Result<TokenData<CrossAccountId>>;114115 #[method(name = "unique_totalSupply")]116 fn total_supply(&self, collection: CollectionId, at: Option<BlockHash>) -> Result<u32>;117 #[method(name = "unique_accountBalance")]118 fn account_balance(119 &self,120 collection: CollectionId,121 account: CrossAccountId,122 at: Option<BlockHash>,123 ) -> Result<u32>;124 #[method(name = "unique_balance")]125 fn balance(126 &self,127 collection: CollectionId,128 account: CrossAccountId,129 token: TokenId,130 at: Option<BlockHash>,131 ) -> Result<String>;132 #[method(name = "unique_allowance")]133 fn allowance(134 &self,135 collection: CollectionId,136 sender: CrossAccountId,137 spender: CrossAccountId,138 token: TokenId,139 at: Option<BlockHash>,140 ) -> Result<String>;141142 #[method(name = "unique_adminlist")]143 fn adminlist(144 &self,145 collection: CollectionId,146 at: Option<BlockHash>,147 ) -> Result<Vec<CrossAccountId>>;148 #[method(name = "unique_allowlist")]149 fn allowlist(150 &self,151 collection: CollectionId,152 at: Option<BlockHash>,153 ) -> Result<Vec<CrossAccountId>>;154 #[method(name = "unique_allowed")]155 fn allowed(156 &self,157 collection: CollectionId,158 user: CrossAccountId,159 at: Option<BlockHash>,160 ) -> Result<bool>;161 #[method(name = "unique_lastTokenId")]162 fn last_token_id(&self, collection: CollectionId, at: Option<BlockHash>) -> Result<TokenId>;163 #[method(name = "unique_collectionById")]164 fn collection_by_id(165 &self,166 collection: CollectionId,167 at: Option<BlockHash>,168 ) -> Result<Option<RpcCollection<AccountId>>>;169 #[method(name = "unique_collectionStats")]170 fn collection_stats(&self, at: Option<BlockHash>) -> Result<CollectionStats>;171172 #[method(name = "unique_nextSponsored")]173 fn next_sponsored(174 &self,175 collection: CollectionId,176 account: CrossAccountId,177 token: TokenId,178 at: Option<BlockHash>,179 ) -> Result<Option<u64>>;180 #[method(name = "unique_effectiveCollectionLimits")]181 fn effective_collection_limits(182 &self,183 collection_id: CollectionId,184 at: Option<BlockHash>,185 ) -> Result<Option<CollectionLimits>>;186}187188mod rmrk_unique_rpc {189 use super::*;190191 #[rpc(server)]192 #[async_trait]193 pub trait RmrkApi<194 BlockHash,195 AccountId,196 CollectionInfo,197 NftInfo,198 ResourceInfo,199 PropertyInfo,200 BaseInfo,201 PartType,202 Theme,203 >204 {205 #[method(name = "rmrk_lastCollectionIdx")]206 207 fn last_collection_idx(&self, at: Option<BlockHash>) -> Result<RmrkCollectionId>;208209 #[method(name = "rmrk_collectionById")]210 211 fn collection_by_id(212 &self,213 id: RmrkCollectionId,214 at: Option<BlockHash>,215 ) -> Result<Option<CollectionInfo>>;216217 #[method(name = "rmrk_nftById")]218 219 fn nft_by_id(220 &self,221 collection_id: RmrkCollectionId,222 nft_id: RmrkNftId,223 at: Option<BlockHash>,224 ) -> Result<Option<NftInfo>>;225226 #[method(name = "rmrk_accountTokens")]227 228 fn account_tokens(229 &self,230 account_id: AccountId,231 collection_id: RmrkCollectionId,232 at: Option<BlockHash>,233 ) -> Result<Vec<RmrkNftId>>;234235 #[method(name = "rmrk_nftChildren")]236 237 fn nft_children(238 &self,239 collection_id: RmrkCollectionId,240 nft_id: RmrkNftId,241 at: Option<BlockHash>,242 ) -> Result<Vec<RmrkNftChild>>;243244 #[method(name = "rmrk_collectionProperties")]245 246 fn collection_properties(247 &self,248 collection_id: RmrkCollectionId,249 filter_keys: Option<Vec<String>>,250 at: Option<BlockHash>,251 ) -> Result<Vec<PropertyInfo>>;252253 #[method(name = "rmrk_nftProperties")]254 255 fn nft_properties(256 &self,257 collection_id: RmrkCollectionId,258 nft_id: RmrkNftId,259 filter_keys: Option<Vec<String>>,260 at: Option<BlockHash>,261 ) -> Result<Vec<PropertyInfo>>;262263 #[method(name = "rmrk_nftResources")]264 265 fn nft_resources(266 &self,267 collection_id: RmrkCollectionId,268 nft_id: RmrkNftId,269 at: Option<BlockHash>,270 ) -> Result<Vec<ResourceInfo>>;271272 #[method(name = "rmrk_nftResourcePriorities")]273 274 fn nft_resource_priorities(275 &self,276 collection_id: RmrkCollectionId,277 nft_id: RmrkNftId,278 at: Option<BlockHash>,279 ) -> Result<Vec<RmrkResourceId>>;280281 #[method(name = "rmrk_base")]282 283 fn base(&self, base_id: RmrkBaseId, at: Option<BlockHash>) -> Result<Option<BaseInfo>>;284285 #[method(name = "rmrk_baseParts")]286 287 fn base_parts(&self, base_id: RmrkBaseId, at: Option<BlockHash>) -> Result<Vec<PartType>>;288289 #[method(name = "rmrk_themeNames")]290 fn theme_names(291 &self,292 base_id: RmrkBaseId,293 at: Option<BlockHash>,294 ) -> Result<Vec<RmrkThemeName>>;295296 #[method(name = "rmrk_themes")]297 fn theme(298 &self,299 base_id: RmrkBaseId,300 theme_name: String,301 filter_keys: Option<Vec<String>>,302 at: Option<BlockHash>,303 ) -> Result<Option<Theme>>;304 }305}306307pub struct Unique<C, P> {308 client: Arc<C>,309 _marker: std::marker::PhantomData<P>,310}311312impl<C, P> Unique<C, P> {313 pub fn new(client: Arc<C>) -> Self {314 Self {315 client,316 _marker: Default::default(),317 }318 }319}320321macro_rules! pass_method {322 (323 $method_name:ident(324 $($(#[map(|$map_arg:ident| $map:expr)])? $name:ident: $ty:ty),* $(,)?325 ) -> $result:ty $(=> $mapper:expr)?,326 327 $runtime_api_macro:ident328 $(; changed_in $ver:expr, $changed_method_name:ident ($($changed_name:expr), * $(,)?) => $fixer:expr)*329 ) => {330 fn $method_name(331 &self,332 $(333 $name: $ty,334 )*335 at: Option<<Block as BlockT>::Hash>,336 ) -> Result<$result> {337 let api = self.client.runtime_api();338 let at = BlockId::hash(at.unwrap_or_else(|| self.client.info().best_hash));339 let _api_version = if let Ok(Some(api_version)) =340 api.api_version::<$runtime_api_macro!()>(&at)341 {342 api_version343 } else {344 345 return Err(anyhow!("api is not available").into())346 };347348 let result = $(if _api_version < $ver {349 api.$changed_method_name(&at, $($changed_name),*).map(|r| r.map($fixer))350 } else)*351 { api.$method_name(&at, $($((|$map_arg: $ty| $map))? ($name)),*) };352353 Ok(result354 .map_err(|e| anyhow!("unable to query: {e}"))?355 .map_err(|e| anyhow!("runtime error: {e:?}"))$(.map($mapper))??)356 }357 };358}359360macro_rules! unique_api {361 () => {362 dyn UniqueRuntimeApi<Block, CrossAccountId, AccountId>363 };364}365366macro_rules! rmrk_api {367 () => {368 dyn RmrkRuntimeApi<Block, AccountId, CollectionInfo, NftInfo, ResourceInfo, PropertyInfo, BaseInfo, PartType, Theme>369 };370}371372#[allow(deprecated)]373impl<C, Block, CrossAccountId, AccountId>374 UniqueApiServer<<Block as BlockT>::Hash, CrossAccountId, AccountId> for Unique<C, Block>375where376 Block: BlockT,377 AccountId: Decode,378 C: 'static + ProvideRuntimeApi<Block> + HeaderBackend<Block>,379 C::Api: UniqueRuntimeApi<Block, CrossAccountId, AccountId>,380 CrossAccountId: pallet_evm::account::CrossAccountId<AccountId>,381{382 pass_method!(383 account_tokens(collection: CollectionId, account: CrossAccountId) -> Vec<TokenId>, unique_api384 );385 pass_method!(386 collection_tokens(collection: CollectionId) -> Vec<TokenId>, unique_api387 );388 pass_method!(389 token_exists(collection: CollectionId, token: TokenId) -> bool, unique_api390 );391 pass_method!(392 token_owner(collection: CollectionId, token: TokenId) -> Option<CrossAccountId>, unique_api393 );394 pass_method!(395 topmost_token_owner(collection: CollectionId, token: TokenId) -> Option<CrossAccountId>, unique_api396 );397 pass_method!(total_supply(collection: CollectionId) -> u32, unique_api);398 pass_method!(account_balance(collection: CollectionId, account: CrossAccountId) -> u32, unique_api);399 pass_method!(balance(collection: CollectionId, account: CrossAccountId, token: TokenId) -> String => |v| v.to_string(), unique_api);400 pass_method!(401 allowance(collection: CollectionId, sender: CrossAccountId, spender: CrossAccountId, token: TokenId) -> String => |v| v.to_string(),402 unique_api403 );404405 pass_method!(collection_properties(406 collection: CollectionId,407408 #[map(|keys| string_keys_to_bytes_keys(keys))]409 keys: Option<Vec<String>>410 ) -> Vec<Property>, unique_api);411412 pass_method!(token_properties(413 collection: CollectionId,414 token_id: TokenId,415416 #[map(|keys| string_keys_to_bytes_keys(keys))]417 keys: Option<Vec<String>>418 ) -> Vec<Property>, unique_api);419420 pass_method!(property_permissions(421 collection: CollectionId,422423 #[map(|keys| string_keys_to_bytes_keys(keys))]424 keys: Option<Vec<String>>425 ) -> Vec<PropertyKeyPermission>, unique_api);426427 pass_method!(token_data(428 collection: CollectionId,429 token_id: TokenId,430431 #[map(|keys| string_keys_to_bytes_keys(keys))]432 keys: Option<Vec<String>>,433 ) -> TokenData<CrossAccountId>, unique_api);434435 pass_method!(adminlist(collection: CollectionId) -> Vec<CrossAccountId>, unique_api);436 pass_method!(allowlist(collection: CollectionId) -> Vec<CrossAccountId>, unique_api);437 pass_method!(allowed(collection: CollectionId, user: CrossAccountId) -> bool, unique_api);438 pass_method!(last_token_id(collection: CollectionId) -> TokenId, unique_api);439 pass_method!(collection_by_id(collection: CollectionId) -> Option<RpcCollection<AccountId>>, unique_api);440 pass_method!(collection_stats() -> CollectionStats, unique_api);441 pass_method!(next_sponsored(collection: CollectionId, account: CrossAccountId, token: TokenId) -> Option<u64>, unique_api);442 pass_method!(effective_collection_limits(collection_id: CollectionId) -> Option<CollectionLimits>, unique_api);443}444445#[allow(deprecated)]446impl<447 C,448 Block,449 AccountId,450 CollectionInfo,451 NftInfo,452 ResourceInfo,453 PropertyInfo,454 BaseInfo,455 PartType,456 Theme,457 >458 rmrk_unique_rpc::RmrkApiServer<459 <Block as BlockT>::Hash,460 AccountId,461 CollectionInfo,462 NftInfo,463 ResourceInfo,464 PropertyInfo,465 BaseInfo,466 PartType,467 Theme,468 > for Unique<C, Block>469where470 C: Send + Sync + 'static + ProvideRuntimeApi<Block> + HeaderBackend<Block>,471 C::Api: RmrkRuntimeApi<472 Block,473 AccountId,474 CollectionInfo,475 NftInfo,476 ResourceInfo,477 PropertyInfo,478 BaseInfo,479 PartType,480 Theme,481 >,482 AccountId: Decode + Encode,483 CollectionInfo: Decode,484 NftInfo: Decode,485 ResourceInfo: Decode,486 PropertyInfo: Decode,487 BaseInfo: Decode,488 PartType: Decode,489 Theme: Decode,490 Block: BlockT,491{492 pass_method!(last_collection_idx() -> RmrkCollectionId, rmrk_api);493 pass_method!(collection_by_id(id: RmrkCollectionId) -> Option<CollectionInfo>, rmrk_api);494 pass_method!(nft_by_id(collection_id: RmrkCollectionId, nft_id: RmrkNftId) -> Option<NftInfo>, rmrk_api);495 pass_method!(account_tokens(account_id: AccountId, collection_id: RmrkCollectionId) -> Vec<RmrkNftId>, rmrk_api);496 pass_method!(nft_children(collection_id: RmrkCollectionId, nft_id: RmrkNftId) -> Vec<RmrkNftChild>, rmrk_api);497 pass_method!(498 collection_properties(499 collection_id: RmrkCollectionId,500501 #[map(|keys| string_keys_to_bytes_keys(keys))]502 filter_keys: Option<Vec<String>>503 ) -> Vec<PropertyInfo>,504 rmrk_api505 );506 pass_method!(507 nft_properties(508 collection_id: RmrkCollectionId,509 nft_id: RmrkNftId,510511 #[map(|keys| string_keys_to_bytes_keys(keys))]512 filter_keys: Option<Vec<String>>513 ) -> Vec<PropertyInfo>,514 rmrk_api515 );516 pass_method!(nft_resources(collection_id: RmrkCollectionId, nft_id: RmrkNftId) -> Vec<ResourceInfo>, rmrk_api);517 pass_method!(nft_resource_priorities(collection_id: RmrkCollectionId, nft_id: RmrkNftId) -> Vec<RmrkResourceId>, rmrk_api);518 pass_method!(base(base_id: RmrkBaseId) -> Option<BaseInfo>, rmrk_api);519 pass_method!(base_parts(base_id: RmrkBaseId) -> Vec<PartType>, rmrk_api);520 pass_method!(theme_names(base_id: RmrkBaseId) -> Vec<RmrkThemeName>, rmrk_api);521 pass_method!(522 theme(523 base_id: RmrkBaseId,524525 #[map(|n| n.into_bytes())]526 theme_name: String,527528 #[map(|keys| string_keys_to_bytes_keys(keys))]529 filter_keys: Option<Vec<String>>530 ) -> Option<Theme>, rmrk_api);531}532533fn string_keys_to_bytes_keys(keys: Option<Vec<String>>) -> Option<Vec<Vec<u8>>> {534 keys.map(|keys| keys.into_iter().map(|key| key.into_bytes()).collect())535}