123456789101112131415161718use std::sync::Arc;1920use codec::Decode;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::{BlockT, ProvideRuntimeApi, ApiExt};32use sp_blockchain::HeaderBackend;33use up_rpc::UniqueApi as UniqueRuntimeApi;34use app_promotion_rpc::AppPromotionApi as AppPromotionRuntimeApi;3536pub use app_promotion_unique_rpc::AppPromotionApiServer;3738#[cfg(feature = "pov-estimate")]39pub mod pov_estimate;4041#[rpc(server)]42#[async_trait]43pub trait UniqueApi<BlockHash, CrossAccountId, AccountId> {44 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>>;5253 54 #[method(name = "unique_collectionTokens")]55 fn collection_tokens(56 &self,57 collection: CollectionId,58 at: Option<BlockHash>,59 ) -> Result<Vec<TokenId>>;6061 62 #[method(name = "unique_tokenExists")]63 fn token_exists(64 &self,65 collection: CollectionId,66 token: TokenId,67 at: Option<BlockHash>,68 ) -> Result<bool>;6970 71 #[method(name = "unique_tokenOwner")]72 fn token_owner(73 &self,74 collection: CollectionId,75 token: TokenId,76 at: Option<BlockHash>,77 ) -> Result<Option<CrossAccountId>>;7879 80 #[method(name = "unique_tokenOwners")]81 fn token_owners(82 &self,83 collection: CollectionId,84 token: TokenId,85 at: Option<BlockHash>,86 ) -> Result<Vec<CrossAccountId>>;8788 89 #[method(name = "unique_topmostTokenOwner")]90 fn topmost_token_owner(91 &self,92 collection: CollectionId,93 token: TokenId,94 at: Option<BlockHash>,95 ) -> Result<Option<CrossAccountId>>;9697 98 #[method(name = "unique_tokenChildren")]99 fn token_children(100 &self,101 collection: CollectionId,102 token: TokenId,103 at: Option<BlockHash>,104 ) -> Result<Vec<TokenChild>>;105106 107 #[method(name = "unique_collectionProperties")]108 fn collection_properties(109 &self,110 collection: CollectionId,111 keys: Option<Vec<String>>,112 at: Option<BlockHash>,113 ) -> Result<Vec<Property>>;114115 116 #[method(name = "unique_tokenProperties")]117 fn token_properties(118 &self,119 collection: CollectionId,120 token_id: TokenId,121 keys: Option<Vec<String>>,122 at: Option<BlockHash>,123 ) -> Result<Vec<Property>>;124125 126 #[method(name = "unique_propertyPermissions")]127 fn property_permissions(128 &self,129 collection: CollectionId,130 keys: Option<Vec<String>>,131 at: Option<BlockHash>,132 ) -> Result<Vec<PropertyKeyPermission>>;133134 135 #[method(name = "unique_tokenData")]136 fn token_data(137 &self,138 collection: CollectionId,139 token_id: TokenId,140 keys: Option<Vec<String>>,141 at: Option<BlockHash>,142 ) -> Result<TokenData<CrossAccountId>>;143144 145 #[method(name = "unique_totalSupply")]146 fn total_supply(&self, collection: CollectionId, at: Option<BlockHash>) -> Result<u32>;147148 149 #[method(name = "unique_accountBalance")]150 fn account_balance(151 &self,152 collection: CollectionId,153 account: CrossAccountId,154 at: Option<BlockHash>,155 ) -> Result<u32>;156157 158 #[method(name = "unique_balance")]159 fn balance(160 &self,161 collection: CollectionId,162 account: CrossAccountId,163 token: TokenId,164 at: Option<BlockHash>,165 ) -> Result<String>;166167 168 #[method(name = "unique_allowance")]169 fn allowance(170 &self,171 collection: CollectionId,172 sender: CrossAccountId,173 spender: CrossAccountId,174 token: TokenId,175 at: Option<BlockHash>,176 ) -> Result<String>;177178 179 #[method(name = "unique_adminlist")]180 fn adminlist(181 &self,182 collection: CollectionId,183 at: Option<BlockHash>,184 ) -> Result<Vec<CrossAccountId>>;185186 187 #[method(name = "unique_allowlist")]188 fn allowlist(189 &self,190 collection: CollectionId,191 at: Option<BlockHash>,192 ) -> Result<Vec<CrossAccountId>>;193194 195 #[method(name = "unique_allowed")]196 fn allowed(197 &self,198 collection: CollectionId,199 user: CrossAccountId,200 at: Option<BlockHash>,201 ) -> Result<bool>;202203 204 #[method(name = "unique_lastTokenId")]205 fn last_token_id(&self, collection: CollectionId, at: Option<BlockHash>) -> Result<TokenId>;206207 208 #[method(name = "unique_collectionById")]209 fn collection_by_id(210 &self,211 collection: CollectionId,212 at: Option<BlockHash>,213 ) -> Result<Option<RpcCollection<AccountId>>>;214215 216 #[method(name = "unique_collectionStats")]217 fn collection_stats(&self, at: Option<BlockHash>) -> Result<CollectionStats>;218219 220 #[method(name = "unique_nextSponsored")]221 fn next_sponsored(222 &self,223 collection: CollectionId,224 account: CrossAccountId,225 token: TokenId,226 at: Option<BlockHash>,227 ) -> Result<Option<u64>>;228229 230 #[method(name = "unique_effectiveCollectionLimits")]231 fn effective_collection_limits(232 &self,233 collection_id: CollectionId,234 at: Option<BlockHash>,235 ) -> Result<Option<CollectionLimits>>;236237 238 #[method(name = "unique_totalPieces")]239 fn total_pieces(240 &self,241 collection_id: CollectionId,242 token_id: TokenId,243 at: Option<BlockHash>,244 ) -> Result<Option<String>>;245246 247 #[method(name = "unique_allowanceForAll")]248 fn allowance_for_all(249 &self,250 collection: CollectionId,251 owner: CrossAccountId,252 operator: CrossAccountId,253 at: Option<BlockHash>,254 ) -> Result<bool>;255}256257mod app_promotion_unique_rpc {258 use super::*;259260 #[rpc(server)]261 #[async_trait]262 pub trait AppPromotionApi<BlockHash, BlockNumber, CrossAccountId, AccountId> {263 264 #[method(name = "appPromotion_totalStaked")]265 fn total_staked(266 &self,267 staker: Option<CrossAccountId>,268 at: Option<BlockHash>,269 ) -> Result<String>;270271 272 #[method(name = "appPromotion_totalStakedPerBlock")]273 fn total_staked_per_block(274 &self,275 staker: CrossAccountId,276 at: Option<BlockHash>,277 ) -> Result<Vec<(BlockNumber, String)>>;278279 280 #[method(name = "appPromotion_pendingUnstake")]281 fn pending_unstake(282 &self,283 staker: Option<CrossAccountId>,284 at: Option<BlockHash>,285 ) -> Result<String>;286287 288 #[method(name = "appPromotion_pendingUnstakePerBlock")]289 fn pending_unstake_per_block(290 &self,291 staker: CrossAccountId,292 at: Option<BlockHash>,293 ) -> Result<Vec<(BlockNumber, String)>>;294 }295}296297#[macro_export]298macro_rules! define_struct_for_server_api {299 ($name:ident { $($arg:ident: $arg_ty:ty),+ $(,)? }) => {300 pub struct $name<Client, Block: BlockT> {301 $($arg: $arg_ty),+,302 _marker: std::marker::PhantomData<Block>,303 }304305 impl<Client, Block: BlockT> $name<Client, Block> {306 pub fn new($($arg: $arg_ty),+) -> Self {307 Self {308 $($arg),+,309 _marker: Default::default(),310 }311 }312 }313 };314}315316define_struct_for_server_api! {317 Unique {318 client: Arc<Client>319 }320}321322define_struct_for_server_api! {323 AppPromotion {324 client: Arc<Client>325 }326}327328macro_rules! pass_method {329 (330 $method_name:ident(331 $($(#[map(|$map_arg:ident| $map:expr)])? $name:ident: $ty:ty),* $(,)?332 ) -> $result:ty $(=> $mapper:expr)?,333 334 $runtime_api_macro:ident335 $(; changed_in $ver:expr, $changed_method_name:ident ($($changed_name:expr), * $(,)?) => $fixer:expr)*336 ) => {337 fn $method_name(338 &self,339 $(340 $name: $ty,341 )*342 at: Option<<Block as BlockT>::Hash>,343 ) -> Result<$result> {344 let api = self.client.runtime_api();345 let at = at.unwrap_or_else(|| self.client.info().best_hash);346 let _api_version = if let Ok(Some(api_version)) =347 api.api_version::<$runtime_api_macro!()>(at)348 {349 api_version350 } else {351 352 return Err(anyhow!("api is not available").into())353 };354355 let result = $(if _api_version < $ver {356 api.$changed_method_name(at, $($changed_name),*).map(|r| r.map($fixer))357 } else)*358 { api.$method_name(at, $($((|$map_arg: $ty| $map))? ($name)),*) };359360 Ok(result361 .map_err(|e| anyhow!("unable to query: {e}"))?362 .map_err(|e| anyhow!("runtime error: {e:?}"))$(.map($mapper))??)363 }364 };365}366367macro_rules! unique_api {368 () => {369 dyn UniqueRuntimeApi<Block, CrossAccountId, AccountId>370 };371}372373macro_rules! app_promotion_api {374 () => {375 dyn AppPromotionRuntimeApi<Block, BlockNumber, CrossAccountId, AccountId>376 };377}378379#[allow(deprecated)]380impl<C, Block, CrossAccountId, AccountId>381 UniqueApiServer<<Block as BlockT>::Hash, CrossAccountId, AccountId> for Unique<C, Block>382where383 Block: BlockT,384 AccountId: Decode,385 C: 'static + ProvideRuntimeApi<Block> + HeaderBackend<Block>,386 C::Api: UniqueRuntimeApi<Block, CrossAccountId, AccountId>,387 CrossAccountId: pallet_evm::account::CrossAccountId<AccountId>,388{389 pass_method!(390 account_tokens(collection: CollectionId, account: CrossAccountId) -> Vec<TokenId>, unique_api391 );392 pass_method!(393 collection_tokens(collection: CollectionId) -> Vec<TokenId>, unique_api394 );395 pass_method!(396 token_exists(collection: CollectionId, token: TokenId) -> bool, unique_api397 );398 pass_method!(399 token_owner(collection: CollectionId, token: TokenId) -> Option<CrossAccountId>, unique_api400 );401 pass_method!(402 topmost_token_owner(collection: CollectionId, token: TokenId) -> Option<CrossAccountId>, unique_api403 );404 pass_method!(token_children(collection: CollectionId, token: TokenId) -> Vec<TokenChild>, unique_api);405 pass_method!(total_supply(collection: CollectionId) -> u32, unique_api);406 pass_method!(account_balance(collection: CollectionId, account: CrossAccountId) -> u32, unique_api);407 pass_method!(balance(collection: CollectionId, account: CrossAccountId, token: TokenId) -> String => |v| v.to_string(), unique_api);408 pass_method!(409 allowance(collection: CollectionId, sender: CrossAccountId, spender: CrossAccountId, token: TokenId) -> String => |v| v.to_string(),410 unique_api411 );412413 pass_method!(collection_properties(414 collection: CollectionId,415416 #[map(|keys| string_keys_to_bytes_keys(keys))]417 keys: Option<Vec<String>>418 ) -> Vec<Property>, unique_api);419420 pass_method!(token_properties(421 collection: CollectionId,422 token_id: TokenId,423424 #[map(|keys| string_keys_to_bytes_keys(keys))]425 keys: Option<Vec<String>>426 ) -> Vec<Property>, unique_api);427428 pass_method!(property_permissions(429 collection: CollectionId,430431 #[map(|keys| string_keys_to_bytes_keys(keys))]432 keys: Option<Vec<String>>433 ) -> Vec<PropertyKeyPermission>, unique_api);434435 pass_method!(436 token_data(437 collection: CollectionId,438 token_id: TokenId,439440 #[map(|keys| string_keys_to_bytes_keys(keys))]441 keys: Option<Vec<String>>,442 ) -> TokenData<CrossAccountId>, unique_api;443 changed_in 3, token_data_before_version_3(collection, token_id, string_keys_to_bytes_keys(keys)) => |value| value.into()444 );445446 pass_method!(adminlist(collection: CollectionId) -> Vec<CrossAccountId>, unique_api);447 pass_method!(allowlist(collection: CollectionId) -> Vec<CrossAccountId>, unique_api);448 pass_method!(allowed(collection: CollectionId, user: CrossAccountId) -> bool, unique_api);449 pass_method!(last_token_id(collection: CollectionId) -> TokenId, unique_api);450 pass_method!(451 collection_by_id(collection: CollectionId) -> Option<RpcCollection<AccountId>>, unique_api;452 changed_in 3, collection_by_id_before_version_3(collection) => |value| value.map(|coll| coll.into())453 );454 pass_method!(collection_stats() -> CollectionStats, unique_api);455 pass_method!(next_sponsored(collection: CollectionId, account: CrossAccountId, token: TokenId) -> Option<u64>, unique_api);456 pass_method!(effective_collection_limits(collection_id: CollectionId) -> Option<CollectionLimits>, unique_api);457 pass_method!(total_pieces(collection_id: CollectionId, token_id: TokenId) -> Option<String> => |o| o.map(|number| number.to_string()) , unique_api);458 pass_method!(token_owners(collection: CollectionId, token: TokenId) -> Vec<CrossAccountId>, unique_api);459 pass_method!(allowance_for_all(collection: CollectionId, owner: CrossAccountId, operator: CrossAccountId) -> bool, unique_api);460}461462impl<C, Block, BlockNumber, CrossAccountId, AccountId>463 app_promotion_unique_rpc::AppPromotionApiServer<464 <Block as BlockT>::Hash,465 BlockNumber,466 CrossAccountId,467 AccountId,468 > for AppPromotion<C, Block>469where470 Block: BlockT,471 BlockNumber: Decode + Member + AtLeast32BitUnsigned,472 AccountId: Decode,473 C: 'static + ProvideRuntimeApi<Block> + HeaderBackend<Block>,474 CrossAccountId: pallet_evm::account::CrossAccountId<AccountId>,475 C::Api: AppPromotionRuntimeApi<Block, BlockNumber, CrossAccountId, AccountId>,476{477 pass_method!(total_staked(staker: Option<CrossAccountId>) -> String => |v| v.to_string(), app_promotion_api);478 pass_method!(total_staked_per_block(staker: CrossAccountId) -> Vec<(BlockNumber, String)> =>479 |v| v480 .into_iter()481 .map(|(b, a)| (b, a.to_string()))482 .collect::<Vec<_>>(), app_promotion_api);483 pass_method!(pending_unstake(staker: Option<CrossAccountId>) -> String => |v| v.to_string(), app_promotion_api);484 pass_method!(pending_unstake_per_block(staker: CrossAccountId) -> Vec<(BlockNumber, String)> =>485 |v| v486 .into_iter()487 .map(|(b, a)| (b, a.to_string()))488 .collect::<Vec<_>>(), app_promotion_api);489}490491fn string_keys_to_bytes_keys(keys: Option<Vec<String>>) -> Option<Vec<Vec<u8>>> {492 keys.map(|keys| keys.into_iter().map(|key| key.into_bytes()).collect())493}