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

difftreelog

source

tests/src/calibrate.ts8.2 KiBsourcehistory
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})();