mirror of
https://github.com/luxfi/uma.git
synced 2026-10-30 15:14:44 +00:00
188 lines
7.4 KiB
JavaScript
188 lines
7.4 KiB
JavaScript
const hre = require("hardhat");
|
|
const { web3 } = hre;
|
|
const { runVotingV2Fixture } = require("@uma/common");
|
|
const { getContract } = hre;
|
|
const { RegistryRolesEnum, getRandomSignedInt, computeVoteHash } = require("@uma/common");
|
|
const { moveToNextRound, moveToNextPhase } = require("../utils/Voting.js");
|
|
const { toBN } = web3.utils;
|
|
|
|
const Registry = getContract("Registry");
|
|
const VotingV2 = getContract("VotingV2ControllableTiming");
|
|
const IdentifierWhitelist = getContract("IdentifierWhitelist");
|
|
const VotingToken = getContract("VotingToken");
|
|
|
|
const { utf8ToHex, padRight } = web3.utils;
|
|
|
|
const toWei = (value) => toBN(web3.utils.toWei(value, "ether"));
|
|
|
|
describe("VotingV2 Gas Test", function () {
|
|
let voting, votingToken, registry, supportedIdentifiers, registeredContract;
|
|
let accounts, account1, account2, account3, account4;
|
|
|
|
before(function () {
|
|
// Skip this test unless GAS_TEST=true is set.
|
|
if (process.env.GAS_TEST !== "true") this.skip();
|
|
});
|
|
|
|
beforeEach(async function () {
|
|
accounts = await web3.eth.getAccounts();
|
|
[account1, account2, account3, account4, registeredContract] = accounts;
|
|
await runVotingV2Fixture(hre);
|
|
voting = await await VotingV2.deployed();
|
|
|
|
supportedIdentifiers = await IdentifierWhitelist.deployed();
|
|
votingToken = await VotingToken.deployed();
|
|
registry = await Registry.deployed();
|
|
|
|
// Allow account1 to mint tokens.
|
|
const minterRole = 1;
|
|
await votingToken.methods.addMember(minterRole, account1).send({ from: accounts[0] });
|
|
|
|
// Seed the three accounts and stake into the voting contract. account1 starts with 100MM tokens, so divide up as:
|
|
// 1: 32MM
|
|
// 2: 32MM
|
|
// 3: 32MM
|
|
// 4: 4MM (can't reach the 5% GAT alone)
|
|
await votingToken.methods.approve(voting.options.address, toWei("32000000")).send({ from: account1 });
|
|
await voting.methods.stake(toWei("32000000")).send({ from: account1 });
|
|
await votingToken.methods.transfer(account2, toWei("32000000")).send({ from: accounts[0] });
|
|
await votingToken.methods.approve(voting.options.address, toWei("32000000")).send({ from: account2 });
|
|
await voting.methods.stake(toWei("32000000")).send({ from: account2 });
|
|
await votingToken.methods.transfer(account3, toWei("32000000")).send({ from: accounts[0] });
|
|
await votingToken.methods.approve(voting.options.address, toWei("32000000")).send({ from: account3 });
|
|
await voting.methods.stake(toWei("32000000")).send({ from: account3 });
|
|
await votingToken.methods.transfer(account4, toWei("4000000")).send({ from: accounts[0] });
|
|
await votingToken.methods.approve(voting.options.address, toWei("4000000")).send({ from: account4 });
|
|
await voting.methods.stake(toWei("4000000")).send({ from: account4 });
|
|
|
|
// Set the inflation rate to 0 by default, so the balances stay fixed until inflation is tested.
|
|
|
|
// Register contract with Registry.
|
|
await registry.methods.addMember(RegistryRolesEnum.CONTRACT_CREATOR, account1).send({ from: accounts[0] });
|
|
await registry.methods.registerContract([], registeredContract).send({ from: account1 });
|
|
|
|
// Reset the rounds.
|
|
await moveToNextRound(voting, accounts[0]);
|
|
});
|
|
|
|
function printAggregates(label, costs, voters, requests) {
|
|
const first = costs[0];
|
|
const last = costs[costs.length - 1];
|
|
const max = Math.max(...costs);
|
|
const min = Math.min(...costs);
|
|
const sum = costs.reduce((a, b) => a + b, 0);
|
|
const average = sum / costs.length;
|
|
const costPerVoter = sum / voters.length;
|
|
const costPerRequest = sum / requests.length;
|
|
console.group(label);
|
|
console.log(`
|
|
First: ${first}
|
|
Last: ${last}
|
|
Max: ${max}
|
|
Min: ${min}
|
|
Average: ${average}
|
|
Average Per Voter: ${costPerVoter}
|
|
Average Per Request: ${costPerRequest}
|
|
Total for all voters: ${sum}
|
|
`);
|
|
console.groupEnd();
|
|
}
|
|
|
|
async function runVoteAndPrintCosts(numRequests, numVoters, startTime) {
|
|
if (numVoters > 4) throw new Error("Cannot have more than 4 voters");
|
|
|
|
const identifier = padRight(utf8ToHex("gas-test"), 64);
|
|
|
|
const requests = [];
|
|
for (let i = 0; i < numRequests; i++) requests.push({ identifier, time: startTime + i });
|
|
|
|
const voters = [account1, account2, account3, account4].slice(0, numVoters);
|
|
|
|
// Make the Oracle support this identifier.
|
|
await supportedIdentifiers.methods.addSupportedIdentifier(identifier).send({ from: accounts[0] });
|
|
|
|
// Request a price and move to the next round where that will be voted on.
|
|
const requestPriceCosts = await Promise.all(
|
|
requests.map(async ({ identifier, time }) => {
|
|
const { gasUsed } = await voting.methods.requestPrice(identifier, time).send({ from: registeredContract });
|
|
return gasUsed;
|
|
})
|
|
);
|
|
|
|
await moveToNextRound(voting, accounts[0]);
|
|
const roundId = (await voting.methods.getCurrentRoundId().call()).toString();
|
|
|
|
// Commit votes.
|
|
const winningPrice = 456;
|
|
const salt = getRandomSignedInt();
|
|
|
|
const commitVoteCosts = await Promise.all(
|
|
voters.map(async (voter) => {
|
|
const multicallData = requests.map(({ identifier, time }) => {
|
|
const hash = computeVoteHash({ price: winningPrice, salt, account: voter, time, roundId, identifier });
|
|
return voting.methods.commitAndEmitEncryptedVote(identifier, time, hash, "0x").encodeABI();
|
|
});
|
|
|
|
const { gasUsed } = await voting.methods.multicall(multicallData).send({ from: voter });
|
|
return gasUsed;
|
|
})
|
|
);
|
|
|
|
// Reveal the votes.
|
|
await moveToNextPhase(voting, accounts[0]);
|
|
|
|
const revealVoteCosts = await Promise.all(
|
|
voters.map(async (voter) => {
|
|
const multicallData = requests.map(({ identifier, time }) =>
|
|
voting.methods.revealVote(identifier, time, winningPrice, salt).encodeABI()
|
|
);
|
|
|
|
const { gasUsed } = await voting.methods.multicall(multicallData).send({ from: voter });
|
|
return gasUsed;
|
|
})
|
|
);
|
|
|
|
// Price should resolve to the one that 2 and 3 voted for.
|
|
await moveToNextRound(voting, accounts[0]);
|
|
|
|
const claimRewardsCosts = await Promise.all(
|
|
voters.map(async (voter) => {
|
|
const { gasUsed } = await voting.methods.withdrawRewards().send({ from: voter });
|
|
return gasUsed;
|
|
})
|
|
);
|
|
|
|
const getPriceCosts = await Promise.all(
|
|
requests.map(async ({ identifier, time }) => {
|
|
const { gasUsed } = await voting.methods.getPrice(identifier, time).send({ from: registeredContract });
|
|
return gasUsed;
|
|
})
|
|
);
|
|
|
|
console.group(`Gas costs for ${numVoters} voters, ${numRequests} requests\n`);
|
|
printAggregates("Request Price", requestPriceCosts, voters, requests);
|
|
printAggregates("Commit", commitVoteCosts, voters, requests);
|
|
printAggregates("Reveal", revealVoteCosts, voters, requests);
|
|
printAggregates("Claim Rewards", claimRewardsCosts, voters, requests);
|
|
printAggregates("Get Price", getPriceCosts, voters, requests);
|
|
printAggregates(
|
|
"Total lifecycle (only voting methods)",
|
|
[...commitVoteCosts, ...revealVoteCosts, ...claimRewardsCosts],
|
|
voters,
|
|
requests
|
|
);
|
|
console.groupEnd();
|
|
}
|
|
|
|
it("Four voter gas test", async function () {
|
|
// Initialize Run
|
|
console.log("\n\nInitialize Run. Gas costs will likely be higher than typical\n");
|
|
await runVoteAndPrintCosts(4, 4, 1000);
|
|
|
|
await moveToNextRound(voting, accounts[0]);
|
|
|
|
console.log("\n\nSteady state run. Gas costs should be normal.\n");
|
|
await runVoteAndPrintCosts(4, 4, 5000);
|
|
});
|
|
});
|