From 17e188aac633cd3ee4cdb4fb46d7478e91ce0e08 Mon Sep 17 00:00:00 2001 From: Yaroslav Bolyukin Date: Mon, 19 Dec 2022 15:44:38 +0000 Subject: [PATCH] Merge pull request #577 from UniqueNetwork/fix/fee-multiplier-update --- --- a/pallets/configuration/src/lib.rs +++ b/pallets/configuration/src/lib.rs @@ -25,12 +25,14 @@ }; use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; use scale_info::TypeInfo; -use sp_arithmetic::traits::{BaseArithmetic, Unsigned}; +use sp_arithmetic::{ + per_things::{Perbill, PerThing}, + traits::{BaseArithmetic, Unsigned}, +}; use smallvec::smallvec; pub use pallet::*; use sp_core::U256; -use sp_runtime::Perbill; #[pallet] mod pallet { @@ -46,8 +48,7 @@ #[pallet::config] pub trait Config: frame_system::Config { #[pallet::constant] - type DefaultWeightToFeeCoefficient: Get; - + type DefaultWeightToFeeCoefficient: Get; #[pallet::constant] type DefaultMinGasPrice: Get; @@ -66,7 +67,7 @@ #[pallet::storage] pub type WeightToFeeCoefficientOverride = StorageValue< - Value = u32, + Value = u64, QueryKind = ValueQuery, OnEmpty = T::DefaultWeightToFeeCoefficient, >; @@ -90,7 +91,7 @@ #[pallet::weight(T::DbWeight::get().writes(1))] pub fn set_weight_to_fee_coefficient_override( origin: OriginFor, - coeff: Option, + coeff: Option, ) -> DispatchResult { ensure_root(origin)?; if let Some(coeff) = coeff { @@ -156,14 +157,17 @@ impl WeightToFeePolynomial for WeightToFee where T: Config, - B: BaseArithmetic + From + Copy + Unsigned, + B: BaseArithmetic + From + From + Copy + Unsigned, { type Balance = B; fn polynomial() -> WeightToFeeCoefficients { smallvec!(WeightToFeeCoefficient { - coeff_integer: >::get().into(), - coeff_frac: Perbill::zero(), + coeff_integer: (>::get() / Perbill::ACCURACY as u64) + .into(), + coeff_frac: Perbill::from_parts( + (>::get() % Perbill::ACCURACY as u64) as u32 + ), negative: false, degree: 1, }) --- a/primitives/common/src/constants.rs +++ b/primitives/common/src/constants.rs @@ -43,10 +43,10 @@ pub const UNIQUE: Balance = 100 * CENTIUNIQUE; // Targeting 0.1 UNQ per transfer -pub const WEIGHT_TO_FEE_COEFF: u32 = /**/175_199_920/**/; +pub const WEIGHT_TO_FEE_COEFF: u64 = /**/77_083_524_944_487_510/**/; // Targeting 0.15 UNQ per transfer via ETH -pub const MIN_GAS_PRICE: u64 = /**/1_014_919_410_810/**/; +pub const MIN_GAS_PRICE: u64 = /**/1_014_919_313_914/**/; /// We assume that ~10% of the block weight is consumed by `on_initalize` handlers. /// This is used to limit the maximal weight of a single extrinsic. @@ -60,5 +60,5 @@ .set_proof_size(MAX_POV_SIZE as u64); parameter_types! { - pub const TransactionByteFee: Balance = 501 * MICROUNIQUE; + pub const TransactionByteFee: Balance = 501 * MICROUNIQUE / 2; } --- a/runtime/common/config/pallets/mod.rs +++ b/runtime/common/config/pallets/mod.rs @@ -104,7 +104,7 @@ pub const DayRelayBlocks: BlockNumber = RELAY_DAYS; } impl pallet_configuration::Config for Runtime { - type DefaultWeightToFeeCoefficient = ConstU32<{ up_common::constants::WEIGHT_TO_FEE_COEFF }>; + type DefaultWeightToFeeCoefficient = ConstU64<{ up_common::constants::WEIGHT_TO_FEE_COEFF }>; type DefaultMinGasPrice = ConstU64<{ up_common::constants::MIN_GAS_PRICE }>; type MaxXcmAllowedLocations = ConstU32<16>; type AppPromotionDailyRate = AppPromotionDailyRate; --- a/runtime/common/config/substrate.rs +++ b/runtime/common/config/substrate.rs @@ -28,10 +28,12 @@ traits::{BlakeTwo256, AccountIdLookup}, Perbill, Permill, Percent, }; +use sp_arithmetic::traits::One; use frame_system::{ limits::{BlockLength, BlockWeights}, EnsureRoot, }; +use pallet_transaction_payment::{Multiplier, ConstFeeMultiplier}; use crate::{ runtime_common::DealWithFees, Runtime, RuntimeEvent, RuntimeCall, RuntimeOrigin, PalletInfo, System, Balances, Treasury, SS58Prefix, Version, @@ -152,6 +154,8 @@ /// This value increases the priority of `Operational` transactions by adding /// a "virtual tip" that's equal to the `OperationalFeeMultiplier * final_fee`. pub const OperationalFeeMultiplier: u8 = 5; + + pub FeeMultiplier: Multiplier = Multiplier::one(); } impl pallet_transaction_payment::Config for Runtime { @@ -160,7 +164,7 @@ type LengthToFee = ConstantMultiplier; type OperationalFeeMultiplier = OperationalFeeMultiplier; type WeightToFee = pallet_configuration::WeightToFee; - type FeeMultiplierUpdate = (); + type FeeMultiplierUpdate = ConstFeeMultiplier; } parameter_types! { --- a/tests/src/calibrate.ts +++ b/tests/src/calibrate.ts @@ -1,59 +1,186 @@ import {IKeyringPair} from '@polkadot/types/types'; - import {usingEthPlaygrounds, EthUniqueHelper} from './eth/util'; +class Fract { + static ZERO = new Fract(0n); + constructor(public readonly a: bigint, public readonly b: bigint = 1n) { + if (b === 0n) throw new Error('division by zero'); + if (b < 0n) throw new Error('missing normalization'); + } -function linearRegression(points: { x: bigint, y: bigint }[]) { - let sumxy = 0n; - let sumx = 0n; - let sumy = 0n; - let sumx2 = 0n; - const n = points.length; - for (let i = 0; i < n; i++) { - const p = points[i]; - sumxy += p.x * p.y; - sumx += p.x; - sumy += p.y; - sumx2 += p.x * p.x; + mul(other: Fract) { + return new Fract(this.a * other.a, this.b * other.b).optimize(); } - const nb = BigInt(n); + div(other: Fract) { + return this.mul(other.inv()); + } - const a = (nb * sumxy - sumx * sumy) / (nb * sumx2 - sumx * sumx); - const b = (sumy - a * sumx) / nb; + plus(other: Fract) { + if (this.b === other.b) { + return new Fract(this.a + other.a, this.b); + } + return new Fract(this.a * other.b + other.a * this.b, this.b * other.b).optimize(); + } - return {a, b}; -} + minus(other: Fract) { + return this.plus(other.neg()); + } -// JS has no builtin function to calculate sqrt of bigint -// https://stackoverflow.com/a/53684036/6190169 -function sqrt(value: bigint) { - if (value < 0n) { - throw 'square root of negative numbers is not supported'; + neg() { + return new Fract(-this.a, this.b); + } + inv() { + if (this.a < 0) { + return new Fract(-this.b, -this.a); + } else { + return new Fract(this.b, this.a); + } } - if (value < 2n) { - return value; + optimize() { + function gcd(x: bigint, y: bigint) { + if (x < 0n) + x = -x; + if (y < 0n) + y = -y; + while(y) { + const t = y; + y = x % y; + x = t; + } + return x; + } + const v = gcd(this.a, this.b); + return new Fract(this.a / v, this.b / v); + } + + toBigInt() { + return this.a / this.b; + } + toNumber() { + const v = this.optimize(); + return Number(v.a) / Number(v.b); + } + toString() { + const v = this.optimize(); + return `${v.a} / ${v.b}`; } - function newtonIteration(n: bigint, x0: bigint): bigint { - const x1 = ((n / x0) + x0) >> 1n; - if (x0 === x1 || x0 === (x1 - 1n)) { - return x0; + lt(other: Fract) { + return this.a * other.b < other.a * this.b; + } + eq(other: Fract) { + return this.a * other.b === other.a * this.b; + } + + sqrt() { + if (this.a < 0n) { + throw 'square root of negative numbers is not supported'; + } + + if (this.lt(new Fract(2n))) { + return this; + } + + function newtonIteration(n: Fract, x0: Fract): Fract { + const x1 = rpn(n, x0, '/', x0, '+', new Fract(2n), '/'); + if (x0.eq(x1) || x0.eq(x1.minus(new Fract(1n)))) { + return x0; + } + return newtonIteration(n, x1); } - return newtonIteration(n, x1); + + return newtonIteration(this, new Fract(1n)); } +} - return newtonIteration(value, 1n); +type Op = Fract | '+' | '-' | '*' | '/' | 'dup' | Op[]; +function rpn(...ops: (Op)[]) { + const stack: Fract[] = []; + for (const op of ops) { + if (op instanceof Fract) { + stack.push(op); + } else if (op === '+') { + if (stack.length < 2) + throw new Error('stack underflow'); + const b = stack.pop()!; + const a = stack.pop()!; + stack.push(a.plus(b)); + } else if (op === '*') { + if (stack.length < 2) + throw new Error('stack underflow'); + const b = stack.pop()!; + const a = stack.pop()!; + stack.push(a.mul(b)); + } else if (op === '-') { + if (stack.length < 2) + throw new Error('stack underflow'); + const b = stack.pop()!; + const a = stack.pop()!; + stack.push(a.minus(b)); + } else if (op === '/') { + if (stack.length < 2) + throw new Error('stack underflow'); + const b = stack.pop()!; + const a = stack.pop()!; + stack.push(a.div(b)); + } else if (op === 'dup') { + if (stack.length < 1) + throw new Error('stack underflow'); + const a = stack.pop()!; + stack.push(a); + stack.push(a); + } else if (Array.isArray(op)) { + stack.push(rpn(...op)); + } else { + throw new Error(`unknown operand: ${op}`); + } + } + if (stack.length != 1) + throw new Error('one element should be left on stack'); + return stack[0]!; +} + +function linearRegression(points: { x: Fract, y: Fract }[]) { + let sumxy = Fract.ZERO; + let sumx = Fract.ZERO; + let sumy = Fract.ZERO; + let sumx2 = Fract.ZERO; + const n = points.length; + for (let i = 0; i < n; i++) { + const p = points[i]; + sumxy = rpn(p.x, p.y, '*', sumxy, '+'); + sumx = sumx.plus(p.x); + sumy = sumy.plus(p.y); + sumx2 = rpn(p.x, p.x, '*', sumx2, '+'); + } + + const nb = new Fract(BigInt(n)); + + const a = rpn( + [nb, sumxy, '*', sumx, sumy, '*', '-'], + [nb, sumx2, '*', sumx, sumx, '*', '-'], + '/', + ); + const b = rpn( + [sumy, a, sumx, '*', '-'], + nb, + '/', + ); + + return {a, b}; } -function _error(points: { x: bigint, y: bigint }[], hypothesis: (a: bigint) => bigint) { - return sqrt(points.map(p => { +const hypothesisLinear = (a: Fract, b: Fract) => (x: Fract) => rpn(x, a, '*', b, '+'); + +function _error(points: { x: Fract, y: Fract }[], hypothesis: (a: Fract) => Fract) { + return points.map(p => { const v = hypothesis(p.x); const vv = p.y; - return (v - vv) ** 2n; - }).reduce((a, b) => a + b, 0n) / BigInt(points.length)); + return rpn(v, vv, '-', 'dup', '*'); + }).reduce((a, b) => a.plus(b), Fract.ZERO).sqrt().div(new Fract(BigInt(points.length))); } async function calibrateWeightToFee(helper: EthUniqueHelper, privateKey: (account: string) => Promise) { @@ -68,15 +195,15 @@ await token.transfer(alice, {Substrate: bob.address}); const aliceBalanceAfter = await helper.balance.getSubstrate(alice.address); - console.log(`Original price: ${Number(aliceBalanceBefore - aliceBalanceAfter) / Number(helper.balance.getOneTokenNominal())} UNQ`); + console.log(`\t[NFT transfer] Original price: ${Number(aliceBalanceBefore - aliceBalanceAfter) / Number(helper.balance.getOneTokenNominal())} UNQ`); } const api = helper.getApi(); - const defaultCoeff = (api.consts.configuration.defaultWeightToFeeCoefficient as any).toBigInt(); + const base = (await api.query.configuration.weightToFeeCoefficientOverride() as any).toBigInt(); for (let i = -5; i < 5; i++) { - await helper.signTransaction(alice, api.tx.sudo.sudo(api.tx.configuration.setWeightToFeeCoefficientOverride(defaultCoeff + defaultCoeff / 1000n * BigInt(i)))); + await helper.signTransaction(alice, api.tx.sudo.sudo(api.tx.configuration.setWeightToFeeCoefficientOverride(base + base / 1000n * BigInt(i)))); - const coefficient = (await api.query.configuration.weightToFeeCoefficientOverride() as any).toBigInt(); + const coefficient = new Fract((await api.query.configuration.weightToFeeCoefficientOverride() as any).toBigInt()); const collection = await helper.nft.mintCollection(alice, {name: 'New', description: 'New collection', tokenPrefix: 'NEW'}); const token = await collection.mintToken(alice, {Substrate: alice.address}); @@ -84,16 +211,18 @@ await token.transfer(alice, {Substrate: bob.address}); const aliceBalanceAfter = await helper.balance.getSubstrate(alice.address); - const transferPrice = aliceBalanceBefore - aliceBalanceAfter; + const transferPrice = new Fract(aliceBalanceBefore - aliceBalanceAfter); dataPoints.push({x: transferPrice, y: coefficient}); } const {a, b} = linearRegression(dataPoints); - // console.log(`Error: ${error(dataPoints, x => a*x+b)}`); + const hyp = hypothesisLinear(a, b); + // console.log(`\t[NFT transfer] Error: ${_error(dataPoints, hyp).toNumber()}`); - const perfectValue = a * helper.balance.getOneTokenNominal() / 10n + b; - await helper.signTransaction(alice, api.tx.sudo.sudo(api.tx.configuration.setWeightToFeeCoefficientOverride(perfectValue.toString()))); + // 0.1 UNQ + const perfectValue = hyp(rpn(new Fract(helper.balance.getOneTokenNominal()), new Fract(1n, 10n), '*')); + await helper.signTransaction(alice, api.tx.sudo.sudo(api.tx.configuration.setWeightToFeeCoefficientOverride(perfectValue.toBigInt()))); { const collection = await helper.nft.mintCollection(alice, {name: 'New', description: 'New collection', tokenPrefix: 'NEW'}); @@ -102,7 +231,7 @@ await token.transfer(alice, {Substrate: bob.address}); const aliceBalanceAfter = await helper.balance.getSubstrate(alice.address); - console.log(`Calibrated price: ${Number(aliceBalanceBefore - aliceBalanceAfter) / Number(helper.balance.getOneTokenNominal())} UNQ`); + console.log(`\t[NFT transfer] Calibrated price: ${Number(aliceBalanceBefore - aliceBalanceAfter) / Number(helper.balance.getOneTokenNominal())} UNQ`); } } @@ -121,35 +250,37 @@ const cost = await helper.eth.calculateFee({Ethereum: caller}, () => contract.methods.transfer(receiver, token.tokenId).send({from: caller, gas: helper.eth.DEFAULT_GAS})); - console.log(`Original price: ${Number(cost) / Number(helper.balance.getOneTokenNominal())} UNQ`); + console.log(`\t[ETH NFT transfer] Original price: ${Number(cost) / Number(helper.balance.getOneTokenNominal())} UNQ`); } const api = helper.getApi(); - const defaultCoeff = (api.consts.configuration.defaultMinGasPrice as any).toBigInt(); + // const defaultCoeff = (api.consts.configuration.defaultMinGasPrice as any).toBigInt(); + const base = (await api.query.configuration.minGasPriceOverride() as any).toBigInt(); for (let i = -8; i < 8; i++) { - const gasPrice = defaultCoeff + defaultCoeff / 100000n * BigInt(i); + const gasPrice = base + base / 100000n * BigInt(i); const gasPriceStr = '0x' + gasPrice.toString(16); await helper.signTransaction(alice, api.tx.sudo.sudo(api.tx.configuration.setMinGasPriceOverride(gasPrice))); - const coefficient = (await api.query.configuration.minGasPriceOverride() as any).toBigInt(); + const coefficient = new Fract((await api.query.configuration.minGasPriceOverride() as any).toBigInt()); const collection = await helper.nft.mintCollection(alice, {name: 'New', description: 'New collection', tokenPrefix: 'NEW'}); const token = await collection.mintToken(alice, {Ethereum: caller}); const address = helper.ethAddress.fromCollectionId(collection.collectionId); const contract = helper.ethNativeContract.collection(address, 'nft', caller); - const transferPrice = await helper.eth.calculateFee({Ethereum: caller}, () => contract.methods.transfer(receiver, token.tokenId).send({from: caller, gasPrice: gasPriceStr, gas: helper.eth.DEFAULT_GAS})); + const transferPrice = new Fract(await helper.eth.calculateFee({Ethereum: caller}, () => contract.methods.transfer(receiver, token.tokenId).send({from: caller, gasPrice: gasPriceStr, gas: helper.eth.DEFAULT_GAS}))); dataPoints.push({x: transferPrice, y: coefficient}); } const {a, b} = linearRegression(dataPoints); - // console.log(`Error: ${error(dataPoints, x => a*x+b)}`); + const hyp = hypothesisLinear(a, b); + // console.log(`\t[ETH NFT transfer] Error: ${_error(dataPoints, hyp).toNumber()}`); - // * 0.15 = * 10000 / 66666 - const perfectValue = a * helper.balance.getOneTokenNominal() * 1000000n / 6666666n + b; - await helper.signTransaction(alice, api.tx.sudo.sudo(api.tx.configuration.setMinGasPriceOverride(perfectValue.toString()))); + // 0.15 UNQ + const perfectValue = hyp(rpn(new Fract(helper.balance.getOneTokenNominal()), new Fract(15n, 100n), '*')); + await helper.signTransaction(alice, api.tx.sudo.sudo(api.tx.configuration.setMinGasPriceOverride(perfectValue.toBigInt()))); { const collection = await helper.nft.mintCollection(alice, {name: 'New', description: 'New collection', tokenPrefix: 'NEW'}); @@ -160,18 +291,26 @@ const cost = await helper.eth.calculateFee({Ethereum: caller}, () => contract.methods.transfer(receiver, token.tokenId).send({from: caller, gas: helper.eth.DEFAULT_GAS})); - console.log(`Calibrated price: ${Number(cost) / Number(helper.balance.getOneTokenNominal())} UNQ`); + console.log(`\t[ETH NFT transfer] Calibrated price: ${Number(cost) / Number(helper.balance.getOneTokenNominal())} UNQ`); } } (async () => { await usingEthPlaygrounds(async (helper: EthUniqueHelper, privateKey) => { - // Second run slightly reduces error sometimes, as price line is not actually straight, this is a curve + // Subsequent runs reduce error, as price line is not actually straight, this is a curve + + const iterations = 3; + + console.log('[Calibrate WeightToFee]'); + for (let i = 0; i < iterations; i++) { + await calibrateWeightToFee(helper, privateKey); + } - await calibrateWeightToFee(helper, privateKey); - await calibrateWeightToFee(helper, privateKey); + console.log(); - await calibrateMinGasPrice(helper, privateKey); - await calibrateMinGasPrice(helper, privateKey); + console.log('[Calibrate MinGasPrice]'); + for (let i = 0; i < iterations; i++) { + await calibrateMinGasPrice(helper, privateKey); + } }); })(); --- /dev/null +++ b/tests/src/eth/ethFeesAreCorrect.test.ts @@ -0,0 +1,66 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +import {IKeyringPair} from '@polkadot/types/types'; +import {itEth, usingEthPlaygrounds, expect} from './util'; + +describe('Eth fees are correct', () => { + let donor: IKeyringPair; + let minter: IKeyringPair; + let alice: IKeyringPair; + + before(async () => { + await usingEthPlaygrounds(async (helper, privateKey) => { + donor = await privateKey({filename: __filename}); + [minter, alice] = await helper.arrange.createAccounts([100n, 200n], donor); + }); + }); + + + itEth('web3 fees are the same as evm.call fees', async ({helper}) => { + const collection = await helper.nft.mintCollection(minter, {}); + + const owner = await helper.eth.createAccountWithBalance(donor); + const receiver = await helper.eth.createAccountWithBalance(donor); + const aliceEth = helper.address.substrateToEth(alice.address); + + const {tokenId: tokenA} = await collection.mintToken(minter, {Ethereum: owner}); + const {tokenId: tokenB} = await collection.mintToken(minter, {Ethereum: aliceEth}); + + const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId); + const contract = helper.ethNativeContract.collection(collectionAddress, 'nft', owner); + + const balanceBeforeWeb3Transfer = await helper.balance.getEthereum(owner); + await contract.methods.transfer(receiver, tokenA).send({from: owner}); + const balanceAfterWeb3Transfer = await helper.balance.getEthereum(owner); + const web3Diff = balanceBeforeWeb3Transfer - balanceAfterWeb3Transfer; + + const encodedCall = contract.methods.transfer(receiver, tokenB) + .encodeABI(); + + const balanceBeforeEvmCall = await helper.balance.getSubstrate(alice.address); + await helper.eth.sendEVM( + alice, + collectionAddress, + encodedCall, + '0', + ); + const balanceAfterEvmCall = await helper.balance.getSubstrate(alice.address); + const evmCallDiff = balanceBeforeEvmCall - balanceAfterEvmCall; + + expect(web3Diff).to.be.equal(evmCallDiff); + }); +}); --- a/tests/src/interfaces/augment-api-consts.ts +++ b/tests/src/interfaces/augment-api-consts.ts @@ -82,7 +82,7 @@ appPromotionDailyRate: Perbill & AugmentedConst; dayRelayBlocks: u32 & AugmentedConst; defaultMinGasPrice: u64 & AugmentedConst; - defaultWeightToFeeCoefficient: u32 & AugmentedConst; + defaultWeightToFeeCoefficient: u64 & AugmentedConst; maxXcmAllowedLocations: u32 & AugmentedConst; /** * Generic const --- a/tests/src/interfaces/augment-api-query.ts +++ b/tests/src/interfaces/augment-api-query.ts @@ -162,7 +162,7 @@ configuration: { appPromomotionConfigurationOverride: AugmentedQuery Observable, []> & QueryableStorageEntry; minGasPriceOverride: AugmentedQuery Observable, []> & QueryableStorageEntry; - weightToFeeCoefficientOverride: AugmentedQuery Observable, []> & QueryableStorageEntry; + weightToFeeCoefficientOverride: AugmentedQuery Observable, []> & QueryableStorageEntry; xcmAllowedLocationsOverride: AugmentedQuery Observable>>, []> & QueryableStorageEntry; /** * Generic query --- a/tests/src/interfaces/augment-api-rpc.ts +++ b/tests/src/interfaces/augment-api-rpc.ts @@ -16,7 +16,7 @@ import type { BlockHash } from '@polkadot/types/interfaces/chain'; import type { PrefixedStorageKey } from '@polkadot/types/interfaces/childstate'; import type { AuthorityId } from '@polkadot/types/interfaces/consensus'; -import type { CodeUploadRequest, CodeUploadResult, ContractCallRequest, ContractExecResult, ContractInstantiateResult, InstantiateRequest } from '@polkadot/types/interfaces/contracts'; +import type { CodeUploadRequest, CodeUploadResult, ContractCallRequest, ContractExecResult, ContractInstantiateResult, InstantiateRequestV1 } from '@polkadot/types/interfaces/contracts'; import type { BlockStats } from '@polkadot/types/interfaces/dev'; import type { CreatedBlock } from '@polkadot/types/interfaces/engine'; import type { EthAccount, EthCallRequest, EthFeeHistory, EthFilter, EthFilterChanges, EthLog, EthReceipt, EthRichBlock, EthSubKind, EthSubParams, EthSyncStatus, EthTransaction, EthTransactionRequest, EthWork } from '@polkadot/types/interfaces/eth'; @@ -24,7 +24,7 @@ import type { EncodedFinalityProofs, JustificationNotification, ReportedRoundStates } from '@polkadot/types/interfaces/grandpa'; import type { MmrLeafBatchProof, MmrLeafProof } from '@polkadot/types/interfaces/mmr'; import type { StorageKind } from '@polkadot/types/interfaces/offchain'; -import type { FeeDetails, RuntimeDispatchInfo } from '@polkadot/types/interfaces/payment'; +import type { FeeDetails, RuntimeDispatchInfoV1 } from '@polkadot/types/interfaces/payment'; import type { RpcMethods } from '@polkadot/types/interfaces/rpc'; import type { AccountId, AccountId32, BlockNumber, H160, H256, H64, Hash, Header, Index, Justification, KeyValue, SignedBlock, StorageData } from '@polkadot/types/interfaces/runtime'; import type { MigrationStatusResult, ReadProof, RuntimeVersion, TraceBlockResponse } from '@polkadot/types/interfaces/state'; @@ -174,7 +174,7 @@ * @deprecated Use the runtime interface `api.call.contractsApi.instantiate` instead * Instantiate a new contract **/ - instantiate: AugmentedRpc<(request: InstantiateRequest | { origin?: any; value?: any; gasLimit?: any; storageDepositLimit?: any; code?: any; data?: any; salt?: any } | string | Uint8Array, at?: BlockHash | string | Uint8Array) => Observable>; + instantiate: AugmentedRpc<(request: InstantiateRequestV1 | { origin?: any; value?: any; gasLimit?: any; code?: any; data?: any; salt?: any } | string | Uint8Array, at?: BlockHash | string | Uint8Array) => Observable>; /** * @deprecated Not available in newer versions of the contracts interfaces * Returns the projected time a given contract will be able to sustain paying its rent @@ -426,13 +426,15 @@ }; payment: { /** + * @deprecated Use `api.call.transactionPaymentApi.queryFeeDetails` instead * Query the detailed fee of a given encoded extrinsic **/ queryFeeDetails: AugmentedRpc<(extrinsic: Bytes | string | Uint8Array, at?: BlockHash | string | Uint8Array) => Observable>; /** + * @deprecated Use `api.call.transactionPaymentApi.queryInfo` instead * Retrieves the fee information for an encoded extrinsic **/ - queryInfo: AugmentedRpc<(extrinsic: Bytes | string | Uint8Array, at?: BlockHash | string | Uint8Array) => Observable>; + queryInfo: AugmentedRpc<(extrinsic: Bytes | string | Uint8Array, at?: BlockHash | string | Uint8Array) => Observable>; }; rmrk: { /** --- a/tests/src/interfaces/augment-api-runtime.ts +++ b/tests/src/interfaces/augment-api-runtime.ts @@ -6,7 +6,7 @@ import '@polkadot/api-base/types/calls'; import type { ApiTypes, AugmentedCall, DecoratedCallBase } from '@polkadot/api-base/types'; -import type { Bytes, Null, Option, Result, U256, Vec, bool, u256, u64 } from '@polkadot/types-codec'; +import type { Bytes, Null, Option, Result, U256, Vec, bool, u256, u32, u64 } from '@polkadot/types-codec'; import type { AnyNumber, ITuple } from '@polkadot/types-codec/types'; import type { CheckInherentsResult, InherentData } from '@polkadot/types/interfaces/blockbuilder'; import type { BlockHash } from '@polkadot/types/interfaces/chain'; @@ -16,6 +16,7 @@ import type { EvmAccount, EvmCallInfo, EvmCreateInfo } from '@polkadot/types/interfaces/evm'; import type { Extrinsic } from '@polkadot/types/interfaces/extrinsics'; import type { OpaqueMetadata } from '@polkadot/types/interfaces/metadata'; +import type { FeeDetails, RuntimeDispatchInfo } from '@polkadot/types/interfaces/payment'; import type { AccountId, Block, H160, H256, Header, Index, KeyTypeId, Permill, SlotDuration } from '@polkadot/types/interfaces/runtime'; import type { RuntimeVersion } from '@polkadot/types/interfaces/state'; import type { ApplyExtrinsicResult, DispatchError } from '@polkadot/types/interfaces/system'; @@ -228,5 +229,20 @@ **/ [key: string]: DecoratedCallBase; }; + /** 0x37c8bb1350a9a2a8/2 */ + transactionPaymentApi: { + /** + * The transaction fee details + **/ + queryFeeDetails: AugmentedCall Observable>; + /** + * The transaction info + **/ + queryInfo: AugmentedCall Observable>; + /** + * Generic call + **/ + [key: string]: DecoratedCallBase; + }; } // AugmentedCalls } // declare module --- a/tests/src/interfaces/augment-api-tx.ts +++ b/tests/src/interfaces/augment-api-tx.ts @@ -217,7 +217,7 @@ configuration: { setAppPromotionConfigurationOverride: AugmentedSubmittable<(configuration: PalletConfigurationAppPromotionConfiguration | { recalculationInterval?: any; pendingInterval?: any; intervalIncome?: any; maxStakersPerCalculation?: any } | string | Uint8Array) => SubmittableExtrinsic, [PalletConfigurationAppPromotionConfiguration]>; setMinGasPriceOverride: AugmentedSubmittable<(coeff: Option | null | Uint8Array | u64 | AnyNumber) => SubmittableExtrinsic, [Option]>; - setWeightToFeeCoefficientOverride: AugmentedSubmittable<(coeff: Option | null | Uint8Array | u32 | AnyNumber) => SubmittableExtrinsic, [Option]>; + setWeightToFeeCoefficientOverride: AugmentedSubmittable<(coeff: Option | null | Uint8Array | u64 | AnyNumber) => SubmittableExtrinsic, [Option]>; setXcmAllowedLocations: AugmentedSubmittable<(locations: Option> | null | Uint8Array | Vec | (XcmV1MultiLocation | { parents?: any; interior?: any } | string | Uint8Array)[]) => SubmittableExtrinsic, [Option>]>; /** * Generic tx @@ -1432,6 +1432,23 @@ **/ destroyCollection: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [u32]>; /** + * Repairs a collection if the data was somehow corrupted. + * + * # Arguments + * + * * `collection_id`: ID of the collection to repair. + **/ + forceRepairCollection: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [u32]>; + /** + * Repairs a token if the data was somehow corrupted. + * + * # Arguments + * + * * `collection_id`: ID of the collection the item belongs to. + * * `item_id`: ID of the item. + **/ + forceRepairItem: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, itemId: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [u32, u32]>; + /** * Remove admin of a collection. * * An admin address can remove itself. List of admins may become empty, @@ -1474,15 +1491,6 @@ * * `address`: ID of the address to be removed from the allowlist. **/ removeFromAllowList: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, address: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array) => SubmittableExtrinsic, [u32, PalletEvmAccountBasicCrossAccountIdRepr]>; - /** - * Repairs a broken item - * - * # Arguments - * - * * `collection_id`: ID of the collection the item belongs to. - * * `item_id`: ID of the item. - **/ - repairItem: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, itemId: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [u32, u32]>; /** * Re-partition a refungible token, while owning all of its parts/pieces. * --- a/tests/src/interfaces/augment-types.ts +++ b/tests/src/interfaces/augment-types.ts @@ -24,7 +24,7 @@ import type { StatementKind } from '@polkadot/types/interfaces/claims'; import type { CollectiveOrigin, MemberCount, ProposalIndex, Votes, VotesTo230 } from '@polkadot/types/interfaces/collective'; import type { AuthorityId, RawVRFOutput } from '@polkadot/types/interfaces/consensus'; -import type { AliveContractInfo, CodeHash, CodeSource, CodeUploadRequest, CodeUploadResult, CodeUploadResultValue, ContractCallFlags, ContractCallRequest, ContractExecResult, ContractExecResultOk, ContractExecResultResult, ContractExecResultSuccessTo255, ContractExecResultSuccessTo260, ContractExecResultTo255, ContractExecResultTo260, ContractExecResultTo267, ContractInfo, ContractInstantiateResult, ContractInstantiateResultTo267, ContractInstantiateResultTo299, ContractReturnFlags, ContractStorageKey, DeletedContract, ExecReturnValue, Gas, HostFnWeights, HostFnWeightsTo264, InstantiateRequest, InstantiateRequestV1, InstantiateRequestV2, InstantiateReturnValue, InstantiateReturnValueOk, InstantiateReturnValueTo267, InstructionWeights, Limits, LimitsTo264, PrefabWasmModule, RentProjection, Schedule, ScheduleTo212, ScheduleTo258, ScheduleTo264, SeedOf, StorageDeposit, TombstoneContractInfo, TrieId } from '@polkadot/types/interfaces/contracts'; +import type { AliveContractInfo, CodeHash, CodeSource, CodeUploadRequest, CodeUploadResult, CodeUploadResultValue, ContractCallFlags, ContractCallRequest, ContractExecResult, ContractExecResultOk, ContractExecResultResult, ContractExecResultSuccessTo255, ContractExecResultSuccessTo260, ContractExecResultTo255, ContractExecResultTo260, ContractExecResultTo267, ContractExecResultU64, ContractInfo, ContractInstantiateResult, ContractInstantiateResultTo267, ContractInstantiateResultTo299, ContractInstantiateResultU64, ContractReturnFlags, ContractStorageKey, DeletedContract, ExecReturnValue, Gas, HostFnWeights, HostFnWeightsTo264, InstantiateRequest, InstantiateRequestV1, InstantiateRequestV2, InstantiateReturnValue, InstantiateReturnValueOk, InstantiateReturnValueTo267, InstructionWeights, Limits, LimitsTo264, PrefabWasmModule, RentProjection, Schedule, ScheduleTo212, ScheduleTo258, ScheduleTo264, SeedOf, StorageDeposit, TombstoneContractInfo, TrieId } from '@polkadot/types/interfaces/contracts'; import type { ContractConstructorSpecLatest, ContractConstructorSpecV0, ContractConstructorSpecV1, ContractConstructorSpecV2, ContractConstructorSpecV3, ContractContractSpecV0, ContractContractSpecV1, ContractContractSpecV2, ContractContractSpecV3, ContractContractSpecV4, ContractCryptoHasher, ContractDiscriminant, ContractDisplayName, ContractEventParamSpecLatest, ContractEventParamSpecV0, ContractEventParamSpecV2, ContractEventSpecLatest, ContractEventSpecV0, ContractEventSpecV1, ContractEventSpecV2, ContractLayoutArray, ContractLayoutCell, ContractLayoutEnum, ContractLayoutHash, ContractLayoutHashingStrategy, ContractLayoutKey, ContractLayoutStruct, ContractLayoutStructField, ContractMessageParamSpecLatest, ContractMessageParamSpecV0, ContractMessageParamSpecV2, ContractMessageSpecLatest, ContractMessageSpecV0, ContractMessageSpecV1, ContractMessageSpecV2, ContractMetadata, ContractMetadataLatest, ContractMetadataV0, ContractMetadataV1, ContractMetadataV2, ContractMetadataV3, ContractMetadataV4, ContractProject, ContractProjectContract, ContractProjectInfo, ContractProjectSource, ContractProjectV0, ContractSelector, ContractStorageLayout, ContractTypeSpec } from '@polkadot/types/interfaces/contractsAbi'; import type { FundIndex, FundInfo, LastContribution, TrieIndex } from '@polkadot/types/interfaces/crowdloan'; import type { CollationInfo, CollationInfoV1, ConfigData, MessageId, OverweightIndex, PageCounter, PageIndexData } from '@polkadot/types/interfaces/cumulus'; @@ -47,7 +47,7 @@ import type { StorageKind } from '@polkadot/types/interfaces/offchain'; import type { DeferredOffenceOf, Kind, OffenceDetails, Offender, OpaqueTimeSlot, ReportIdOf, Reporter } from '@polkadot/types/interfaces/offences'; import type { AbridgedCandidateReceipt, AbridgedHostConfiguration, AbridgedHrmpChannel, AssignmentId, AssignmentKind, AttestedCandidate, AuctionIndex, AuthorityDiscoveryId, AvailabilityBitfield, AvailabilityBitfieldRecord, BackedCandidate, Bidder, BufferedSessionChange, CandidateCommitments, CandidateDescriptor, CandidateEvent, CandidateHash, CandidateInfo, CandidatePendingAvailability, CandidateReceipt, CollatorId, CollatorSignature, CommittedCandidateReceipt, CoreAssignment, CoreIndex, CoreOccupied, CoreState, DisputeLocation, DisputeResult, DisputeState, DisputeStatement, DisputeStatementSet, DoubleVoteReport, DownwardMessage, ExplicitDisputeStatement, GlobalValidationData, GlobalValidationSchedule, GroupIndex, GroupRotationInfo, HeadData, HostConfiguration, HrmpChannel, HrmpChannelId, HrmpOpenChannelRequest, InboundDownwardMessage, InboundHrmpMessage, InboundHrmpMessages, IncomingParachain, IncomingParachainDeploy, IncomingParachainFixed, InvalidDisputeStatementKind, LeasePeriod, LeasePeriodOf, LocalValidationData, MessageIngestionType, MessageQueueChain, MessagingStateSnapshot, MessagingStateSnapshotEgressEntry, MultiDisputeStatementSet, NewBidder, OccupiedCore, OccupiedCoreAssumption, OldV1SessionInfo, OutboundHrmpMessage, ParaGenesisArgs, ParaId, ParaInfo, ParaLifecycle, ParaPastCodeMeta, ParaScheduling, ParaValidatorIndex, ParachainDispatchOrigin, ParachainInherentData, ParachainProposal, ParachainsInherentData, ParathreadClaim, ParathreadClaimQueue, ParathreadEntry, PersistedValidationData, PvfCheckStatement, QueuedParathread, RegisteredParachainInfo, RelayBlockNumber, RelayChainBlockNumber, RelayChainHash, RelayHash, Remark, ReplacementTimes, Retriable, ScheduledCore, Scheduling, ScrapedOnChainVotes, ServiceQuality, SessionInfo, SessionInfoValidatorGroup, SignedAvailabilityBitfield, SignedAvailabilityBitfields, SigningContext, SlotRange, SlotRange10, Statement, SubId, SystemInherentData, TransientValidationData, UpgradeGoAhead, UpgradeRestriction, UpwardMessage, ValidDisputeStatementKind, ValidationCode, ValidationCodeHash, ValidationData, ValidationDataType, ValidationFunctionParams, ValidatorSignature, ValidityAttestation, VecInboundHrmpMessage, WinnersData, WinnersData10, WinnersDataTuple, WinnersDataTuple10, WinningData, WinningData10, WinningDataEntry } from '@polkadot/types/interfaces/parachains'; -import type { FeeDetails, InclusionFee, RuntimeDispatchInfo } from '@polkadot/types/interfaces/payment'; +import type { FeeDetails, InclusionFee, RuntimeDispatchInfo, RuntimeDispatchInfoV1, RuntimeDispatchInfoV2 } from '@polkadot/types/interfaces/payment'; import type { Approvals } from '@polkadot/types/interfaces/poll'; import type { ProxyAnnouncement, ProxyDefinition, ProxyType } from '@polkadot/types/interfaces/proxy'; import type { AccountStatus, AccountValidity } from '@polkadot/types/interfaces/purchase'; @@ -273,10 +273,12 @@ ContractExecResultTo255: ContractExecResultTo255; ContractExecResultTo260: ContractExecResultTo260; ContractExecResultTo267: ContractExecResultTo267; + ContractExecResultU64: ContractExecResultU64; ContractInfo: ContractInfo; ContractInstantiateResult: ContractInstantiateResult; ContractInstantiateResultTo267: ContractInstantiateResultTo267; ContractInstantiateResultTo299: ContractInstantiateResultTo299; + ContractInstantiateResultU64: ContractInstantiateResultU64; ContractLayoutArray: ContractLayoutArray; ContractLayoutCell: ContractLayoutCell; ContractLayoutEnum: ContractLayoutEnum; @@ -1057,6 +1059,8 @@ RpcMethods: RpcMethods; RuntimeDbWeight: RuntimeDbWeight; RuntimeDispatchInfo: RuntimeDispatchInfo; + RuntimeDispatchInfoV1: RuntimeDispatchInfoV1; + RuntimeDispatchInfoV2: RuntimeDispatchInfoV2; RuntimeVersion: RuntimeVersion; RuntimeVersionApi: RuntimeVersionApi; RuntimeVersionPartial: RuntimeVersionPartial; --- a/tests/src/interfaces/default/types.ts +++ b/tests/src/interfaces/default/types.ts @@ -1303,7 +1303,7 @@ export interface PalletConfigurationCall extends Enum { readonly isSetWeightToFeeCoefficientOverride: boolean; readonly asSetWeightToFeeCoefficientOverride: { - readonly coeff: Option; + readonly coeff: Option; } & Struct; readonly isSetMinGasPriceOverride: boolean; readonly asSetMinGasPriceOverride: { @@ -2319,12 +2319,16 @@ readonly operator: PalletEvmAccountBasicCrossAccountIdRepr; readonly approve: bool; } & Struct; - readonly isRepairItem: boolean; - readonly asRepairItem: { + readonly isForceRepairCollection: boolean; + readonly asForceRepairCollection: { + readonly collectionId: u32; + } & Struct; + readonly isForceRepairItem: boolean; + readonly asForceRepairItem: { readonly collectionId: u32; readonly itemId: u32; } & Struct; - readonly type: 'CreateCollection' | 'CreateCollectionEx' | 'DestroyCollection' | 'AddToAllowList' | 'RemoveFromAllowList' | 'ChangeCollectionOwner' | 'AddCollectionAdmin' | 'RemoveCollectionAdmin' | 'SetCollectionSponsor' | 'ConfirmSponsorship' | 'RemoveCollectionSponsor' | 'CreateItem' | 'CreateMultipleItems' | 'SetCollectionProperties' | 'DeleteCollectionProperties' | 'SetTokenProperties' | 'DeleteTokenProperties' | 'SetTokenPropertyPermissions' | 'CreateMultipleItemsEx' | 'SetTransfersEnabledFlag' | 'BurnItem' | 'BurnFrom' | 'Transfer' | 'Approve' | 'TransferFrom' | 'SetCollectionLimits' | 'SetCollectionPermissions' | 'Repartition' | 'SetAllowanceForAll' | 'RepairItem'; + readonly type: 'CreateCollection' | 'CreateCollectionEx' | 'DestroyCollection' | 'AddToAllowList' | 'RemoveFromAllowList' | 'ChangeCollectionOwner' | 'AddCollectionAdmin' | 'RemoveCollectionAdmin' | 'SetCollectionSponsor' | 'ConfirmSponsorship' | 'RemoveCollectionSponsor' | 'CreateItem' | 'CreateMultipleItems' | 'SetCollectionProperties' | 'DeleteCollectionProperties' | 'SetTokenProperties' | 'DeleteTokenProperties' | 'SetTokenPropertyPermissions' | 'CreateMultipleItemsEx' | 'SetTransfersEnabledFlag' | 'BurnItem' | 'BurnFrom' | 'Transfer' | 'Approve' | 'TransferFrom' | 'SetCollectionLimits' | 'SetCollectionPermissions' | 'Repartition' | 'SetAllowanceForAll' | 'ForceRepairCollection' | 'ForceRepairItem'; } /** @name PalletUniqueError */ --- a/tests/src/interfaces/lookup.ts +++ b/tests/src/interfaces/lookup.ts @@ -2282,8 +2282,11 @@ operator: 'PalletEvmAccountBasicCrossAccountIdRepr', approve: 'bool', }, - repair_item: { + force_repair_collection: { collectionId: 'u32', + }, + force_repair_item: { + collectionId: 'u32', itemId: 'u32' } } @@ -2452,7 +2455,7 @@ PalletConfigurationCall: { _enum: { set_weight_to_fee_coefficient_override: { - coeff: 'Option', + coeff: 'Option', }, set_min_gas_price_override: { coeff: 'Option', --- a/tests/src/interfaces/types-lookup.ts +++ b/tests/src/interfaces/types-lookup.ts @@ -2517,12 +2517,16 @@ readonly operator: PalletEvmAccountBasicCrossAccountIdRepr; readonly approve: bool; } & Struct; - readonly isRepairItem: boolean; - readonly asRepairItem: { + readonly isForceRepairCollection: boolean; + readonly asForceRepairCollection: { + readonly collectionId: u32; + } & Struct; + readonly isForceRepairItem: boolean; + readonly asForceRepairItem: { readonly collectionId: u32; readonly itemId: u32; } & Struct; - readonly type: 'CreateCollection' | 'CreateCollectionEx' | 'DestroyCollection' | 'AddToAllowList' | 'RemoveFromAllowList' | 'ChangeCollectionOwner' | 'AddCollectionAdmin' | 'RemoveCollectionAdmin' | 'SetCollectionSponsor' | 'ConfirmSponsorship' | 'RemoveCollectionSponsor' | 'CreateItem' | 'CreateMultipleItems' | 'SetCollectionProperties' | 'DeleteCollectionProperties' | 'SetTokenProperties' | 'DeleteTokenProperties' | 'SetTokenPropertyPermissions' | 'CreateMultipleItemsEx' | 'SetTransfersEnabledFlag' | 'BurnItem' | 'BurnFrom' | 'Transfer' | 'Approve' | 'TransferFrom' | 'SetCollectionLimits' | 'SetCollectionPermissions' | 'Repartition' | 'SetAllowanceForAll' | 'RepairItem'; + readonly type: 'CreateCollection' | 'CreateCollectionEx' | 'DestroyCollection' | 'AddToAllowList' | 'RemoveFromAllowList' | 'ChangeCollectionOwner' | 'AddCollectionAdmin' | 'RemoveCollectionAdmin' | 'SetCollectionSponsor' | 'ConfirmSponsorship' | 'RemoveCollectionSponsor' | 'CreateItem' | 'CreateMultipleItems' | 'SetCollectionProperties' | 'DeleteCollectionProperties' | 'SetTokenProperties' | 'DeleteTokenProperties' | 'SetTokenPropertyPermissions' | 'CreateMultipleItemsEx' | 'SetTransfersEnabledFlag' | 'BurnItem' | 'BurnFrom' | 'Transfer' | 'Approve' | 'TransferFrom' | 'SetCollectionLimits' | 'SetCollectionPermissions' | 'Repartition' | 'SetAllowanceForAll' | 'ForceRepairCollection' | 'ForceRepairItem'; } /** @name UpDataStructsCollectionMode (237) */ @@ -2675,7 +2679,7 @@ interface PalletConfigurationCall extends Enum { readonly isSetWeightToFeeCoefficientOverride: boolean; readonly asSetWeightToFeeCoefficientOverride: { - readonly coeff: Option; + readonly coeff: Option; } & Struct; readonly isSetMinGasPriceOverride: boolean; readonly asSetMinGasPriceOverride: { --- a/tests/src/nesting/collectionProperties.test.ts +++ b/tests/src/nesting/collectionProperties.test.ts @@ -209,7 +209,7 @@ before(async () => { await usingPlaygrounds(async (helper, privateKey) => { const donor = await privateKey({filename: __filename}); - [alice, bob] = await helper.arrange.createAccounts([100n, 10n], donor); + [alice, bob] = await helper.arrange.createAccounts([1000n, 100n], donor); }); }); -- gitstuff