git.delta.rocks / unique-network / refs/commits / da2d6006e0d9

difftreelog

source

tests/src/calibrate.ts7.3 KiBsourcehistory
1import {ApiPromise} from '@polkadot/api';2import {IKeyringPair} from '@polkadot/types/types';3import Web3 from 'web3';4import {collectionIdToAddress, createEthAccount, createEthAccountWithBalance, GAS_ARGS, recordEthFee, usingWeb3} from './eth/util/helpers';5import usingApi, {executeTransaction} from './substrate/substrate-api';6import {createCollectionExpectSuccess, createItemExpectSuccess, transferExpectSuccess, UNIQUE, waitNewBlocks} from './util/helpers';7import nonFungibleAbi from './eth/nonFungibleAbi.json';89function linearRegression(points: { x: bigint, y: bigint }[]) {10  let sumxy = 0n;11  let sumx = 0n;12  let sumy = 0n;13  let sumx2 = 0n;14  const n = points.length;15  for (let i = 0; i < n; i++) {16    const p = points[i];17    sumxy += p.x * p.y;18    sumx += p.x;19    sumy += p.y;20    sumx2 += p.x * p.x;21  }2223  const nb = BigInt(n);2425  const a = (nb * sumxy - sumx * sumy) / (nb * sumx2 - sumx * sumx);26  const b = (sumy - a * sumx) / nb;2728  return {a, b};29}3031// JS has no builtin function to calculate sqrt of bigint32// https://stackoverflow.com/a/53684036/619016933function sqrt(value: bigint) {34  if (value < 0n) {35    throw 'square root of negative numbers is not supported';36  }3738  if (value < 2n) {39    return value;40  }4142  function newtonIteration(n: bigint, x0: bigint): bigint {43    const x1 = ((n / x0) + x0) >> 1n;44    if (x0 === x1 || x0 === (x1 - 1n)) {45      return x0;46    }47    return newtonIteration(n, x1);48  }4950  return newtonIteration(value, 1n);51}5253function _error(points: { x: bigint, y: bigint }[], hypothesis: (a: bigint) => bigint) {54  return sqrt(points.map(p => {55    const v = hypothesis(p.x);56    const vv = p.y;5758    return (v - vv) ** 2n;59  }).reduce((a, b) => a + b, 0n) / BigInt(points.length));60}6162async function calibrateWeightToFee(api: ApiPromise, privateKey: (account: string) => IKeyringPair) {63  const alice = privateKey('//Alice');64  const bob = privateKey('//Bob');65  const dataPoints = [];6667  {68    const collectionId = await createCollectionExpectSuccess();69    const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT');70    const aliceBalanceBefore = (await api.query.system.account(alice.address)).data.free.toBigInt();71    await transferExpectSuccess(collectionId, tokenId, alice, bob, 1, 'NFT');72    const aliceBalanceAfter = (await api.query.system.account(alice.address)).data.free.toBigInt();7374    console.log(`Original price: ${Number(aliceBalanceBefore - aliceBalanceAfter) / Number(UNIQUE)} UNQ`);75  }7677  const defaultCoeff = (api.consts.configuration.defaultWeightToFeeCoefficient as any).toBigInt();78  for (let i = -5; i < 5; i++) {79    await executeTransaction(api, alice, api.tx.sudo.sudo(api.tx.configuration.setWeightToFeeCoefficientOverride(defaultCoeff + defaultCoeff / 1000n * BigInt(i))));8081    const coefficient = (await api.query.configuration.weightToFeeCoefficientOverride() as any).toBigInt();82    const collectionId = await createCollectionExpectSuccess();83    const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT');8485    const aliceBalanceBefore = (await api.query.system.account(alice.address)).data.free.toBigInt();86    await transferExpectSuccess(collectionId, tokenId, alice, bob, 1, 'NFT');87    const aliceBalanceAfter = (await api.query.system.account(alice.address)).data.free.toBigInt();8889    const transferPrice = aliceBalanceBefore - aliceBalanceAfter;9091    dataPoints.push({x: transferPrice, y: coefficient});92  }93  const {a, b} = linearRegression(dataPoints);9495  // console.log(`Error: ${error(dataPoints, x => a*x+b)}`);9697  const perfectValue = a * UNIQUE / 10n + b;98  await executeTransaction(api, alice, api.tx.sudo.sudo(api.tx.configuration.setWeightToFeeCoefficientOverride(perfectValue.toString())));99100  {101    const collectionId = await createCollectionExpectSuccess();102    const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT');103    const aliceBalanceBefore = (await api.query.system.account(alice.address)).data.free.toBigInt();104    await transferExpectSuccess(collectionId, tokenId, alice, bob, 1, 'NFT');105    const aliceBalanceAfter = (await api.query.system.account(alice.address)).data.free.toBigInt();106107    console.log(`Calibrated price: ${Number(aliceBalanceBefore - aliceBalanceAfter) / Number(UNIQUE)} UNQ`);108  }109}110111async function calibrateMinGasPrice(api: ApiPromise, web3: Web3, privateKey: (account: string) => IKeyringPair) {112  const alice = privateKey('//Alice');113  const caller = await createEthAccountWithBalance(api, web3, privateKey);114  const receiver = createEthAccount(web3);115  const dataPoints = [];116117  {118    const collectionId = await createCollectionExpectSuccess();119    const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT', {Ethereum: caller});120121    const address = collectionIdToAddress(collectionId);122    const contract = new web3.eth.Contract(nonFungibleAbi as any, address, {from: caller, ...GAS_ARGS});123124    const cost = await recordEthFee(api, caller, () => contract.methods.transfer(receiver, tokenId).send(caller));125126    console.log(`Original price: ${Number(cost) / Number(UNIQUE)} UNQ`);127  }128129  const defaultCoeff = (api.consts.configuration.defaultMinGasPrice as any).toBigInt();130  for (let i = -8; i < 8; i++) {131    const gasPrice = defaultCoeff + defaultCoeff / 100000n * BigInt(i);132    const gasPriceStr = '0x' + gasPrice.toString(16);133    await executeTransaction(api, alice, api.tx.sudo.sudo(api.tx.configuration.setMinGasPriceOverride(gasPrice)));134135    const coefficient = (await api.query.configuration.minGasPriceOverride() as any).toBigInt();136    const collectionId = await createCollectionExpectSuccess();137    const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT', {Ethereum: caller});138139    const address = collectionIdToAddress(collectionId);140    const contract = new web3.eth.Contract(nonFungibleAbi as any, address, {from: caller, gasPrice: gasPriceStr, ...GAS_ARGS});141142    const transferPrice = await recordEthFee(api, caller, () => contract.methods.transfer(receiver, tokenId).send(caller));143144    dataPoints.push({x: transferPrice, y: coefficient});145  }146147  const {a, b} = linearRegression(dataPoints);148149  // console.log(`Error: ${error(dataPoints, x => a*x+b)}`);150151  // * 0.15 = * 10000 / 66666152  const perfectValue = a * UNIQUE * 1000000n / 6666666n + b;153  await executeTransaction(api, alice, api.tx.sudo.sudo(api.tx.configuration.setMinGasPriceOverride(perfectValue.toString())));154155  {156    const collectionId = await createCollectionExpectSuccess();157    const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT', {Ethereum: caller});158159    const address = collectionIdToAddress(collectionId);160    const contract = new web3.eth.Contract(nonFungibleAbi as any, address, {from: caller, ...GAS_ARGS});161162    const cost = await recordEthFee(api, caller, () => contract.methods.transfer(receiver, tokenId).send(caller));163164    console.log(`Calibrated price: ${Number(cost) / Number(UNIQUE)} UNQ`);165  }166}167168(async () => {169  await usingApi(async (api, privateKey) => {170    // Second run slightly reduces error sometimes, as price line is not actually straight, this is a curve171172    await calibrateWeightToFee(api, privateKey);173    await calibrateWeightToFee(api, privateKey);174175    await usingWeb3(async web3 => {176      await calibrateMinGasPrice(api, web3, privateKey);177      await calibrateMinGasPrice(api, web3, privateKey);178    });179180    await api.disconnect();181  });182})();