Skip to content

Commit

Permalink
@celo/contractkit: migrate Governance wrapper tests to anvil (#305)
Browse files Browse the repository at this point in the history
  • Loading branch information
shazarre authored Jul 23, 2024
1 parent 17f48bc commit 6d6c3fb
Show file tree
Hide file tree
Showing 6 changed files with 116 additions and 132 deletions.
5 changes: 5 additions & 0 deletions .changeset/pretty-comics-lay.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@celo/dev-utils': patch
---

Introduced setDequeueFrequency and setReferendumStageDuration helper functions, decreased web3.eth.transactionPollingInterval to 10ms
12 changes: 11 additions & 1 deletion packages/dev-utils/src/anvil-test.ts
Original file line number Diff line number Diff line change
@@ -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'
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -69,6 +70,15 @@ export const withImpersonatedAccount = async (
await stopImpersonatingAccount(web3, account)
}

export const asCoreContractsOwner = async (
web3: Web3,
fn: (ownerAddress: StrongAddress) => Promise<void>
) => {
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])
}
Expand Down
29 changes: 29 additions & 0 deletions packages/dev-utils/src/chain-setup.ts
Original file line number Diff line number Diff line change
@@ -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'
Expand All @@ -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,
})
})
}
5 changes: 5 additions & 0 deletions packages/dev-utils/src/test-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
184 changes: 58 additions & 126 deletions packages/sdk/contractkit/src/wrappers/Governance.test.ts
Original file line number Diff line number Diff line change
@@ -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'
Expand All @@ -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[] = []
Expand All @@ -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[]
Expand All @@ -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]

Expand All @@ -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', () => {
Expand Down Expand Up @@ -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) => {
Expand Down Expand Up @@ -238,8 +270,6 @@ testWithGanache('Governance Wrapper', (web3: Web3) => {

const exists = await governance.proposalExists(proposalID)
expect(exists).toBeFalsy()

// await verifyRepointResult(repoints)
},
10 * ONE_SEC
)
Expand All @@ -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)
Expand Down
13 changes: 8 additions & 5 deletions packages/sdk/contractkit/src/wrappers/LockedGold.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,28 +22,31 @@ 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()
await lockedGold.unlock(value).sendAndWaitForReceipt()
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()
Expand Down

0 comments on commit 6d6c3fb

Please sign in to comment.