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, TokenChild,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 46 #[method(name = "unique_accountTokens")]47 fn account_tokens(48 &self,49 collection: CollectionId,50 account: CrossAccountId,51 at: Option<BlockHash>,52 ) -> Result<Vec<TokenId>>;5354 55 #[method(name = "unique_collectionTokens")]56 fn collection_tokens(57 &self,58 collection: CollectionId,59 at: Option<BlockHash>,60 ) -> Result<Vec<TokenId>>;6162 63 #[method(name = "unique_tokenExists")]64 fn token_exists(65 &self,66 collection: CollectionId,67 token: TokenId,68 at: Option<BlockHash>,69 ) -> Result<bool>;7071 72 #[method(name = "unique_tokenOwner")]73 fn token_owner(74 &self,75 collection: CollectionId,76 token: TokenId,77 at: Option<BlockHash>,78 ) -> Result<Option<CrossAccountId>>;7980 81 #[method(name = "unique_tokenOwners")]82 fn token_owners(83 &self,84 collection: CollectionId,85 token: TokenId,86 at: Option<BlockHash>,87 ) -> Result<Vec<CrossAccountId>>;8889 90 #[method(name = "unique_topmostTokenOwner")]91 fn topmost_token_owner(92 &self,93 collection: CollectionId,94 token: TokenId,95 at: Option<BlockHash>,96 ) -> Result<Option<CrossAccountId>>;9798 99 #[method(name = "unique_tokenChildren")]100 fn token_children(101 &self,102 collection: CollectionId,103 token: TokenId,104 at: Option<BlockHash>,105 ) -> Result<Vec<TokenChild>>;106107 108 #[method(name = "unique_collectionProperties")]109 fn collection_properties(110 &self,111 collection: CollectionId,112 keys: Option<Vec<String>>,113 at: Option<BlockHash>,114 ) -> Result<Vec<Property>>;115116 117 #[method(name = "unique_tokenProperties")]118 fn token_properties(119 &self,120 collection: CollectionId,121 token_id: TokenId,122 keys: Option<Vec<String>>,123 at: Option<BlockHash>,124 ) -> Result<Vec<Property>>;125126 127 #[method(name = "unique_propertyPermissions")]128 fn property_permissions(129 &self,130 collection: CollectionId,131 keys: Option<Vec<String>>,132 at: Option<BlockHash>,133 ) -> Result<Vec<PropertyKeyPermission>>;134135 136 #[method(name = "unique_tokenData")]137 fn token_data(138 &self,139 collection: CollectionId,140 token_id: TokenId,141 keys: Option<Vec<String>>,142 at: Option<BlockHash>,143 ) -> Result<TokenData<CrossAccountId>>;144145 146 #[method(name = "unique_totalSupply")]147 fn total_supply(&self, collection: CollectionId, at: Option<BlockHash>) -> Result<u32>;148149 150 #[method(name = "unique_accountBalance")]151 fn account_balance(152 &self,153 collection: CollectionId,154 account: CrossAccountId,155 at: Option<BlockHash>,156 ) -> Result<u32>;157158 159 #[method(name = "unique_balance")]160 fn balance(161 &self,162 collection: CollectionId,163 account: CrossAccountId,164 token: TokenId,165 at: Option<BlockHash>,166 ) -> Result<String>;167168 169 #[method(name = "unique_allowance")]170 fn allowance(171 &self,172 collection: CollectionId,173 sender: CrossAccountId,174 spender: CrossAccountId,175 token: TokenId,176 at: Option<BlockHash>,177 ) -> Result<String>;178179 180 #[method(name = "unique_adminlist")]181 fn adminlist(182 &self,183 collection: CollectionId,184 at: Option<BlockHash>,185 ) -> Result<Vec<CrossAccountId>>;186187 188 #[method(name = "unique_allowlist")]189 fn allowlist(190 &self,191 collection: CollectionId,192 at: Option<BlockHash>,193 ) -> Result<Vec<CrossAccountId>>;194195 196 #[method(name = "unique_allowed")]197 fn allowed(198 &self,199 collection: CollectionId,200 user: CrossAccountId,201 at: Option<BlockHash>,202 ) -> Result<bool>;203204 205 #[method(name = "unique_lastTokenId")]206 fn last_token_id(&self, collection: CollectionId, at: Option<BlockHash>) -> Result<TokenId>;207208 209 #[method(name = "unique_collectionById")]210 fn collection_by_id(211 &self,212 collection: CollectionId,213 at: Option<BlockHash>,214 ) -> Result<Option<RpcCollection<AccountId>>>;215216 217 #[method(name = "unique_collectionStats")]218 fn collection_stats(&self, at: Option<BlockHash>) -> Result<CollectionStats>;219220 221 #[method(name = "unique_nextSponsored")]222 fn next_sponsored(223 &self,224 collection: CollectionId,225 account: CrossAccountId,226 token: TokenId,227 at: Option<BlockHash>,228 ) -> Result<Option<u64>>;229230 231 #[method(name = "unique_effectiveCollectionLimits")]232 fn effective_collection_limits(233 &self,234 collection_id: CollectionId,235 at: Option<BlockHash>,236 ) -> Result<Option<CollectionLimits>>;237238 239 #[method(name = "unique_totalPieces")]240 fn total_pieces(241 &self,242 collection_id: CollectionId,243 token_id: TokenId,244 at: Option<BlockHash>,245 ) -> Result<Option<u128>>;246}247248mod rmrk_unique_rpc {249 use super::*;250251 #[rpc(server)]252 #[async_trait]253 pub trait RmrkApi<254 BlockHash,255 AccountId,256 CollectionInfo,257 NftInfo,258 ResourceInfo,259 PropertyInfo,260 BaseInfo,261 PartType,262 Theme,263 >264 {265 266 #[method(name = "rmrk_lastCollectionIdx")]267 fn last_collection_idx(&self, at: Option<BlockHash>) -> Result<RmrkCollectionId>;268269 270 #[method(name = "rmrk_collectionById")]271 fn collection_by_id(272 &self,273 id: RmrkCollectionId,274 at: Option<BlockHash>,275 ) -> Result<Option<CollectionInfo>>;276277 278 #[method(name = "rmrk_nftById")]279 fn nft_by_id(280 &self,281 collection_id: RmrkCollectionId,282 nft_id: RmrkNftId,283 at: Option<BlockHash>,284 ) -> Result<Option<NftInfo>>;285286 287 #[method(name = "rmrk_accountTokens")]288 fn account_tokens(289 &self,290 account_id: AccountId,291 collection_id: RmrkCollectionId,292 at: Option<BlockHash>,293 ) -> Result<Vec<RmrkNftId>>;294295 296 #[method(name = "rmrk_nftChildren")]297 fn nft_children(298 &self,299 collection_id: RmrkCollectionId,300 nft_id: RmrkNftId,301 at: Option<BlockHash>,302 ) -> Result<Vec<RmrkNftChild>>;303304 305 #[method(name = "rmrk_collectionProperties")]306 fn collection_properties(307 &self,308 collection_id: RmrkCollectionId,309 filter_keys: Option<Vec<String>>,310 at: Option<BlockHash>,311 ) -> Result<Vec<PropertyInfo>>;312313 314 #[method(name = "rmrk_nftProperties")]315 fn nft_properties(316 &self,317 collection_id: RmrkCollectionId,318 nft_id: RmrkNftId,319 filter_keys: Option<Vec<String>>,320 at: Option<BlockHash>,321 ) -> Result<Vec<PropertyInfo>>;322323 324 #[method(name = "rmrk_nftResources")]325 fn nft_resources(326 &self,327 collection_id: RmrkCollectionId,328 nft_id: RmrkNftId,329 at: Option<BlockHash>,330 ) -> Result<Vec<ResourceInfo>>;331332 333 #[method(name = "rmrk_nftResourcePriority")]334 fn nft_resource_priority(335 &self,336 collection_id: RmrkCollectionId,337 nft_id: RmrkNftId,338 resource_id: RmrkResourceId,339 at: Option<BlockHash>,340 ) -> Result<Option<u32>>;341342 343 #[method(name = "rmrk_base")]344 fn base(&self, base_id: RmrkBaseId, at: Option<BlockHash>) -> Result<Option<BaseInfo>>;345346 347 #[method(name = "rmrk_baseParts")]348 fn base_parts(&self, base_id: RmrkBaseId, at: Option<BlockHash>) -> Result<Vec<PartType>>;349350 351 #[method(name = "rmrk_themeNames")]352 fn theme_names(353 &self,354 base_id: RmrkBaseId,355 at: Option<BlockHash>,356 ) -> Result<Vec<RmrkThemeName>>;357358 359 #[method(name = "rmrk_themes")]360 fn theme(361 &self,362 base_id: RmrkBaseId,363 theme_name: String,364 filter_keys: Option<Vec<String>>,365 at: Option<BlockHash>,366 ) -> Result<Option<Theme>>;367 }368}369370pub struct Unique<C, P> {371 client: Arc<C>,372 _marker: std::marker::PhantomData<P>,373}374375impl<C, P> Unique<C, P> {376 pub fn new(client: Arc<C>) -> Self {377 Self {378 client,379 _marker: Default::default(),380 }381 }382}383384pub struct Rmrk<C, P> {385 client: Arc<C>,386 _marker: std::marker::PhantomData<P>,387}388389impl<C, P> Rmrk<C, P> {390 pub fn new(client: Arc<C>) -> Self {391 Self {392 client,393 _marker: Default::default(),394 }395 }396}397398macro_rules! pass_method {399 (400 $method_name:ident(401 $($(#[map(|$map_arg:ident| $map:expr)])? $name:ident: $ty:ty),* $(,)?402 ) -> $result:ty $(=> $mapper:expr)?,403 404 $runtime_api_macro:ident405 $(; changed_in $ver:expr, $changed_method_name:ident ($($changed_name:expr), * $(,)?) => $fixer:expr)*406 ) => {407 fn $method_name(408 &self,409 $(410 $name: $ty,411 )*412 at: Option<<Block as BlockT>::Hash>,413 ) -> Result<$result> {414 let api = self.client.runtime_api();415 let at = BlockId::hash(at.unwrap_or_else(|| self.client.info().best_hash));416 let _api_version = if let Ok(Some(api_version)) =417 api.api_version::<$runtime_api_macro!()>(&at)418 {419 api_version420 } else {421 422 return Err(anyhow!("api is not available").into())423 };424425 let result = $(if _api_version < $ver {426 api.$changed_method_name(&at, $($changed_name),*).map(|r| r.map($fixer))427 } else)*428 { api.$method_name(&at, $($((|$map_arg: $ty| $map))? ($name)),*) };429430 Ok(result431 .map_err(|e| anyhow!("unable to query: {e}"))?432 .map_err(|e| anyhow!("runtime error: {e:?}"))$(.map($mapper))??)433 }434 };435}436437macro_rules! unique_api {438 () => {439 dyn UniqueRuntimeApi<Block, CrossAccountId, AccountId>440 };441}442443macro_rules! rmrk_api {444 () => {445 dyn RmrkRuntimeApi<Block, AccountId, CollectionInfo, NftInfo, ResourceInfo, PropertyInfo, BaseInfo, PartType, Theme>446 };447}448449#[allow(deprecated)]450impl<C, Block, CrossAccountId, AccountId>451 UniqueApiServer<<Block as BlockT>::Hash, CrossAccountId, AccountId> for Unique<C, Block>452where453 Block: BlockT,454 AccountId: Decode,455 C: 'static + ProvideRuntimeApi<Block> + HeaderBackend<Block>,456 C::Api: UniqueRuntimeApi<Block, CrossAccountId, AccountId>,457 CrossAccountId: pallet_evm::account::CrossAccountId<AccountId>,458{459 pass_method!(460 account_tokens(collection: CollectionId, account: CrossAccountId) -> Vec<TokenId>, unique_api461 );462 pass_method!(463 collection_tokens(collection: CollectionId) -> Vec<TokenId>, unique_api464 );465 pass_method!(466 token_exists(collection: CollectionId, token: TokenId) -> bool, unique_api467 );468 pass_method!(469 token_owner(collection: CollectionId, token: TokenId) -> Option<CrossAccountId>, unique_api470 );471 pass_method!(472 topmost_token_owner(collection: CollectionId, token: TokenId) -> Option<CrossAccountId>, unique_api473 );474 pass_method!(token_children(collection: CollectionId, token: TokenId) -> Vec<TokenChild>, unique_api);475 pass_method!(total_supply(collection: CollectionId) -> u32, unique_api);476 pass_method!(account_balance(collection: CollectionId, account: CrossAccountId) -> u32, unique_api);477 pass_method!(balance(collection: CollectionId, account: CrossAccountId, token: TokenId) -> String => |v| v.to_string(), unique_api);478 pass_method!(479 allowance(collection: CollectionId, sender: CrossAccountId, spender: CrossAccountId, token: TokenId) -> String => |v| v.to_string(),480 unique_api481 );482483 pass_method!(collection_properties(484 collection: CollectionId,485486 #[map(|keys| string_keys_to_bytes_keys(keys))]487 keys: Option<Vec<String>>488 ) -> Vec<Property>, unique_api);489490 pass_method!(token_properties(491 collection: CollectionId,492 token_id: TokenId,493494 #[map(|keys| string_keys_to_bytes_keys(keys))]495 keys: Option<Vec<String>>496 ) -> Vec<Property>, unique_api);497498 pass_method!(property_permissions(499 collection: CollectionId,500501 #[map(|keys| string_keys_to_bytes_keys(keys))]502 keys: Option<Vec<String>>503 ) -> Vec<PropertyKeyPermission>, unique_api);504505 pass_method!(token_data(506 collection: CollectionId,507 token_id: TokenId,508509 #[map(|keys| string_keys_to_bytes_keys(keys))]510 keys: Option<Vec<String>>,511 ) -> TokenData<CrossAccountId>, unique_api);512513 pass_method!(adminlist(collection: CollectionId) -> Vec<CrossAccountId>, unique_api);514 pass_method!(allowlist(collection: CollectionId) -> Vec<CrossAccountId>, unique_api);515 pass_method!(allowed(collection: CollectionId, user: CrossAccountId) -> bool, unique_api);516 pass_method!(last_token_id(collection: CollectionId) -> TokenId, unique_api);517 pass_method!(collection_by_id(collection: CollectionId) -> Option<RpcCollection<AccountId>>, unique_api);518 pass_method!(collection_stats() -> CollectionStats, unique_api);519 pass_method!(next_sponsored(collection: CollectionId, account: CrossAccountId, token: TokenId) -> Option<u64>, unique_api);520 pass_method!(effective_collection_limits(collection_id: CollectionId) -> Option<CollectionLimits>, unique_api);521 pass_method!(total_pieces(collection_id: CollectionId, token_id: TokenId) -> Option<u128>, unique_api);522 pass_method!(token_owners(collection: CollectionId, token: TokenId) -> Vec<CrossAccountId>, unique_api);523}524525#[allow(deprecated)]526impl<527 C,528 Block,529 AccountId,530 CollectionInfo,531 NftInfo,532 ResourceInfo,533 PropertyInfo,534 BaseInfo,535 PartType,536 Theme,537 >538 rmrk_unique_rpc::RmrkApiServer<539 <Block as BlockT>::Hash,540 AccountId,541 CollectionInfo,542 NftInfo,543 ResourceInfo,544 PropertyInfo,545 BaseInfo,546 PartType,547 Theme,548 > for Rmrk<C, Block>549where550 C: Send + Sync + 'static + ProvideRuntimeApi<Block> + HeaderBackend<Block>,551 C::Api: RmrkRuntimeApi<552 Block,553 AccountId,554 CollectionInfo,555 NftInfo,556 ResourceInfo,557 PropertyInfo,558 BaseInfo,559 PartType,560 Theme,561 >,562 AccountId: Decode + Encode,563 CollectionInfo: Decode,564 NftInfo: Decode,565 ResourceInfo: Decode,566 PropertyInfo: Decode,567 BaseInfo: Decode,568 PartType: Decode,569 Theme: Decode,570 Block: BlockT,571{572 pass_method!(last_collection_idx() -> RmrkCollectionId, rmrk_api);573 pass_method!(collection_by_id(id: RmrkCollectionId) -> Option<CollectionInfo>, rmrk_api);574 pass_method!(nft_by_id(collection_id: RmrkCollectionId, nft_id: RmrkNftId) -> Option<NftInfo>, rmrk_api);575 pass_method!(account_tokens(account_id: AccountId, collection_id: RmrkCollectionId) -> Vec<RmrkNftId>, rmrk_api);576 pass_method!(nft_children(collection_id: RmrkCollectionId, nft_id: RmrkNftId) -> Vec<RmrkNftChild>, rmrk_api);577 pass_method!(578 collection_properties(579 collection_id: RmrkCollectionId,580581 #[map(|keys| string_keys_to_bytes_keys(keys))]582 filter_keys: Option<Vec<String>>583 ) -> Vec<PropertyInfo>,584 rmrk_api585 );586 pass_method!(587 nft_properties(588 collection_id: RmrkCollectionId,589 nft_id: RmrkNftId,590591 #[map(|keys| string_keys_to_bytes_keys(keys))]592 filter_keys: Option<Vec<String>>593 ) -> Vec<PropertyInfo>,594 rmrk_api595 );596 pass_method!(nft_resources(collection_id: RmrkCollectionId, nft_id: RmrkNftId) -> Vec<ResourceInfo>, rmrk_api);597 pass_method!(nft_resource_priority(collection_id: RmrkCollectionId, nft_id: RmrkNftId, resource_id: RmrkResourceId) -> Option<u32>, rmrk_api);598 pass_method!(base(base_id: RmrkBaseId) -> Option<BaseInfo>, rmrk_api);599 pass_method!(base_parts(base_id: RmrkBaseId) -> Vec<PartType>, rmrk_api);600 pass_method!(theme_names(base_id: RmrkBaseId) -> Vec<RmrkThemeName>, rmrk_api);601 pass_method!(602 theme(603 base_id: RmrkBaseId,604605 #[map(|n| n.into_bytes())]606 theme_name: String,607608 #[map(|keys| string_keys_to_bytes_keys(keys))]609 filter_keys: Option<Vec<String>>610 ) -> Option<Theme>, rmrk_api);611}612613fn string_keys_to_bytes_keys(keys: Option<Vec<String>>) -> Option<Vec<Vec<u8>>> {614 keys.map(|keys| keys.into_iter().map(|key| key.into_bytes()).collect())615}