From 6d6c3fbf1917a66f93772e7484310bce0df9414d Mon Sep 17 00:00:00 2001 From: Leszek Stachowski Date: Tue, 23 Jul 2024 14:47:32 +0200 Subject: [PATCH] @celo/contractkit: migrate Governance wrapper tests to anvil (#305) --- .changeset/pretty-comics-lay.md | 5 + packages/dev-utils/src/anvil-test.ts | 12 +- packages/dev-utils/src/chain-setup.ts | 29 +++ packages/dev-utils/src/test-utils.ts | 5 + .../src/wrappers/Governance.test.ts | 184 ++++++------------ .../src/wrappers/LockedGold.test.ts | 13 +- 6 files changed, 116 insertions(+), 132 deletions(-) create mode 100644 .changeset/pretty-comics-lay.md diff --git a/.changeset/pretty-comics-lay.md b/.changeset/pretty-comics-lay.md new file mode 100644 index 000000000..dd4aa6d92 --- /dev/null +++ b/.changeset/pretty-comics-lay.md @@ -0,0 +1,5 @@ +--- +'@celo/dev-utils': patch +--- + +Introduced setDequeueFrequency and setReferendumStageDuration helper functions, decreased web3.eth.transactionPollingInterval to 10ms diff --git a/packages/dev-utils/src/anvil-test.ts b/packages/dev-utils/src/anvil-test.ts index 52de8e02e..0f7798f30 100644 --- a/packages/dev-utils/src/anvil-test.ts +++ b/packages/dev-utils/src/anvil-test.ts @@ -1,3 +1,4 @@ +import { StrongAddress } from '@celo/base' import { PROXY_ADMIN_ADDRESS } from '@celo/connect' import { Anvil, CreateAnvilOptions, createAnvil } from '@viem/anvil' import Web3 from 'web3' @@ -40,7 +41,7 @@ export function createInstance(): Anvil { export function testWithAnvil(name: string, fn: (web3: Web3) => void) { const anvil = createInstance() - // for each test case, we start and stop a new anvil instance + // for each test suite, we start and stop a new anvil instance return testWithWeb3(name, `http://127.0.0.1:${anvil.port}`, fn, { beforeAll: async () => { await anvil.start() @@ -69,6 +70,15 @@ export const withImpersonatedAccount = async ( await stopImpersonatingAccount(web3, account) } +export const asCoreContractsOwner = async ( + web3: Web3, + fn: (ownerAddress: StrongAddress) => Promise +) => { + await withImpersonatedAccount(web3, DEFAULT_OWNER_ADDRESS, async () => { + await fn(DEFAULT_OWNER_ADDRESS) + }) +} + export function setCode(web3: Web3, address: string, code: string) { return jsonRpcCall(web3, 'anvil_setCode', [address, code]) } diff --git a/packages/dev-utils/src/chain-setup.ts b/packages/dev-utils/src/chain-setup.ts index 5d7c11601..716607f27 100644 --- a/packages/dev-utils/src/chain-setup.ts +++ b/packages/dev-utils/src/chain-setup.ts @@ -1,3 +1,4 @@ +import { newGovernance } from '@celo/abis/web3/Governance' import { newValidators } from '@celo/abis/web3/Validators' import { StrongAddress } from '@celo/base' import Web3 from 'web3' @@ -16,3 +17,31 @@ export async function setCommissionUpdateDelay( }) }) } + +export async function setDequeueFrequency( + web3: Web3, + governanceContractAddress: StrongAddress, + frequency: number +) { + withImpersonatedAccount(web3, DEFAULT_OWNER_ADDRESS, async () => { + const governance = newGovernance(web3, governanceContractAddress) + + await governance.methods.setDequeueFrequency(frequency).send({ + from: DEFAULT_OWNER_ADDRESS, + }) + }) +} + +export async function setReferendumStageDuration( + web3: Web3, + governanceContractAddress: StrongAddress, + duration: number +) { + withImpersonatedAccount(web3, DEFAULT_OWNER_ADDRESS, async () => { + const governance = newGovernance(web3, governanceContractAddress) + + await governance.methods.setReferendumStageDuration(duration).send({ + from: DEFAULT_OWNER_ADDRESS, + }) + }) +} diff --git a/packages/dev-utils/src/test-utils.ts b/packages/dev-utils/src/test-utils.ts index cdcd2c72d..2bda6c226 100644 --- a/packages/dev-utils/src/test-utils.ts +++ b/packages/dev-utils/src/test-utils.ts @@ -66,6 +66,11 @@ export function testWithWeb3( ) { const web3 = new Web3(rpcUrl) + // @ts-ignore with anvil setup the tx receipt is apparently not immedietaly + // available after the tx is send, so by default it was waiting for 1000 ms + // before polling again making the tests slow + web3.eth.transactionPollingInterval = 10 + describe(name, () => { let snapId: string | null = null diff --git a/packages/sdk/contractkit/src/wrappers/Governance.test.ts b/packages/sdk/contractkit/src/wrappers/Governance.test.ts index 40b41888f..340020a88 100644 --- a/packages/sdk/contractkit/src/wrappers/Governance.test.ts +++ b/packages/sdk/contractkit/src/wrappers/Governance.test.ts @@ -1,7 +1,7 @@ import { Registry } from '@celo/abis/web3/Registry' import { Address, StrongAddress } from '@celo/base/lib/address' -import { concurrentMap } from '@celo/base/lib/async' -import { testWithAnvil } from '@celo/dev-utils/lib/anvil-test' +import { asCoreContractsOwner, testWithAnvil } from '@celo/dev-utils/lib/anvil-test' +import { setDequeueFrequency, setReferendumStageDuration } from '@celo/dev-utils/lib/chain-setup' import { NetworkConfig, testWithGanache, timeTravel } from '@celo/dev-utils/lib/ganache-test' import BigNumber from 'bignumber.js' import Web3 from 'web3' @@ -15,10 +15,30 @@ import { MultiSigWrapper } from './MultiSig' const expConfig = NetworkConfig.governance +// Only on ganache we can test 1.4.1.0 version testWithGanache('Governance Wrapper', (web3: Web3) => { + describe('Hotfixes', () => { + it('gets L1 hotfix record pre 1.5.0.0', async () => { + const kit = newKitFromWeb3(web3) + const governance = await kit.contracts.getGovernance() + // Sanity check to make sure we're pre 1.5.0.0 + expect((await governance.version()).toString()).toBe('1.4.1.0') + + const hotfixRecord = await governance.getHotfixRecord(Buffer.from('0x', 'hex')) + expect(hotfixRecord).toMatchInlineSnapshot(` + { + "approved": false, + "executed": false, + "preparedEpoch": "0", + } + `) + }) + }) +}) + +testWithAnvil('Governance Wrapper', (web3: Web3) => { const ONE_SEC = 1000 const kit = newKitFromWeb3(web3) - const minDeposit = web3.utils.toWei(expConfig.minDeposit.toString(), 'ether') const ONE_CGLD = web3.utils.toWei('1', 'ether') let accounts: StrongAddress[] = [] @@ -27,6 +47,7 @@ testWithGanache('Governance Wrapper', (web3: Web3) => { let lockedGold: LockedGoldWrapper let accountWrapper: AccountsWrapper let registry: Registry + let minDeposit: string beforeAll(async () => { accounts = (await web3.eth.getAccounts()) as StrongAddress[] @@ -36,12 +57,16 @@ testWithGanache('Governance Wrapper', (web3: Web3) => { registry = await kit._web3Contracts.getRegistry() lockedGold = await kit.contracts.getLockedGold() accountWrapper = await kit.contracts.getAccounts() + minDeposit = (await governance.minDeposit()).toFixed() + + await setDequeueFrequency(web3, governance.address, expConfig.dequeueFrequency) + await setReferendumStageDuration(web3, governance.address, expConfig.referendumStageDuration) - await concurrentMap(4, accounts.slice(0, 4), async (account) => { + for (const account of accounts.slice(0, 4)) { await accountWrapper.createAccount().sendAndWaitForReceipt({ from: account }) await lockedGold.lock().sendAndWaitForReceipt({ from: account, value: ONE_CGLD }) - }) - }, 5 * ONE_SEC) + } + }) type Repoint = [CeloContract, Address] @@ -56,20 +81,25 @@ testWithGanache('Governance Wrapper', (web3: Web3) => { return proposals as Proposal } - // const verifyRepointResult = (repoints: Repoint[]) => - // concurrentMap(4, repoints, async (repoint) => { - // const newAddress = await registry.methods.getAddressForStringOrDie(repoint[0]).call() - // expect(newAddress).toBe(repoint[1]) - // }) - it('#getConfig', async () => { - const config = await governance.getConfig() - expect(config.concurrentProposals).toEqBigNumber(expConfig.concurrentProposals) - expect(config.dequeueFrequency).toEqBigNumber(expConfig.dequeueFrequency) - expect(config.minDeposit).toEqBigNumber(minDeposit) - expect(config.queueExpiry).toEqBigNumber(expConfig.queueExpiry) - expect(config.stageDurations.Referendum).toEqBigNumber(expConfig.referendumStageDuration) - expect(config.stageDurations.Execution).toEqBigNumber(expConfig.executionStageDuration) + expect(await governance.getConfig()).toMatchInlineSnapshot(` + { + "concurrentProposals": "3", + "dequeueFrequency": "30", + "minDeposit": "100000000000000000000", + "participationParameters": { + "baseline": "0.005", + "baselineFloor": "0.01", + "baselineQuorumFactor": "1", + "baselineUpdateFactor": "0.2", + }, + "queueExpiry": "2419200", + "stageDurations": { + "Execution": "604800", + "Referendum": "100", + }, + } + `) }) describe('Proposals', () => { @@ -105,12 +135,14 @@ testWithGanache('Governance Wrapper', (web3: Web3) => { // protocol/truffle-config defines approver address as accounts[0] const approveFn = async () => { - const tx = await governance.approve(proposalID) - const multisigTx = await governanceApproverMultiSig.submitOrConfirmTransaction( - governance.address, - tx.txo - ) - await multisigTx.sendAndWaitForReceipt({ from: accounts[0] }) + await asCoreContractsOwner(web3, async (ownerAddress) => { + const tx = await governance.approve(proposalID) + const multisigTx = await governanceApproverMultiSig.submitOrConfirmTransaction( + governance.address, + tx.txo + ) + await multisigTx.sendAndWaitForReceipt({ from: ownerAddress }) + }) } const voteFn = async (voter: Address) => { @@ -238,8 +270,6 @@ testWithGanache('Governance Wrapper', (web3: Web3) => { const exists = await governance.proposalExists(proposalID) expect(exists).toBeFalsy() - - // await verifyRepointResult(repoints) }, 10 * ONE_SEC ) @@ -266,107 +296,9 @@ testWithGanache('Governance Wrapper', (web3: Web3) => { expect(voter.votes[0]).toEqual(expectedVoteRecord) }) }) - - describe('Hotfixes', () => { - it('gets L1 hotfix record pre 1.5.0.0', async () => { - // Sanity check to make sure we're pre 1.5.0.0 - expect((await governance.version()).toString()).toBe('1.4.1.0') - - const hotfixRecord = await governance.getHotfixRecord(Buffer.from('0x', 'hex')) - expect(hotfixRecord).toMatchInlineSnapshot(` - { - "approved": false, - "executed": false, - "preparedEpoch": "0", - } - `) - }) - }) - - // Disabled until validator set precompile is available in ganache - // https://github.com/celo-org/celo-monorepo/issues/1737 - - // describe('Hotfixes', () => { - // const repoints: Repoint[] = [ - // [CeloContract.Random, '0x0000000000000000000000000000000000000003'], - // [CeloContract.Escrow, '0x0000000000000000000000000000000000000004'], - // ] - - // let hotfixProposal: Proposal - // let hotfixHash: Buffer - // beforeAll(async () => { - // hotfixProposal = await registryRepointProposal(repoints) - // hotfixHash = proposalToHash(kit, hotfixProposal) - // }) - - // const whitelistFn = async (whitelister: Address) => { - // const tx = governance.whitelistHotfix(proposalToHash(kit, hotfixProposal)) - // await tx.sendAndWaitForReceipt({ from: whitelister }) - // } - - // // validator keys correspond to accounts 6-9 - // const whitelistQuorumFn = () => concurrentMap(1, accounts.slice(6, 10), whitelistFn) - - // // protocol/truffle-config defines approver address as accounts[0] - // const approveFn = async () => { - // const tx = governance.approveHotfix(proposalToHash(kit, hotfixProposal)) - // await tx.sendAndWaitForReceipt({ from: accounts[0] }) - // } - - // const prepareFn = async () => { - // const tx = governance.prepareHotfix(hotfixHash) - // await tx.sendAndWaitForReceipt() - // } - - // it('#whitelistHotfix', async () => { - // await whitelistFn(accounts[9]) - - // const whitelisted = await governance.isHotfixWhitelistedBy(hotfixHash, accounts[9]) - // expect(whitelisted).toBeTruthy() - // }) - - // it('#approveHotfix', async () => { - // await approveFn() - - // const record = await governance.getHotfixRecord(hotfixHash) - // expect(record.approved).toBeTruthy() - // }) - - // it( - // '#prepareHotfix', - // async () => { - // await whitelistQuorumFn() - // await approveFn() - // await prepareFn() - - // const validators = await kit.contracts.getValidators() - // const record = await governance.getHotfixRecord(hotfixHash) - // expect(record.preparedEpoch).toBe(await validators.getEpochNumber()) - // }, - // 10 * ONE_SEC - // ) - - // it( - // '#executeHotfix', - // async () => { - // await whitelistQuorumFn() - // await approveFn() - // await prepareFn() - - // const tx = governance.executeHotfix(hotfixProposal) - // await tx.sendAndWaitForReceipt() - - // const record = await governance.getHotfixRecord(hotfixHash) - // expect(record.executed).toBeTruthy() - - // await verifyRepointResult(repoints) - // }, - // 10 * ONE_SEC - // ) - // }) }) -testWithAnvil('GovernanceWrapper', (web3: Web3) => { +testWithAnvil('Governance Wrapper', (web3: Web3) => { describe('Hotfixes', () => { it('gets L1 hotfix record for version >= 1.5.0.0', async () => { const kit = newKitFromWeb3(web3) diff --git a/packages/sdk/contractkit/src/wrappers/LockedGold.test.ts b/packages/sdk/contractkit/src/wrappers/LockedGold.test.ts index 594b06d7b..f57705c86 100644 --- a/packages/sdk/contractkit/src/wrappers/LockedGold.test.ts +++ b/packages/sdk/contractkit/src/wrappers/LockedGold.test.ts @@ -22,16 +22,16 @@ testWithAnvil('LockedGold Wrapper', (web3) => { } }) - test('SBAT lock gold', async () => { + it('locks gold', async () => { await lockedGold.lock().sendAndWaitForReceipt({ value }) }) - test('SBAT unlock gold', async () => { + it('unlocks gold', async () => { await lockedGold.lock().sendAndWaitForReceipt({ value }) await lockedGold.unlock(value).sendAndWaitForReceipt() }) - test('SBAT relock gold', async () => { + it('relocks gold', async () => { // Make 5 pending withdrawals. await lockedGold.lock().sendAndWaitForReceipt({ value: value * 5 }) await lockedGold.unlock(value).sendAndWaitForReceipt() @@ -39,11 +39,14 @@ testWithAnvil('LockedGold Wrapper', (web3) => { await lockedGold.unlock(value).sendAndWaitForReceipt() await lockedGold.unlock(value).sendAndWaitForReceipt() await lockedGold.unlock(value).sendAndWaitForReceipt() + // Re-lock 2.5 of them const txos = await lockedGold.relock(account, value * 2.5) - await Promise.all(txos.map((txo) => txo.sendAndWaitForReceipt())) - // + for (const txo of txos) { + await txo.sendAndWaitForReceipt() + } }) + test('should return the count of pending withdrawals', async () => { await lockedGold.lock().sendAndWaitForReceipt({ value: value * 2 }) await lockedGold.unlock(value).sendAndWaitForReceipt()