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

difftreelog

fix calibrate.ts

Daniel Shiposha2022-11-28parent: #571be54.patch.diff
in: master

1 file changed

modifiedtests/src/calibrate.tsdiffbeforeafterboth
before · tests/src/calibrate.ts
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})();
after · tests/src/calibrate.ts
1import {IKeyringPair} from '@polkadot/types/types';2import {usingEthPlaygrounds, EthUniqueHelper} from './eth/util';34function linearRegression(points: { x: bigint, y: bigint }[]) {5  let sumxy = 0n;6  let sumx = 0n;7  let sumy = 0n;8  let sumx2 = 0n;9  const n = points.length;10  for (let i = 0; i < n; i++) {11    const p = points[i];12    sumxy += p.x * p.y;13    sumx += p.x;14    sumy += p.y;15    sumx2 += p.x * p.x;16  }1718  const nb = BigInt(n);1920  const precision = 100000000n;2122  // This is a workaround to beat the lack of precision of the `Number` type.23  // We divide `BigInt`s. But since it is an integer division, we should take care of the precision on our own.24  // After the division we can convert the result back to the `Number` and then we set the correct precision.25  // It is crucial to have the correct slope for the regression line.26  const a = Number((precision * (nb * sumxy - sumx * sumy)) / (nb * sumx2 - sumx * sumx)) / Number(precision);27  const b = (Number(sumy) - a * Number(sumx)) / Number(nb);2829  return {a, b};30}3132// JS has no builtin function to calculate sqrt of bigint33// https://stackoverflow.com/a/53684036/619016934function sqrt(value: bigint) {35  if (value < 0n) {36    throw 'square root of negative numbers is not supported';37  }3839  if (value < 2n) {40    return value;41  }4243  function newtonIteration(n: bigint, x0: bigint): bigint {44    const x1 = ((n / x0) + x0) >> 1n;45    if (x0 === x1 || x0 === (x1 - 1n)) {46      return x0;47    }48    return newtonIteration(n, x1);49  }5051  return newtonIteration(value, 1n);52}5354function _error(points: { x: bigint, y: bigint }[], hypothesis: (a: bigint) => bigint) {55  return sqrt(points.map(p => {56    const v = hypothesis(p.x);57    const vv = p.y;5859    return (v - vv) ** 2n;60  }).reduce((a, b) => a + b, 0n) / BigInt(points.length));61}6263async function calibrateWeightToFee(helper: EthUniqueHelper, privateKey: (account: string) => Promise<IKeyringPair>) {64  const alice = await privateKey('//Alice');65  const bob = await privateKey('//Bob');66  const dataPoints = [];6768  {69    const collection = await helper.nft.mintCollection(alice, {name: 'New', description: 'New collection', tokenPrefix: 'NEW'});70    const token = await collection.mintToken(alice, {Substrate: alice.address});71    const aliceBalanceBefore = await helper.balance.getSubstrate(alice.address);72    await token.transfer(alice, {Substrate: bob.address});73    const aliceBalanceAfter = await helper.balance.getSubstrate(alice.address);7475    console.log(`\t[NFT Transfer] Original price: ${Number(aliceBalanceBefore - aliceBalanceAfter) / Number(helper.balance.getOneTokenNominal())} UNQ`);76  }7778  const api = helper.getApi();79  const base = (await api.query.configuration.weightToFeeCoefficientOverride() as any).toBigInt();80  for (let i = -5; i < 5; i++) {81    await helper.signTransaction(alice, api.tx.sudo.sudo(api.tx.configuration.setWeightToFeeCoefficientOverride(base + base / 1000n * BigInt(i))));8283    const coefficient = (await api.query.configuration.weightToFeeCoefficientOverride() as any).toBigInt();84    const collection = await helper.nft.mintCollection(alice, {name: 'New', description: 'New collection', tokenPrefix: 'NEW'});85    const token = await collection.mintToken(alice, {Substrate: alice.address});8687    const aliceBalanceBefore = await helper.balance.getSubstrate(alice.address);88    await token.transfer(alice, {Substrate: bob.address});89    const aliceBalanceAfter = await helper.balance.getSubstrate(alice.address);9091    const transferPrice = aliceBalanceBefore - aliceBalanceAfter;9293    dataPoints.push({x: transferPrice, y: coefficient});94  }95  const {a, b} = linearRegression(dataPoints);9697  // console.log(`Error: ${error(dataPoints, x => a*x+b)}`);9899  const perfectValue = BigInt(Math.ceil(a * Number(helper.balance.getOneTokenNominal()) / 10 + b));100  await helper.signTransaction(alice, api.tx.sudo.sudo(api.tx.configuration.setWeightToFeeCoefficientOverride(perfectValue.toString())));101102  {103    const collection = await helper.nft.mintCollection(alice, {name: 'New', description: 'New collection', tokenPrefix: 'NEW'});104    const token = await collection.mintToken(alice, {Substrate: alice.address});105    const aliceBalanceBefore = await helper.balance.getSubstrate(alice.address);106    await token.transfer(alice, {Substrate: bob.address});107    const aliceBalanceAfter = await helper.balance.getSubstrate(alice.address);108109    console.log(`\t[NFT Transfer] Calibrated price: ${Number(aliceBalanceBefore - aliceBalanceAfter) / Number(helper.balance.getOneTokenNominal())} UNQ`);110  }111}112113async function calibrateMinGasPrice(helper: EthUniqueHelper, privateKey: (account: string) => Promise<IKeyringPair>) {114  const alice = await privateKey('//Alice');115  const caller = await helper.eth.createAccountWithBalance(alice);116  const receiver = helper.eth.createAccount();117  const dataPoints = [];118119  {120    const collection = await helper.nft.mintCollection(alice, {name: 'New', description: 'New collection', tokenPrefix: 'NEW'});121    const token = await collection.mintToken(alice, {Ethereum: caller});122123    const address = helper.ethAddress.fromCollectionId(collection.collectionId);124    const contract = helper.ethNativeContract.collection(address, 'nft', caller);125126    const cost = await helper.eth.calculateFee({Ethereum: caller}, () => contract.methods.transfer(receiver, token.tokenId).send({from: caller, gas: helper.eth.DEFAULT_GAS}));127128    console.log(`\t[ETH NFT transfer] Original price: ${Number(cost) / Number(helper.balance.getOneTokenNominal())} UNQ`);129  }130131  const api = helper.getApi();132  // const defaultCoeff = (api.consts.configuration.defaultMinGasPrice as any).toBigInt();133  const base = (await api.query.configuration.minGasPriceOverride() as any).toBigInt();134  for (let i = -8; i < 8; i++) {135    const gasPrice = base + base / 100000n * BigInt(i);136    const gasPriceStr = '0x' + gasPrice.toString(16);137    await helper.signTransaction(alice, api.tx.sudo.sudo(api.tx.configuration.setMinGasPriceOverride(gasPrice)));138139    const coefficient = (await api.query.configuration.minGasPriceOverride() as any).toBigInt();140    const collection = await helper.nft.mintCollection(alice, {name: 'New', description: 'New collection', tokenPrefix: 'NEW'});141    const token = await collection.mintToken(alice, {Ethereum: caller});142143    const address = helper.ethAddress.fromCollectionId(collection.collectionId);144    const contract = helper.ethNativeContract.collection(address, 'nft', caller);145146    const transferPrice = await helper.eth.calculateFee({Ethereum: caller}, () => contract.methods.transfer(receiver, token.tokenId).send({from: caller, gasPrice: gasPriceStr, gas: helper.eth.DEFAULT_GAS}));147148    dataPoints.push({x: transferPrice, y: coefficient});149  }150151  const {a, b} = linearRegression(dataPoints);152153  // console.log(`Error: ${error(dataPoints, x => a*x+b)}`);154155  // * 0.15 = * 10000 / 66666156  const perfectValue = BigInt(Math.ceil(a * Number(helper.balance.getOneTokenNominal() * 1000000n / 6666666n) + b));157  await helper.signTransaction(alice, api.tx.sudo.sudo(api.tx.configuration.setMinGasPriceOverride(perfectValue.toString())));158159  {160    const collection = await helper.nft.mintCollection(alice, {name: 'New', description: 'New collection', tokenPrefix: 'NEW'});161    const token = await collection.mintToken(alice, {Ethereum: caller});162163    const address = helper.ethAddress.fromCollectionId(collection.collectionId);164    const contract = helper.ethNativeContract.collection(address, 'nft', caller);165166    const cost = await helper.eth.calculateFee({Ethereum: caller}, () => contract.methods.transfer(receiver, token.tokenId).send({from: caller, gas: helper.eth.DEFAULT_GAS}));167168    console.log(`\t[ETH NFT transfer] Calibrated price: ${Number(cost) / Number(helper.balance.getOneTokenNominal())} UNQ`);169  }170}171172(async () => {173  await usingEthPlaygrounds(async (helper: EthUniqueHelper, privateKey) => {174    // Subsequent runs reduce error, as price line is not actually straight, this is a curve175176    const iterations = 3;177178    console.log('[Calibrate WeightToFee]');179    for (let i = 0; i < iterations; i++) {180      await calibrateWeightToFee(helper, privateKey);181    }182183    console.log();184185    console.log('[Calibrate MinGasPrice]');186    for (let i = 0; i < iterations; i++) {187      await calibrateMinGasPrice(helper, privateKey);188    }189  });190})();