12345678#![cfg_attr(not(feature = "std"), no_std)]910#![recursion_limit = "256"]111213#[cfg(feature = "std")]14include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs"));1516use pallet_grandpa::fg_primitives;17use pallet_grandpa::{AuthorityId as GrandpaId, AuthorityList as GrandpaAuthorityList};18use sp_api::impl_runtime_apis;19use sp_consensus_aura::sr25519::AuthorityId as AuraId;20use sp_core::{ crypto::KeyTypeId, crypto::Public, OpaqueMetadata };21use sp_runtime::{22 Permill, Perbill, Perquintill, Percent,23 24 ModuleId, FixedPointNumber,25 create_runtime_str, generic, impl_opaque_keys,26 traits::{27 Convert, ConvertInto, BlakeTwo256, Block as BlockT, IdentifyAccount, 28 IdentityLookup, NumberFor, Verify,29 },30 transaction_validity::{TransactionSource, TransactionValidity},31 ApplyExtrinsicResult, MultiSignature,32};33use sp_std::prelude::*;34#[cfg(feature = "std")]35use sp_version::NativeVersion;36use sp_version::RuntimeVersion;37pub use pallet_transaction_payment::{Multiplier, TargetedFeeAdjustment, CurrencyAdapter, FeeDetails, RuntimeDispatchInfo};3839pub use pallet_balances::Call as BalancesCall;40pub use pallet_contracts::Schedule as ContractsSchedule;41use pallet_contracts::WeightInfo;42pub use frame_support::{43 construct_runtime,44 dispatch::DispatchResult,45 parameter_types,46 traits::{47 Currency, ExistenceRequirement, Get, KeyOwnerProofSystem, OnUnbalanced, Randomness,48 LockIdentifier,49 },50 weights::{51 constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},52 DispatchInfo, GetDispatchInfo, Pays, PostDispatchInfo, Weight,53 WeightToFeePolynomial, WeightToFeeCoefficient, WeightToFeeCoefficients54 },55 StorageValue,56};5758use frame_system::{59 self as system,60 EnsureRoot, 61 limits::{BlockWeights, BlockLength}};62use sp_std::{marker::PhantomData};63use sp_arithmetic::{traits::{BaseArithmetic, Unsigned}};64use smallvec::{smallvec, SmallVec};6566pub use pallet_timestamp::Call as TimestampCall;6768mod chain_extension;69use crate::chain_extension::NFTExtension;70717273pub struct CurrencyToVoteHandler;7475impl CurrencyToVoteHandler {76 fn factor() -> Balance {77 (Balances::total_issuance() / u64::max_value() as Balance).max(1)78 }79}8081impl Convert<Balance, u64> for CurrencyToVoteHandler {82 fn convert(x: Balance) -> u64 {83 (x / Self::factor()) as u6484 }85}8687impl Convert<u128, Balance> for CurrencyToVoteHandler {88 fn convert(x: u128) -> Balance {89 x * Self::factor()90 }91}92939495extern crate pallet_nft;96pub use pallet_nft::*;979899pub type BlockNumber = u32;100101102pub type Signature = MultiSignature;103104105106pub type AccountId = <<Signature as Verify>::Signer as IdentifyAccount>::AccountId;107108109110pub type AccountIndex = u32;111112113pub type Balance = u128;114115116pub type Index = u32;117118119pub type Hash = sp_core::H256;120121122pub type DigestItem = generic::DigestItem<Hash>;123124mod nft_weights;125126127128129130pub mod opaque {131 use super::*;132133 pub use sp_runtime::OpaqueExtrinsic as UncheckedExtrinsic;134135 136 pub type Header = generic::Header<BlockNumber, BlakeTwo256>;137 138 pub type Block = generic::Block<Header, UncheckedExtrinsic>;139 140 pub type BlockId = generic::BlockId<Block>;141142 impl_opaque_keys! {143 pub struct SessionKeys {144 pub aura: Aura,145 pub grandpa: Grandpa,146 }147 }148}149150151pub const VERSION: RuntimeVersion = RuntimeVersion {152 spec_name: create_runtime_str!("nft"),153 impl_name: create_runtime_str!("nft"),154 authoring_version: 1,155 spec_version: 3,156 impl_version: 1,157 apis: RUNTIME_API_VERSIONS,158 transaction_version: 1,159};160161pub const MILLISECS_PER_BLOCK: u64 = 6000;162163pub const SLOT_DURATION: u64 = MILLISECS_PER_BLOCK;164165166pub const MINUTES: BlockNumber = 60_000 / (MILLISECS_PER_BLOCK as BlockNumber);167pub const HOURS: BlockNumber = MINUTES * 60;168pub const DAYS: BlockNumber = HOURS * 24;169170171#[cfg(feature = "std")]172pub fn native_version() -> NativeVersion {173 NativeVersion {174 runtime_version: VERSION,175 can_author_with: Default::default(),176 }177}178179180pub struct ValiudatorsOnly<Runtime: pallet_aura::Config>(PhantomData<Runtime>);181impl frame_support::traits::Contains<AccountId> for ValiudatorsOnly<Runtime> {182 fn contains(t: &AccountId) -> bool {183 let arr: [u8; 32] = *t.as_ref();184 let raw_key: Vec<u8> = Vec::from(arr);185186 match pallet_aura::Module::<Runtime>::authorities().iter().find(|auth| auth.to_raw_vec() == raw_key) {187 Some(_) => true,188 None => false,189 } 190 }191 fn sorted_members() -> Vec<AccountId> {192 let mut members: Vec<AccountId> = Vec::new();193 for auth in pallet_aura::Module::<Runtime>::authorities() {194 let mut arr: [u8; 32] = Default::default(); 195 let bor_arr = auth.clone().to_raw_vec();196 let slice = bor_arr.as_slice();197 arr.copy_from_slice(slice);198 members.push(AccountId::from(arr));199 }200 members 201 }202 fn count() -> usize {203 pallet_aura::Module::<Runtime>::authorities().len()204 }205}206207impl frame_support::traits::ContainsLengthBound for ValiudatorsOnly<Runtime> {208 fn min_len() -> usize {209 1210 }211 fn max_len() -> usize {212 100213 }214}215216type NegativeImbalance = <Balances as Currency<AccountId>>::NegativeImbalance;217218pub struct DealWithFees;219impl OnUnbalanced<NegativeImbalance> for DealWithFees {220 fn on_unbalanceds<B>(mut fees_then_tips: impl Iterator<Item=NegativeImbalance>) {221 if let Some(fees) = fees_then_tips.next() {222 223 let mut split = fees.ration(100, 0);224 if let Some(tips) = fees_then_tips.next() {225 226 tips.ration_merge_into(100, 0, &mut split);227 }228 Treasury::on_unbalanced(split.0);229 230 }231 }232}233234235236const AVERAGE_ON_INITIALIZE_RATIO: Perbill = Perbill::from_percent(10);237238239const NORMAL_DISPATCH_RATIO: Perbill = Perbill::from_percent(75);240241const MAXIMUM_BLOCK_WEIGHT: Weight = 2 * WEIGHT_PER_SECOND;242243parameter_types! {244 pub const BlockHashCount: BlockNumber = 2400;245 pub RuntimeBlockLength: BlockLength =246 BlockLength::max_with_normal_ratio(5 * 1024 * 1024, NORMAL_DISPATCH_RATIO);247 pub const AvailableBlockRatio: Perbill = Perbill::from_percent(75);248 pub const MaximumBlockLength: u32 = 5 * 1024 * 1024;249 pub RuntimeBlockWeights: BlockWeights = BlockWeights::builder()250 .base_block(BlockExecutionWeight::get())251 .for_class(DispatchClass::all(), |weights| {252 weights.base_extrinsic = ExtrinsicBaseWeight::get();253 })254 .for_class(DispatchClass::Normal, |weights| {255 weights.max_total = Some(NORMAL_DISPATCH_RATIO * MAXIMUM_BLOCK_WEIGHT);256 })257 .for_class(DispatchClass::Operational, |weights| {258 weights.max_total = Some(MAXIMUM_BLOCK_WEIGHT);259 260 261 weights.reserved = Some(262 MAXIMUM_BLOCK_WEIGHT - NORMAL_DISPATCH_RATIO * MAXIMUM_BLOCK_WEIGHT263 );264 })265 .avg_block_initialization(AVERAGE_ON_INITIALIZE_RATIO)266 .build_or_panic();267 pub const Version: RuntimeVersion = VERSION;268 pub const SS58Prefix: u8 = 42;269}270271impl system::Config for Runtime {272 273 type BaseCallFilter = ();274 275 type AccountId = AccountId;276 277 type Call = Call;278 279 type Lookup = IdentityLookup<AccountId>;280 281 type Index = Index;282 283 type BlockNumber = BlockNumber;284 285 type Hash = Hash;286 287 type Hashing = BlakeTwo256;288 289 type Header = generic::Header<BlockNumber, BlakeTwo256>;290 291 type Event = Event;292 293 type Origin = Origin;294 295 type BlockHashCount = BlockHashCount;296 297 type DbWeight = RocksDbWeight;298 299 300 type BlockWeights = RuntimeBlockWeights;301 302 type Version = Version;303 304 type PalletInfo = PalletInfo;305 306 type OnNewAccount = ();307 308 type OnKilledAccount = ();309 310 type AccountData = pallet_balances::AccountData<Balance>;311 312 type SystemWeightInfo = system::weights::SubstrateWeight<Runtime>;313 314 type BlockLength = RuntimeBlockLength;315 type SS58Prefix = SS58Prefix;316}317318impl pallet_aura::Config for Runtime {319 type AuthorityId = AuraId;320}321322impl pallet_grandpa::Config for Runtime {323 type Event = Event;324 type Call = Call;325326 type KeyOwnerProofSystem = ();327328 type KeyOwnerProof =329 <Self::KeyOwnerProofSystem as KeyOwnerProofSystem<(KeyTypeId, GrandpaId)>>::Proof;330331 type KeyOwnerIdentification = <Self::KeyOwnerProofSystem as KeyOwnerProofSystem<(332 KeyTypeId,333 GrandpaId,334 )>>::IdentificationTuple;335336 type HandleEquivocation = ();337338 type WeightInfo = ();339}340341parameter_types! {342 pub const MinimumPeriod: u64 = SLOT_DURATION / 2;343}344345impl pallet_timestamp::Config for Runtime {346 347 type Moment = u64;348 type OnTimestampSet = Aura;349 type MinimumPeriod = MinimumPeriod;350 type WeightInfo = ();351}352353parameter_types! {354 355 pub const ExistentialDeposit: u128 = 0;356 pub const MaxLocks: u32 = 50;357}358359impl pallet_balances::Config for Runtime {360 type MaxLocks = MaxLocks;361 362 type Balance = Balance;363 364 type Event = Event;365 type DustRemoval = Treasury;366 type ExistentialDeposit = ExistentialDeposit;367 type AccountStore = System;368 type WeightInfo = ();369}370371pub const MICROUNIQUE: Balance = 1_000_000_000;372pub const MILLIUNIQUE: Balance = 1_000 * MICROUNIQUE;373pub const CENTIUNIQUE: Balance = 10 * MILLIUNIQUE;374pub const UNIQUE: Balance = 100 * CENTIUNIQUE;375376pub const fn deposit(items: u32, bytes: u32) -> Balance {377 items as Balance * 15 * CENTIUNIQUE + (bytes as Balance) * 6 * CENTIUNIQUE378}379380parameter_types! {381 pub const TombstoneDeposit: Balance = deposit(382 0,383 sp_std::mem::size_of::<pallet_contracts::ContractInfo<Runtime>>() as u32384 );385 pub const DepositPerContract: Balance = TombstoneDeposit::get();386 pub const DepositPerStorageByte: Balance = deposit(0, 1);387 pub const DepositPerStorageItem: Balance = deposit(1, 0);388 pub RentFraction: Perbill = Perbill::from_rational_approximation(1u32, 30 * DAYS);389 pub const SurchargeReward: Balance = 150 * MILLIUNIQUE;390 pub const SignedClaimHandicap: u32 = 2;391 pub const MaxDepth: u32 = 32;392 pub const MaxValueSize: u32 = 16 * 1024;393 394 pub DeletionWeightLimit: Weight = AVERAGE_ON_INITIALIZE_RATIO *395 RuntimeBlockWeights::get().max_block;396 397 398 pub DeletionQueueDepth: u32 = ((DeletionWeightLimit::get() / (399 <Runtime as pallet_contracts::Config>::WeightInfo::on_initialize_per_queue_item(1) -400 <Runtime as pallet_contracts::Config>::WeightInfo::on_initialize_per_queue_item(0)401 )) / 5) as u32;402}403404405impl pallet_contracts::Config for Runtime {406 type Time = Timestamp;407 type Randomness = RandomnessCollectiveFlip;408 type Currency = Balances;409 type Event = Event;410 type RentPayment = ();411 type SignedClaimHandicap = SignedClaimHandicap;412 type TombstoneDeposit = TombstoneDeposit;413 type DepositPerContract = DepositPerContract;414 type DepositPerStorageByte = DepositPerStorageByte;415 type DepositPerStorageItem = DepositPerStorageItem;416 type RentFraction = RentFraction;417 type SurchargeReward = SurchargeReward;418 type MaxDepth = MaxDepth;419 type MaxValueSize = MaxValueSize;420 type WeightPrice = pallet_transaction_payment::Module<Self>;421 type WeightInfo = pallet_contracts::weights::SubstrateWeight<Self>;422 type ChainExtension = NFTExtension;423 type DeletionQueueDepth = DeletionQueueDepth;424 type DeletionWeightLimit = DeletionWeightLimit;425}426427parameter_types! {428 pub const TransactionByteFee: Balance = 501 * MICROUNIQUE; 429}430431432pub struct LinearFee<T>(sp_std::marker::PhantomData<T>);433434impl<T> WeightToFeePolynomial for LinearFee<T> where435 T: BaseArithmetic + From<u32> + Copy + Unsigned436{437 type Balance = T;438439 fn polynomial() -> WeightToFeeCoefficients<Self::Balance> {440 smallvec!(WeightToFeeCoefficient {441 coeff_integer: 146_700u32.into(), 442 coeff_frac: Perbill::zero(),443 negative: false,444 degree: 1,445 })446 }447}448449impl pallet_transaction_payment::Config for Runtime {450 type OnChargeTransaction = CurrencyAdapter<Balances, DealWithFees>;451 type TransactionByteFee = TransactionByteFee;452 type WeightToFee = LinearFee<Balance>;453 type FeeMultiplierUpdate = ();454}455456parameter_types! {457 pub const ProposalBond: Permill = Permill::from_percent(5);458 pub const ProposalBondMinimum: Balance = 1 * UNIQUE;459 pub const SpendPeriod: BlockNumber = 5 * MINUTES;460 pub const Burn: Permill = Permill::from_percent(0);461 pub const TipCountdown: BlockNumber = 1 * DAYS;462 pub const TipFindersFee: Percent = Percent::from_percent(20);463 pub const TipReportDepositBase: Balance = 1 * UNIQUE;464 pub const DataDepositPerByte: Balance = 1 * CENTIUNIQUE;465 pub const BountyDepositBase: Balance = 1 * UNIQUE;466 pub const BountyDepositPayoutDelay: BlockNumber = 1 * DAYS;467 pub const TreasuryModuleId: ModuleId = ModuleId(*b"py/trsry");468 pub const BountyUpdatePeriod: BlockNumber = 14 * DAYS;469 pub const MaximumReasonLength: u32 = 16384;470 pub const BountyCuratorDeposit: Permill = Permill::from_percent(50);471 pub const BountyValueMinimum: Balance = 5 * UNIQUE;472}473474impl pallet_treasury::Config for Runtime {475 type ModuleId = TreasuryModuleId;476 type Currency = Balances;477 type ApproveOrigin = EnsureRoot<AccountId>;478 type RejectOrigin = EnsureRoot<AccountId>;479 type Event = Event;480 type OnSlash = ();481 type ProposalBond = ProposalBond;482 type ProposalBondMinimum = ProposalBondMinimum;483 type SpendPeriod = SpendPeriod;484 type Burn = Burn;485 type BurnDestination = ();486 type SpendFunds = ();487 type WeightInfo = pallet_treasury::weights::SubstrateWeight<Runtime>;488}489490impl pallet_sudo::Config for Runtime {491 type Event = Event;492 type Call = Call;493}494495parameter_types! {496 pub const MinVestedTransfer: Balance = 10 * UNIQUE;497}498499impl pallet_vesting::Config for Runtime {500 type Event = Event;501 type Currency = Balances;502 type BlockNumberToBalance = ConvertInto;503 type MinVestedTransfer = MinVestedTransfer;504 type WeightInfo = ();505}506507508impl pallet_nft::Config for Runtime {509 type Event = Event;510 type WeightInfo = nft_weights::WeightInfo;511}512513construct_runtime!(514 pub enum Runtime where515 Block = Block,516 NodeBlock = opaque::Block,517 UncheckedExtrinsic = UncheckedExtrinsic518 {519 System: system::{Module, Call, Config, Storage, Event<T>},520 RandomnessCollectiveFlip: pallet_randomness_collective_flip::{Module, Call, Storage},521 Contracts: pallet_contracts::{Module, Call, Config<T>, Storage, Event<T>},522 Timestamp: pallet_timestamp::{Module, Call, Storage, Inherent},523 Aura: pallet_aura::{Module, Config<T>, Inherent},524 Grandpa: pallet_grandpa::{Module, Call, Storage, Config, Event},525 Balances: pallet_balances::{Module, Call, Storage, Config<T>, Event<T>},526 TransactionPayment: pallet_transaction_payment::{Module, Storage},527 Sudo: pallet_sudo::{Module, Call, Config<T>, Storage, Event<T>},528 Nft: pallet_nft::{Module, Call, Config<T>, Storage, Event<T>},529 Treasury: pallet_treasury::{Module, Call, Storage, Config, Event<T>},530 Vesting: pallet_vesting::{Module, Call, Config<T>, Storage, Event<T>},531 }532);533534535pub type Address = AccountId;536537pub type Header = generic::Header<BlockNumber, BlakeTwo256>;538539pub type Block = generic::Block<Header, UncheckedExtrinsic>;540541pub type SignedBlock = generic::SignedBlock<Block>;542543pub type BlockId = generic::BlockId<Block>;544545pub type SignedExtra = (546 system::CheckSpecVersion<Runtime>,547 system::CheckTxVersion<Runtime>,548 system::CheckGenesis<Runtime>,549 system::CheckEra<Runtime>,550 system::CheckNonce<Runtime>,551 system::CheckWeight<Runtime>,552 pallet_nft::ChargeTransactionPayment<Runtime>,553);554555pub type UncheckedExtrinsic = generic::UncheckedExtrinsic<Address, Call, Signature, SignedExtra>;556557pub type CheckedExtrinsic = generic::CheckedExtrinsic<AccountId, Call, SignedExtra>;558559pub type Executive =560 frame_executive::Executive<Runtime, Block, system::ChainContext<Runtime>, Runtime, AllModules>;561562impl_runtime_apis! {563564 impl pallet_contracts_rpc_runtime_api::ContractsApi<Block, AccountId, Balance, BlockNumber>565 for Runtime566 {567 fn call(568 origin: AccountId,569 dest: AccountId,570 value: Balance,571 gas_limit: u64,572 input_data: Vec<u8>,573 ) -> pallet_contracts_primitives::ContractExecResult {574 Contracts::bare_call(origin, dest, value, gas_limit, input_data)575 }576577 fn get_storage(578 address: AccountId,579 key: [u8; 32],580 ) -> pallet_contracts_primitives::GetStorageResult {581 Contracts::get_storage(address, key)582 }583584 fn rent_projection(585 address: AccountId,586 ) -> pallet_contracts_primitives::RentProjectionResult<BlockNumber> {587 Contracts::rent_projection(address)588 }589 }590591 impl sp_api::Core<Block> for Runtime {592 fn version() -> RuntimeVersion {593 VERSION594 }595596 fn execute_block(block: Block) {597 Executive::execute_block(block)598 }599600 fn initialize_block(header: &<Block as BlockT>::Header) {601 Executive::initialize_block(header)602 }603 }604605 impl sp_api::Metadata<Block> for Runtime {606 fn metadata() -> OpaqueMetadata {607 Runtime::metadata().into()608 }609 }610611 impl sp_block_builder::BlockBuilder<Block> for Runtime {612 fn apply_extrinsic(extrinsic: <Block as BlockT>::Extrinsic) -> ApplyExtrinsicResult {613 Executive::apply_extrinsic(extrinsic)614 }615616 fn finalize_block() -> <Block as BlockT>::Header {617 Executive::finalize_block()618 }619620 fn inherent_extrinsics(data: sp_inherents::InherentData) -> Vec<<Block as BlockT>::Extrinsic> {621 data.create_extrinsics()622 }623624 fn check_inherents(625 block: Block,626 data: sp_inherents::InherentData,627 ) -> sp_inherents::CheckInherentsResult {628 data.check_extrinsics(&block)629 }630631 fn random_seed() -> <Block as BlockT>::Hash {632 RandomnessCollectiveFlip::random_seed()633 }634 }635636 impl sp_transaction_pool::runtime_api::TaggedTransactionQueue<Block> for Runtime {637 fn validate_transaction(638 source: TransactionSource,639 tx: <Block as BlockT>::Extrinsic,640 ) -> TransactionValidity {641 Executive::validate_transaction(source, tx)642 }643 }644645 impl sp_offchain::OffchainWorkerApi<Block> for Runtime {646 fn offchain_worker(header: &<Block as BlockT>::Header) {647 Executive::offchain_worker(header)648 }649 }650651 impl sp_consensus_aura::AuraApi<Block, AuraId> for Runtime {652 fn slot_duration() -> u64 {653 Aura::slot_duration()654 }655656 fn authorities() -> Vec<AuraId> {657 Aura::authorities()658 }659 }660661 impl sp_session::SessionKeys<Block> for Runtime {662 fn generate_session_keys(seed: Option<Vec<u8>>) -> Vec<u8> {663 opaque::SessionKeys::generate(seed)664 }665666 fn decode_session_keys(667 encoded: Vec<u8>,668 ) -> Option<Vec<(Vec<u8>, KeyTypeId)>> {669 opaque::SessionKeys::decode_into_raw_public_keys(&encoded)670 }671 }672673 impl fg_primitives::GrandpaApi<Block> for Runtime {674 fn grandpa_authorities() -> GrandpaAuthorityList {675 Grandpa::grandpa_authorities()676 }677678 fn submit_report_equivocation_unsigned_extrinsic(679 _equivocation_proof: fg_primitives::EquivocationProof<680 <Block as BlockT>::Hash,681 NumberFor<Block>,682 >,683 _key_owner_proof: fg_primitives::OpaqueKeyOwnershipProof,684 ) -> Option<()> {685 None686 }687688 fn generate_key_ownership_proof(689 _set_id: fg_primitives::SetId,690 _authority_id: GrandpaId,691 ) -> Option<fg_primitives::OpaqueKeyOwnershipProof> {692 693 694 695 None696 }697 }698 699 impl frame_system_rpc_runtime_api::AccountNonceApi<Block, AccountId, Index> for Runtime {700 fn account_nonce(account: AccountId) -> Index {701 System::account_nonce(account)702 }703 }704705 impl pallet_transaction_payment_rpc_runtime_api::TransactionPaymentApi<Block, Balance> for Runtime {706 fn query_info(uxt: <Block as BlockT>::Extrinsic, len: u32) -> RuntimeDispatchInfo<Balance> {707 TransactionPayment::query_info(uxt, len)708 }709 fn query_fee_details(uxt: <Block as BlockT>::Extrinsic, len: u32) -> FeeDetails<Balance> {710 TransactionPayment::query_fee_details(uxt, len)711 }712 }713714 #[cfg(feature = "runtime-benchmarks")]715 impl frame_benchmarking::Benchmark<Block> for Runtime {716 fn dispatch_benchmark(717 config: frame_benchmarking::BenchmarkConfig718 ) -> Result<Vec<frame_benchmarking::BenchmarkBatch>, sp_runtime::RuntimeString> {719 use frame_benchmarking::{Benchmarking, BenchmarkBatch, add_benchmark, TrackedStorageKey};720721 let whitelist: Vec<TrackedStorageKey> = vec![722 723 hex_literal::hex!("d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d").to_vec().into(),724 725 726 727 728 729 730 731 732 ];733734 let mut batches = Vec::<BenchmarkBatch>::new();735 let params = (&config, &whitelist);736737 add_benchmark!(params, batches, pallet_nft, Nft);738739 if batches.is_empty() { return Err("Benchmark not found for this pallet.".into()) }740 Ok(batches)741 }742 }743}