Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

@celo/contractkit: migrate Governance wrapper tests to anvil #305

Merged
merged 3 commits into from
Jul 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a note here you can have more than changeset for a pr.

in this case decreased web3.eth.transactionPollingInterval to 10ms seems to me like a separate patch than the other 2. which actually would be minor bump as they are new features

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
Loading