UNPKG

@flarenetwork/ftso_price_provider_kick_off_package

Version:

Kick of package for FTSO price providers. Includes user facing interfaces and mock contracts to test price provider pipeline.

148 lines (121 loc) 6.39 kB
let sleep = require('util').promisify(setTimeout); const { ethers } = require('hardhat'); // @ts-ignore import { time, expectEvent } from '@openzeppelin/test-helpers'; import { toBN } from '../../test/utils/test-helpers'; import { IFtsoRegistryInstance, IVoterWhitelisterInstance, IPriceSubmitterInstance, IFtsoInstance, } from "../../typechain-truffle"; async function getTime(): Promise<number>{ await time.advanceBlock(); const blockNum = await ethers.provider.getBlockNumber(); const block = await ethers.provider.getBlock(blockNum); const timestamp = block.timestamp; return timestamp } export function submitHash(ftsoIndices: number[], prices: number[], random: BN, address: string): string { return ethers.utils.keccak256(web3.eth.abi.encodeParameters([ "uint256[]", "uint256[]", "uint256", "address" ], [ ftsoIndices, prices, random, address ])); } // TODO: Implement this to read prices from interesting places function getPrice(epochId: number, asset: string): number{ return Math.floor(Math.random() * 200 + 10000); } // TODO: Maybe change random generation function getRandom(epochId: number): BN { return toBN(2).pow(toBN(128)).addn(Math.floor(Math.random() * 1000)); } const MockPriceSubmitter = artifacts.require("MockPriceSubmitter"); const MockFtsoRegistry = artifacts.require("MockFtsoRegistry"); const MockVoterWhitelister = artifacts.require("MockVoterWhitelister"); const MockFtso = artifacts.require("MockNpmFtso"); async function main() { console.log(await getTime()); console.log((new Date()).getTime()); // Just the first from autogenerated accounts const priceProviderPrivateKey = "0xc5e8f61d1ab959b397eecc0a37a6517b8e67a0e7cf1f4bce5591f3ed80199122"; const priceProviderAccount = web3.eth.accounts.privateKeyToAccount(priceProviderPrivateKey); // Initialize data // Price submitter is at a fixed address, change this to the address reported by `yarn hh_node`. const priceSubmitter: IPriceSubmitterInstance = await MockPriceSubmitter.at("0x7c2C195CD6D34B8F845992d380aADB2730bB9C6F"); const ftsoRegistry: IFtsoRegistryInstance = await MockFtsoRegistry.at(await priceSubmitter.getFtsoRegistry()); const voterWhitelister: IVoterWhitelisterInstance = await MockVoterWhitelister.at(await priceSubmitter.getVoterWhitelister()); // Get indices for specific symbols const symbols = ["SGB", "XRP", "LTC", "XLM", "XDG", "ADA", "ALGO", "BCH", "DGB", "BTC"]; const ftsos = await Promise.all( symbols.map(async sym => await MockFtso.at(await ftsoRegistry.getFtsoBySymbol(sym))) ) as IFtsoInstance[]; const ftsoAddresses = await Promise.all( symbols.map(async sym => await ftsoRegistry.getFtsoBySymbol(sym)) ); // Get indices on which to submit const ftsoIndices = await Promise.all( symbols.map(async sym => (await ftsoRegistry.getFtsoIndex(sym)).toNumber()) ) // Combine them for easier future use const currencyIndices = new Map( symbols.map((c, i) => [c, ftsoIndices[i]]) ); // Whitelist ourselves for EVERY ftso. This always works in mock case // since there is no vote power calculation, so everyone gets whitelisted. // In a real setting, this call can be quite expensive and can potentially fail // if the voter does not have enough power or provide enough gas for the transaction const tx = await voterWhitelister.requestFullVoterWhitelisting(priceProviderAccount.address); // Check the whitelist for any changes const whitelist = await priceSubmitter.voterWhitelistBitmap(priceProviderAccount.address); // Get submission config const { 0: firstEpochStartTsBN, 1: submitPeriodSecondsBN, 2: revealPeriodSecondsBN, } = (await ftsos[0].getPriceEpochConfiguration()); const [firstEpochStartTs, submitPeriodSeconds, revealPeriodSeconds] = [firstEpochStartTsBN, submitPeriodSecondsBN, revealPeriodSecondsBN].map(x => x.toNumber()); // Sync time to start on next full transaction id // For a real setting, make sure that computer time is synced with a reliable time provider // Take blockchain time let now = await getTime(); const startingEpoch = (Math.floor((now - firstEpochStartTs) / submitPeriodSeconds) + 1); let next = startingEpoch * submitPeriodSeconds + firstEpochStartTs; let diff = Math.floor(next - now) + 1; console.log(`Waiting for ${diff} seconds until first start`); await sleep(diff * 1000); let currentEpoch = startingEpoch; while(true){ // Force hardhat to mine a new block which will have an updated timestamp. if we don't hardhat timestamp will not update. time.advanceBlock(); console.log("Start submit for epoch: ", currentEpoch); // Prepare prices and random const random = getRandom(currentEpoch); // Just a mock here, real price should not be random const prices = symbols.map(sym => getPrice(currentEpoch, sym)); const hash = submitHash(ftsoIndices, prices, random, priceProviderAccount.address); console.log("Prices: ", prices); console.log("Random: ", random); // Submit price, on everything const submission = await priceSubmitter.submitHash(currentEpoch, hash, {from: priceProviderAccount.address} ); expectEvent(submission, "HashSubmitted", { epochId: currentEpoch.toString(), hash: hash}); currentEpoch = currentEpoch + 1; now = await getTime(); next = currentEpoch * submitPeriodSeconds + firstEpochStartTs; diff = Math.floor(next - now); console.log(`Waiting for ${diff} seconds until reveal`); await sleep(diff * 1000); // Reval prices time.advanceBlock(); const reveal = await priceSubmitter.revealPrices(currentEpoch - 1, ftsoIndices, prices, random, {from: priceProviderAccount.address}); await expectEvent(reveal, "PricesRevealed", { ftsos: ftsoAddresses, epochId: (currentEpoch - 1).toString(), prices: prices.map(x => toBN(x)) }); console.log("Revealed prices for epoch ", currentEpoch - 1); // start loop again, the price submission has already started } } main() .then(() => process.exit(0)) .catch(error => { console.error(error); process.exit(1); });