1234567891011121314151617use std::sync::Arc;1819use codec::{Decode, Encode};20use jsonrpc_core::{Error as RpcError, ErrorCode, Result};21use jsonrpc_derive::rpc;22use up_data_structs::{23 RpcCollection, CollectionId, CollectionStats, CollectionLimits, TokenId, Property,24 PropertyKeyPermission, TokenData,25};26use sp_api::{BlockId, BlockT, ProvideRuntimeApi, ApiExt};27use sp_blockchain::HeaderBackend;28use up_rpc::UniqueApi as UniqueRuntimeApi;293031use rmrk_rpc::RmrkApi as RmrkRuntimeApi;32use up_data_structs::{33 RmrkCollectionId, RmrkNftId, RmrkBaseId, RmrkNftChild,34 RmrkThemeName, RmrkPropertyKey, RmrkResourceId35};3637pub use rmrk_unique_rpc::RmrkApi;3839#[rpc]40pub trait UniqueApi<BlockHash, CrossAccountId, AccountId> {41 #[rpc(name = "unique_accountTokens")]42 fn account_tokens(43 &self,44 collection: CollectionId,45 account: CrossAccountId,46 at: Option<BlockHash>,47 ) -> Result<Vec<TokenId>>;48 #[rpc(name = "unique_collectionTokens")]49 fn collection_tokens(50 &self,51 collection: CollectionId,52 at: Option<BlockHash>,53 ) -> Result<Vec<TokenId>>;54 #[rpc(name = "unique_tokenExists")]55 fn token_exists(56 &self,57 collection: CollectionId,58 token: TokenId,59 at: Option<BlockHash>,60 ) -> Result<bool>;6162 #[rpc(name = "unique_tokenOwner")]63 fn token_owner(64 &self,65 collection: CollectionId,66 token: TokenId,67 at: Option<BlockHash>,68 ) -> Result<Option<CrossAccountId>>;69 #[rpc(name = "unique_topmostTokenOwner")]70 fn topmost_token_owner(71 &self,72 collection: CollectionId,73 token: TokenId,74 at: Option<BlockHash>,75 ) -> Result<Option<CrossAccountId>>;76 #[rpc(name = "unique_constMetadata")]77 fn const_metadata(78 &self,79 collection: CollectionId,80 token: TokenId,81 at: Option<BlockHash>,82 ) -> Result<Vec<u8>>;83 #[rpc(name = "unique_variableMetadata")]84 fn variable_metadata(85 &self,86 collection: CollectionId,87 token: TokenId,88 at: Option<BlockHash>,89 ) -> Result<Vec<u8>>;9091 #[rpc(name = "unique_collectionProperties")]92 fn collection_properties(93 &self,94 collection: CollectionId,95 keys: Vec<String>,96 at: Option<BlockHash>,97 ) -> Result<Vec<Property>>;9899 #[rpc(name = "unique_tokenProperties")]100 fn token_properties(101 &self,102 collection: CollectionId,103 token_id: TokenId,104 properties: Vec<String>,105 at: Option<BlockHash>,106 ) -> Result<Vec<Property>>;107108 #[rpc(name = "unique_propertyPermissions")]109 fn property_permissions(110 &self,111 collection: CollectionId,112 keys: Vec<String>,113 at: Option<BlockHash>,114 ) -> Result<Vec<PropertyKeyPermission>>;115116 #[rpc(name = "unique_tokenData")]117 fn token_data(118 &self,119 collection: CollectionId,120 token_id: TokenId,121 keys: Vec<String>,122 at: Option<BlockHash>,123 ) -> Result<TokenData<CrossAccountId>>;124125 #[rpc(name = "unique_totalSupply")]126 fn total_supply(&self, collection: CollectionId, at: Option<BlockHash>) -> Result<u32>;127 #[rpc(name = "unique_accountBalance")]128 fn account_balance(129 &self,130 collection: CollectionId,131 account: CrossAccountId,132 at: Option<BlockHash>,133 ) -> Result<u32>;134 #[rpc(name = "unique_balance")]135 fn balance(136 &self,137 collection: CollectionId,138 account: CrossAccountId,139 token: TokenId,140 at: Option<BlockHash>,141 ) -> Result<String>;142 #[rpc(name = "unique_allowance")]143 fn allowance(144 &self,145 collection: CollectionId,146 sender: CrossAccountId,147 spender: CrossAccountId,148 token: TokenId,149 at: Option<BlockHash>,150 ) -> Result<String>;151152 #[rpc(name = "unique_adminlist")]153 fn adminlist(154 &self,155 collection: CollectionId,156 at: Option<BlockHash>,157 ) -> Result<Vec<CrossAccountId>>;158 #[rpc(name = "unique_allowlist")]159 fn allowlist(160 &self,161 collection: CollectionId,162 at: Option<BlockHash>,163 ) -> Result<Vec<CrossAccountId>>;164 #[rpc(name = "unique_allowed")]165 fn allowed(166 &self,167 collection: CollectionId,168 user: CrossAccountId,169 at: Option<BlockHash>,170 ) -> Result<bool>;171 #[rpc(name = "unique_lastTokenId")]172 fn last_token_id(&self, collection: CollectionId, at: Option<BlockHash>) -> Result<TokenId>;173 #[rpc(name = "unique_collectionById")]174 fn collection_by_id(175 &self,176 collection: CollectionId,177 at: Option<BlockHash>,178 ) -> Result<Option<RpcCollection<AccountId>>>;179 #[rpc(name = "unique_collectionStats")]180 fn collection_stats(&self, at: Option<BlockHash>) -> Result<CollectionStats>;181182 #[rpc(name = "unique_nextSponsored")]183 fn next_sponsored(184 &self,185 collection: CollectionId,186 account: CrossAccountId,187 token: TokenId,188 at: Option<BlockHash>,189 ) -> Result<Option<u64>>;190 #[rpc(name = "unique_effectiveCollectionLimits")]191 fn effective_collection_limits(192 &self,193 collection_id: CollectionId,194 at: Option<BlockHash>,195 ) -> Result<Option<CollectionLimits>>;196}197198mod rmrk_unique_rpc {199 use super::*;200201 #[rpc(server)]202 pub trait RmrkApi<203 BlockHash,204 AccountId,205 CollectionInfo,206 NftInfo,207 ResourceInfo,208 PropertyInfo,209 BaseInfo,210 PartType,211 Theme,212 >213 {214 #[rpc(name = "rmrk_lastCollectionIdx")]215 216 fn last_collection_idx(&self, at: Option<BlockHash>) -> Result<RmrkCollectionId>;217218 #[rpc(name = "rmrk_collectionById")]219 220 fn collection_by_id(221 &self,222 id: RmrkCollectionId,223 at: Option<BlockHash>,224 ) -> Result<Option<CollectionInfo>>;225226 #[rpc(name = "rmrk_nftById")]227 228 fn nft_by_id(229 &self,230 collection_id: RmrkCollectionId,231 nft_id: RmrkNftId,232 at: Option<BlockHash>,233 ) -> Result<Option<NftInfo>>;234235 #[rpc(name = "rmrk_accountTokens")]236 237 fn account_tokens(238 &self,239 account_id: AccountId,240 collection_id: RmrkCollectionId,241 at: Option<BlockHash>,242 ) -> Result<Vec<RmrkNftId>>;243244 #[rpc(name = "rmrk_nftChildren")]245 246 fn nft_children(247 &self,248 collection_id: RmrkCollectionId,249 nft_id: RmrkNftId,250 at: Option<BlockHash>,251 ) -> Result<Vec<RmrkNftChild>>;252253 #[rpc(name = "rmrk_collectionProperties")]254 255 fn collection_properties(256 &self,257 collection_id: RmrkCollectionId,258 filter_keys: Option<Vec<RmrkPropertyKey>>, 259 at: Option<BlockHash>,260 ) -> Result<Vec<PropertyInfo>>;261262 #[rpc(name = "rmrk_nftProperties")]263 264 fn nft_properties(265 &self,266 collection_id: RmrkCollectionId,267 nft_id: RmrkNftId,268 filter_keys: Option<Vec<RmrkPropertyKey>>,269 at: Option<BlockHash>,270 ) -> Result<Vec<PropertyInfo>>;271272 #[rpc(name = "rmrk_nftResources")]273 274 fn nft_resources(275 &self,276 collection_id: RmrkCollectionId,277 nft_id: RmrkNftId,278 at: Option<BlockHash>,279 ) -> Result<Vec<ResourceInfo>>;280281 #[rpc(name = "rmrk_nftResourcePriorities")]282 283 fn nft_resource_priorities(284 &self,285 collection_id: RmrkCollectionId,286 nft_id: RmrkNftId,287 at: Option<BlockHash>,288 ) -> Result<Vec<RmrkResourceId>>;289290 #[rpc(name = "rmrk_base")]291 292 fn base(&self, base_id: RmrkBaseId, at: Option<BlockHash>) -> Result<Option<BaseInfo>>;293294 #[rpc(name = "rmrk_baseParts")]295 296 fn base_parts(&self, base_id: RmrkBaseId, at: Option<BlockHash>) -> Result<Vec<PartType>>;297298 #[rpc(name = "rmrk_themeNames")]299 fn theme_names(300 &self,301 base_id: RmrkBaseId,302 at: Option<BlockHash>,303 ) -> Result<Vec<RmrkThemeName>>;304305 #[rpc(name = "rmrk_themes")]306 fn theme(307 &self,308 base_id: RmrkBaseId,309 theme_name: RmrkThemeName, 310 filter_keys: Option<Vec<RmrkPropertyKey>>,311 at: Option<BlockHash>,312 ) -> Result<Option<Theme>>;313 }314}315316pub struct Unique<C, P> {317 client: Arc<C>,318 _marker: std::marker::PhantomData<P>,319}320321impl<C, P> Unique<C, P> {322 pub fn new(client: Arc<C>) -> Self {323 Self {324 client,325 _marker: Default::default(),326 }327 }328}329330pub enum Error {331 RuntimeError,332}333334impl From<Error> for i64 {335 fn from(e: Error) -> i64 {336 match e {337 Error::RuntimeError => 1,338 }339 }340}341342macro_rules! pass_method {343 (344 $method_name:ident(345 $($(#[map(|$map_arg:ident| $map:expr)])? $name:ident: $ty:ty),* $(,)?346 ) -> $result:ty $(=> $mapper:expr)?,347 348 $runtime_api_macro:ident349 $(; changed_in $ver:expr, $changed_method_name:ident ($($changed_name:expr), * $(,)?) => $fixer:expr)*350 ) => {351 fn $method_name(352 &self,353 $(354 $name: $ty,355 )*356 at: Option<<Block as BlockT>::Hash>,357 ) -> Result<$result> {358 let api = self.client.runtime_api();359 let at = BlockId::hash(at.unwrap_or_else(|| self.client.info().best_hash));360 let _api_version = if let Ok(Some(api_version)) =361 api.api_version::<$runtime_api_macro!()>(&at)362 {363 api_version364 } else {365 366 return Err(RpcError {367 code: ErrorCode::InvalidParams,368 message: "Api is not available".into(),369 data: None,370 })371 };372373 let result = $(if _api_version < $ver {374 api.$changed_method_name(&at, $($changed_name),*).map(|r| r.map($fixer))375 } else)*376 { api.$method_name(&at, $($((|$map_arg: $ty| $map))? ($name)),*) };377378 let result = result.map_err(|e| RpcError {379 code: ErrorCode::ServerError(Error::RuntimeError.into()),380 message: "Unable to query".into(),381 data: Some(format!("{:?}", e).into()),382 })?;383 result.map_err(|e| RpcError {384 code: ErrorCode::InvalidParams,385 message: "Runtime returned error".into(),386 data: Some(format!("{:?}", e).into()),387 })$(.map($mapper))?388 }389 };390}391392macro_rules! unique_api {393 () => {394 dyn UniqueRuntimeApi<Block, CrossAccountId, AccountId>395 };396}397398macro_rules! rmrk_api {399 () => {400 dyn RmrkRuntimeApi<Block, AccountId, CollectionInfo, NftInfo, ResourceInfo, PropertyInfo, BaseInfo, PartType, Theme>401 };402}403404#[allow(deprecated)]405impl<C, Block, CrossAccountId, AccountId>406 UniqueApi<<Block as BlockT>::Hash, CrossAccountId, AccountId> for Unique<C, Block>407where408 Block: BlockT,409 AccountId: Decode,410 C: 'static + ProvideRuntimeApi<Block> + HeaderBackend<Block>,411 C::Api: UniqueRuntimeApi<Block, CrossAccountId, AccountId>,412 CrossAccountId: pallet_evm::account::CrossAccountId<AccountId>,413{414 pass_method!(415 account_tokens(collection: CollectionId, account: CrossAccountId) -> Vec<TokenId>, unique_api416 );417 pass_method!(418 collection_tokens(collection: CollectionId) -> Vec<TokenId>, unique_api419 );420 pass_method!(421 token_exists(collection: CollectionId, token: TokenId) -> bool, unique_api422 );423 pass_method!(424 token_owner(collection: CollectionId, token: TokenId) -> Option<CrossAccountId>, unique_api425 );426 pass_method!(427 topmost_token_owner(collection: CollectionId, token: TokenId) -> Option<CrossAccountId>, unique_api428 );429 pass_method!(430 const_metadata(collection: CollectionId, token: TokenId) -> Vec<u8>, unique_api431 );432 pass_method!(433 variable_metadata(collection: CollectionId, token: TokenId) -> Vec<u8>,434 unique_api435 );436 pass_method!(total_supply(collection: CollectionId) -> u32, unique_api);437 pass_method!(account_balance(collection: CollectionId, account: CrossAccountId) -> u32, unique_api);438 pass_method!(balance(collection: CollectionId, account: CrossAccountId, token: TokenId) -> String => |v| v.to_string(), unique_api);439 pass_method!(440 allowance(collection: CollectionId, sender: CrossAccountId, spender: CrossAccountId, token: TokenId) -> String => |v| v.to_string(),441 unique_api442 );443444 pass_method!(collection_properties(445 collection: CollectionId,446447 #[map(|keys| string_keys_to_bytes_keys(keys))]448 keys: Vec<String>449 ) -> Vec<Property>, unique_api);450451 pass_method!(token_properties(452 collection: CollectionId,453 token_id: TokenId,454455 #[map(|keys| string_keys_to_bytes_keys(keys))]456 properties: Vec<String>457 ) -> Vec<Property>, unique_api);458459 pass_method!(property_permissions(460 collection: CollectionId,461462 #[map(|keys| string_keys_to_bytes_keys(keys))]463 keys: Vec<String>464 ) -> Vec<PropertyKeyPermission>, unique_api);465466 pass_method!(token_data(467 collection: CollectionId,468 token_id: TokenId,469470 #[map(|keys| string_keys_to_bytes_keys(keys))]471 keys: Vec<String>,472 ) -> TokenData<CrossAccountId>, unique_api);473474 pass_method!(adminlist(collection: CollectionId) -> Vec<CrossAccountId>, unique_api);475 pass_method!(allowlist(collection: CollectionId) -> Vec<CrossAccountId>, unique_api);476 pass_method!(allowed(collection: CollectionId, user: CrossAccountId) -> bool, unique_api);477 pass_method!(last_token_id(collection: CollectionId) -> TokenId, unique_api);478 pass_method!(collection_by_id(collection: CollectionId) -> Option<RpcCollection<AccountId>>, unique_api);479 pass_method!(collection_stats() -> CollectionStats, unique_api);480 pass_method!(next_sponsored(collection: CollectionId, account: CrossAccountId, token: TokenId) -> Option<u64>, unique_api);481 pass_method!(effective_collection_limits(collection_id: CollectionId) -> Option<CollectionLimits>, unique_api);482}483484#[allow(deprecated)]485impl<486 C,487 Block,488 AccountId,489 CollectionInfo,490 NftInfo,491 ResourceInfo,492 PropertyInfo,493 BaseInfo,494 PartType,495 Theme,496 >497 rmrk_unique_rpc::RmrkApi<498 <Block as BlockT>::Hash,499 AccountId,500 CollectionInfo,501 NftInfo,502 ResourceInfo,503 PropertyInfo,504 BaseInfo,505 PartType,506 Theme,507 > for Unique<C, Block>508where509 C: Send + Sync + 'static + ProvideRuntimeApi<Block> + HeaderBackend<Block>,510 C::Api: RmrkRuntimeApi<511 Block,512 AccountId,513 CollectionInfo,514 NftInfo,515 ResourceInfo,516 PropertyInfo,517 BaseInfo,518 PartType,519 Theme,520 >,521 AccountId: Decode + Encode,522 CollectionInfo: Decode,523 NftInfo: Decode,524 ResourceInfo: Decode,525 PropertyInfo: Decode,526 BaseInfo: Decode,527 PartType: Decode,528 Theme: Decode,529 Block: BlockT,530{531 pass_method!(last_collection_idx() -> RmrkCollectionId, rmrk_api);532 pass_method!(collection_by_id(id: RmrkCollectionId) -> Option<CollectionInfo>, rmrk_api);533 pass_method!(nft_by_id(collection_id: RmrkCollectionId, nft_id: RmrkNftId) -> Option<NftInfo>, rmrk_api);534 pass_method!(account_tokens(account_id: AccountId, collection_id: RmrkCollectionId) -> Vec<RmrkNftId>, rmrk_api);535 pass_method!(nft_children(collection_id: RmrkCollectionId, nft_id: RmrkNftId) -> Vec<RmrkNftChild>, rmrk_api);536 pass_method!(537 collection_properties(collection_id: RmrkCollectionId, filter_keys: Option<Vec<RmrkPropertyKey>>) -> Vec<PropertyInfo>,538 rmrk_api539 );540 pass_method!(541 nft_properties(collection_id: RmrkCollectionId, nft_id: RmrkNftId, filter_keys: Option<Vec<RmrkPropertyKey>>) -> Vec<PropertyInfo>,542 rmrk_api543 );544 pass_method!(nft_resources(collection_id: RmrkCollectionId, nft_id: RmrkNftId) -> Vec<ResourceInfo>, rmrk_api);545 pass_method!(nft_resource_priorities(collection_id: RmrkCollectionId, nft_id: RmrkNftId) -> Vec<RmrkResourceId>, rmrk_api);546 pass_method!(base(base_id: RmrkBaseId) -> Option<BaseInfo>, rmrk_api);547 pass_method!(base_parts(base_id: RmrkBaseId) -> Vec<PartType>, rmrk_api);548 pass_method!(theme_names(base_id: RmrkBaseId) -> Vec<RmrkThemeName>, rmrk_api);549 pass_method!(theme(base_id: RmrkBaseId, theme_name: RmrkThemeName, filter_keys: Option<Vec<RmrkPropertyKey>>) -> Option<Theme>, rmrk_api);550}551552fn string_keys_to_bytes_keys(keys: Vec<String>) -> Vec<Vec<u8>> {553 keys.into_iter().map(|key| key.into_bytes()).collect()554}