From 0daf5cb79b557eaf53bfa71df91f612d9297157c Mon Sep 17 00:00:00 2001 From: kozyrevdev <73348153+kozyrevdev@users.noreply.github.com> Date: Wed, 06 Apr 2022 17:26:10 +0000 Subject: [PATCH] Merge pull request #317 from UniqueNetwork/feature/CORE-238 CORE-283 add effective_collection_limits --- --- a/Cargo.lock +++ b/Cargo.lock @@ -9760,6 +9760,55 @@ ] [[package]] +name = "sc-consensus-epochs" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.17#22d40c761a985482f93bbbea5ba4199bdba74f8e" +dependencies = [ + "fork-tree", + "parity-scale-codec", + "sc-client-api", + "sc-consensus", + "sp-blockchain", + "sp-runtime", +] + +[[package]] +name = "sc-consensus-manual-seal" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.17#22d40c761a985482f93bbbea5ba4199bdba74f8e" +dependencies = [ + "assert_matches", + "async-trait", + "futures 0.3.21", + "jsonrpc-core", + "jsonrpc-core-client", + "jsonrpc-derive", + "log", + "parity-scale-codec", + "sc-client-api", + "sc-consensus", + "sc-consensus-aura", + "sc-consensus-babe", + "sc-consensus-epochs", + "sc-transaction-pool", + "sc-transaction-pool-api", + "serde", + "sp-api", + "sp-blockchain", + "sp-consensus", + "sp-consensus-aura", + "sp-consensus-babe", + "sp-consensus-slots", + "sp-core", + "sp-inherents", + "sp-keystore", + "sp-runtime", + "sp-timestamp", + "substrate-prometheus-endpoint", + "thiserror", +] + +[[package]] name = "sc-consensus-slots" version = "0.10.0-dev" source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.18#fc3fd073d3a0acf9933c3994b660ebd7b5833f65" @@ -12212,7 +12261,7 @@ "chrono", "lazy_static", "matchers", - "parking_lot 0.11.2", + "parking_lot 0.10.2", "regex", "serde", "serde_json", --- a/client/rpc/src/lib.rs +++ b/client/rpc/src/lib.rs @@ -19,7 +19,7 @@ use codec::Decode; use jsonrpc_core::{Error as RpcError, ErrorCode, Result}; use jsonrpc_derive::rpc; -use up_data_structs::{Collection, CollectionId, CollectionStats, TokenId}; +use up_data_structs::{Collection, CollectionId, CollectionStats, CollectionLimits, TokenId}; use sp_api::{BlockId, BlockT, ProvideRuntimeApi, ApiExt}; use sp_blockchain::HeaderBackend; use up_rpc::UniqueApi as UniqueRuntimeApi; @@ -119,6 +119,12 @@ ) -> Result>>; #[rpc(name = "unique_collectionStats")] fn collection_stats(&self, at: Option) -> Result; + #[rpc(name = "unique_effectiveCollectionLimits")] + fn effective_collection_limits( + &self, + collection_id: CollectionId, + at: Option, + ) -> Result>; } pub struct Unique { @@ -222,4 +228,5 @@ pass_method!(last_token_id(collection: CollectionId) -> TokenId); pass_method!(collection_by_id(collection: CollectionId) -> Option>); pass_method!(collection_stats() -> CollectionStats); + pass_method!(effective_collection_limits(collection_id: CollectionId) -> Option); } --- a/pallets/common/src/lib.rs +++ b/pallets/common/src/lib.rs @@ -33,7 +33,7 @@ TokenId, Weight, WithdrawReasons, CollectionStats, MAX_TOKEN_OWNERSHIP, CollectionMode, NFT_SPONSOR_TRANSFER_TIMEOUT, FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT, REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT, MAX_SPONSOR_TIMEOUT, CUSTOM_DATA_LIMIT, CollectionLimits, - CustomDataLimit, CreateCollectionData, SponsorshipState, CreateItemExData, + CustomDataLimit, CreateCollectionData, SponsorshipState, CreateItemExData, SponsoringRateLimit, }; pub use pallet::*; use sp_core::H160; @@ -417,6 +417,39 @@ alive: created.0 - destroyed.0, } } + + pub fn effective_collection_limits(collection: CollectionId) -> Option { + let collection = >::get(collection); + if collection.is_none() { + return None; + } + + let collection = collection.unwrap(); + let limits = collection.limits; + let effective_limits = CollectionLimits { + account_token_ownership_limit: Some(limits.account_token_ownership_limit()), + sponsored_data_size: Some(limits.sponsored_data_size()), + sponsored_data_rate_limit: Some( + limits + .sponsored_data_rate_limit + .unwrap_or(SponsoringRateLimit::SponsoringDisabled), + ), + token_limit: Some(limits.token_limit()), + sponsor_transfer_timeout: Some(limits.sponsor_transfer_timeout( + match collection.mode { + CollectionMode::NFT => NFT_SPONSOR_TRANSFER_TIMEOUT, + CollectionMode::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT, + CollectionMode::ReFungible => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT, + }, + )), + sponsor_approve_timeout: Some(limits.sponsor_approve_timeout()), + owner_can_transfer: Some(limits.owner_can_transfer()), + owner_can_destroy: Some(limits.owner_can_destroy()), + transfers_enabled: Some(limits.transfers_enabled()), + }; + + Some(effective_limits) + } } impl Pallet { --- a/primitives/rpc/src/lib.rs +++ b/primitives/rpc/src/lib.rs @@ -16,7 +16,7 @@ #![cfg_attr(not(feature = "std"), no_std)] -use up_data_structs::{CollectionId, TokenId, Collection, CollectionStats}; +use up_data_structs::{CollectionId, TokenId, Collection, CollectionStats, CollectionLimits}; use sp_std::vec::Vec; use sp_core::H160; use codec::Decode; @@ -59,5 +59,6 @@ fn last_token_id(collection: CollectionId) -> Result; fn collection_by_id(collection: CollectionId) -> Result>>; fn collection_stats() -> Result; + fn effective_collection_limits(collection_id: CollectionId) -> Result>; } } --- a/runtime/common/src/runtime_apis.rs +++ b/runtime/common/src/runtime_apis.rs @@ -69,6 +69,10 @@ fn collection_stats() -> Result { Ok(>::collection_stats()) } + + fn effective_collection_limits(collection: CollectionId) -> Result, DispatchError> { + Ok(>::effective_collection_limits(collection)) + } } impl sp_api::Core for Runtime { --- a/tests/package.json +++ b/tests/package.json @@ -67,6 +67,7 @@ "testPalletPresence": "mocha --timeout 9999999 -r ts-node/register ./**/pallet-presence.test.ts", "testBlockProduction": "mocha --timeout 9999999 -r ts-node/register ./**/block-production.test.ts", "testEnableDisableTransfers": "mocha --timeout 9999999 -r ts-node/register ./**/enableDisableTransfer.test.ts", + "testLimits": "mocha --timeout 9999999 -r ts-node/register ./**/limits.test.ts", "polkadot-types-fetch-metadata": "curl -H 'Content-Type: application/json' -d '{\"id\":\"1\", \"jsonrpc\":\"2.0\", \"method\": \"state_getMetadata\", \"params\":[]}' http://localhost:9933 > src/interfaces/metadata.json", "polkadot-types-from-defs": "echo 'export default {}' > src/interfaces/lookup.ts && ts-node ./node_modules/.bin/polkadot-types-from-defs --endpoint src/interfaces/metadata.json --input src/interfaces/ --package .", "polkadot-types-from-chain": "ts-node ./node_modules/.bin/polkadot-types-from-chain --endpoint src/interfaces/metadata.json --output src/interfaces/ --package .", --- a/tests/src/interfaces/augment-api-rpc.ts +++ b/tests/src/interfaces/augment-api-rpc.ts @@ -1,7 +1,7 @@ // Auto-generated via `yarn polkadot-types-from-chain`, do not edit /* eslint-disable */ -import type { PalletEvmAccountBasicCrossAccountIdRepr, UpDataStructsCollection, UpDataStructsCollectionStats } from './unique'; +import type { PalletEvmAccountBasicCrossAccountIdRepr, UpDataStructsCollection, UpDataStructsCollectionStats, UpDataStructsCollectionStats } from './unique'; import type { AugmentedRpc } from '@polkadot/rpc-core/types'; import type { Metadata, StorageKey } from '@polkadot/types'; import type { Bytes, HashMap, Json, Null, Option, Text, U256, U64, Vec, bool, u128, u32, u64 } from '@polkadot/types-codec'; @@ -604,6 +604,10 @@ **/ constMetadata: AugmentedRpc<(collection: u32 | AnyNumber | Uint8Array, tokenId: u32 | AnyNumber | Uint8Array, at?: Hash | string | Uint8Array) => Observable>; /** + * Get effective collection limits + **/ + effectiveCollectionLimits: AugmentedRpc<(collection: u32 | AnyNumber | Uint8Array, at?: Hash | string | Uint8Array) => Observable>>; + /** * Get last token id **/ lastTokenId: AugmentedRpc<(collection: u32 | AnyNumber | Uint8Array, at?: Hash | string | Uint8Array) => Observable>; --- a/tests/src/interfaces/unique/definitions.ts +++ b/tests/src/interfaces/unique/definitions.ts @@ -55,5 +55,6 @@ collectionById: fun('Get collection by specified id', [collectionParam], 'Option'), collectionStats: fun('Get collection stats', [], 'UpDataStructsCollectionStats'), allowed: fun('Check if user is allowed to use collection', [collectionParam, crossAccountParam()], 'bool'), + effectiveCollectionLimits: fun('Get effective collection limits', [collectionParam], 'Option'), }, }; --- a/tests/src/limits.test.ts +++ b/tests/src/limits.test.ts @@ -395,4 +395,73 @@ expect(aliceBalanceAfterSponsoredTransaction1 < aliceBalanceBefore).to.be.true; //expect(aliceBalanceAfterSponsoredTransaction1).to.be.lessThan(aliceBalanceBefore); }); + + it('Effective collection limits', async () => { + await usingApi(async (api) => { + const collectionId = await createCollectionExpectSuccess({mode: {type: 'NFT'}}); + + { // Check that limits is undefined + const collection = await api.rpc.unique.collectionById(collectionId); + expect(collection.isSome).to.be.true; + const limits = collection.unwrap().limits; + expect(limits).to.be.any; + + expect(limits.accountTokenOwnershipLimit.toHuman()).to.be.null; + expect(limits.sponsoredDataSize.toHuman()).to.be.null; + expect(limits.sponsoredDataRateLimit.toHuman()).to.be.null; + expect(limits.tokenLimit.toHuman()).to.be.null; + expect(limits.sponsorTransferTimeout.toHuman()).to.be.null; + expect(limits.sponsorApproveTimeout.toHuman()).to.be.null; + expect(limits.ownerCanTransfer.toHuman()).to.be.null; + expect(limits.ownerCanDestroy.toHuman()).to.be.null; + expect(limits.transfersEnabled.toHuman()).to.be.null; + } + + { // Check that limits is undefined for non-existent collection + const limits = await api.rpc.unique.effectiveCollectionLimits(11111); + expect(limits.toHuman()).to.be.null; + } + + { // Check that default values defined for collection limits + const limitsOpt = await api.rpc.unique.effectiveCollectionLimits(collectionId); + expect(limitsOpt.isNone).to.be.false; + const limits = limitsOpt.unwrap(); + + expect(limits.accountTokenOwnershipLimit.toHuman()).to.be.eq('100,000'); + expect(limits.sponsoredDataSize.toHuman()).to.be.eq('2,048'); + expect(limits.sponsoredDataRateLimit.toHuman()).to.be.eq('SponsoringDisabled'); + expect(limits.tokenLimit.toHuman()).to.be.eq('4,294,967,295'); + expect(limits.sponsorTransferTimeout.toHuman()).to.be.eq('5'); + expect(limits.sponsorApproveTimeout.toHuman()).to.be.eq('5'); + expect(limits.ownerCanTransfer.toHuman()).to.be.true; + expect(limits.ownerCanDestroy.toHuman()).to.be.true; + expect(limits.transfersEnabled.toHuman()).to.be.true; + } + + { //Check the values for collection limits + await setCollectionLimitsExpectSuccess(alice, collectionId, { + accountTokenOwnershipLimit: 99_999, + sponsoredDataSize: 1024, + tokenLimit: 123, + transfersEnabled: false, + }); + + const limitsOpt = await api.rpc.unique.effectiveCollectionLimits(collectionId); + expect(limitsOpt.isNone).to.be.false; + const limits = limitsOpt.unwrap(); + + expect(limits.accountTokenOwnershipLimit.toHuman()).to.be.eq('99,999'); + expect(limits.sponsoredDataSize.toHuman()).to.be.eq('1,024'); + expect(limits.sponsoredDataRateLimit.toHuman()).to.be.eq('SponsoringDisabled'); + expect(limits.tokenLimit.toHuman()).to.be.eq('123'); + expect(limits.sponsorTransferTimeout.toHuman()).to.be.eq('5'); + expect(limits.sponsorApproveTimeout.toHuman()).to.be.eq('5'); + expect(limits.ownerCanTransfer.toHuman()).to.be.true; + expect(limits.ownerCanDestroy.toHuman()).to.be.true; + expect(limits.transfersEnabled.toHuman()).to.be.false; + } + }); + }); }); + + -- gitstuff