git.delta.rocks / unique-network / refs/commits / 1a0647ecee1e

difftreelog

source

tests/src/calibrate.ts7.4 KiBsourcehistory
1import {IKeyringPair} from '@polkadot/types/types';23import {usingEthPlaygrounds, EthUniqueHelper} from './eth/util';456function linearRegression(points: { x: bigint, y: bigint }[]) {7  let sumxy = 0n;8  let sumx = 0n;9  let sumy = 0n;10  let sumx2 = 0n;11  const n = points.length;12  for (let i = 0; i < n; i++) {13    const p = points[i];14    sumxy += p.x * p.y;15    sumx += p.x;16    sumy += p.y;17    sumx2 += p.x * p.x;18  }1920  const nb = BigInt(n);2122  const a = (nb * sumxy - sumx * sumy) / (nb * sumx2 - sumx * sumx);23  const b = (sumy - a * sumx) / nb;2425  return {a, b};26}2728// JS has no builtin function to calculate sqrt of bigint29// https://stackoverflow.com/a/53684036/619016930function sqrt(value: bigint) {31  if (value < 0n) {32    throw 'square root of negative numbers is not supported';33  }3435  if (value < 2n) {36    return value;37  }3839  function newtonIteration(n: bigint, x0: bigint): bigint {40    const x1 = ((n / x0) + x0) >> 1n;41    if (x0 === x1 || x0 === (x1 - 1n)) {42      return x0;43    }44    return newtonIteration(n, x1);45  }4647  return newtonIteration(value, 1n);48}4950function _error(points: { x: bigint, y: bigint }[], hypothesis: (a: bigint) => bigint) {51  return sqrt(points.map(p => {52    const v = hypothesis(p.x);53    const vv = p.y;5455    return (v - vv) ** 2n;56  }).reduce((a, b) => a + b, 0n) / BigInt(points.length));57}5859async function calibrateWeightToFee(helper: EthUniqueHelper, privateKey: (account: string) => Promise<IKeyringPair>) {60  const alice = await privateKey('//Alice');61  const bob = await privateKey('//Bob');62  const dataPoints = [];6364  {65    const collection = await helper.nft.mintCollection(alice, {name: 'New', description: 'New collection', tokenPrefix: 'NEW'});66    const token = await collection.mintToken(alice, {Substrate: alice.address});67    const aliceBalanceBefore = await helper.balance.getSubstrate(alice.address);68    await token.transfer(alice, {Substrate: bob.address});69    const aliceBalanceAfter = await helper.balance.getSubstrate(alice.address);7071    console.log(`Original price: ${Number(aliceBalanceBefore - aliceBalanceAfter) / Number(helper.balance.getOneTokenNominal())} UNQ`);72  }7374  const api = helper.getApi();75  const defaultCoeff = (api.consts.configuration.defaultWeightToFeeCoefficient as any).toBigInt();76  for (let i = -5; i < 5; i++) {77    await helper.signTransaction(alice, api.tx.sudo.sudo(api.tx.configuration.setWeightToFeeCoefficientOverride(defaultCoeff + defaultCoeff / 1000n * BigInt(i))));7879    const coefficient = (await api.query.configuration.weightToFeeCoefficientOverride() as any).toBigInt();80    const collection = await helper.nft.mintCollection(alice, {name: 'New', description: 'New collection', tokenPrefix: 'NEW'});81    const token = await collection.mintToken(alice, {Substrate: alice.address});8283    const aliceBalanceBefore = await helper.balance.getSubstrate(alice.address);84    await token.transfer(alice, {Substrate: bob.address});85    const aliceBalanceAfter = await helper.balance.getSubstrate(alice.address);8687    const transferPrice = aliceBalanceBefore - aliceBalanceAfter;8889    dataPoints.push({x: transferPrice, y: coefficient});90  }91  const {a, b} = linearRegression(dataPoints);9293  // console.log(`Error: ${error(dataPoints, x => a*x+b)}`);9495  const perfectValue = a * helper.balance.getOneTokenNominal() / 10n + b;96  await helper.signTransaction(alice, api.tx.sudo.sudo(api.tx.configuration.setWeightToFeeCoefficientOverride(perfectValue.toString())));9798  {99    const collection = await helper.nft.mintCollection(alice, {name: 'New', description: 'New collection', tokenPrefix: 'NEW'});100    const token = await collection.mintToken(alice, {Substrate: alice.address});101    const aliceBalanceBefore = await helper.balance.getSubstrate(alice.address);102    await token.transfer(alice, {Substrate: bob.address});103    const aliceBalanceAfter = await helper.balance.getSubstrate(alice.address);104105    console.log(`Calibrated price: ${Number(aliceBalanceBefore - aliceBalanceAfter) / Number(helper.balance.getOneTokenNominal())} UNQ`);106  }107}108109async function calibrateMinGasPrice(helper: EthUniqueHelper, privateKey: (account: string) => Promise<IKeyringPair>) {110  const alice = await privateKey('//Alice');111  const caller = await helper.eth.createAccountWithBalance(alice);112  const receiver = helper.eth.createAccount();113  const dataPoints = [];114115  {116    const collection = await helper.nft.mintCollection(alice, {name: 'New', description: 'New collection', tokenPrefix: 'NEW'});117    const token = await collection.mintToken(alice, {Ethereum: caller});118119    const address = helper.ethAddress.fromCollectionId(collection.collectionId);120    const contract = helper.ethNativeContract.collection(address, 'nft', caller);121122    const cost = await helper.eth.calculateFee({Ethereum: caller}, () => contract.methods.transfer(receiver, token.tokenId).send({from: caller, gas: helper.eth.DEFAULT_GAS}));123124    console.log(`Original price: ${Number(cost) / Number(helper.balance.getOneTokenNominal())} UNQ`);125  }126127  const api = helper.getApi();128  const defaultCoeff = (api.consts.configuration.defaultMinGasPrice as any).toBigInt();129  for (let i = -8; i < 8; i++) {130    const gasPrice = defaultCoeff + defaultCoeff / 100000n * BigInt(i);131    const gasPriceStr = '0x' + gasPrice.toString(16);132    await helper.signTransaction(alice, api.tx.sudo.sudo(api.tx.configuration.setMinGasPriceOverride(gasPrice)));133134    const coefficient = (await api.query.configuration.minGasPriceOverride() as any).toBigInt();135    const collection = await helper.nft.mintCollection(alice, {name: 'New', description: 'New collection', tokenPrefix: 'NEW'});136    const token = await collection.mintToken(alice, {Ethereum: caller});137138    const address = helper.ethAddress.fromCollectionId(collection.collectionId);139    const contract = helper.ethNativeContract.collection(address, 'nft', caller);140141    const transferPrice = await helper.eth.calculateFee({Ethereum: caller}, () => contract.methods.transfer(receiver, token.tokenId).send({from: caller, gasPrice: gasPriceStr, gas: helper.eth.DEFAULT_GAS}));142143    dataPoints.push({x: transferPrice, y: coefficient});144  }145146  const {a, b} = linearRegression(dataPoints);147148  // console.log(`Error: ${error(dataPoints, x => a*x+b)}`);149150  // * 0.15 = * 10000 / 66666151  const perfectValue = a * helper.balance.getOneTokenNominal() * 1000000n / 6666666n + b;152  await helper.signTransaction(alice, api.tx.sudo.sudo(api.tx.configuration.setMinGasPriceOverride(perfectValue.toString())));153154  {155    const collection = await helper.nft.mintCollection(alice, {name: 'New', description: 'New collection', tokenPrefix: 'NEW'});156    const token = await collection.mintToken(alice, {Ethereum: caller});157158    const address = helper.ethAddress.fromCollectionId(collection.collectionId);159    const contract = helper.ethNativeContract.collection(address, 'nft', caller);160161    const cost = await helper.eth.calculateFee({Ethereum: caller}, () => contract.methods.transfer(receiver, token.tokenId).send({from: caller, gas: helper.eth.DEFAULT_GAS}));162163    console.log(`Calibrated price: ${Number(cost) / Number(helper.balance.getOneTokenNominal())} UNQ`);164  }165}166167(async () => {168  await usingEthPlaygrounds(async (helper: EthUniqueHelper, privateKey) => {169    // Second run slightly reduces error sometimes, as price line is not actually straight, this is a curve170171    await calibrateWeightToFee(helper, privateKey);172    await calibrateWeightToFee(helper, privateKey);173174    await calibrateMinGasPrice(helper, privateKey);175    await calibrateMinGasPrice(helper, privateKey);176  });177})();