diff --git a/packages/cli/src/commands/governance/approve.test.ts b/packages/cli/src/commands/governance/approve.test.ts index 2e3bb2db9..3c14339fc 100644 --- a/packages/cli/src/commands/governance/approve.test.ts +++ b/packages/cli/src/commands/governance/approve.test.ts @@ -1,6 +1,5 @@ import { hexToBuffer, StrongAddress } from '@celo/base' -import { CeloProvider } from '@celo/connect/lib/celo-provider' -import { newKitFromWeb3 } from '@celo/contractkit' +import { newKitFromProvider } from '@celo/contractkit' import { GovernanceWrapper } from '@celo/contractkit/lib/wrappers/Governance' import { DEFAULT_OWNER_ADDRESS, @@ -16,16 +15,16 @@ import Safe, { } from '@safe-global/protocol-kit' import BigNumber from 'bignumber.js' import fetch from 'cross-fetch' -import Web3 from 'web3' import { changeMultiSigOwner } from '../../test-utils/chain-setup' import { stripAnsiCodesAndTxHashes, stripAnsiCodesFromNestedArray, - testLocallyWithWeb3Node, + testLocallyWithNode, } from '../../test-utils/cliUtils' import { deployMultiCall } from '../../test-utils/multicall' import { createMultisig, setupSafeContracts } from '../../test-utils/multisigUtils' import Approve from './approve' +import { parseEther } from 'viem' // Mock fetch for HTTP status tests jest.mock('cross-fetch') @@ -34,13 +33,13 @@ process.env.NO_SYNCCHECK = 'true' testWithAnvilL2( 'governance:approve cmd', - (web3: Web3) => { + (client) => { const HOTFIX_HASH = '0xbf670baa773b342120e1af45433a465bbd6fa289a5cf72763d63d95e4e22482d' const HOTFIX_BUFFER = hexToBuffer(HOTFIX_HASH) beforeEach(async () => { // need to set multical deployment on the address it was found on alfajores // since this test impersonates the old alfajores chain id - await deployMultiCall(web3, '0xcA11bde05977b3631167028862bE2a173976CA11') + await deployMultiCall(client, '0xcA11bde05977b3631167028862bE2a173976CA11') jest.spyOn(console, 'log').mockImplementation(() => { // noop }) @@ -51,38 +50,34 @@ testWithAnvilL2( describe('hotfix', () => { it('fails when address is not security council multisig signatory', async () => { - const kit = newKitFromWeb3(web3) - const accounts = await web3.eth.getAccounts() + const kit = newKitFromProvider(client) + const accounts = await kit.connection.getAccounts() const governance = await kit.contracts.getGovernance() const writeMock = jest.spyOn(ux.write, 'stdout') const logMock = jest.spyOn(console, 'log') const multisig = await governance.getApproverMultisig() - await withImpersonatedAccount(web3, DEFAULT_OWNER_ADDRESS, async () => { + await withImpersonatedAccount(client, DEFAULT_OWNER_ADDRESS, async () => { // setApprover to 0x5409ED021D9299bf6814279A6A1411A7e866A631 to avoid "Council cannot be approver" error - await ( - await kit.sendTransaction({ - to: governance.address, - from: DEFAULT_OWNER_ADDRESS, - data: '0x3156560e0000000000000000000000005409ed021d9299bf6814279a6a1411a7e866a631', - }) - ).waitReceipt() + await kit.sendTransaction({ + to: governance.address, + from: DEFAULT_OWNER_ADDRESS, + data: '0x3156560e0000000000000000000000005409ed021d9299bf6814279a6a1411a7e866a631', + }) // setSecurityCouncil to multisig address - await ( - await kit.sendTransaction({ - to: governance.address, - from: DEFAULT_OWNER_ADDRESS, - // cast calldata "setSecurityCouncil(address)" - data: `0x1c1083e2000000000000000000000000${multisig.address - .replace('0x', '') - .toLowerCase()}`, - }) - ).waitReceipt() + await kit.sendTransaction({ + to: governance.address, + from: DEFAULT_OWNER_ADDRESS, + // cast calldata "setSecurityCouncil(address)" + data: `0x1c1083e2000000000000000000000000${multisig.address + .replace('0x', '') + .toLowerCase()}`, + }) }) await expect( - testLocallyWithWeb3Node( + testLocallyWithNode( Approve, [ '--from', @@ -93,7 +88,7 @@ testWithAnvilL2( '--type', 'securityCouncil', ], - web3 + client ) ).rejects.toThrow("Some checks didn't pass!") @@ -130,17 +125,17 @@ testWithAnvilL2( }) it('fails when address is not approver multisig signatory', async () => { - const kit = newKitFromWeb3(web3) - const accounts = await web3.eth.getAccounts() + const kit = newKitFromProvider(client) + const accounts = await kit.connection.getAccounts() const governance = await kit.contracts.getGovernance() const writeMock = jest.spyOn(ux.write, 'stdout') const logMock = jest.spyOn(console, 'log') await expect( - testLocallyWithWeb3Node( + testLocallyWithNode( Approve, ['--from', accounts[0], '--hotfix', HOTFIX_HASH, '--useMultiSig'], - web3 + client ) ).rejects.toThrow("Some checks didn't pass!") @@ -177,39 +172,35 @@ testWithAnvilL2( }) it('fails when address is not security council', async () => { - const [approver, securityCouncil, account] = await web3.eth.getAccounts() - const kit = newKitFromWeb3(web3) + const kit = newKitFromProvider(client) + const [approver, securityCouncil, account] = await kit.connection.getAccounts() const governance = await kit.contracts.getGovernance() const writeMock = jest.spyOn(ux.write, 'stdout') const logMock = jest.spyOn(console, 'log') - await withImpersonatedAccount(web3, DEFAULT_OWNER_ADDRESS, async () => { + await withImpersonatedAccount(client, DEFAULT_OWNER_ADDRESS, async () => { // setApprover to approver value - await ( - await kit.sendTransaction({ - to: governance.address, - from: DEFAULT_OWNER_ADDRESS, - data: `0x3156560e000000000000000000000000${approver.replace('0x', '').toLowerCase()}`, - }) - ).waitReceipt() + await kit.sendTransaction({ + to: governance.address, + from: DEFAULT_OWNER_ADDRESS, + data: `0x3156560e000000000000000000000000${approver.replace('0x', '').toLowerCase()}`, + }) // setSecurityCouncil to securityCouncil value - await ( - await kit.sendTransaction({ - to: governance.address, - from: DEFAULT_OWNER_ADDRESS, - data: `0x1c1083e2000000000000000000000000${securityCouncil - .replace('0x', '') - .toLowerCase()}`, - }) - ).waitReceipt() + await kit.sendTransaction({ + to: governance.address, + from: DEFAULT_OWNER_ADDRESS, + data: `0x1c1083e2000000000000000000000000${securityCouncil + .replace('0x', '') + .toLowerCase()}`, + }) }) await expect( - testLocallyWithWeb3Node( + testLocallyWithNode( Approve, ['--from', account, '--hotfix', HOTFIX_HASH, '--type', 'securityCouncil'], - web3 + client ) ).rejects.toThrow("Some checks didn't pass!") @@ -243,36 +234,32 @@ testWithAnvilL2( }) it('fails when address is not approver', async () => { - const kit = newKitFromWeb3(web3) - const [approver, securityCouncil, account] = await web3.eth.getAccounts() + const kit = newKitFromProvider(client) + const [approver, securityCouncil, account] = await kit.connection.getAccounts() const governance = await kit.contracts.getGovernance() const writeMock = jest.spyOn(ux.write, 'stdout') const logMock = jest.spyOn(console, 'log') - await withImpersonatedAccount(web3, DEFAULT_OWNER_ADDRESS, async () => { + await withImpersonatedAccount(client, DEFAULT_OWNER_ADDRESS, async () => { // setApprover to approver value - await ( - await kit.sendTransaction({ - to: governance.address, - from: DEFAULT_OWNER_ADDRESS, - data: `0x3156560e000000000000000000000000${approver.replace('0x', '').toLowerCase()}`, - }) - ).waitReceipt() + await kit.sendTransaction({ + to: governance.address, + from: DEFAULT_OWNER_ADDRESS, + data: `0x3156560e000000000000000000000000${approver.replace('0x', '').toLowerCase()}`, + }) // setSecurityCouncil to securityCouncil value - await ( - await kit.sendTransaction({ - to: governance.address, - from: DEFAULT_OWNER_ADDRESS, - data: `0x1c1083e2000000000000000000000000${securityCouncil - .replace('0x', '') - .toLowerCase()}`, - }) - ).waitReceipt() + await kit.sendTransaction({ + to: governance.address, + from: DEFAULT_OWNER_ADDRESS, + data: `0x1c1083e2000000000000000000000000${securityCouncil + .replace('0x', '') + .toLowerCase()}`, + }) }) await expect( - testLocallyWithWeb3Node(Approve, ['--from', account, '--hotfix', HOTFIX_HASH], web3) + testLocallyWithNode(Approve, ['--from', account, '--hotfix', HOTFIX_HASH], client) ).rejects.toThrow("Some checks didn't pass!") expect(await governance.getHotfixRecord(HOTFIX_BUFFER)).toMatchInlineSnapshot(` @@ -305,38 +292,34 @@ testWithAnvilL2( }) it('succeeds when address is a direct security council', async () => { - const [approver, securityCouncil] = await web3.eth.getAccounts() - const kit = newKitFromWeb3(web3) + const kit = newKitFromProvider(client) + const [approver, securityCouncil] = await kit.connection.getAccounts() const governance = await kit.contracts.getGovernance() const writeMock = jest.spyOn(ux.write, 'stdout') const logMock = jest.spyOn(console, 'log') - await withImpersonatedAccount(web3, DEFAULT_OWNER_ADDRESS, async () => { + await withImpersonatedAccount(client, DEFAULT_OWNER_ADDRESS, async () => { // setApprover to approver value - await ( - await kit.sendTransaction({ - to: governance.address, - from: DEFAULT_OWNER_ADDRESS, - data: `0x3156560e000000000000000000000000${approver.replace('0x', '').toLowerCase()}`, - }) - ).waitReceipt() + await kit.sendTransaction({ + to: governance.address, + from: DEFAULT_OWNER_ADDRESS, + data: `0x3156560e000000000000000000000000${approver.replace('0x', '').toLowerCase()}`, + }) // setSecurityCouncil to securityCouncil value - await ( - await kit.sendTransaction({ - to: governance.address, - from: DEFAULT_OWNER_ADDRESS, - data: `0x1c1083e2000000000000000000000000${securityCouncil - .replace('0x', '') - .toLowerCase()}`, - }) - ).waitReceipt() + await kit.sendTransaction({ + to: governance.address, + from: DEFAULT_OWNER_ADDRESS, + data: `0x1c1083e2000000000000000000000000${securityCouncil + .replace('0x', '') + .toLowerCase()}`, + }) }) - await testLocallyWithWeb3Node( + await testLocallyWithNode( Approve, ['--from', securityCouncil, '--hotfix', HOTFIX_HASH, '--type', 'securityCouncil'], - web3 + client ) expect(await governance.getHotfixRecord(HOTFIX_BUFFER)).toMatchInlineSnapshot(` @@ -350,70 +333,59 @@ testWithAnvilL2( expect( logMock.mock.calls.map((args) => args.map(stripAnsiCodesAndTxHashes)) ).toMatchInlineSnapshot(` - [ - [ - "Running Checks:", - ], - [ - " ✔ 0x6Ecbe1DB9EF729CBe972C83Fb886247691Fb6beb is security council address ", - ], - [ - " ✔ Hotfix 0xbf670baa773b342120e1af45433a465bbd6fa289a5cf72763d63d95e4e22482d is not already approved by security council ", - ], - [ - " ✔ Hotfix 0xbf670baa773b342120e1af45433a465bbd6fa289a5cf72763d63d95e4e22482d is not already executed ", - ], - [ - "All checks passed", - ], - [ - "SendTransaction: approveTx", - ], - [ - "txHash: 0xtxhash", - ], - [ - "HotfixApproved:", - ], - [ - "hash: 0xbf670baa773b342120e1af45433a465bbd6fa289a5cf72763d63d95e4e22482d - approver: 0x6Ecbe1DB9EF729CBe972C83Fb886247691Fb6beb", - ], - ] - `) + [ + [ + "Running Checks:", + ], + [ + " ✔ 0x6Ecbe1DB9EF729CBe972C83Fb886247691Fb6beb is security council address ", + ], + [ + " ✔ Hotfix 0xbf670baa773b342120e1af45433a465bbd6fa289a5cf72763d63d95e4e22482d is not already approved by security council ", + ], + [ + " ✔ Hotfix 0xbf670baa773b342120e1af45433a465bbd6fa289a5cf72763d63d95e4e22482d is not already executed ", + ], + [ + "All checks passed", + ], + [ + "SendTransaction: approveTx", + ], + [ + "txHash: 0xtxhash", + ], + ] + `) expect(writeMock.mock.calls).toMatchInlineSnapshot(`[]`) }) it('succeeds when address is a direct approver', async () => { - const kit = newKitFromWeb3(web3) - const [approver, securityCouncil] = await web3.eth.getAccounts() + const kit = newKitFromProvider(client) + const [approver, securityCouncil] = await kit.connection.getAccounts() const governance = await kit.contracts.getGovernance() const writeMock = jest.spyOn(ux.write, 'stdout') const logMock = jest.spyOn(console, 'log') - await withImpersonatedAccount(web3, DEFAULT_OWNER_ADDRESS, async () => { + await withImpersonatedAccount(client, DEFAULT_OWNER_ADDRESS, async () => { // setApprover to approver value - await ( - await kit.sendTransaction({ - to: governance.address, - from: DEFAULT_OWNER_ADDRESS, - data: `0x3156560e000000000000000000000000${approver.replace('0x', '').toLowerCase()}`, - }) - ).waitReceipt() + await kit.sendTransaction({ + to: governance.address, + from: DEFAULT_OWNER_ADDRESS, + data: `0x3156560e000000000000000000000000${approver.replace('0x', '').toLowerCase()}`, + }) // setSecurityCouncil to securityCouncil value - await ( - await kit.sendTransaction({ - to: governance.address, - from: DEFAULT_OWNER_ADDRESS, - data: `0x1c1083e2000000000000000000000000${securityCouncil - .replace('0x', '') - .toLowerCase()}`, - }) - ).waitReceipt() + await kit.sendTransaction({ + to: governance.address, + from: DEFAULT_OWNER_ADDRESS, + data: `0x1c1083e2000000000000000000000000${securityCouncil + .replace('0x', '') + .toLowerCase()}`, + }) }) - await testLocallyWithWeb3Node(Approve, ['--from', approver, '--hotfix', HOTFIX_HASH], web3) + await testLocallyWithNode(Approve, ['--from', approver, '--hotfix', HOTFIX_HASH], client) expect(await governance.getHotfixRecord(HOTFIX_BUFFER)).toMatchInlineSnapshot(` { @@ -426,43 +398,36 @@ testWithAnvilL2( expect( logMock.mock.calls.map((args) => args.map(stripAnsiCodesAndTxHashes)) ).toMatchInlineSnapshot(` - [ - [ - "Running Checks:", - ], - [ - " ✔ 0x5409ED021D9299bf6814279A6A1411A7e866A631 is approver address ", - ], - [ - " ✔ Hotfix 0xbf670baa773b342120e1af45433a465bbd6fa289a5cf72763d63d95e4e22482d is not already approved ", - ], - [ - " ✔ Hotfix 0xbf670baa773b342120e1af45433a465bbd6fa289a5cf72763d63d95e4e22482d is not already executed ", - ], - [ - "All checks passed", - ], - [ - "SendTransaction: approveTx", - ], - [ - "txHash: 0xtxhash", - ], - [ - "HotfixApproved:", - ], - [ - "hash: 0xbf670baa773b342120e1af45433a465bbd6fa289a5cf72763d63d95e4e22482d - approver: 0x5409ED021D9299bf6814279A6A1411A7e866A631", - ], - ] - `) + [ + [ + "Running Checks:", + ], + [ + " ✔ 0x5409ED021D9299bf6814279A6A1411A7e866A631 is approver address ", + ], + [ + " ✔ Hotfix 0xbf670baa773b342120e1af45433a465bbd6fa289a5cf72763d63d95e4e22482d is not already approved ", + ], + [ + " ✔ Hotfix 0xbf670baa773b342120e1af45433a465bbd6fa289a5cf72763d63d95e4e22482d is not already executed ", + ], + [ + "All checks passed", + ], + [ + "SendTransaction: approveTx", + ], + [ + "txHash: 0xtxhash", + ], + ] + `) expect(writeMock.mock.calls).toMatchInlineSnapshot(`[]`) }) it('succeeds when address is security council multisig signatory', async () => { - const kit = newKitFromWeb3(web3) - const accounts = (await web3.eth.getAccounts()) as StrongAddress[] + const kit = newKitFromProvider(client) + const accounts = (await kit.connection.getAccounts()) as StrongAddress[] const governance = await kit.contracts.getGovernance() const writeMock = jest.spyOn(ux.write, 'stdout') const logMock = jest.spyOn(console, 'log') @@ -470,35 +435,31 @@ testWithAnvilL2( await changeMultiSigOwner(kit, accounts[0]) - await withImpersonatedAccount(web3, DEFAULT_OWNER_ADDRESS, async () => { + await withImpersonatedAccount(client, DEFAULT_OWNER_ADDRESS, async () => { // setApprover to 0x5409ED021D9299bf6814279A6A1411A7e866A631 to avoid "Council cannot be approver" error - await ( - await kit.sendTransaction({ - to: governance.address, - from: DEFAULT_OWNER_ADDRESS, - // cast calldata "setApprover(address)" "0x5409ED021D9299bf6814279A6A1411A7e866A631" - data: '0x3156560e0000000000000000000000005409ed021d9299bf6814279a6a1411a7e866a631', - }) - ).waitReceipt() + await kit.sendTransaction({ + to: governance.address, + from: DEFAULT_OWNER_ADDRESS, + // cast calldata "setApprover(address)" "0x5409ED021D9299bf6814279A6A1411A7e866A631" + data: '0x3156560e0000000000000000000000005409ed021d9299bf6814279a6a1411a7e866a631', + }) // setSecurityCouncil to multisig address - await ( - await kit.sendTransaction({ - to: governance.address, - from: DEFAULT_OWNER_ADDRESS, - // cast calldata "setSecurityCouncil(address)" - data: `0x1c1083e2000000000000000000000000${multisig.address - .replace('0x', '') - .toLowerCase()}`, - }) - ).waitReceipt() + await kit.sendTransaction({ + to: governance.address, + from: DEFAULT_OWNER_ADDRESS, + // cast calldata "setSecurityCouncil(address)" + data: `0x1c1083e2000000000000000000000000${multisig.address + .replace('0x', '') + .toLowerCase()}`, + }) }) // Sanity checks expect(await governance.getApprover()).toBe(accounts[0]) expect(await governance.getSecurityCouncil()).toEqual(multisig.address) - await testLocallyWithWeb3Node( + await testLocallyWithNode( Approve, [ '--from', @@ -509,7 +470,7 @@ testWithAnvilL2( '--type', 'securityCouncil', ], - web3 + client ) expect(await governance.getHotfixRecord(HOTFIX_BUFFER)).toMatchInlineSnapshot(` @@ -555,11 +516,11 @@ testWithAnvilL2( }) it('succeeds when address is security council safe signatory', async () => { - await setupSafeContracts(web3) + await setupSafeContracts(client) - const kit = newKitFromWeb3(web3) + const kit = newKitFromProvider(client) const [approver, securityCouncilSafeSignatory1] = - (await web3.eth.getAccounts()) as StrongAddress[] + (await kit.connection.getAccounts()) as StrongAddress[] const securityCouncilSafeSignatory2: StrongAddress = '0x6C666E57A5E8715cFE93f92790f98c4dFf7b69e2' const securityCouncilSafeSignatory2PrivateKey = @@ -579,44 +540,45 @@ testWithAnvilL2( const protocolKit = await Safe.init({ predictedSafe: predictSafe, - provider: (web3.currentProvider as any as CeloProvider).toEip1193Provider(), + provider: kit.connection.currentProvider as any, signer: securityCouncilSafeSignatory1, }) const deploymentTransaction = await protocolKit.createSafeDeploymentTransaction() - const receipt = await web3.eth.sendTransaction({ + const txHash = await kit.connection.sendTransaction({ from: securityCouncilSafeSignatory1, ...deploymentTransaction, }) + const receipt = await kit.connection.viemClient.waitForTransactionReceipt({ + hash: txHash as `0x${string}`, + }) - // @ts-expect-error the function is able to extract safe adddress without having - const safeAddress = getSafeAddressFromDeploymentTx(receipt, '1.3.0') + const safeAddress = getSafeAddressFromDeploymentTx( + receipt as unknown as Parameters[0], + '1.3.0' + ) protocolKit.connect({ safeAddress }) - await withImpersonatedAccount(web3, DEFAULT_OWNER_ADDRESS, async () => { + await withImpersonatedAccount(client, DEFAULT_OWNER_ADDRESS, async () => { // setApprover to 0x5409ED021D9299bf6814279A6A1411A7e866A631 to avoid "Council cannot be approver" error - await ( - await kit.sendTransaction({ - to: governance.address, - from: DEFAULT_OWNER_ADDRESS, - // cast calldata "setApprover(address)" "0x5409ED021D9299bf6814279A6A1411A7e866A631" - data: '0x3156560e0000000000000000000000005409ed021d9299bf6814279a6a1411a7e866a631', - }) - ).waitReceipt() + await kit.sendTransaction({ + to: governance.address, + from: DEFAULT_OWNER_ADDRESS, + // cast calldata "setApprover(address)" "0x5409ED021D9299bf6814279A6A1411A7e866A631" + data: '0x3156560e0000000000000000000000005409ed021d9299bf6814279a6a1411a7e866a631', + }) // setSecurityCouncil to safe address - await ( - await kit.sendTransaction({ - to: governance.address, - from: DEFAULT_OWNER_ADDRESS, - // cast calldata "setSecurityCouncil(address)" - data: `0x1c1083e2000000000000000000000000${safeAddress - .replace('0x', '') - .toLowerCase()}`, - }) - ).waitReceipt() + await kit.sendTransaction({ + to: governance.address, + from: DEFAULT_OWNER_ADDRESS, + // cast calldata "setSecurityCouncil(address)" + data: `0x1c1083e2000000000000000000000000${safeAddress + .replace('0x', '') + .toLowerCase()}`, + }) }) // Sanity checks @@ -636,7 +598,7 @@ testWithAnvilL2( } `) - await testLocallyWithWeb3Node( + await testLocallyWithNode( Approve, [ '--from', @@ -647,11 +609,11 @@ testWithAnvilL2( '--type', 'securityCouncil', ], - web3 + client ) // Run the same command twice with same arguments to make sure it doesn't have any effect - await testLocallyWithWeb3Node( + await testLocallyWithNode( Approve, [ '--from', @@ -662,7 +624,7 @@ testWithAnvilL2( '--type', 'securityCouncil', ], - web3 + client ) expect(await governance.getHotfixRecord(HOTFIX_BUFFER)).toMatchInlineSnapshot(` @@ -675,12 +637,8 @@ testWithAnvilL2( `) // Make sure the account has enough balance to pay for the transaction - await setBalance( - web3, - securityCouncilSafeSignatory2, - BigInt(web3.utils.toWei('1', 'ether')) - ) - await testLocallyWithWeb3Node( + await setBalance(client, securityCouncilSafeSignatory2, BigInt(parseEther('1').toString())) + await testLocallyWithNode( Approve, [ '--from', @@ -694,7 +652,7 @@ testWithAnvilL2( '--privateKey', securityCouncilSafeSignatory2PrivateKey, ], - web3 + client ) expect(await governance.getHotfixRecord(HOTFIX_BUFFER)).toMatchInlineSnapshot(` @@ -771,8 +729,8 @@ testWithAnvilL2( }) it('succeeds when address is approver multisig signatory', async () => { - const kit = newKitFromWeb3(web3) - const accounts = (await web3.eth.getAccounts()) as StrongAddress[] + const kit = newKitFromProvider(client) + const accounts = (await kit.connection.getAccounts()) as StrongAddress[] await changeMultiSigOwner(kit, accounts[0]) @@ -780,10 +738,10 @@ testWithAnvilL2( const writeMock = jest.spyOn(ux.write, 'stdout') const logMock = jest.spyOn(console, 'log') - await testLocallyWithWeb3Node( + await testLocallyWithNode( Approve, ['--from', accounts[0], '--hotfix', HOTFIX_HASH, '--useMultiSig'], - web3 + client ) expect(await governance.getHotfixRecord(HOTFIX_BUFFER)).toMatchInlineSnapshot(` @@ -828,8 +786,8 @@ testWithAnvilL2( }) it('succeeds when address is security council multisig signatory', async () => { - const kit = newKitFromWeb3(web3) - const accounts = (await web3.eth.getAccounts()) as StrongAddress[] + const kit = newKitFromProvider(client) + const accounts = (await kit.connection.getAccounts()) as StrongAddress[] await changeMultiSigOwner(kit, accounts[0]) @@ -838,30 +796,26 @@ testWithAnvilL2( const logMock = jest.spyOn(console, 'log') const multisig = await governance.getApproverMultisig() - await withImpersonatedAccount(web3, DEFAULT_OWNER_ADDRESS, async () => { + await withImpersonatedAccount(client, DEFAULT_OWNER_ADDRESS, async () => { // setApprover to 0x5409ED021D9299bf6814279A6A1411A7e866A631 to avoid "Council cannot be approver" error - await ( - await kit.sendTransaction({ - to: governance.address, - from: DEFAULT_OWNER_ADDRESS, - data: '0x3156560e0000000000000000000000005409ed021d9299bf6814279a6a1411a7e866a631', - }) - ).waitReceipt() + await kit.sendTransaction({ + to: governance.address, + from: DEFAULT_OWNER_ADDRESS, + data: '0x3156560e0000000000000000000000005409ed021d9299bf6814279a6a1411a7e866a631', + }) // setSecurityCouncil to multisig address - await ( - await kit.sendTransaction({ - to: governance.address, - from: DEFAULT_OWNER_ADDRESS, - // cast calldata "setSecurityCouncil(address)" - data: `0x1c1083e2000000000000000000000000${multisig.address - .replace('0x', '') - .toLowerCase()}`, - }) - ).waitReceipt() + await kit.sendTransaction({ + to: governance.address, + from: DEFAULT_OWNER_ADDRESS, + // cast calldata "setSecurityCouncil(address)" + data: `0x1c1083e2000000000000000000000000${multisig.address + .replace('0x', '') + .toLowerCase()}`, + }) }) - await testLocallyWithWeb3Node( + await testLocallyWithNode( Approve, [ '--from', @@ -872,7 +826,7 @@ testWithAnvilL2( '--type', 'securityCouncil', ], - web3 + client ) expect(await governance.getHotfixRecord(HOTFIX_BUFFER)).toMatchInlineSnapshot(` @@ -923,23 +877,30 @@ testWithAnvilL2( let accounts: StrongAddress[] beforeEach(async () => { - accounts = (await web3.eth.getAccounts()) as StrongAddress[] - const kit = newKitFromWeb3(web3) + const kit = newKitFromProvider(client) + accounts = (await kit.connection.getAccounts()) as StrongAddress[] governance = await kit.contracts.getGovernance() // Create and dequeue a proposal const minDeposit = (await governance.minDeposit()).toString() - await governance - .propose([], 'https://example.com/proposal') - .sendAndWaitForReceipt({ from: accounts[0], value: minDeposit }) + const proposeHash = await governance.propose([], 'https://example.com/proposal', { + from: accounts[0], + value: minDeposit, + }) + await kit.connection.viemClient.waitForTransactionReceipt({ + hash: proposeHash as `0x${string}`, + }) proposalId = new BigNumber(1) // Dequeue the proposal const dequeueFrequency = (await governance.dequeueFrequency()).toNumber() const { timeTravel } = await import('@celo/dev-utils/ganache-test') - await timeTravel(dequeueFrequency + 1, web3) - await governance.dequeueProposalsIfReady().sendAndWaitForReceipt({ from: accounts[0] }) + await timeTravel(dequeueFrequency + 1, client) + const dequeueHash = await governance.dequeueProposalsIfReady({ from: accounts[0] }) + await kit.connection.viemClient.waitForTransactionReceipt({ + hash: dequeueHash as `0x${string}`, + }) // Make accounts[0] the multisig owner await changeMultiSigOwner(kit, accounts[0]) @@ -949,10 +910,10 @@ testWithAnvilL2( const writeMock = jest.spyOn(ux.write, 'stdout') const logMock = jest.spyOn(console, 'log') - await testLocallyWithWeb3Node( + await testLocallyWithNode( Approve, ['--from', accounts[0], '--proposalID', proposalId.toString(), '--useMultiSig'], - web3 + client ) expect(await governance.isApproved(proposalId)).toBe(true) @@ -1014,7 +975,7 @@ testWithAnvilL2( }) await expect( - testLocallyWithWeb3Node( + testLocallyWithNode( Approve, [ '--from', @@ -1024,7 +985,7 @@ testWithAnvilL2( '--useMultiSig', '--submit', ], - web3 + client ) ).rejects.toThrow("Some checks didn't pass!") @@ -1070,7 +1031,7 @@ testWithAnvilL2( }) await expect( - testLocallyWithWeb3Node( + testLocallyWithNode( Approve, [ '--from', @@ -1080,7 +1041,7 @@ testWithAnvilL2( '--useMultiSig', '--submit', ], - web3 + client ) ).resolves.toBeUndefined() @@ -1124,20 +1085,20 @@ testWithAnvilL2( it('should confirm existing multisig transaction when --multisigTx is provided', async () => { const logMock = jest.spyOn(console, 'log') - const kit = newKitFromWeb3(web3) + const kit = newKitFromProvider(client) // Create a 2-signer multisig so the transaction won't execute immediately const twoSignerMultisig = await createMultisig(kit, [accounts[0], accounts[1]], 2, 2) // Set the new multisig as the governance approver - await withImpersonatedAccount(web3, DEFAULT_OWNER_ADDRESS, async () => { - await ( - await kit.sendTransaction({ - to: governance.address, - from: DEFAULT_OWNER_ADDRESS, - data: `0x3156560e000000000000000000000000${twoSignerMultisig.replace('0x', '').toLowerCase()}`, - }) - ).waitReceipt() + await withImpersonatedAccount(client, DEFAULT_OWNER_ADDRESS, async () => { + await kit.sendTransaction({ + to: governance.address, + from: DEFAULT_OWNER_ADDRESS, + data: `0x3156560e000000000000000000000000${twoSignerMultisig + .replace('0x', '') + .toLowerCase()}`, + }) }) // Get the new multisig wrapper @@ -1145,10 +1106,18 @@ testWithAnvilL2( // First, submit the transaction to multisig from accounts[0] // This won't execute because it requires 2 confirmations - const approveTx = await governance.approve(proposalId) - await ( - await multisig.submitTransaction(governance.address, approveTx.txo) - ).sendAndWaitForReceipt({ from: accounts[0] }) + const dequeue = await governance.getDequeue() + const proposalIndex = dequeue.findIndex((id: BigNumber) => id.isEqualTo(proposalId)) + const approveData = governance.encodeFunctionData('approve', [ + proposalId.toString(), + proposalIndex, + ]) + const submitHash = await multisig.submitTransaction(governance.address, approveData, '0', { + from: accounts[0], + }) + await kit.connection.viemClient.waitForTransactionReceipt({ + hash: submitHash as `0x${string}`, + }) // Verify proposal is not yet approved expect(await governance.isApproved(proposalId)).toBe(false) @@ -1173,7 +1142,7 @@ testWithAnvilL2( // Now confirm it with the multisigTx from accounts[1] await expect( - testLocallyWithWeb3Node( + testLocallyWithNode( Approve, [ '--from', @@ -1184,7 +1153,7 @@ testWithAnvilL2( '--multisigTx', '0', ], - web3 + client ) ).resolves.toBeUndefined() @@ -1193,39 +1162,39 @@ testWithAnvilL2( expect( logMock.mock.calls.map((args) => args.map(stripAnsiCodesAndTxHashes)) ).toMatchInlineSnapshot(` - [ - [ - "Running Checks:", - ], - [ - " ✔ ${twoSignerMultisig} is approver address ", - ], - [ - " ✔ ${accounts[1]} is multisig signatory ", - ], - [ - " ✔ 1 is an existing proposal ", - ], - [ - " ✔ 1 is in stage Referendum or Execution ", - ], - [ - " ✔ 1 not already approved ", - ], - [ - " ✔ multisgTXId provided is valid ", - ], - [ - "All checks passed", - ], - [ - "SendTransaction: approveTx", - ], - [ - "txHash: 0xtxhash", - ], - ] - `) + [ + [ + "Running Checks:", + ], + [ + " ✔ 0x0B1ba0af832d7C05fD64161E0Db78E85978E8082 is approver address ", + ], + [ + " ✔ 0x6Ecbe1DB9EF729CBe972C83Fb886247691Fb6beb is multisig signatory ", + ], + [ + " ✔ 1 is an existing proposal ", + ], + [ + " ✔ 1 is in stage Referendum or Execution ", + ], + [ + " ✔ 1 not already approved ", + ], + [ + " ✔ multisgTXId provided is valid ", + ], + [ + "All checks passed", + ], + [ + "SendTransaction: approveTx", + ], + [ + "txHash: 0xtxhash", + ], + ] + `) }) it('should fail when invalid --multisigTx is provided', async () => { @@ -1250,7 +1219,7 @@ testWithAnvilL2( }) await expect( - testLocallyWithWeb3Node( + testLocallyWithNode( Approve, [ '--from', @@ -1261,7 +1230,7 @@ testWithAnvilL2( '--multisigTx', '0', // Invalid ID ], - web3 + client ) ).rejects.toThrow("Some checks didn't pass!") @@ -1316,10 +1285,10 @@ testWithAnvilL2( // Without --submit flag, this should work because the default behavior // is submitOrConfirmTransaction which will confirm if it exists - await testLocallyWithWeb3Node( + await testLocallyWithNode( Approve, ['--from', accounts[0], '--proposalID', proposalId.toString(), '--useMultiSig'], - web3 + client ) expect(stripAnsiCodesFromNestedArray(logMock.mock.calls)).toMatchInlineSnapshot(` diff --git a/packages/cli/src/commands/governance/approve.ts b/packages/cli/src/commands/governance/approve.ts index 72173cbe3..5f09fa9ac 100644 --- a/packages/cli/src/commands/governance/approve.ts +++ b/packages/cli/src/commands/governance/approve.ts @@ -1,23 +1,17 @@ import { StrongAddress } from '@celo/base' -import { CeloTransactionObject } from '@celo/connect' +import { type Provider } from '@celo/connect' import { GovernanceWrapper } from '@celo/contractkit/lib/wrappers/Governance' import { MultiSigWrapper } from '@celo/contractkit/lib/wrappers/MultiSig' -import { toBuffer } from '@ethereumjs/util' +import { hexToBytes } from 'viem' import { Flags } from '@oclif/core' import fetch from 'cross-fetch' import debugFactory from 'debug' import { Hex } from 'viem' - -import Web3 from 'web3' import { BaseCommand } from '../../base' import { newCheckBuilder } from '../../utils/checks' -import { displaySendTx, failWith } from '../../utils/cli' +import { displayViemTx, failWith } from '../../utils/cli' import { CustomFlags } from '../../utils/command' -import { - createSafeFromWeb3, - performSafeTransaction, - safeTransactionMetadataFromCeloTransactionObject, -} from '../../utils/safe' +import { createSafe, performSafeTransaction, safeTransactionMetadata } from '../../utils/safe' enum HotfixApprovalType { APPROVER = 'approver', @@ -80,6 +74,7 @@ export default class Approve extends BaseCommand { async run() { const checkBuilder = newCheckBuilder(this) const kit = await this.getKit() + const publicClient = await this.getPublicClient() const res = await this.parse(Approve) const account = res.flags.from const useMultiSig = res.flags.useMultiSig @@ -98,7 +93,7 @@ export default class Approve extends BaseCommand { const approver = useMultiSig ? governanceApproverMultiSig!.address : account await addDefaultChecks( - await this.getWeb3(), + (await this.getKit()).connection.currentProvider, checkBuilder, governance, !!hotfix, @@ -111,11 +106,11 @@ export default class Approve extends BaseCommand { governanceApproverMultiSig ) - let governanceTx: CeloTransactionObject - let logEvent: string + let encodedGovernanceData: `0x${string}` | undefined if (id) { if (await governance.isQueued(id)) { - await governance.dequeueProposalsIfReady().sendAndWaitForReceipt() + const dequeueHash = await governance.dequeueProposalsIfReady() + await publicClient.waitForTransactionReceipt({ hash: dequeueHash }) } await checkBuilder @@ -126,78 +121,96 @@ export default class Approve extends BaseCommand { 'Proposal has not been submitted to multisig', res.flags.submit, async () => { - // We would prefer it allow for submissions if there is ambiguity, only fail if we confirm that it has been submitted const confrimations = await fetchConfirmationsForProposals(id) return confrimations === null || confrimations.count === 0 } ) .addConditionalCheck('multisgTXId provided is valid', !!res.flags.multisigTx, async () => { const confirmations = await fetchConfirmationsForProposals(id) - // if none are found the api could be wrong, so we allow it. if (!confirmations || confirmations.count === 0) { return true } - // if we have confirmations, ensure one matches the provided id return confirmations.approvals.some( (approval) => approval.multisigTx.toString() === res.flags.multisigTx ) }) .runChecks() - governanceTx = await governance.approve(id) - logEvent = 'ProposalApproved' + + if (useMultiSig || useSafe) { + const dequeue = await governance.getDequeue() + const proposalIndex = dequeue.findIndex((d) => d.eq(id)) + encodedGovernanceData = governance.encodeFunctionData('approve', [ + id, + proposalIndex.toString(), + ]) + } } else if (hotfix) { await checkBuilder.runChecks() - // TODO dedup toBuffer - governanceTx = governance.approveHotfix(toBuffer(hotfix) as Buffer) - logEvent = 'HotfixApproved' + if (useMultiSig || useSafe) { + encodedGovernanceData = governance.encodeFunctionData('approveHotfix', [hotfix]) + } } else { failWith('Proposal ID or hotfix must be provided') } if (approvalType === 'securityCouncil' && useSafe) { await performSafeTransaction( - await this.getWeb3(), - await governance.getSecurityCouncil(), + (await this.getKit()).connection.currentProvider, + (await governance.getSecurityCouncil()) as StrongAddress, account, - await safeTransactionMetadataFromCeloTransactionObject(governanceTx, governance.address) + safeTransactionMetadata(encodedGovernanceData!, governance.address) ) } else if ( approvalType === 'securityCouncil' && useMultiSig && governanceSecurityCouncilMultiSig ) { - const tx = await governanceSecurityCouncilMultiSig.submitOrConfirmTransaction( - governance.address, - governanceTx.txo + await displayViemTx( + 'approveTx', + governanceSecurityCouncilMultiSig.submitOrConfirmTransaction( + governance.address, + encodedGovernanceData! + ), + publicClient ) - - await displaySendTx('approveTx', tx, {}, logEvent) } else if (res.flags.multisigTx && useMultiSig) { - const tx = await governanceApproverMultiSig!.confirmTransaction( - parseInt(res.flags.multisigTx) + await displayViemTx( + 'approveTx', + governanceApproverMultiSig!.confirmTransaction(parseInt(res.flags.multisigTx)), + publicClient ) - await displaySendTx('approveTx', tx, {}, logEvent) } else if (res.flags.submit && useMultiSig) { - const tx = await governanceApproverMultiSig!.submitTransaction( - governance.address, - governanceTx.txo + await displayViemTx( + 'approveTx', + governanceApproverMultiSig!.submitTransaction(governance.address, encodedGovernanceData!), + publicClient + ) + } else if (useMultiSig) { + await displayViemTx( + 'approveTx', + governanceApproverMultiSig!.submitOrConfirmTransaction( + governance.address, + encodedGovernanceData! + ), + publicClient ) - await displaySendTx('approveTx', tx, {}, logEvent) } else { - const tx = useMultiSig - ? await governanceApproverMultiSig!.submitOrConfirmTransaction( - governance.address, - governanceTx.txo - ) - : governanceTx - await displaySendTx('approveTx', tx, {}, logEvent) + if (id) { + await displayViemTx('approveTx', governance.approve(id), publicClient) + } else { + await displayViemTx( + 'approveTx', + governance.approveHotfix(Buffer.from(hexToBytes(hotfix! as `0x${string}`))), + publicClient + ) + } } } } const addDefaultChecks = async ( - web3: Web3, + provider: Provider, checkBuilder: ReturnType, governance: GovernanceWrapper, isHotfix: boolean, @@ -210,7 +223,7 @@ const addDefaultChecks = async ( governanceApproverMultiSig: MultiSigWrapper | undefined ) => { if (isHotfix) { - const hotfixBuf = toBuffer(hotfix) as Buffer + const hotfixBuf = Buffer.from(hexToBytes(hotfix as `0x${string}`)) if (approvalType === HotfixApprovalType.APPROVER || approvalType === undefined) { if (useMultiSig) { @@ -238,10 +251,10 @@ const addDefaultChecks = async ( }) } else if (useSafe) { checkBuilder.addCheck(`${account} is security council safe signatory`, async () => { - const protocolKit = await createSafeFromWeb3( - web3, + const protocolKit = await createSafe( + provider, account, - await governance.getSecurityCouncil() + (await governance.getSecurityCouncil()) as StrongAddress ) return await protocolKit.isOwner(account) diff --git a/packages/cli/src/commands/governance/build-proposals.test.ts b/packages/cli/src/commands/governance/build-proposals.test.ts index 6bcf10fa3..0b3a0b327 100644 --- a/packages/cli/src/commands/governance/build-proposals.test.ts +++ b/packages/cli/src/commands/governance/build-proposals.test.ts @@ -2,8 +2,7 @@ import CeloTokenABI from '@celo/abis/GoldToken.json' import { testWithAnvilL2 } from '@celo/dev-utils/anvil-test' import { readJSON, removeSync } from 'fs-extra' import inquirer from 'inquirer' -import Web3 from 'web3' -import { testLocallyWithWeb3Node } from '../../test-utils/cliUtils' +import { testLocallyWithNode } from '../../test-utils/cliUtils' import BuildProposal from './build-proposal' process.env.NO_SYNCCHECK = 'true' @@ -13,7 +12,7 @@ jest.mock('inquirer') const TX_PATH_FOR_TEST = './test-tx.json' -testWithAnvilL2('governance:build-proposal cmd', (web3: Web3) => { +testWithAnvilL2('governance:build-proposal cmd', (provider) => { describe('building proposal to transfer funds from governance', () => { beforeEach(async () => { const promptSpy = jest @@ -37,7 +36,7 @@ testWithAnvilL2('governance:build-proposal cmd', (web3: Web3) => { promptSpy.mockResolvedValueOnce({ 'Celo Contract': '✔ done' }) }) it('generates the json', async () => { - await testLocallyWithWeb3Node(BuildProposal, ['--output', TX_PATH_FOR_TEST], web3) + await testLocallyWithNode(BuildProposal, ['--output', TX_PATH_FOR_TEST], provider) const result = await readJSON(TX_PATH_FOR_TEST) expect(result).toMatchInlineSnapshot(` [ diff --git a/packages/cli/src/commands/governance/dequeue.test.ts b/packages/cli/src/commands/governance/dequeue.test.ts index db19e39e3..33fbb0ab3 100644 --- a/packages/cli/src/commands/governance/dequeue.test.ts +++ b/packages/cli/src/commands/governance/dequeue.test.ts @@ -1,16 +1,15 @@ -import { newKitFromWeb3 } from '@celo/contractkit' +import { newKitFromProvider } from '@celo/contractkit' import { testWithAnvilL2 } from '@celo/dev-utils/anvil-test' import { timeTravel } from '@celo/dev-utils/ganache-test' -import Web3 from 'web3' -import { testLocallyWithWeb3Node } from '../../test-utils/cliUtils' +import { testLocallyWithNode } from '../../test-utils/cliUtils' import Dequeue from './dequeue' process.env.NO_SYNCCHECK = 'true' -testWithAnvilL2('governance:dequeue cmd', (web3: Web3) => { +testWithAnvilL2('governance:dequeue cmd', (provider) => { it('does not dequeue anything if no proposals are ready', async () => { - const kit = newKitFromWeb3(web3) - const [account] = await web3.eth.getAccounts() + const kit = newKitFromProvider(provider) + const [account] = await kit.connection.getAccounts() const governanceWrapper = await kit.contracts.getGovernance() const minDeposit = (await governanceWrapper.minDeposit()).toFixed() @@ -21,12 +20,16 @@ testWithAnvilL2('governance:dequeue cmd', (web3: Web3) => { expect(initialDequeue).toEqual([]) // Create first proposal - await governanceWrapper - .propose([], 'URL') - .sendAndWaitForReceipt({ from: account, value: minDeposit }) + const proposeHash = await governanceWrapper.propose([], 'URL', { + from: account, + value: minDeposit, + }) + await kit.connection.viemClient.waitForTransactionReceipt({ + hash: proposeHash as `0x${string}`, + }) // Run dequeue operation - await testLocallyWithWeb3Node(Dequeue, ['--from', account], web3) + await testLocallyWithNode(Dequeue, ['--from', account], provider) // After first dequeue, we should have either proposal dequeued or still in queue const afterFirstDequeue = await governanceWrapper.getDequeue() @@ -35,12 +38,16 @@ testWithAnvilL2('governance:dequeue cmd', (web3: Web3) => { expect(totalProposals).toBe(1) // Should have exactly 1 proposal in system // Create second proposal - await governanceWrapper - .propose([], 'URL2') - .sendAndWaitForReceipt({ from: account, value: minDeposit }) + const proposeHash2 = await governanceWrapper.propose([], 'URL2', { + from: account, + value: minDeposit, + }) + await kit.connection.viemClient.waitForTransactionReceipt({ + hash: proposeHash2 as `0x${string}`, + }) // Run dequeue again - await testLocallyWithWeb3Node(Dequeue, ['--from', account], web3) + await testLocallyWithNode(Dequeue, ['--from', account], provider) // After second dequeue, we should have 2 total proposals in the system const finalDequeue = await governanceWrapper.getDequeue() @@ -50,8 +57,8 @@ testWithAnvilL2('governance:dequeue cmd', (web3: Web3) => { }) it('dequeues proposals after time has passed', async () => { - const kit = newKitFromWeb3(web3) - const [account] = await web3.eth.getAccounts() + const kit = newKitFromProvider(provider) + const [account] = await kit.connection.getAccounts() const governanceWrapper = await kit.contracts.getGovernance() const minDeposit = (await governanceWrapper.minDeposit()).toFixed() const dequeueFrequency = (await governanceWrapper.dequeueFrequency()).toNumber() @@ -61,12 +68,16 @@ testWithAnvilL2('governance:dequeue cmd', (web3: Web3) => { expect(await governanceWrapper.getDequeue()).toEqual([]) // Create first proposal - await governanceWrapper - .propose([], 'URL') - .sendAndWaitForReceipt({ from: account, value: minDeposit }) + const proposeHash = await governanceWrapper.propose([], 'URL', { + from: account, + value: minDeposit, + }) + await kit.connection.viemClient.waitForTransactionReceipt({ + hash: proposeHash as `0x${string}`, + }) // Run dequeue immediately (should not dequeue due to timing) - await testLocallyWithWeb3Node(Dequeue, ['--from', account], web3) + await testLocallyWithNode(Dequeue, ['--from', account], provider) // Should have 1 proposal total in the system const afterFirstDequeue = await governanceWrapper.getDequeue() @@ -74,15 +85,19 @@ testWithAnvilL2('governance:dequeue cmd', (web3: Web3) => { expect(afterFirstDequeue.length + afterFirstQueue.length).toBe(1) // Create second proposal - await governanceWrapper - .propose([], 'URL2') - .sendAndWaitForReceipt({ from: account, value: minDeposit }) + const proposeHash2 = await governanceWrapper.propose([], 'URL2', { + from: account, + value: minDeposit, + }) + await kit.connection.viemClient.waitForTransactionReceipt({ + hash: proposeHash2 as `0x${string}`, + }) // Advance time to allow dequeuing - await timeTravel(dequeueFrequency + 1, web3) + await timeTravel(dequeueFrequency + 1, provider) // Now dequeue should work - await testLocallyWithWeb3Node(Dequeue, ['--from', account], web3) + await testLocallyWithNode(Dequeue, ['--from', account], provider) // Should have 2 proposals total, and some should be dequeued const finalDequeue = await governanceWrapper.getDequeue() diff --git a/packages/cli/src/commands/governance/dequeue.ts b/packages/cli/src/commands/governance/dequeue.ts index 10be3a09e..8683532a9 100644 --- a/packages/cli/src/commands/governance/dequeue.ts +++ b/packages/cli/src/commands/governance/dequeue.ts @@ -1,5 +1,5 @@ import { BaseCommand } from '../../base' -import { displaySendTx } from '../../utils/cli' +import { displayViemTx } from '../../utils/cli' import { CustomFlags } from '../../utils/command' export default class Dequeue extends BaseCommand { @@ -14,11 +14,12 @@ export default class Dequeue extends BaseCommand { async run() { const kit = await this.getKit() + const publicClient = await this.getPublicClient() const res = await this.parse(Dequeue) const account = res.flags.from kit.defaultAccount = account const governance = await kit.contracts.getGovernance() - await displaySendTx('dequeue', governance.dequeueProposalsIfReady(), {}, 'ProposalsDequeued') + await displayViemTx('dequeue', governance.dequeueProposalsIfReady(), publicClient) } } diff --git a/packages/cli/src/commands/governance/execute.test.ts b/packages/cli/src/commands/governance/execute.test.ts index 10b67b33e..ef2691575 100644 --- a/packages/cli/src/commands/governance/execute.test.ts +++ b/packages/cli/src/commands/governance/execute.test.ts @@ -1,5 +1,5 @@ import { AbiItem, PROXY_ADMIN_ADDRESS } from '@celo/connect' -import { newKitFromWeb3 } from '@celo/contractkit' +import { newKitFromProvider } from '@celo/contractkit' import { Proposal } from '@celo/contractkit/lib/wrappers/Governance' import { DEFAULT_OWNER_ADDRESS, @@ -10,13 +10,13 @@ import { import { timeTravel } from '@celo/dev-utils/ganache-test' import fs from 'fs' import path from 'node:path' -import Web3 from 'web3' -import { stripAnsiCodesAndTxHashes, testLocallyWithWeb3Node } from '../../test-utils/cliUtils' +import { stripAnsiCodesAndTxHashes, testLocallyWithNode } from '../../test-utils/cliUtils' import Execute from './execute' +import { decodeFunctionResult, encodeFunctionData, parseEther } from 'viem' process.env.NO_SYNCCHECK = 'true' -testWithAnvilL2('governance:execute cmd', (web3: Web3) => { +testWithAnvilL2('governance:execute cmd', (provider) => { const PROPOSAL_TRANSACTION_TEST_KEY = '3' const PROPOSAL_TRANSACTION_TEST_VALUE = '4' const PROPOSAL_TRANSACTIONS = [ @@ -64,9 +64,9 @@ testWithAnvilL2('governance:execute cmd', (web3: Web3) => { }) it('should execute a proposal successfuly', async () => { - const kit = newKitFromWeb3(web3) + const kit = newKitFromProvider(provider) const governanceWrapper = await kit.contracts.getGovernance() - const [approver, proposer, voter] = await web3.eth.getAccounts() + const [approver, proposer, voter] = await kit.connection.getAccounts() const minDeposit = (await governanceWrapper.minDeposit()).toFixed() const lockedGold = await kit.contracts.getLockedGold() const majorityOfVotes = (await lockedGold.getTotalLockedGold()).multipliedBy(0.6) @@ -74,25 +74,32 @@ testWithAnvilL2('governance:execute cmd', (web3: Web3) => { const dequeueFrequency = (await governanceWrapper.dequeueFrequency()).toNumber() const proposalId = 1 - await setCode(web3, PROXY_ADMIN_ADDRESS, TEST_TRANSACTIONS_BYTECODE) + await setCode(provider, PROXY_ADMIN_ADDRESS, TEST_TRANSACTIONS_BYTECODE) - await governanceWrapper - .propose(PROPOSAL_TRANSACTIONS, 'URL') - .sendAndWaitForReceipt({ from: proposer, value: minDeposit }) + const proposeHash = await governanceWrapper.propose(PROPOSAL_TRANSACTIONS, 'URL', { + from: proposer, + value: minDeposit, + }) + await kit.connection.viemClient.waitForTransactionReceipt({ + hash: proposeHash as `0x${string}`, + }) const accountWrapper = await kit.contracts.getAccounts() const lockedGoldWrapper = await kit.contracts.getLockedGold() - await accountWrapper.createAccount().sendAndWaitForReceipt({ from: voter }) - await lockedGoldWrapper - .lock() - .sendAndWaitForReceipt({ from: voter, value: majorityOfVotes.toFixed() }) + const createHash = await accountWrapper.createAccount({ from: voter }) + await kit.connection.viemClient.waitForTransactionReceipt({ hash: createHash as `0x${string}` }) + const lockHash = await lockedGoldWrapper.lock({ from: voter, value: majorityOfVotes.toFixed() }) + await kit.connection.viemClient.waitForTransactionReceipt({ hash: lockHash as `0x${string}` }) - await timeTravel(dequeueFrequency + 1, web3) + await timeTravel(dequeueFrequency + 1, provider) - await governanceWrapper.dequeueProposalsIfReady().sendAndWaitForReceipt({ + const dequeueHash = await governanceWrapper.dequeueProposalsIfReady({ from: proposer, }) + await kit.connection.viemClient.waitForTransactionReceipt({ + hash: dequeueHash as `0x${string}`, + }) expect(await governanceWrapper.getDequeue()).toMatchInlineSnapshot(` [ @@ -102,85 +109,106 @@ testWithAnvilL2('governance:execute cmd', (web3: Web3) => { expect(await governanceWrapper.getQueue()).toMatchInlineSnapshot(`[]`) // send some funds to DEFAULT_OWNER_ADDRESS to execute transactions - await ( - await kit.sendTransaction({ - to: DEFAULT_OWNER_ADDRESS, - from: approver, - value: web3.utils.toWei('1', 'ether'), - }) - ).waitReceipt() + await kit.sendTransaction({ + to: DEFAULT_OWNER_ADDRESS, + from: approver, + value: parseEther('1').toString(), + }) - await withImpersonatedAccount(web3, DEFAULT_OWNER_ADDRESS, async () => { + await withImpersonatedAccount(provider, DEFAULT_OWNER_ADDRESS, async () => { // setApprover to approverAccount - await ( - await kit.sendTransaction({ - to: governanceWrapper.address, - from: DEFAULT_OWNER_ADDRESS, - data: `0x3156560e000000000000000000000000${approver.replace('0x', '').toLowerCase()}`, - }) - ).waitReceipt() + await kit.sendTransaction({ + to: governanceWrapper.address, + from: DEFAULT_OWNER_ADDRESS, + data: `0x3156560e000000000000000000000000${approver.replace('0x', '').toLowerCase()}`, + }) }) - await (await governanceWrapper.approve(proposalId)).sendAndWaitForReceipt({ from: approver }) + const approveHash = await governanceWrapper.approve(proposalId, { from: approver }) + await kit.connection.viemClient.waitForTransactionReceipt({ + hash: approveHash as `0x${string}`, + }) - await lockedGoldWrapper.lock().sendAndWaitForReceipt({ from: voter, value: minDeposit }) - await (await governanceWrapper.vote(proposalId, 'Yes')).sendAndWaitForReceipt({ from: voter }) - await timeTravel((await governanceWrapper.stageDurations()).Referendum.toNumber() + 1, web3) + const lockHash2 = await lockedGoldWrapper.lock({ from: voter, value: minDeposit }) + await kit.connection.viemClient.waitForTransactionReceipt({ hash: lockHash2 as `0x${string}` }) + const voteHash = await governanceWrapper.vote(proposalId, 'Yes', { from: voter }) + await kit.connection.viemClient.waitForTransactionReceipt({ hash: voteHash as `0x${string}` }) + await timeTravel((await governanceWrapper.stageDurations()).Referendum.toNumber() + 1, provider) - const testTransactionsContract = new web3.eth.Contract( + const testTransactionsContract = kit.connection.getCeloContract( TEST_TRANSACTIONS_ABI, PROXY_ADMIN_ADDRESS ) // TestTransaction contract returns 0 if a value is not set for a given key + const getValueCallData = encodeFunctionData({ + abi: testTransactionsContract.abi, + functionName: 'getValue', + args: [PROPOSAL_TRANSACTION_TEST_KEY], + }) + const { data: getValueResultData } = await kit.connection.viemClient.call({ + to: testTransactionsContract.address, + data: getValueCallData, + }) expect( - await testTransactionsContract.methods.getValue(PROPOSAL_TRANSACTION_TEST_KEY).call() - ).toEqual('0') + decodeFunctionResult({ + abi: testTransactionsContract.abi, + functionName: 'getValue', + data: getValueResultData!, + }) + ).toEqual(0n) logMock.mockClear() - await testLocallyWithWeb3Node( + await testLocallyWithNode( Execute, ['--proposalID', proposalId.toString(), '--from', proposer], - web3 + provider ) + const getValueCallData2 = encodeFunctionData({ + abi: testTransactionsContract.abi, + functionName: 'getValue', + args: [PROPOSAL_TRANSACTION_TEST_KEY], + }) + const { data: getValueResultData2 } = await kit.connection.viemClient.call({ + to: testTransactionsContract.address, + data: getValueCallData2, + }) expect( - await testTransactionsContract.methods.getValue(PROPOSAL_TRANSACTION_TEST_KEY).call() - ).toEqual(PROPOSAL_TRANSACTION_TEST_VALUE) + decodeFunctionResult({ + abi: testTransactionsContract.abi, + functionName: 'getValue', + data: getValueResultData2!, + }) + ).toEqual(BigInt(PROPOSAL_TRANSACTION_TEST_VALUE)) expect( logMock.mock.calls.map((args) => args.map(stripAnsiCodesAndTxHashes)) ).toMatchInlineSnapshot(` + [ [ - [ - "Running Checks:", - ], - [ - " ✔ 1 is an existing proposal ", - ], - [ - " ✔ 1 is in stage Execution ", - ], - [ - " ✔ Proposal 1 is passing corresponding constitutional quorum ", - ], - [ - "All checks passed", - ], - [ - "SendTransaction: executeTx", - ], - [ - "txHash: 0xtxhash", - ], - [ - "ProposalExecuted:", - ], - [ - "proposalId: 1", - ], - ] - `) + "Running Checks:", + ], + [ + " ✔ 1 is an existing proposal ", + ], + [ + " ✔ 1 is in stage Execution ", + ], + [ + " ✔ Proposal 1 is passing corresponding constitutional quorum ", + ], + [ + "All checks passed", + ], + [ + "SendTransaction: executeTx", + ], + [ + "txHash: 0xtxhash", + ], + ] + `) }) }) diff --git a/packages/cli/src/commands/governance/execute.ts b/packages/cli/src/commands/governance/execute.ts index d312bd322..c79f1f4e4 100644 --- a/packages/cli/src/commands/governance/execute.ts +++ b/packages/cli/src/commands/governance/execute.ts @@ -1,7 +1,7 @@ import { Flags } from '@oclif/core' import { BaseCommand } from '../../base' import { newCheckBuilder } from '../../utils/checks' -import { displaySendTx } from '../../utils/cli' +import { displayViemTx } from '../../utils/cli' import { CustomFlags } from '../../utils/command' export default class Execute extends BaseCommand { @@ -17,6 +17,7 @@ export default class Execute extends BaseCommand { async run() { const kit = await this.getKit() + const publicClient = await this.getPublicClient() const res = await this.parse(Execute) const id = res.flags.proposalID const account = res.flags.from @@ -29,6 +30,6 @@ export default class Execute extends BaseCommand { .runChecks() const governance = await kit.contracts.getGovernance() - await displaySendTx('executeTx', await governance.execute(id), {}, 'ProposalExecuted') + await displayViemTx('executeTx', governance.execute(id), publicClient) } } diff --git a/packages/cli/src/commands/governance/executehotfix.test.ts b/packages/cli/src/commands/governance/executehotfix.test.ts index b2c3314e7..04a69fb80 100644 --- a/packages/cli/src/commands/governance/executehotfix.test.ts +++ b/packages/cli/src/commands/governance/executehotfix.test.ts @@ -1,5 +1,5 @@ import { hexToBuffer } from '@celo/base' -import { newKitFromWeb3 } from '@celo/contractkit' +import { newKitFromProvider } from '@celo/contractkit' import { HotfixRecord } from '@celo/contractkit/lib/wrappers/Governance' import { DEFAULT_OWNER_ADDRESS, @@ -10,20 +10,20 @@ import { } from '@celo/dev-utils/anvil-test' import fs from 'fs' import path from 'node:path' -import Web3 from 'web3' -import { AbiItem, PROXY_ADMIN_ADDRESS } from '../../../../sdk/connect/lib' +import { AbiItem, PROXY_ADMIN_ADDRESS } from '@celo/connect' import { EXTRA_LONG_TIMEOUT_MS, stripAnsiCodesAndTxHashes, - testLocallyWithWeb3Node, + testLocallyWithNode, } from '../../test-utils/cliUtils' import Approve from './approve' import ExecuteHotfix from './executehotfix' import PrepareHotfix from './preparehotfix' +import { decodeFunctionResult, encodeFunctionData, parseEther } from 'viem' process.env.NO_SYNCCHECK = 'true' -testWithAnvilL2('governance:executehotfix cmd', (web3: Web3) => { +testWithAnvilL2('governance:executehotfix cmd', (provider) => { const HOTFIX_HASH = '0x8ad3719bb2577b277bcafc1f00ac2f1c3fa5e565173303684d0a8d4f3661680c' const HOTFIX_BUFFER = hexToBuffer(HOTFIX_HASH) const HOTFIX_TRANSACTION_TEST_KEY = '3' @@ -75,86 +75,91 @@ testWithAnvilL2('governance:executehotfix cmd', (web3: Web3) => { it( 'should execute a hotfix successfuly', async () => { - const kit = newKitFromWeb3(web3) + const kit = newKitFromProvider(provider) const governanceWrapper = await kit.contracts.getGovernance() - const [approverAccount, securityCouncilAccount] = await web3.eth.getAccounts() + const [approverAccount, securityCouncilAccount] = await kit.connection.getAccounts() const logMock = jest.spyOn(console, 'log') - await setCode(web3, PROXY_ADMIN_ADDRESS, TEST_TRANSACTIONS_BYTECODE) + await setCode(provider, PROXY_ADMIN_ADDRESS, TEST_TRANSACTIONS_BYTECODE) // send some funds to DEFAULT_OWNER_ADDRESS to execute transactions - await ( - await kit.sendTransaction({ - to: DEFAULT_OWNER_ADDRESS, - from: approverAccount, - value: web3.utils.toWei('1', 'ether'), - }) - ).waitReceipt() + await kit.sendTransaction({ + to: DEFAULT_OWNER_ADDRESS, + from: approverAccount, + value: parseEther('1').toString(), + }) - await withImpersonatedAccount(web3, DEFAULT_OWNER_ADDRESS, async () => { + await withImpersonatedAccount(provider, DEFAULT_OWNER_ADDRESS, async () => { // setHotfixExecutionTimeWindow to EXECUTION_TIME_LIMIT (86400) - await ( - await kit.sendTransaction({ - to: governanceWrapper.address, - from: DEFAULT_OWNER_ADDRESS, - data: '0x745407c80000000000000000000000000000000000000000000000000000000000015180', - }) - ).waitReceipt() + await kit.sendTransaction({ + to: governanceWrapper.address, + from: DEFAULT_OWNER_ADDRESS, + data: '0x745407c80000000000000000000000000000000000000000000000000000000000015180', + }) // setApprover to 0x5409ED021D9299bf6814279A6A1411A7e866A631 - await ( - await kit.sendTransaction({ - to: governanceWrapper.address, - from: DEFAULT_OWNER_ADDRESS, - data: `0x3156560e000000000000000000000000${approverAccount - .replace('0x', '') - .toLowerCase()}`, - }) - ).waitReceipt() + await kit.sendTransaction({ + to: governanceWrapper.address, + from: DEFAULT_OWNER_ADDRESS, + data: `0x3156560e000000000000000000000000${approverAccount + .replace('0x', '') + .toLowerCase()}`, + }) // setSecurityCouncil to 0x6Ecbe1DB9EF729CBe972C83Fb886247691Fb6beb - await ( - await kit.sendTransaction({ - to: governanceWrapper.address, - from: DEFAULT_OWNER_ADDRESS, - data: `0x1c1083e2000000000000000000000000${securityCouncilAccount - .replace('0x', '') - .toLowerCase()}`, - }) - ).waitReceipt() + await kit.sendTransaction({ + to: governanceWrapper.address, + from: DEFAULT_OWNER_ADDRESS, + data: `0x1c1083e2000000000000000000000000${securityCouncilAccount + .replace('0x', '') + .toLowerCase()}`, + }) }) - await testLocallyWithWeb3Node( + await testLocallyWithNode( Approve, ['--hotfix', HOTFIX_HASH, '--from', approverAccount], - web3 + provider ) - await testLocallyWithWeb3Node( + await testLocallyWithNode( Approve, ['--hotfix', HOTFIX_HASH, '--from', securityCouncilAccount, '--type', 'securityCouncil'], - web3 + provider ) - await testLocallyWithWeb3Node( + await testLocallyWithNode( PrepareHotfix, ['--hash', HOTFIX_HASH, '--from', approverAccount], - web3 + provider ) - const testTransactionsContract = new web3.eth.Contract( + const testTransactionsContract = kit.connection.getCeloContract( TEST_TRANSACTIONS_ABI, PROXY_ADMIN_ADDRESS ) // TestTransaction contract returns 0 if a value is not set for a given key + const getValueCallData = encodeFunctionData({ + abi: testTransactionsContract.abi, + functionName: 'getValue', + args: [HOTFIX_TRANSACTION_TEST_KEY], + }) + const { data: getValueResultData } = await kit.connection.viemClient.call({ + to: testTransactionsContract.address, + data: getValueCallData, + }) expect( - await testTransactionsContract.methods.getValue(HOTFIX_TRANSACTION_TEST_KEY).call() - ).toEqual('0') + decodeFunctionResult({ + abi: testTransactionsContract.abi, + functionName: 'getValue', + data: getValueResultData!, + }) + ).toEqual(0n) logMock.mockClear() - await testLocallyWithWeb3Node( + await testLocallyWithNode( ExecuteHotfix, [ '--jsonTransactions', @@ -164,12 +169,25 @@ testWithAnvilL2('governance:executehotfix cmd', (web3: Web3) => { '--salt', SALT, ], - web3 + provider ) + const getValueCallData2 = encodeFunctionData({ + abi: testTransactionsContract.abi, + functionName: 'getValue', + args: [HOTFIX_TRANSACTION_TEST_KEY], + }) + const { data: getValueResultData2 } = await kit.connection.viemClient.call({ + to: testTransactionsContract.address, + data: getValueCallData2, + }) expect( - await testTransactionsContract.methods.getValue(HOTFIX_TRANSACTION_TEST_KEY).call() - ).toEqual(HOTFIX_TRANSACTION_TEST_VALUE) + decodeFunctionResult({ + abi: testTransactionsContract.abi, + functionName: 'getValue', + data: getValueResultData2!, + }) + ).toEqual(BigInt(HOTFIX_TRANSACTION_TEST_VALUE)) expect( logMock.mock.calls.map((args) => args.map(stripAnsiCodesAndTxHashes)) @@ -199,12 +217,6 @@ testWithAnvilL2('governance:executehotfix cmd', (web3: Web3) => { [ "txHash: 0xtxhash", ], - [ - "HotfixExecuted:", - ], - [ - "hash: 0x8ad3719bb2577b277bcafc1f00ac2f1c3fa5e565173303684d0a8d4f3661680c", - ], ] `) }, @@ -214,82 +226,87 @@ testWithAnvilL2('governance:executehotfix cmd', (web3: Web3) => { it( 'fails if execution time limit has been reached', async () => { - const kit = newKitFromWeb3(web3) + const kit = newKitFromProvider(provider) const governanceWrapper = await kit.contracts.getGovernance() - const [approverAccount, securityCouncilAccount] = await web3.eth.getAccounts() + const [approverAccount, securityCouncilAccount] = await kit.connection.getAccounts() const logMock = jest.spyOn(console, 'log') - await setCode(web3, PROXY_ADMIN_ADDRESS, TEST_TRANSACTIONS_BYTECODE) + await setCode(provider, PROXY_ADMIN_ADDRESS, TEST_TRANSACTIONS_BYTECODE) // send some funds to DEFAULT_OWNER_ADDRESS to execute transactions - await ( - await kit.sendTransaction({ - to: DEFAULT_OWNER_ADDRESS, - from: approverAccount, - value: web3.utils.toWei('1', 'ether'), - }) - ).waitReceipt() + await kit.sendTransaction({ + to: DEFAULT_OWNER_ADDRESS, + from: approverAccount, + value: parseEther('1').toString(), + }) - await withImpersonatedAccount(web3, DEFAULT_OWNER_ADDRESS, async () => { + await withImpersonatedAccount(provider, DEFAULT_OWNER_ADDRESS, async () => { // setHotfixExecutionTimeWindow to 1 second - await ( - await kit.sendTransaction({ - to: governanceWrapper.address, - from: DEFAULT_OWNER_ADDRESS, - data: '0x745407c80000000000000000000000000000000000000000000000000000000000000001', - }) - ).waitReceipt() + await kit.sendTransaction({ + to: governanceWrapper.address, + from: DEFAULT_OWNER_ADDRESS, + data: '0x745407c80000000000000000000000000000000000000000000000000000000000000001', + }) // setApprover to 0x5409ED021D9299bf6814279A6A1411A7e866A631 - await ( - await kit.sendTransaction({ - to: governanceWrapper.address, - from: DEFAULT_OWNER_ADDRESS, - data: `0x3156560e000000000000000000000000${approverAccount - .replace('0x', '') - .toLowerCase()}`, - }) - ).waitReceipt() + await kit.sendTransaction({ + to: governanceWrapper.address, + from: DEFAULT_OWNER_ADDRESS, + data: `0x3156560e000000000000000000000000${approverAccount + .replace('0x', '') + .toLowerCase()}`, + }) // setSecurityCouncil to 0x6Ecbe1DB9EF729CBe972C83Fb886247691Fb6beb - await ( - await kit.sendTransaction({ - to: governanceWrapper.address, - from: DEFAULT_OWNER_ADDRESS, - data: `0x1c1083e2000000000000000000000000${securityCouncilAccount - .replace('0x', '') - .toLowerCase()}`, - }) - ).waitReceipt() + await kit.sendTransaction({ + to: governanceWrapper.address, + from: DEFAULT_OWNER_ADDRESS, + data: `0x1c1083e2000000000000000000000000${securityCouncilAccount + .replace('0x', '') + .toLowerCase()}`, + }) }) - await testLocallyWithWeb3Node( + await testLocallyWithNode( Approve, ['--hotfix', HOTFIX_HASH, '--from', approverAccount], - web3 + provider ) - await testLocallyWithWeb3Node( + await testLocallyWithNode( Approve, ['--hotfix', HOTFIX_HASH, '--from', securityCouncilAccount, '--type', 'securityCouncil'], - web3 + provider ) - await testLocallyWithWeb3Node( + await testLocallyWithNode( PrepareHotfix, ['--hash', HOTFIX_HASH, '--from', approverAccount], - web3 + provider ) - const testTransactionsContract = new web3.eth.Contract( + const testTransactionsContract = kit.connection.getCeloContract( TEST_TRANSACTIONS_ABI, PROXY_ADMIN_ADDRESS ) // TestTransaction contract returns 0 if a value is not set for a given key + const getValueCallData = encodeFunctionData({ + abi: testTransactionsContract.abi, + functionName: 'getValue', + args: [HOTFIX_TRANSACTION_TEST_KEY], + }) + const { data: getValueResultData } = await kit.connection.viemClient.call({ + to: testTransactionsContract.address, + data: getValueCallData, + }) expect( - await testTransactionsContract.methods.getValue(HOTFIX_TRANSACTION_TEST_KEY).call() - ).toEqual('0') + decodeFunctionResult({ + abi: testTransactionsContract.abi, + functionName: 'getValue', + data: getValueResultData!, + }) + ).toEqual(0n) const timestampAfterExecutionLimit = ( (await governanceWrapper.getHotfixRecord(HOTFIX_BUFFER)) as HotfixRecord @@ -299,12 +316,12 @@ testWithAnvilL2('governance:executehotfix cmd', (web3: Web3) => { .spyOn(global.Date, 'now') .mockImplementation(() => timestampAfterExecutionLimit.multipliedBy(1000).toNumber()) - await setNextBlockTimestamp(web3, timestampAfterExecutionLimit.toNumber()) + await setNextBlockTimestamp(provider, timestampAfterExecutionLimit.toNumber()) logMock.mockClear() await expect( - testLocallyWithWeb3Node( + testLocallyWithNode( ExecuteHotfix, [ '--jsonTransactions', @@ -314,14 +331,27 @@ testWithAnvilL2('governance:executehotfix cmd', (web3: Web3) => { '--salt', SALT, ], - web3 + provider ) ).rejects.toThrow("Some checks didn't pass!") // Should still return 0 because the hotfix should not have been executed + const getValueCallData2 = encodeFunctionData({ + abi: testTransactionsContract.abi, + functionName: 'getValue', + args: [HOTFIX_TRANSACTION_TEST_KEY], + }) + const { data: getValueResultData2 } = await kit.connection.viemClient.call({ + to: testTransactionsContract.address, + data: getValueCallData2, + }) expect( - await testTransactionsContract.methods.getValue(HOTFIX_TRANSACTION_TEST_KEY).call() - ).toEqual('0') + decodeFunctionResult({ + abi: testTransactionsContract.abi, + functionName: 'getValue', + data: getValueResultData2!, + }) + ).toEqual(0n) expect( logMock.mock.calls.map((args) => args.map(stripAnsiCodesAndTxHashes)) diff --git a/packages/cli/src/commands/governance/executehotfix.ts b/packages/cli/src/commands/governance/executehotfix.ts index 1ff5cbb97..256be469d 100644 --- a/packages/cli/src/commands/governance/executehotfix.ts +++ b/packages/cli/src/commands/governance/executehotfix.ts @@ -4,7 +4,7 @@ import { Flags } from '@oclif/core' import { readFileSync } from 'fs-extra' import { BaseCommand } from '../../base' import { newCheckBuilder } from '../../utils/checks' -import { displaySendTx } from '../../utils/cli' +import { displayViemTx } from '../../utils/cli' import { CustomFlags } from '../../utils/command' export default class ExecuteHotfix extends BaseCommand { @@ -23,6 +23,7 @@ export default class ExecuteHotfix extends BaseCommand { async run() { const kit = await this.getKit() + const publicClient = await this.getPublicClient() const res = await this.parse(ExecuteHotfix) const account = res.flags.from kit.defaultAccount = account @@ -44,11 +45,6 @@ export default class ExecuteHotfix extends BaseCommand { .hotfixExecutionTimeLimitNotReached(hash) .runChecks() - await displaySendTx( - 'executeHotfixTx', - governance.executeHotfix(hotfix, saltBuff), - {}, - 'HotfixExecuted' - ) + await displayViemTx('executeHotfixTx', governance.executeHotfix(hotfix, saltBuff), publicClient) } } diff --git a/packages/cli/src/commands/governance/hashhotfix.test.ts b/packages/cli/src/commands/governance/hashhotfix.test.ts index 37acf253d..a68960184 100644 --- a/packages/cli/src/commands/governance/hashhotfix.test.ts +++ b/packages/cli/src/commands/governance/hashhotfix.test.ts @@ -2,13 +2,12 @@ import { PROXY_ADMIN_ADDRESS } from '@celo/connect' import { setCode, testWithAnvilL2 } from '@celo/dev-utils/anvil-test' import fs from 'fs' import path from 'node:path' -import Web3 from 'web3' -import { stripAnsiCodesAndTxHashes, testLocallyWithWeb3Node } from '../../test-utils/cliUtils' +import { stripAnsiCodesAndTxHashes, testLocallyWithNode } from '../../test-utils/cliUtils' import HashHotfix from './hashhotfix' process.env.NO_SYNCCHECK = 'true' -testWithAnvilL2('governance:hashhotfix cmd', (web3: Web3) => { +testWithAnvilL2('governance:hashhotfix cmd', (provider) => { const SALT = '0x614dccb5ac13cba47c2430bdee7829bb8c8f3603a8ace22e7680d317b39e3658' const HOTFIX_TRANSACTION_TEST_KEY = '3' const HOTFIX_TRANSACTION_TEST_VALUE = '4' @@ -37,10 +36,10 @@ testWithAnvilL2('governance:hashhotfix cmd', (web3: Web3) => { it('should hash a hotfix successfuly with --force flag', async () => { const logMock = jest.spyOn(console, 'log') - await testLocallyWithWeb3Node( + await testLocallyWithNode( HashHotfix, ['--jsonTransactions', HOTFIX_TRANSACTIONS_FILE_PATH, '--salt', SALT, '--force'], - web3 + provider ) expect( @@ -58,14 +57,14 @@ testWithAnvilL2('governance:hashhotfix cmd', (web3: Web3) => { }) it('should verify and hash a hotfix successfuly', async () => { - await setCode(web3, PROXY_ADMIN_ADDRESS, TEST_TRANSACTIONS_BYTECODE) + await setCode(provider, PROXY_ADMIN_ADDRESS, TEST_TRANSACTIONS_BYTECODE) const logMock = jest.spyOn(console, 'log') - await testLocallyWithWeb3Node( + await testLocallyWithNode( HashHotfix, ['--jsonTransactions', HOTFIX_TRANSACTIONS_FILE_PATH, '--salt', SALT], - web3 + provider ) expect( @@ -91,10 +90,10 @@ testWithAnvilL2('governance:hashhotfix cmd', (web3: Web3) => { it('should fail when hotfix does not pass verification', async () => { const logMock = jest.spyOn(console, 'log') - await testLocallyWithWeb3Node( + await testLocallyWithNode( HashHotfix, ['--jsonTransactions', HOTFIX_TRANSACTIONS_FILE_PATH, '--salt', SALT], - web3 + provider ) expect( @@ -105,7 +104,10 @@ testWithAnvilL2('governance:hashhotfix cmd', (web3: Web3) => { "Simulating proposal execution", ], [ - " ✘ Transaction 0 failure: Error: EVM error OpcodeNotFound", + " ✘ Transaction 0 failure: UnknownRpcError: An unknown RPC error occurred. + + Details: EVM error OpcodeNotFound + Version: viem@2.33.2", ], ] `) diff --git a/packages/cli/src/commands/governance/preparehotfix.test.ts b/packages/cli/src/commands/governance/preparehotfix.test.ts index ea87c5222..4506ee234 100644 --- a/packages/cli/src/commands/governance/preparehotfix.test.ts +++ b/packages/cli/src/commands/governance/preparehotfix.test.ts @@ -1,92 +1,84 @@ import { hexToBuffer } from '@celo/base' -import { newKitFromWeb3 } from '@celo/contractkit' +import { newKitFromProvider } from '@celo/contractkit' import { DEFAULT_OWNER_ADDRESS, setNextBlockTimestamp, testWithAnvilL2, withImpersonatedAccount, } from '@celo/dev-utils/anvil-test' -import Web3 from 'web3' -import { testLocallyWithWeb3Node } from '../../test-utils/cliUtils' +import { testLocallyWithNode } from '../../test-utils/cliUtils' import { getCurrentTimestamp } from '../../utils/cli' import Approve from './approve' import PrepareHotfix from './preparehotfix' +import { parseEther } from 'viem' process.env.NO_SYNCCHECK = 'true' -testWithAnvilL2('governance:preparehotfix cmd', (web3: Web3) => { +testWithAnvilL2('governance:preparehotfix cmd', (provider) => { const HOTFIX_HASH = '0x8ad3719bb2577b277bcafc1f00ac2f1c3fa5e565173303684d0a8d4f3661680c' const HOTFIX_BUFFER = hexToBuffer(HOTFIX_HASH) const EXECUTION_TIME_LIMIT = 86400 it('should prepare a hotfix successfuly', async () => { - const kit = newKitFromWeb3(web3) + const kit = newKitFromProvider(provider) const governanceWrapper = await kit.contracts.getGovernance() - const [approverAccount, securityCouncilAccount] = await web3.eth.getAccounts() + const [approverAccount, securityCouncilAccount] = await kit.connection.getAccounts() // arbitrary 100 seconds to the future to avoid // Timestamp error: X is lower than or equal to previous block's timestamp const nextTimestamp = getCurrentTimestamp() + 100 // send some funds to DEFAULT_OWNER_ADDRESS to execute transactions - await ( - await kit.sendTransaction({ - to: DEFAULT_OWNER_ADDRESS, - from: approverAccount, - value: web3.utils.toWei('1', 'ether'), - }) - ).waitReceipt() + await kit.sendTransaction({ + to: DEFAULT_OWNER_ADDRESS, + from: approverAccount, + value: parseEther('1').toString(), + }) - await withImpersonatedAccount(web3, DEFAULT_OWNER_ADDRESS, async () => { + await withImpersonatedAccount(provider, DEFAULT_OWNER_ADDRESS, async () => { // setHotfixExecutionTimeWindow to EXECUTION_TIME_LIMIT (86400) - await ( - await kit.sendTransaction({ - to: governanceWrapper.address, - from: DEFAULT_OWNER_ADDRESS, - data: '0x745407c80000000000000000000000000000000000000000000000000000000000015180', - }) - ).waitReceipt() + await kit.sendTransaction({ + to: governanceWrapper.address, + from: DEFAULT_OWNER_ADDRESS, + data: '0x745407c80000000000000000000000000000000000000000000000000000000000015180', + }) // setApprover to 0x5409ED021D9299bf6814279A6A1411A7e866A631 - await ( - await kit.sendTransaction({ - to: governanceWrapper.address, - from: DEFAULT_OWNER_ADDRESS, - data: `0x3156560e000000000000000000000000${approverAccount - .replace('0x', '') - .toLowerCase()}`, - }) - ).waitReceipt() + await kit.sendTransaction({ + to: governanceWrapper.address, + from: DEFAULT_OWNER_ADDRESS, + data: `0x3156560e000000000000000000000000${approverAccount + .replace('0x', '') + .toLowerCase()}`, + }) // setSecurityCouncil to 0x6Ecbe1DB9EF729CBe972C83Fb886247691Fb6beb - await ( - await kit.sendTransaction({ - to: governanceWrapper.address, - from: DEFAULT_OWNER_ADDRESS, - data: `0x1c1083e2000000000000000000000000${securityCouncilAccount - .replace('0x', '') - .toLowerCase()}`, - }) - ).waitReceipt() + await kit.sendTransaction({ + to: governanceWrapper.address, + from: DEFAULT_OWNER_ADDRESS, + data: `0x1c1083e2000000000000000000000000${securityCouncilAccount + .replace('0x', '') + .toLowerCase()}`, + }) }) - await testLocallyWithWeb3Node( + await testLocallyWithNode( Approve, ['--hotfix', HOTFIX_HASH, '--from', approverAccount], - web3 + provider ) - await testLocallyWithWeb3Node( + await testLocallyWithNode( Approve, ['--hotfix', HOTFIX_HASH, '--from', securityCouncilAccount, '--type', 'securityCouncil'], - web3 + provider ) - await setNextBlockTimestamp(web3, nextTimestamp) + await setNextBlockTimestamp(provider, nextTimestamp) - await testLocallyWithWeb3Node( + await testLocallyWithNode( PrepareHotfix, ['--hash', HOTFIX_HASH, '--from', approverAccount], - web3 + provider ) expect(await governanceWrapper.getHotfixRecord(HOTFIX_BUFFER)).toMatchInlineSnapshot(` diff --git a/packages/cli/src/commands/governance/preparehotfix.ts b/packages/cli/src/commands/governance/preparehotfix.ts index 5c7403016..5a4e5891a 100644 --- a/packages/cli/src/commands/governance/preparehotfix.ts +++ b/packages/cli/src/commands/governance/preparehotfix.ts @@ -1,8 +1,8 @@ -import { toBuffer } from '@ethereumjs/util' +import { hexToBytes } from 'viem' import { Flags } from '@oclif/core' import { BaseCommand } from '../../base' import { newCheckBuilder } from '../../utils/checks' -import { displaySendTx } from '../../utils/cli' +import { displayViemTx } from '../../utils/cli' import { CustomFlags } from '../../utils/command' export default class PrepareHotfix extends BaseCommand { @@ -20,12 +20,13 @@ export default class PrepareHotfix extends BaseCommand { async run() { const kit = await this.getKit() + const publicClient = await this.getPublicClient() const res = await this.parse(PrepareHotfix) const account = res.flags.from kit.defaultAccount = account const governance = await kit.contracts.getGovernance() - const hash = toBuffer(res.flags.hash) as Buffer + const hash = Buffer.from(hexToBytes(res.flags.hash as `0x${string}`)) await newCheckBuilder(this, account) .hotfixApproved(hash) @@ -33,6 +34,6 @@ export default class PrepareHotfix extends BaseCommand { .hotfixNotExecuted(hash) .runChecks() - await displaySendTx('prepareHotfixTx', governance.prepareHotfix(hash), {}, 'HotfixPrepared') + await displayViemTx('prepareHotfixTx', governance.prepareHotfix(hash), publicClient) } } diff --git a/packages/cli/src/commands/governance/propose.test.ts b/packages/cli/src/commands/governance/propose.test.ts index c013741b3..e01d37643 100644 --- a/packages/cli/src/commands/governance/propose.test.ts +++ b/packages/cli/src/commands/governance/propose.test.ts @@ -1,22 +1,21 @@ import { StrongAddress } from '@celo/base' -import { CeloProvider } from '@celo/connect/lib/celo-provider' -import { newKitFromWeb3 } from '@celo/contractkit' +import { newKitFromProvider } from '@celo/contractkit' import { GoldTokenWrapper } from '@celo/contractkit/lib/wrappers/GoldTokenWrapper' import { GovernanceWrapper } from '@celo/contractkit/lib/wrappers/Governance' import { setBalance, testWithAnvilL2, withImpersonatedAccount } from '@celo/dev-utils/anvil-test' import { ux } from '@oclif/core' import Safe, { getSafeAddressFromDeploymentTx } from '@safe-global/protocol-kit' import * as fs from 'fs' -import Web3 from 'web3' import { EXTRA_LONG_TIMEOUT_MS, stripAnsiCodesFromNestedArray, - testLocallyWithWeb3Node, + testLocallyWithNode, } from '../../test-utils/cliUtils' import { deployMultiCall } from '../../test-utils/multicall' import { createMultisig, setupSafeContracts } from '../../test-utils/multisigUtils' import Approve from '../multisig/approve' import Propose from './propose' +import { encodeFunctionData, parseEther } from 'viem' // Mock fetch for HTTP status tests jest.mock('cross-fetch') @@ -149,7 +148,7 @@ const structAbiDefinition = { testWithAnvilL2( 'governance:propose cmd', - (web3: Web3) => { + (client) => { const TRANSACTION_FILE_PATH = 'governance-propose-l2.test.json' let governance: GovernanceWrapper @@ -157,16 +156,16 @@ testWithAnvilL2( let goldTokenContract: GoldTokenWrapper['contract'] let minDeposit: string - const kit = newKitFromWeb3(web3) + const kit = newKitFromProvider(client) let accounts: StrongAddress[] = [] beforeEach(async () => { // need to set multical deployment on the address it was found on alfajores // since this test impersonates the old alfajores chain id - await deployMultiCall(web3, '0xcA11bde05977b3631167028862bE2a173976CA11') + await deployMultiCall(client, '0xcA11bde05977b3631167028862bE2a173976CA11') - accounts = (await web3.eth.getAccounts()) as StrongAddress[] + accounts = (await kit.connection.getAccounts()) as StrongAddress[] kit.defaultAccount = accounts[0] governance = await kit.contracts.getGovernance() goldToken = await kit.contracts.getGoldToken() @@ -185,18 +184,16 @@ testWithAnvilL2( const transactionsToBeSaved = JSON.stringify(transactions) fs.writeFileSync(TRANSACTION_FILE_PATH, transactionsToBeSaved, { flag: 'w' }) - await ( - await kit.sendTransaction({ - to: governance.address, - from: accounts[0], - value: web3.utils.toWei('1', 'ether'), - }) - ).waitReceipt() + await kit.sendTransaction({ + to: governance.address, + from: accounts[0], + value: parseEther('1').toString(), + }) const proposalBefore = await governance.getProposal(1) expect(proposalBefore).toEqual([]) - await testLocallyWithWeb3Node( + await testLocallyWithNode( Propose, [ '--jsonTransactions', @@ -208,16 +205,18 @@ testWithAnvilL2( '--descriptionURL', 'https://github.com/celo-org/governance/blob/main/CGPs/cgp-123.md', ], - web3 + client ) const proposal = await governance.getProposal(1) expect(proposal.length).toEqual(transactions.length) expect(proposal[0].to).toEqual(goldToken.address) expect(proposal[0].value).toEqual(transactions[0].value) - const expectedInput = goldTokenContract.methods - .transfer(transactions[0].args[0], transactions[0].args[1]) - .encodeABI() + const expectedInput = encodeFunctionData({ + abi: goldTokenContract.abi, + functionName: 'transfer', + args: [transactions[0].args[0] as `0x${string}`, BigInt(transactions[0].args[1])], + }) expect(proposal[0].input).toEqual(expectedInput) }, EXTRA_LONG_TIMEOUT_MS * 2 @@ -229,13 +228,11 @@ testWithAnvilL2( const transactionsToBeSaved = JSON.stringify(transactions) fs.writeFileSync(TRANSACTION_FILE_PATH, transactionsToBeSaved, { flag: 'w' }) - await ( - await kit.sendTransaction({ - from: accounts[0], - to: governance.address, - value: web3.utils.toWei('1', 'ether'), - }) - ).waitReceipt() + await kit.sendTransaction({ + from: accounts[0], + to: governance.address, + value: parseEther('1').toString(), + }) const multisigWithOneSigner = await createMultisig(kit, [accounts[0]], 1, 1) /** @@ -245,18 +242,16 @@ testWithAnvilL2( * is too much. But I'm leaving this in case we update the devchain to match * Alfajores or Mainnet parameters in the future. */ - await ( - await kit.sendTransaction({ - from: accounts[2], - to: multisigWithOneSigner, - value: web3.utils.toWei('20000', 'ether'), // 2x min deposit on Mainnet - }) - ).waitReceipt() + await kit.sendTransaction({ + from: accounts[2], + to: multisigWithOneSigner, + value: parseEther('20000').toString(), // 2x min deposit on Mainnet + }) const proposalBefore = await governance.getProposal(1) expect(proposalBefore).toEqual([]) - await testLocallyWithWeb3Node( + await testLocallyWithNode( Propose, [ '--jsonTransactions', @@ -271,16 +266,18 @@ testWithAnvilL2( '--descriptionURL', 'https://github.com/celo-org/governance/blob/main/CGPs/cgp-123.md', ], - web3 + client ) const proposal = await governance.getProposal(1) expect(proposal.length).toEqual(transactions.length) expect(proposal[0].to).toEqual(goldToken.address) expect(proposal[0].value).toEqual(transactions[0].value) - const expectedInput = goldTokenContract.methods - .transfer(transactions[0].args[0], transactions[0].args[1]) - .encodeABI() + const expectedInput = encodeFunctionData({ + abi: goldTokenContract.abi, + functionName: 'transfer', + args: [transactions[0].args[0] as `0x${string}`, BigInt(transactions[0].args[1])], + }) expect(proposal[0].input).toEqual(expectedInput) }, EXTRA_LONG_TIMEOUT_MS @@ -292,13 +289,11 @@ testWithAnvilL2( const transactionsToBeSaved = JSON.stringify(transactions) fs.writeFileSync(TRANSACTION_FILE_PATH, transactionsToBeSaved, { flag: 'w' }) - await ( - await kit.sendTransaction({ - to: governance.address, - from: accounts[0], - value: web3.utils.toWei('1', 'ether'), - }) - ).waitReceipt() + await kit.sendTransaction({ + to: governance.address, + from: accounts[0], + value: parseEther('1').toString(), + }) const multisigWithTwoSigners = await createMultisig(kit, [accounts[0], accounts[1]], 2, 2) /** @@ -308,19 +303,17 @@ testWithAnvilL2( * is too much. But I'm leaving this in case we update the devchain to match * Alfajores or Mainnet parameters in the future. */ - await ( - await kit.sendTransaction({ - from: accounts[2], - to: multisigWithTwoSigners, - value: web3.utils.toWei('20000', 'ether'), // 2x min deposit on Mainnet - }) - ).waitReceipt() + await kit.sendTransaction({ + from: accounts[2], + to: multisigWithTwoSigners, + value: parseEther('20000').toString(), // 2x min deposit on Mainnet + }) const proposalBefore = await governance.getProposal(1) expect(proposalBefore).toEqual([]) // Submit proposal from signer A - await testLocallyWithWeb3Node( + await testLocallyWithNode( Propose, [ '--jsonTransactions', @@ -335,26 +328,28 @@ testWithAnvilL2( '--descriptionURL', 'https://github.com/celo-org/governance/blob/main/CGPs/cgp-123.md', ], - web3 + client ) const proposalBetween = await governance.getProposal(1) expect(proposalBetween).toEqual([]) // Approve proposal from signer B - await testLocallyWithWeb3Node( + await testLocallyWithNode( Approve, ['--from', accounts[1], '--for', multisigWithTwoSigners, '--tx', '0'], - web3 + client ) const proposal = await governance.getProposal(1) expect(proposal.length).toEqual(transactions.length) expect(proposal[0].to).toEqual(goldToken.address) expect(proposal[0].value).toEqual(transactions[0].value) - const expectedInput = goldTokenContract.methods - .transfer(transactions[0].args[0], transactions[0].args[1]) - .encodeABI() + const expectedInput = encodeFunctionData({ + abi: goldTokenContract.abi, + functionName: 'transfer', + args: [transactions[0].args[0] as `0x${string}`, BigInt(transactions[0].args[1])], + }) expect(proposal[0].input).toEqual(expectedInput) }, EXTRA_LONG_TIMEOUT_MS @@ -362,13 +357,13 @@ testWithAnvilL2( describe('with safe', () => { beforeEach(async () => { - await setupSafeContracts(web3) + await setupSafeContracts(client) }) test( 'will successfully create proposal based on Core contract (1 owner)', async () => { - const [owner1] = (await web3.eth.getAccounts()) as StrongAddress[] + const [owner1] = (await kit.connection.getAccounts()) as StrongAddress[] const safeAccountConfig = { owners: [owner1], threshold: 1, @@ -379,25 +374,28 @@ testWithAnvilL2( } const protocolKit = await Safe.init({ predictedSafe: predictSafe, - provider: (web3.currentProvider as any as CeloProvider).toEip1193Provider(), + provider: kit.connection.currentProvider as any, signer: owner1, }) const deploymentTransaction = await protocolKit.createSafeDeploymentTransaction() - const receipt = await web3.eth.sendTransaction({ + const txHash = await kit.connection.sendTransaction({ from: owner1, ...deploymentTransaction, }) + const receipt = await kit.connection.viemClient.waitForTransactionReceipt({ + hash: txHash as `0x${string}`, + }) const safeAddress = getSafeAddressFromDeploymentTx( receipt as unknown as Parameters[0], '1.3.0' ) as StrongAddress await protocolKit.connect({ safeAddress }) - const balance = BigInt(web3.utils.toWei('100', 'ether')) - await setBalance(web3, goldToken.address, balance) - await setBalance(web3, governance.address, balance) - await setBalance(web3, owner1, balance) - await setBalance(web3, safeAddress, balance) + const balance = BigInt(parseEther('100').toString()) + await setBalance(client, goldToken.address, balance) + await setBalance(client, governance.address, balance) + await setBalance(client, owner1, balance) + await setBalance(client, safeAddress, balance) const transactionsToBeSaved = JSON.stringify(transactions) fs.writeFileSync(TRANSACTION_FILE_PATH, transactionsToBeSaved, { flag: 'w' }) @@ -406,7 +404,7 @@ testWithAnvilL2( expect(proposalBefore).toEqual([]) // Submit proposal from signer A - await testLocallyWithWeb3Node( + await testLocallyWithNode( Propose, [ '--jsonTransactions', @@ -421,15 +419,17 @@ testWithAnvilL2( '--descriptionURL', 'https://github.com/celo-org/governance/blob/main/CGPs/cgp-123.md', ], - web3 + client ) const proposal = await governance.getProposal(1) expect(proposal.length).toEqual(transactions.length) expect(proposal[0].to).toEqual(goldToken.address) expect(proposal[0].value).toEqual(transactions[0].value) - const expectedInput = goldTokenContract.methods - .transfer(transactions[0].args[0], transactions[0].args[1]) - .encodeABI() + const expectedInput = encodeFunctionData({ + abi: goldTokenContract.abi, + functionName: 'transfer', + args: [transactions[0].args[0] as `0x${string}`, BigInt(transactions[0].args[1])], + }) expect(proposal[0].input).toEqual(expectedInput) }, EXTRA_LONG_TIMEOUT_MS @@ -438,7 +438,7 @@ testWithAnvilL2( test( 'will successfully create proposal based on Core contract (2 owners)', async () => { - const [owner1] = (await web3.eth.getAccounts()) as StrongAddress[] + const [owner1] = (await kit.connection.getAccounts()) as StrongAddress[] const owner2 = '0x6C666E57A5E8715cFE93f92790f98c4dFf7b69e2' const safeAccountConfig = { owners: [owner1, owner2], @@ -450,26 +450,29 @@ testWithAnvilL2( } const protocolKit = await Safe.init({ predictedSafe: predictSafe, - provider: (web3.currentProvider as any as CeloProvider).toEip1193Provider(), + provider: kit.connection.currentProvider as any, signer: owner1, }) const deploymentTransaction = await protocolKit.createSafeDeploymentTransaction() - const receipt = await web3.eth.sendTransaction({ + const txHash = await kit.connection.sendTransaction({ from: owner1, ...deploymentTransaction, }) + const receipt = await kit.connection.viemClient.waitForTransactionReceipt({ + hash: txHash as `0x${string}`, + }) const safeAddress = getSafeAddressFromDeploymentTx( receipt as unknown as Parameters[0], '1.3.0' ) as StrongAddress await protocolKit.connect({ safeAddress }) - const balance = BigInt(web3.utils.toWei('100', 'ether')) - await setBalance(web3, goldToken.address, balance) - await setBalance(web3, governance.address, balance) - await setBalance(web3, owner1, balance) - await setBalance(web3, owner2, balance) - await setBalance(web3, safeAddress, balance) + const balance = BigInt(parseEther('100').toString()) + await setBalance(client, goldToken.address, balance) + await setBalance(client, governance.address, balance) + await setBalance(client, owner1, balance) + await setBalance(client, owner2, balance) + await setBalance(client, safeAddress, balance) const transactionsToBeSaved = JSON.stringify(transactions) fs.writeFileSync(TRANSACTION_FILE_PATH, transactionsToBeSaved, { flag: 'w' }) @@ -478,7 +481,7 @@ testWithAnvilL2( expect(proposalBefore).toEqual([]) // Submit proposal from signer 1 - await testLocallyWithWeb3Node( + await testLocallyWithNode( Propose, [ '--jsonTransactions', @@ -493,13 +496,13 @@ testWithAnvilL2( '--descriptionURL', 'https://github.com/celo-org/governance/blob/main/CGPs/cgp-123.md', ], - web3 + client ) const proposalBefore2ndOwner = await governance.getProposal(1) expect(proposalBefore2ndOwner).toEqual([]) - await withImpersonatedAccount(web3, owner2, async () => { - await testLocallyWithWeb3Node( + await withImpersonatedAccount(client, owner2, async () => { + await testLocallyWithNode( Propose, [ '--jsonTransactions', @@ -514,7 +517,7 @@ testWithAnvilL2( '--descriptionURL', 'https://github.com/celo-org/governance/blob/main/CGPs/cgp-123.md', ], - web3 + client ) }) @@ -522,9 +525,11 @@ testWithAnvilL2( expect(proposal.length).toEqual(transactions.length) expect(proposal[0].to).toEqual(goldToken.address) expect(proposal[0].value).toEqual(transactions[0].value) - const expectedInput = goldTokenContract.methods - .transfer(transactions[0].args[0], transactions[0].args[1]) - .encodeABI() + const expectedInput = encodeFunctionData({ + abi: goldTokenContract.abi, + functionName: 'transfer', + args: [transactions[0].args[0] as `0x${string}`, BigInt(transactions[0].args[1])], + }) expect(proposal[0].input).toEqual(expectedInput) }, EXTRA_LONG_TIMEOUT_MS @@ -537,18 +542,16 @@ testWithAnvilL2( const transactionsToBeSaved = JSON.stringify(transactionsUnknownAddress) fs.writeFileSync(TRANSACTION_FILE_PATH, transactionsToBeSaved, { flag: 'w' }) - await ( - await kit.sendTransaction({ - to: governance.address, - from: accounts[0], - value: web3.utils.toWei('1', 'ether'), - }) - ).waitReceipt() + await kit.sendTransaction({ + to: governance.address, + from: accounts[0], + value: parseEther('1').toString(), + }) const proposalBefore = await governance.getProposal(1) expect(proposalBefore).toEqual([]) - await testLocallyWithWeb3Node( + await testLocallyWithNode( Propose, [ '--jsonTransactions', @@ -562,16 +565,18 @@ testWithAnvilL2( '--force', '--noInfo', ], - web3 + client ) const proposal = await governance.getProposal(1) expect(proposal.length).toEqual(transactions.length) expect(proposal[0].to).toEqual(randomAddress) expect(proposal[0].value).toEqual(transactions[0].value) - const expectedInput = goldTokenContract.methods - .transfer(transactions[0].args[0], transactions[0].args[1]) - .encodeABI() + const expectedInput = encodeFunctionData({ + abi: goldTokenContract.abi, + functionName: 'transfer', + args: [transactions[0].args[0] as `0x${string}`, BigInt(transactions[0].args[1])], + }) expect(proposal[0].input).toEqual(expectedInput) }, EXTRA_LONG_TIMEOUT_MS @@ -583,18 +588,16 @@ testWithAnvilL2( const transactionsToBeSaved = JSON.stringify(transactionsWithStruct) fs.writeFileSync(TRANSACTION_FILE_PATH, transactionsToBeSaved, { flag: 'w' }) - await ( - await kit.sendTransaction({ - to: governance.address, - from: accounts[0], - value: web3.utils.toWei('1', 'ether'), - }) - ).waitReceipt() + await kit.sendTransaction({ + to: governance.address, + from: accounts[0], + value: parseEther('1').toString(), + }) const proposalBefore = await governance.getProposal(1) expect(proposalBefore).toEqual([]) - await testLocallyWithWeb3Node( + await testLocallyWithNode( Propose, [ '--jsonTransactions', @@ -608,7 +611,7 @@ testWithAnvilL2( '--force', '--noInfo', ], - web3 + client ) const proposal = await governance.getProposal(1) @@ -616,9 +619,10 @@ testWithAnvilL2( expect(proposal[0].to).toEqual('0x3d79EdAaBC0EaB6F08ED885C05Fc0B014290D95A') expect(proposal[0].value).toEqual(transactions[0].value) - const expectedInput = kit.connection - .getAbiCoder() - .encodeFunctionCall(structAbiDefinition, [JSON.parse(transactionsWithStruct[0].args[0])]) + const expectedInput = encodeFunctionData({ + abi: [structAbiDefinition] as any, + args: [JSON.parse(transactionsWithStruct[0].args[0])] as any, + }) expect(proposal[0].input).toEqual(expectedInput) }, @@ -629,7 +633,7 @@ testWithAnvilL2( 'fails when descriptionURl is missing', async () => { await expect( - testLocallyWithWeb3Node( + testLocallyWithNode( Propose, [ '--from', @@ -639,7 +643,7 @@ testWithAnvilL2( '--jsonTransactions', './exampleProposal.json', ], - web3 + client ) ).rejects.toThrow('Missing required flag descriptionURL') }, @@ -650,7 +654,7 @@ testWithAnvilL2( 'fails when descriptionURl is invalid', async () => { await expect( - testLocallyWithWeb3Node( + testLocallyWithNode( Propose, [ '--from', @@ -663,7 +667,7 @@ testWithAnvilL2( 'https://github.com/suspicious-org/governance/blob/main/CGPs/cgp-123.md', ], - web3 + client ) ).rejects.toThrowErrorMatchingInlineSnapshot(` "Parsing --descriptionURL @@ -678,7 +682,7 @@ testWithAnvilL2( 'can submit empty proposal', async () => { await expect( - testLocallyWithWeb3Node( + testLocallyWithNode( Propose, [ '--from', @@ -690,7 +694,7 @@ testWithAnvilL2( '--descriptionURL', 'https://github.com/celo-org/governance/blob/main/CGPs/cgp-123.md', ], - web3 + client ) ).resolves.toBe(undefined) }, @@ -702,7 +706,7 @@ testWithAnvilL2( async () => { const spyStart = jest.spyOn(ux.action, 'start') const spyStop = jest.spyOn(ux.action, 'stop') - await testLocallyWithWeb3Node( + await testLocallyWithNode( Propose, [ '--from', @@ -714,7 +718,7 @@ testWithAnvilL2( '--descriptionURL', 'https://github.com/celo-org/governance/blob/main/CGPs/cgp-123.md', ], - web3 + client ) expect(spyStart).toHaveBeenCalledWith('Sending Transaction: proposeTx') expect(spyStop).toHaveBeenCalled() @@ -728,7 +732,7 @@ testWithAnvilL2( const spyStart = jest.spyOn(ux.action, 'start') const spyStop = jest.spyOn(ux.action, 'stop') - await testLocallyWithWeb3Node( + await testLocallyWithNode( Propose, [ '--from', @@ -740,7 +744,7 @@ testWithAnvilL2( '--descriptionURL', 'https://github.com/celo-org/governance/blob/main/CGPs/cgp-123.md', ], - web3 + client ) expect(spyStart).toHaveBeenCalledWith('Sending Transaction: proposeTx') expect(spyStop).toHaveBeenCalled() @@ -759,7 +763,7 @@ testWithAnvilL2( const mockLog = jest.spyOn(console, 'log').mockImplementation(() => {}) await expect( - testLocallyWithWeb3Node( + testLocallyWithNode( Propose, [ '--from', @@ -772,7 +776,7 @@ testWithAnvilL2( 'https://github.com/celo-org/governance/blob/main/CGPs/cgp-404.md', ], - web3 + client ) ).rejects.toThrowErrorMatchingInlineSnapshot(`"Some checks didn't pass!"`) expect(stripAnsiCodesFromNestedArray(mockLog.mock.calls)).toMatchInlineSnapshot(` @@ -804,7 +808,7 @@ testWithAnvilL2( mockFetch.mockRejectedValue(new Error('Network error')) await expect( - testLocallyWithWeb3Node( + testLocallyWithNode( Propose, [ '--from', @@ -817,7 +821,7 @@ testWithAnvilL2( 'https://github.com/celo-org/governance/blob/main/CGPs/cgp-error.md', ], - web3 + client ) ).rejects.toThrowErrorMatchingInlineSnapshot(`"Some checks didn't pass!"`) const mockLog = jest.spyOn(console, 'log').mockImplementation(() => {}) diff --git a/packages/cli/src/commands/governance/propose.ts b/packages/cli/src/commands/governance/propose.ts index be7d42652..f017eca02 100644 --- a/packages/cli/src/commands/governance/propose.ts +++ b/packages/cli/src/commands/governance/propose.ts @@ -1,10 +1,11 @@ import { ProposalBuilder, proposalToJSON, ProposalTransactionJSON } from '@celo/governance' +import { proposalToParams } from '@celo/contractkit/lib/wrappers/Governance' import { Flags } from '@oclif/core' import { BigNumber } from 'bignumber.js' import { readFileSync } from 'fs' import { BaseCommand } from '../../base' import { newCheckBuilder } from '../../utils/checks' -import { displaySendTx, printValueMapRecursive } from '../../utils/cli' +import { displayViemTx, printValueMapRecursive } from '../../utils/cli' import { CustomFlags } from '../../utils/command' import { MultiSigFlags, SafeFlags } from '../../utils/flags' import { @@ -12,11 +13,7 @@ import { addExistingProposalJSONFileToBuilder, checkProposal, } from '../../utils/governance' -import { - createSafeFromWeb3, - performSafeTransaction, - safeTransactionMetadataFromCeloTransactionObject, -} from '../../utils/safe' +import { createSafe, performSafeTransaction, safeTransactionMetadata } from '../../utils/safe' export default class Propose extends BaseCommand { static description = 'Submit a governance proposal' @@ -57,6 +54,7 @@ export default class Propose extends BaseCommand { async run() { const kit = await this.getKit() + const publicClient = await this.getPublicClient() const res = await this.parse(Propose) const account = res.flags.from @@ -90,7 +88,11 @@ export default class Propose extends BaseCommand { proposerMultiSig!.isOwner(account) ) .addConditionalCheck(`${account} is a safe owner`, useSafe, async () => { - const safe = await createSafeFromWeb3(await this.getWeb3(), account, proposer) + const safe = await createSafe( + (await this.getKit()).connection.currentProvider, + account, + proposer + ) return safe.isOwner(account) }) .runChecks() @@ -120,32 +122,35 @@ export default class Propose extends BaseCommand { } } - const governanceTx = governance.propose(proposal, res.flags.descriptionURL) - - if (useMultiSig) { - const multiSigTx = await proposerMultiSig!.submitOrConfirmTransaction( - governance.address, - governanceTx.txo, - deposit.toFixed() + if (useMultiSig || useSafe) { + const proposeData = governance.encodeFunctionData( + 'propose', + proposalToParams(proposal, res.flags.descriptionURL) as unknown[] ) - await displaySendTx('proposeTx', multiSigTx, {}, 'ProposalQueued') - } else if (useSafe) { - await performSafeTransaction( - await this.getWeb3(), - proposer, - account, - await safeTransactionMetadataFromCeloTransactionObject( - governanceTx, - governance.address, - deposit.toFixed() + + if (useMultiSig) { + await displayViemTx( + 'proposeTx', + proposerMultiSig!.submitOrConfirmTransaction( + governance.address, + proposeData, + deposit.toFixed() + ), + publicClient ) - ) + } else { + await performSafeTransaction( + (await this.getKit()).connection.currentProvider, + proposer, + account, + safeTransactionMetadata(proposeData, governance.address, deposit.toFixed()) + ) + } } else { - await displaySendTx( + await displayViemTx( 'proposeTx', - governanceTx, - { value: deposit.toFixed() }, - 'ProposalQueued' + governance.propose(proposal, res.flags.descriptionURL, { value: deposit.toFixed() }), + publicClient ) } } diff --git a/packages/cli/src/commands/governance/revokeupvote.test.ts b/packages/cli/src/commands/governance/revokeupvote.test.ts index 1d0d603b1..f7f8fb863 100644 --- a/packages/cli/src/commands/governance/revokeupvote.test.ts +++ b/packages/cli/src/commands/governance/revokeupvote.test.ts @@ -1,43 +1,49 @@ import { StrongAddress } from '@celo/base' -import { newKitFromWeb3 } from '@celo/contractkit' +import { newKitFromProvider } from '@celo/contractkit' import { GovernanceWrapper } from '@celo/contractkit/lib/wrappers/Governance' import { testWithAnvilL2 } from '@celo/dev-utils/anvil-test' import BigNumber from 'bignumber.js' -import Web3 from 'web3' -import { testLocallyWithWeb3Node } from '../../test-utils/cliUtils' +import { testLocallyWithNode } from '../../test-utils/cliUtils' import Register from '../account/register' import Lock from '../lockedcelo/lock' import RevokeUpvote from './revokeupvote' process.env.NO_SYNCCHECK = 'true' -testWithAnvilL2('governance:revokeupvote cmd', (web3: Web3) => { +testWithAnvilL2('governance:revokeupvote cmd', (provider) => { let minDeposit: BigNumber - const kit = newKitFromWeb3(web3) + const kit = newKitFromProvider(provider) const proposalId = '2' let accounts: StrongAddress[] = [] let governance: GovernanceWrapper beforeEach(async () => { - accounts = (await web3.eth.getAccounts()) as StrongAddress[] + accounts = (await kit.connection.getAccounts()) as StrongAddress[] kit.defaultAccount = accounts[0] governance = await kit.contracts.getGovernance() minDeposit = await governance.minDeposit() for (let i = 1; i <= 2; i++) { - await governance - .propose([], `URL${i}`) - .sendAndWaitForReceipt({ from: accounts[0], value: minDeposit.toFixed() }) + const proposeHash = await governance.propose([], `URL${i}`, { + from: accounts[0], + value: minDeposit.toFixed(), + }) + await kit.connection.viemClient.waitForTransactionReceipt({ + hash: proposeHash as `0x${string}`, + }) } for (let i = 1; i <= 4; i++) { - await testLocallyWithWeb3Node(Register, ['--from', accounts[i]], web3) - await testLocallyWithWeb3Node(Lock, ['--from', accounts[i], '--value', i.toString()], web3) + await testLocallyWithNode(Register, ['--from', accounts[i]], provider) + await testLocallyWithNode(Lock, ['--from', accounts[i], '--value', i.toString()], provider) - await (await governance.upvote(proposalId, accounts[i])).sendAndWaitForReceipt({ + const upvoteHash = await governance.upvote(proposalId, accounts[i], { from: accounts[i], }) + await kit.connection.viemClient.waitForTransactionReceipt({ + hash: upvoteHash as `0x${string}`, + }) } }) @@ -53,7 +59,7 @@ testWithAnvilL2('governance:revokeupvote cmd', (web3: Web3) => { `) // Revoke upvote from account 2 (2 upvotes) - await testLocallyWithWeb3Node(RevokeUpvote, ['--from', accounts[2]], web3) + await testLocallyWithNode(RevokeUpvote, ['--from', accounts[2]], provider) // 1 + 3 + 4 = 8 upvotes expect(await governance.getQueue()).toMatchInlineSnapshot(` diff --git a/packages/cli/src/commands/governance/revokeupvote.ts b/packages/cli/src/commands/governance/revokeupvote.ts index 034834d40..39ed4f3d8 100644 --- a/packages/cli/src/commands/governance/revokeupvote.ts +++ b/packages/cli/src/commands/governance/revokeupvote.ts @@ -1,6 +1,6 @@ import { BaseCommand } from '../../base' import { newCheckBuilder } from '../../utils/checks' -import { displaySendTx } from '../../utils/cli' +import { displayViemTx } from '../../utils/cli' import { CustomFlags } from '../../utils/command' export default class RevokeUpvote extends BaseCommand { @@ -15,6 +15,7 @@ export default class RevokeUpvote extends BaseCommand { async run() { const kit = await this.getKit() + const publicClient = await this.getPublicClient() const res = await this.parse(RevokeUpvote) const signer = res.flags.from kit.defaultAccount = signer @@ -24,11 +25,6 @@ export default class RevokeUpvote extends BaseCommand { // TODO(nategraf): Check whether there are upvotes to revoke before sending transaction. const governance = await kit.contracts.getGovernance() const account = await (await kit.contracts.getAccounts()).voteSignerToAccount(signer) - await displaySendTx( - 'revokeUpvoteTx', - await governance.revokeUpvote(account), - {}, - 'ProposalUpvoteRevoked' - ) + await displayViemTx('revokeUpvoteTx', governance.revokeUpvote(account), publicClient) } } diff --git a/packages/cli/src/commands/governance/show.test.ts b/packages/cli/src/commands/governance/show.test.ts index bf6e9a7f3..cc36b01f4 100644 --- a/packages/cli/src/commands/governance/show.test.ts +++ b/packages/cli/src/commands/governance/show.test.ts @@ -1,17 +1,16 @@ -import { newKitFromWeb3 } from '@celo/contractkit' +import { newKitFromProvider } from '@celo/contractkit' import { unixSecondsTimestampToDateString } from '@celo/contractkit/lib/wrappers/BaseWrapper' import { Proposal } from '@celo/contractkit/lib/wrappers/Governance' import { testWithAnvilL2 } from '@celo/dev-utils/anvil-test' import { timeTravel } from '@celo/dev-utils/ganache-test' import fs from 'fs' import path from 'node:path' -import Web3 from 'web3' -import { stripAnsiCodesAndTxHashes, testLocallyWithWeb3Node } from '../../test-utils/cliUtils' +import { stripAnsiCodesAndTxHashes, testLocallyWithNode } from '../../test-utils/cliUtils' import Show from './show' process.env.NO_SYNCCHECK = 'true' -testWithAnvilL2('governance:show cmd', (web3: Web3) => { +testWithAnvilL2('governance:show cmd', (provider) => { const PROPOSAL_TRANSACTIONS = [ { to: '0x4200000000000000000000000000000000000018', @@ -34,33 +33,41 @@ testWithAnvilL2('governance:show cmd', (web3: Web3) => { }) it('shows a proposal in "Referendum" stage', async () => { - const kit = newKitFromWeb3(web3) + const kit = newKitFromProvider(provider) const governanceWrapper = await kit.contracts.getGovernance() - const [proposer, voter] = await web3.eth.getAccounts() + const [proposer, voter] = await kit.connection.getAccounts() const minDeposit = (await governanceWrapper.minDeposit()).toFixed() const logMock = jest.spyOn(console, 'log') const dequeueFrequency = (await governanceWrapper.dequeueFrequency()).toNumber() const proposalId = 1 - await governanceWrapper - .propose(PROPOSAL_TRANSACTIONS, 'URL') - .sendAndWaitForReceipt({ from: proposer, value: minDeposit }) + const proposeHash = await governanceWrapper.propose(PROPOSAL_TRANSACTIONS, 'URL', { + from: proposer, + value: minDeposit, + }) + await kit.connection.viemClient.waitForTransactionReceipt({ + hash: proposeHash as `0x${string}`, + }) const accountWrapper = await kit.contracts.getAccounts() const lockedGoldWrapper = await kit.contracts.getLockedGold() - await accountWrapper.createAccount().sendAndWaitForReceipt({ from: voter }) - await lockedGoldWrapper.lock().sendAndWaitForReceipt({ from: voter, value: minDeposit }) + const createHash = await accountWrapper.createAccount({ from: voter }) + await kit.connection.viemClient.waitForTransactionReceipt({ hash: createHash as `0x${string}` }) + const lockHash = await lockedGoldWrapper.lock({ from: voter, value: minDeposit }) + await kit.connection.viemClient.waitForTransactionReceipt({ hash: lockHash as `0x${string}` }) - await timeTravel(dequeueFrequency + 1, web3) + await timeTravel(dequeueFrequency + 1, provider) - await governanceWrapper.dequeueProposalsIfReady().sendAndWaitForReceipt({ - from: proposer, + const dequeueHash = await governanceWrapper.dequeueProposalsIfReady({ from: proposer }) + await kit.connection.viemClient.waitForTransactionReceipt({ + hash: dequeueHash as `0x${string}`, }) - await (await governanceWrapper.vote(proposalId, 'Yes')).sendAndWaitForReceipt({ from: voter }) + const voteHash = await governanceWrapper.vote(proposalId, 'Yes', { from: voter }) + await kit.connection.viemClient.waitForTransactionReceipt({ hash: voteHash as `0x${string}` }) - await testLocallyWithWeb3Node(Show, ['--proposalID', proposalId.toString()], web3) + await testLocallyWithNode(Show, ['--proposalID', proposalId.toString()], provider) const schedule = await governanceWrapper.proposalSchedule(proposalId) const timestamp = await (await governanceWrapper.getProposalMetadata(proposalId)).timestamp diff --git a/packages/cli/src/commands/governance/show.ts b/packages/cli/src/commands/governance/show.ts index 3546e2cb5..92fcaf321 100644 --- a/packages/cli/src/commands/governance/show.ts +++ b/packages/cli/src/commands/governance/show.ts @@ -1,5 +1,5 @@ import { ProposalBuilder, proposalToJSON } from '@celo/governance' -import { toBuffer } from '@ethereumjs/util' +import { hexToBytes } from 'viem' import { Flags } from '@oclif/core' import chalk from 'chalk' import { writeFileSync } from 'fs' @@ -148,7 +148,7 @@ export default class Show extends BaseCommand { }) } } else if (hotfix) { - const hotfixBuf = toBuffer(hotfix) as Buffer + const hotfixBuf = Buffer.from(hexToBytes(hotfix as `0x${string}`)) const record = await governance.getHotfixRecord(hotfixBuf) printValueMap(record) } else if (account) { diff --git a/packages/cli/src/commands/governance/test-proposal.test.ts b/packages/cli/src/commands/governance/test-proposal.test.ts index 5fad962f1..6fe51a053 100644 --- a/packages/cli/src/commands/governance/test-proposal.test.ts +++ b/packages/cli/src/commands/governance/test-proposal.test.ts @@ -1,10 +1,10 @@ import { PROXY_ADMIN_ADDRESS } from '@celo/connect' +import { newKitFromProvider } from '@celo/contractkit' import { setCode, testWithAnvilL2 } from '@celo/dev-utils/anvil-test' import * as celoGovernance from '@celo/governance' import fs from 'fs' import path from 'node:path' -import Web3 from 'web3' -import { stripAnsiCodesAndTxHashes, testLocallyWithWeb3Node } from '../../test-utils/cliUtils' +import { stripAnsiCodesAndTxHashes, testLocallyWithNode } from '../../test-utils/cliUtils' import TestProposal from './test-proposal' process.env.NO_SYNCCHECK = 'true' @@ -17,7 +17,7 @@ jest.mock('@celo/governance', () => { } }) -testWithAnvilL2('governance:test-proposal cmd', (web3: Web3) => { +testWithAnvilL2('governance:test-proposal cmd', (provider) => { const PROPOSAL_TRANSACTION_TEST_KEY = '3' const PROPOSAL_TRANSACTION_TEST_VALUE = '4' const PROPOSAL_TRANSACTIONS = [ @@ -50,15 +50,16 @@ testWithAnvilL2('governance:test-proposal cmd', (web3: Web3) => { return {} as any }) - await setCode(web3, PROXY_ADMIN_ADDRESS, TEST_TRANSACTIONS_BYTECODE) + await setCode(provider, PROXY_ADMIN_ADDRESS, TEST_TRANSACTIONS_BYTECODE) - const [account] = await web3.eth.getAccounts() + const kit = newKitFromProvider(provider) + const [account] = await kit.connection.getAccounts() const logMock = jest.spyOn(console, 'log') - await testLocallyWithWeb3Node( + await testLocallyWithNode( TestProposal, ['--jsonTransactions', PROPOSAL_TRANSACTIONS_FILE_PATH, '--from', account], - web3 + provider ) // Verify we're passing correct arguments to 'proposalToJSON' diff --git a/packages/cli/src/commands/governance/upvote.test.ts b/packages/cli/src/commands/governance/upvote.test.ts index ce7677a44..16586adac 100644 --- a/packages/cli/src/commands/governance/upvote.test.ts +++ b/packages/cli/src/commands/governance/upvote.test.ts @@ -1,11 +1,10 @@ import { StrongAddress } from '@celo/base' -import { newKitFromWeb3 } from '@celo/contractkit' +import { newKitFromProvider } from '@celo/contractkit' import { GovernanceWrapper } from '@celo/contractkit/lib/wrappers/Governance' import { testWithAnvilL2 } from '@celo/dev-utils/anvil-test' import { timeTravel } from '@celo/dev-utils/ganache-test' import BigNumber from 'bignumber.js' -import Web3 from 'web3' -import { testLocallyWithWeb3Node } from '../../test-utils/cliUtils' +import { testLocallyWithNode } from '../../test-utils/cliUtils' import Register from '../account/register' import Lock from '../lockedcelo/lock' import Dequeue from './dequeue' @@ -13,9 +12,9 @@ import Upvote from './upvote' process.env.NO_SYNCCHECK = 'true' -testWithAnvilL2('governance:upvote cmd', (web3: Web3) => { +testWithAnvilL2('governance:upvote cmd', (provider) => { let minDeposit: string - const kit = newKitFromWeb3(web3) + const kit = newKitFromProvider(provider) const proposalID = new BigNumber(1) const proposalID2 = new BigNumber(2) const proposalID3 = new BigNumber(3) @@ -26,7 +25,7 @@ testWithAnvilL2('governance:upvote cmd', (web3: Web3) => { let governance: GovernanceWrapper beforeEach(async () => { - accounts = (await web3.eth.getAccounts()) as StrongAddress[] + accounts = (await kit.connection.getAccounts()) as StrongAddress[] kit.defaultAccount = accounts[0] governance = await kit.contracts.getGovernance() minDeposit = (await governance.minDeposit()).toFixed() @@ -35,37 +34,57 @@ testWithAnvilL2('governance:upvote cmd', (web3: Web3) => { // If the devchain is published less than dequeueFrequency ago, the tests // will fail, so we need to make sure that by calling timeTravel() we will // hit the next dequeue - await timeTravel(dequeueFrequency, web3) + await timeTravel(dequeueFrequency, provider) - await governance - .propose([], 'URL') - .sendAndWaitForReceipt({ from: accounts[0], value: minDeposit }) + const proposeHash1 = await governance.propose([], 'URL', { + from: accounts[0], + value: minDeposit, + }) + await kit.connection.viemClient.waitForTransactionReceipt({ + hash: proposeHash1 as `0x${string}`, + }) // this will reset lastDequeue to now // there is 3 concurrent proposals possible to be dequeued - await testLocallyWithWeb3Node(Dequeue, ['--from', accounts[0]], web3) - await governance - .propose([], 'URL2') - .sendAndWaitForReceipt({ from: accounts[0], value: minDeposit }) - await governance - .propose([], 'URL3') - .sendAndWaitForReceipt({ from: accounts[0], value: minDeposit }) - await governance - .propose([], 'URL4') - .sendAndWaitForReceipt({ from: accounts[0], value: minDeposit }) - await governance - .propose([], 'URL5') - .sendAndWaitForReceipt({ from: accounts[0], value: minDeposit }) + await testLocallyWithNode(Dequeue, ['--from', accounts[0]], provider) + const proposeHash2 = await governance.propose([], 'URL2', { + from: accounts[0], + value: minDeposit, + }) + await kit.connection.viemClient.waitForTransactionReceipt({ + hash: proposeHash2 as `0x${string}`, + }) + const proposeHash3 = await governance.propose([], 'URL3', { + from: accounts[0], + value: minDeposit, + }) + await kit.connection.viemClient.waitForTransactionReceipt({ + hash: proposeHash3 as `0x${string}`, + }) + const proposeHash4 = await governance.propose([], 'URL4', { + from: accounts[0], + value: minDeposit, + }) + await kit.connection.viemClient.waitForTransactionReceipt({ + hash: proposeHash4 as `0x${string}`, + }) + const proposeHash5 = await governance.propose([], 'URL5', { + from: accounts[0], + value: minDeposit, + }) + await kit.connection.viemClient.waitForTransactionReceipt({ + hash: proposeHash5 as `0x${string}`, + }) - await timeTravel(dequeueFrequency, web3) - await testLocallyWithWeb3Node(Register, ['--from', accounts[0]], web3) - await testLocallyWithWeb3Node(Lock, ['--from', accounts[0], '--value', '100'], web3) + await timeTravel(dequeueFrequency, provider) + await testLocallyWithNode(Register, ['--from', accounts[0]], provider) + await testLocallyWithNode(Lock, ['--from', accounts[0], '--value', '100'], provider) }) test('will dequeue proposal if ready', async () => { - await testLocallyWithWeb3Node( + await testLocallyWithNode( Upvote, ['--proposalID', proposalID2.toString(10), '--from', accounts[0]], - web3 + provider ) const queue = await governance.getQueue() @@ -76,10 +95,10 @@ testWithAnvilL2('governance:upvote cmd', (web3: Web3) => { }) test('can upvote proposal which cannot be dequeued', async () => { - await testLocallyWithWeb3Node( + await testLocallyWithNode( Upvote, ['--proposalID', proposalID5.toString(10), '--from', accounts[0]], - web3 + provider ) const queue = await governance.getQueue() diff --git a/packages/cli/src/commands/governance/upvote.ts b/packages/cli/src/commands/governance/upvote.ts index 896331101..680f1aabf 100644 --- a/packages/cli/src/commands/governance/upvote.ts +++ b/packages/cli/src/commands/governance/upvote.ts @@ -1,9 +1,10 @@ +import { PublicCeloClient } from '@celo/actions' import { GovernanceWrapper } from '@celo/contractkit/src/wrappers/Governance' import { Flags } from '@oclif/core' import chalk from 'chalk' import { BaseCommand } from '../../base' import { newCheckBuilder } from '../../utils/checks' -import { displaySendTx } from '../../utils/cli' +import { displayViemTx } from '../../utils/cli' import { CustomFlags } from '../../utils/command' export default class Upvote extends BaseCommand { @@ -19,6 +20,7 @@ export default class Upvote extends BaseCommand { async run() { const kit = await this.getKit() + const publicClient = await this.getPublicClient() const res = await this.parse(Upvote) const signer = res.flags.from const id = res.flags.proposalID @@ -33,10 +35,13 @@ export default class Upvote extends BaseCommand { const account = await (await kit.contracts.getAccounts()).voteSignerToAccount(signer) - const consideredProposals = await this.dequeueAllPossibleProposals(governance as any) + const consideredProposals = await this.dequeueAllPossibleProposals( + governance as any, + publicClient + ) if (!consideredProposals.some((k) => k.id === id)) { - await displaySendTx('upvoteTx', await governance.upvote(id, account), {}, 'ProposalUpvoted') + await displayViemTx('upvoteTx', governance.upvote(id, account), publicClient) } else { console.info(chalk.green('Proposal was dequeued, no need to upvote it.')) } @@ -52,7 +57,7 @@ export default class Upvote extends BaseCommand { * 4. Since none of the proposals were actually dequeued, next call will allow to dequeue again * 5. Upvote function will try to dequeue again and possibly it will hit the proposal and bug that we have */ - async dequeueAllPossibleProposals(governance: GovernanceWrapper) { + async dequeueAllPossibleProposals(governance: GovernanceWrapper, publicClient: PublicCeloClient) { const concurrentProposalCount = (await governance.concurrentProposals()).toNumber() const queue = await governance.getQueue() const originalLastDequeue = await governance.lastDequeue() @@ -72,7 +77,7 @@ export default class Upvote extends BaseCommand { ) ).filter((k) => k.expired === false) - await displaySendTx('dequeue', governance.dequeueProposalsIfReady(), {}) + await displayViemTx('dequeue', governance.dequeueProposalsIfReady(), publicClient) if (originalLastDequeue !== (await governance.lastDequeue())) { break } diff --git a/packages/cli/src/commands/governance/vote.test.ts b/packages/cli/src/commands/governance/vote.test.ts index e1c93cd4a..c4ba02663 100644 --- a/packages/cli/src/commands/governance/vote.test.ts +++ b/packages/cli/src/commands/governance/vote.test.ts @@ -1,12 +1,11 @@ import { StrongAddress } from '@celo/base' -import { newKitFromWeb3 } from '@celo/contractkit' +import { newKitFromProvider } from '@celo/contractkit' import { GovernanceWrapper } from '@celo/contractkit/lib/wrappers/Governance' import { testWithAnvilL2 } from '@celo/dev-utils/anvil-test' import { timeTravel } from '@celo/dev-utils/ganache-test' import BigNumber from 'bignumber.js' -import Web3 from 'web3' import { changeMultiSigOwner } from '../../test-utils/chain-setup' -import { testLocallyWithWeb3Node } from '../../test-utils/cliUtils' +import { testLocallyWithNode } from '../../test-utils/cliUtils' import Register from '../account/register' import Lock from '../lockedcelo/lock' import Approve from './approve' @@ -15,40 +14,44 @@ import Vote from './vote' process.env.NO_SYNCCHECK = 'true' -testWithAnvilL2('governance:vote cmd', (web3: Web3) => { +testWithAnvilL2('governance:vote cmd', (provider) => { let minDeposit: string - const kit = newKitFromWeb3(web3) + const kit = newKitFromProvider(provider) const proposalID = new BigNumber(1) let accounts: StrongAddress[] = [] let governance: GovernanceWrapper beforeEach(async () => { - accounts = (await web3.eth.getAccounts()) as StrongAddress[] + accounts = (await kit.connection.getAccounts()) as StrongAddress[] kit.defaultAccount = accounts[0] governance = await kit.contracts.getGovernance() minDeposit = (await governance.minDeposit()).toFixed() - await governance - .propose([], 'URL') - .sendAndWaitForReceipt({ from: accounts[0], value: minDeposit }) + const proposeHash = await governance.propose([], 'URL', { + from: accounts[0], + value: minDeposit, + }) + await kit.connection.viemClient.waitForTransactionReceipt({ + hash: proposeHash as `0x${string}`, + }) const dequeueFrequency = (await governance.dequeueFrequency()).toNumber() - await timeTravel(dequeueFrequency, web3) - await testLocallyWithWeb3Node(Dequeue, ['--from', accounts[0]], web3) + await timeTravel(dequeueFrequency, provider) + await testLocallyWithNode(Dequeue, ['--from', accounts[0]], provider) await changeMultiSigOwner(kit, accounts[0]) - await testLocallyWithWeb3Node( + await testLocallyWithNode( Approve, ['--from', accounts[0], '--proposalID', proposalID.toString(10), '--useMultiSig'], - web3 + provider ) - await testLocallyWithWeb3Node(Register, ['--from', accounts[0]], web3) - await testLocallyWithWeb3Node(Lock, ['--from', accounts[0], '--value', '100'], web3) + await testLocallyWithNode(Register, ['--from', accounts[0]], provider) + await testLocallyWithNode(Lock, ['--from', accounts[0], '--value', '100'], provider) }) test('can vote yes', async () => { - await testLocallyWithWeb3Node( + await testLocallyWithNode( Vote, ['--from', accounts[0], '--proposalID', proposalID.toString(10), '--value', 'Yes'], - web3 + provider ) const votes = await governance.getVotes(proposalID) expect(votes.Yes.toNumber()).toEqual(100) diff --git a/packages/cli/src/commands/governance/votePartially.test.ts b/packages/cli/src/commands/governance/votePartially.test.ts index ce8059303..3c99f9720 100644 --- a/packages/cli/src/commands/governance/votePartially.test.ts +++ b/packages/cli/src/commands/governance/votePartially.test.ts @@ -1,12 +1,11 @@ import { StrongAddress } from '@celo/base' -import { newKitFromWeb3 } from '@celo/contractkit' +import { newKitFromProvider } from '@celo/contractkit' import { GovernanceWrapper } from '@celo/contractkit/lib/wrappers/Governance' import { testWithAnvilL2 } from '@celo/dev-utils/anvil-test' import { timeTravel } from '@celo/dev-utils/ganache-test' import BigNumber from 'bignumber.js' -import Web3 from 'web3' import { changeMultiSigOwner } from '../../test-utils/chain-setup' -import { testLocallyWithWeb3Node } from '../../test-utils/cliUtils' +import { testLocallyWithNode } from '../../test-utils/cliUtils' import Register from '../account/register' import Lock from '../lockedcelo/lock' import Approve from './approve' @@ -15,37 +14,41 @@ import VotePartially from './votePartially' process.env.NO_SYNCCHECK = 'true' -testWithAnvilL2('governance:vote-partially cmd', (web3: Web3) => { +testWithAnvilL2('governance:vote-partially cmd', (provider) => { let minDeposit: string - const kit = newKitFromWeb3(web3) + const kit = newKitFromProvider(provider) const proposalID = new BigNumber(1) let accounts: StrongAddress[] = [] let governance: GovernanceWrapper beforeEach(async () => { - accounts = (await web3.eth.getAccounts()) as StrongAddress[] + accounts = (await kit.connection.getAccounts()) as StrongAddress[] kit.defaultAccount = accounts[0] governance = await kit.contracts.getGovernance() minDeposit = (await governance.minDeposit()).toFixed() - await governance - .propose([], 'URL') - .sendAndWaitForReceipt({ from: accounts[0], value: minDeposit }) + const proposeHash = await governance.propose([], 'URL', { + from: accounts[0], + value: minDeposit, + }) + await kit.connection.viemClient.waitForTransactionReceipt({ + hash: proposeHash as `0x${string}`, + }) const dequeueFrequency = (await governance.dequeueFrequency()).toNumber() - await timeTravel(dequeueFrequency + 1, web3) - await testLocallyWithWeb3Node(Dequeue, ['--from', accounts[0]], web3) + await timeTravel(dequeueFrequency + 1, provider) + await testLocallyWithNode(Dequeue, ['--from', accounts[0]], provider) await changeMultiSigOwner(kit, accounts[0]) - await testLocallyWithWeb3Node( + await testLocallyWithNode( Approve, ['--from', accounts[0], '--proposalID', proposalID.toString(10), '--useMultiSig'], - web3 + provider ) - await testLocallyWithWeb3Node(Register, ['--from', accounts[0]], web3) - await testLocallyWithWeb3Node(Lock, ['--from', accounts[0], '--value', '100'], web3) + await testLocallyWithNode(Register, ['--from', accounts[0]], provider) + await testLocallyWithNode(Lock, ['--from', accounts[0], '--value', '100'], provider) }) test('can vote partially yes and no', async () => { - await testLocallyWithWeb3Node( + await testLocallyWithNode( VotePartially, [ '--from', @@ -59,7 +62,7 @@ testWithAnvilL2('governance:vote-partially cmd', (web3: Web3) => { '--abstain', '0', ], - web3 + provider ) const votes = await governance.getVotes(proposalID) expect(votes.Yes.toNumber()).toEqual(10) diff --git a/packages/cli/src/commands/governance/votePartially.ts b/packages/cli/src/commands/governance/votePartially.ts index 3d5c2881f..a87be8dc5 100644 --- a/packages/cli/src/commands/governance/votePartially.ts +++ b/packages/cli/src/commands/governance/votePartially.ts @@ -2,7 +2,7 @@ import { Flags } from '@oclif/core' import chalk from 'chalk' import { BaseCommand } from '../../base' import { newCheckBuilder } from '../../utils/checks' -import { displaySendTx } from '../../utils/cli' +import { displayViemTx } from '../../utils/cli' import { CustomFlags } from '../../utils/command' export default class VotePartially extends BaseCommand { @@ -25,6 +25,7 @@ export default class VotePartially extends BaseCommand { async run() { const kit = await this.getKit() + const publicClient = await this.getPublicClient() const res = await this.parse(VotePartially) const signer = res.flags.from const id = res.flags.proposalID @@ -43,16 +44,10 @@ export default class VotePartially extends BaseCommand { return } - await displaySendTx( + await displayViemTx( 'voteTx', - await governance.votePartially( - id, - res.flags.yes ?? 0, - res.flags.no ?? 0, - res.flags.abstain ?? 0 - ), - {}, - 'ProposalPartiallyVoted' + governance.votePartially(id, res.flags.yes ?? 0, res.flags.no ?? 0, res.flags.abstain ?? 0), + publicClient ) } } diff --git a/packages/cli/src/commands/governance/withdraw.test.ts b/packages/cli/src/commands/governance/withdraw.test.ts index 2d6be1f0f..c3af14b79 100644 --- a/packages/cli/src/commands/governance/withdraw.test.ts +++ b/packages/cli/src/commands/governance/withdraw.test.ts @@ -1,14 +1,12 @@ import { StrongAddress } from '@celo/base' -import { CeloProvider } from '@celo/connect/lib/celo-provider' -import { newKitFromWeb3 } from '@celo/contractkit' +import { newKitFromProvider } from '@celo/contractkit' import { GovernanceWrapper, Proposal } from '@celo/contractkit/lib/wrappers/Governance' import { setBalance, testWithAnvilL2, withImpersonatedAccount } from '@celo/dev-utils/anvil-test' import { timeTravel } from '@celo/dev-utils/ganache-test' import { ProposalBuilder } from '@celo/governance' import Safe, { getSafeAddressFromDeploymentTx } from '@safe-global/protocol-kit' import BigNumber from 'bignumber.js' -import Web3 from 'web3' -import { stripAnsiCodesFromNestedArray, testLocallyWithWeb3Node } from '../../test-utils/cliUtils' +import { stripAnsiCodesFromNestedArray, testLocallyWithNode } from '../../test-utils/cliUtils' import { deployMultiCall } from '../../test-utils/multicall' import { createMultisig, setupSafeContracts } from '../../test-utils/multisigUtils' import Withdraw from './withdraw' @@ -17,12 +15,12 @@ process.env.NO_SYNCCHECK = 'true' testWithAnvilL2( 'governance:withdraw', - (web3: Web3) => { + (client) => { const logMock = jest.spyOn(console, 'log') const errorMock = jest.spyOn(console, 'error') let minDeposit: string - const kit = newKitFromWeb3(web3) + const kit = newKitFromProvider(client) let accounts: StrongAddress[] = [] let governance: GovernanceWrapper @@ -31,37 +29,51 @@ testWithAnvilL2( logMock.mockClear().mockImplementation() errorMock.mockClear().mockImplementation() - await deployMultiCall(web3, '0xcA11bde05977b3631167028862bE2a173976CA11') + await deployMultiCall(client, '0xcA11bde05977b3631167028862bE2a173976CA11') - accounts = (await web3.eth.getAccounts()) as StrongAddress[] + accounts = (await kit.connection.getAccounts()) as StrongAddress[] kit.defaultAccount = accounts[0] governance = await kit.contracts.getGovernance() minDeposit = (await governance.minDeposit()).toFixed() const proposal: Proposal = await new ProposalBuilder(kit).build() - await governance - .propose(proposal, 'URL') - .sendAndWaitForReceipt({ from: accounts[0], value: minDeposit }) + const proposeHash = await governance.propose(proposal, 'URL', { + from: accounts[0], + value: minDeposit, + }) + await kit.connection.viemClient.waitForTransactionReceipt({ + hash: proposeHash as `0x${string}`, + }) const dequeueFrequency = (await governance.dequeueFrequency()).toNumber() - await timeTravel(dequeueFrequency + 1, web3) - await governance.dequeueProposalsIfReady().sendAndWaitForReceipt() + await timeTravel(dequeueFrequency + 1, client) + const dequeueHash = await governance.dequeueProposalsIfReady() + await kit.connection.viemClient.waitForTransactionReceipt({ + hash: dequeueHash as `0x${string}`, + }) }) test('can withdraw', async () => { - const balanceBefore = await kit.connection.getBalance(accounts[0]) + const balanceBefore = await kit.connection.viemClient.getBalance({ + address: accounts[0] as `0x${string}`, + }) - await testLocallyWithWeb3Node(Withdraw, ['--from', accounts[0]], web3) + await testLocallyWithNode(Withdraw, ['--from', accounts[0]], client) - const balanceAfter = await kit.connection.getBalance(accounts[0]) - const latestTransactionReceipt = await web3.eth.getTransactionReceipt( - (await web3.eth.getBlock('latest')).transactions[0] - ) + const balanceAfter = await kit.connection.viemClient.getBalance({ + address: accounts[0] as `0x${string}`, + }) + const latestBlock = await kit.connection.viemClient.getBlock({ blockTag: 'latest' }) + const latestTransactionReceipt = await kit.connection.viemClient.getTransactionReceipt({ + hash: latestBlock.transactions[0], + }) // Safety check if the latest transaction was originated by expected account expect(latestTransactionReceipt.from.toLowerCase()).toEqual(accounts[0].toLowerCase()) - const difference = new BigNumber(balanceAfter) - .minus(balanceBefore) - .plus(latestTransactionReceipt.effectiveGasPrice * latestTransactionReceipt.gasUsed) + const difference = new BigNumber(balanceAfter.toString()) + .minus(balanceBefore.toString()) + .plus( + (latestTransactionReceipt.effectiveGasPrice * latestTransactionReceipt.gasUsed).toString() + ) expect(difference.toFixed()).toEqual(minDeposit) @@ -96,68 +108,73 @@ testWithAnvilL2( multisigAddress = await createMultisig(kit, [multisigOwner], 1, 1) await withImpersonatedAccount( - web3, + client, multisigAddress, async () => { - await governance - .propose(await new ProposalBuilder(kit).build(), 'http://example.com/proposal.json') - .sendAndWaitForReceipt({ from: multisigAddress, value: minDeposit }) + const proposeHash2 = await governance.propose( + await new ProposalBuilder(kit).build(), + 'http://example.com/proposal.json', + { from: multisigAddress, value: minDeposit } + ) + await kit.connection.viemClient.waitForTransactionReceipt({ + hash: proposeHash2 as `0x${string}`, + }) }, // make sure the multisig contract has enough balance to perform the transaction new BigNumber(minDeposit).multipliedBy(2) ) // Zero out the balance for easier testing - await setBalance(web3, multisigAddress, 0) + await setBalance(client, multisigAddress, 0) // Dequeue so the proposal can be refunded const dequeueFrequency = (await governance.dequeueFrequency()).toNumber() - await timeTravel(dequeueFrequency + 1, web3) - await governance.dequeueProposalsIfReady().sendAndWaitForReceipt() + await timeTravel(dequeueFrequency + 1, client) + const dequeueHash2 = await governance.dequeueProposalsIfReady() + await kit.connection.viemClient.waitForTransactionReceipt({ + hash: dequeueHash2 as `0x${string}`, + }) }) it('can withdraw using --useMultiSig', async () => { // Safety check - expect(await kit.connection.getBalance(multisigAddress)).toEqual('0') + expect( + await kit.connection.viemClient.getBalance({ address: multisigAddress as `0x${string}` }) + ).toEqual(0n) - await testLocallyWithWeb3Node( + await testLocallyWithNode( Withdraw, ['--useMultiSig', '--for', multisigAddress, '--from', multisigOwner], - web3 + client ) // After withdrawing the refunded deposit should be the minDeposit (as we zeroed out the balance before) - expect(await kit.connection.getBalance(multisigAddress)).toEqual(minDeposit) + expect( + await kit.connection.viemClient.getBalance({ address: multisigAddress as `0x${string}` }) + ).toEqual(BigInt(minDeposit)) expect(stripAnsiCodesFromNestedArray(logMock.mock.calls)).toMatchInlineSnapshot(` - [ - [ - "Running Checks:", - ], - [ - " ✔ 0x871DD7C2B4b25E1Aa18728e9D5f2Af4C4e431f5c has refunded governance deposits ", - ], - [ - " ✔ The provided address is an owner of the multisig ", - ], - [ - "All checks passed", - ], - [ - "SendTransaction: withdraw", - ], - [ - "txHash: 0xtxhash", - ], - [ - "Deposit:", - ], - [ - "sender: 0x2EB25B5eb9d5A4f61deb1e4F846343F862eB67D9 - value: 100000000000000000000", - ], - ] - `) + [ + [ + "Running Checks:", + ], + [ + " ✔ 0x871DD7C2B4b25E1Aa18728e9D5f2Af4C4e431f5c has refunded governance deposits ", + ], + [ + " ✔ The provided address is an owner of the multisig ", + ], + [ + "All checks passed", + ], + [ + "SendTransaction: withdraw", + ], + [ + "txHash: 0xtxhash", + ], + ] + `) expect(stripAnsiCodesFromNestedArray(errorMock.mock.calls)).toMatchInlineSnapshot(`[]`) }) @@ -165,18 +182,22 @@ testWithAnvilL2( const otherAccount = accounts[1] // Safety check - expect(await kit.connection.getBalance(multisigAddress)).toEqual('0') + expect( + await kit.connection.viemClient.getBalance({ address: multisigAddress as `0x${string}` }) + ).toEqual(0n) await expect( - testLocallyWithWeb3Node( + testLocallyWithNode( Withdraw, ['--useMultiSig', '--for', multisigAddress, '--from', otherAccount], - web3 + client ) ).rejects.toMatchInlineSnapshot(`[Error: Some checks didn't pass!]`) // After failing to withdraw the deposit, the balance should still be zero - expect(await kit.connection.getBalance(multisigAddress)).toEqual('0') + expect( + await kit.connection.viemClient.getBalance({ address: multisigAddress as `0x${string}` }) + ).toEqual(0n) expect(stripAnsiCodesFromNestedArray(logMock.mock.calls)).toMatchInlineSnapshot(` [ @@ -200,10 +221,10 @@ testWithAnvilL2( let owners: StrongAddress[] beforeEach(async () => { - await setupSafeContracts(web3) + await setupSafeContracts(client) owners = [ - (await web3.eth.getAccounts())[0] as StrongAddress, + (await kit.connection.getAccounts())[0] as StrongAddress, '0x6C666E57A5E8715cFE93f92790f98c4dFf7b69e2', ] const safeAccountConfig = { @@ -216,14 +237,17 @@ testWithAnvilL2( } const protocolKit = await Safe.init({ predictedSafe: predictSafe, - provider: (web3.currentProvider as any as CeloProvider).toEip1193Provider(), + provider: kit.connection.currentProvider as any, signer: owners[0], }) const deploymentTransaction = await protocolKit.createSafeDeploymentTransaction() - const receipt = await web3.eth.sendTransaction({ + const txHash = await kit.connection.sendTransaction({ from: owners[0], ...deploymentTransaction, }) + const receipt = await kit.connection.viemClient.waitForTransactionReceipt({ + hash: txHash as `0x${string}`, + }) safeAddress = getSafeAddressFromDeploymentTx( receipt as unknown as Parameters[0], '1.3.0' @@ -231,44 +255,56 @@ testWithAnvilL2( await protocolKit.connect({ safeAddress }) const balance = new BigNumber(minDeposit).multipliedBy(2) - await setBalance(web3, safeAddress, balance) + await setBalance(client, safeAddress, balance) for (const owner of owners) { - await setBalance(web3, owner, balance) + await setBalance(client, owner, balance) } - await withImpersonatedAccount(web3, safeAddress, async () => { - await governance - .propose(await new ProposalBuilder(kit).build(), 'http://example.com/proposal.json') - .sendAndWaitForReceipt({ from: safeAddress, value: minDeposit }) + await withImpersonatedAccount(client, safeAddress, async () => { + const proposeHash3 = await governance.propose( + await new ProposalBuilder(kit).build(), + 'http://example.com/proposal.json', + { from: safeAddress, value: minDeposit } + ) + await kit.connection.viemClient.waitForTransactionReceipt({ + hash: proposeHash3 as `0x${string}`, + }) }) // Dequeue so the proposal can be refunded const dequeueFrequency = (await governance.dequeueFrequency()).toNumber() - await timeTravel(dequeueFrequency + 1, web3) - await governance.dequeueProposalsIfReady().sendAndWaitForReceipt() + await timeTravel(dequeueFrequency + 1, client) + const dequeueHash3 = await governance.dequeueProposalsIfReady() + await kit.connection.viemClient.waitForTransactionReceipt({ + hash: dequeueHash3 as `0x${string}`, + }) }) it('can withdraw using --useSafe', async () => { // Safety check - const amountBeforeRefund = await kit.connection.getBalance(safeAddress) + const amountBeforeRefund = await kit.connection.viemClient.getBalance({ + address: safeAddress as `0x${string}`, + }) for (const owner of owners) { - await withImpersonatedAccount(web3, owner, async () => { - await testLocallyWithWeb3Node( + await withImpersonatedAccount(client, owner, async () => { + await testLocallyWithNode( Withdraw, ['--from', owner, '--useSafe', '--safeAddress', safeAddress], - web3 + client ) }) if (owner !== owners.at(-1)) { - expect(await kit.connection.getBalance(safeAddress)).toEqual(amountBeforeRefund) + expect( + await kit.connection.viemClient.getBalance({ address: safeAddress as `0x${string}` }) + ).toEqual(amountBeforeRefund) } } // After withdrawing the refunded deposit should be the minDeposit (as we zeroed out the balance before) - expect(await kit.connection.getBalance(safeAddress)).toEqual( - (BigInt(minDeposit) + BigInt(amountBeforeRefund)).toString() - ) + expect( + await kit.connection.viemClient.getBalance({ address: safeAddress as `0x${string}` }) + ).toEqual(BigInt(minDeposit) + amountBeforeRefund) expect(stripAnsiCodesFromNestedArray(logMock.mock.calls)).toMatchInlineSnapshot(` [ diff --git a/packages/cli/src/commands/governance/withdraw.ts b/packages/cli/src/commands/governance/withdraw.ts index c82439dc1..c94088578 100644 --- a/packages/cli/src/commands/governance/withdraw.ts +++ b/packages/cli/src/commands/governance/withdraw.ts @@ -3,14 +3,10 @@ import { ContractKit } from '@celo/contractkit' import { MultiSigWrapper } from '@celo/contractkit/lib/wrappers/MultiSig' import { BaseCommand } from '../../base' import { newCheckBuilder } from '../../utils/checks' -import { displaySendTx } from '../../utils/cli' +import { displayViemTx } from '../../utils/cli' import { CustomFlags } from '../../utils/command' import { MultiSigFlags, SafeFlags } from '../../utils/flags' -import { - createSafeFromWeb3, - performSafeTransaction, - safeTransactionMetadataFromCeloTransactionObject, -} from '../../utils/safe' +import { createSafe, performSafeTransaction, safeTransactionMetadata } from '../../utils/safe' export default class Withdraw extends BaseCommand { static description = 'Withdraw refunded governance proposal deposits.' @@ -27,6 +23,7 @@ export default class Withdraw extends BaseCommand { async run() { const kit = await this.getKit() + const publicClient = await this.getPublicClient() const res = await this.parse(Withdraw) const addressToRefund = this.getAddressToRefund(res.flags) const multiSigWrapper = await this.getMultiSigWrapper(kit, res.flags) @@ -37,8 +34,8 @@ export default class Withdraw extends BaseCommand { checkBuilder.isMultiSigOwner(res.flags.from, res.flags.for as StrongAddress) } else if (res.flags.useSafe) { checkBuilder.addCheck(`${res.flags.from} is a safe owner`, async () => { - const safe = await createSafeFromWeb3( - await this.getWeb3(), + const safe = await createSafe( + (await this.getKit()).connection.currentProvider, res.flags.from, res.flags.safeAddress! ) @@ -49,26 +46,26 @@ export default class Withdraw extends BaseCommand { await checkBuilder.runChecks() const governance = await kit.contracts.getGovernance() - const withdrawTx = governance.withdraw() + const withdrawData = governance.encodeFunctionData('withdraw', []) if (multiSigWrapper) { const multiSigTx = await multiSigWrapper.submitOrConfirmTransaction( governance.address, - withdrawTx.txo + withdrawData ) // "Deposit" event is emitted when the MultiSig contract receives the funds - await displaySendTx('withdraw', multiSigTx, {}, 'Deposit') + await displayViemTx('withdraw', Promise.resolve(multiSigTx), publicClient) } else if (res.flags.useSafe) { await performSafeTransaction( - await this.getWeb3(), + (await this.getKit()).connection.currentProvider, res.flags.safeAddress!, res.flags.from, - await safeTransactionMetadataFromCeloTransactionObject(withdrawTx, governance.address) + safeTransactionMetadata(withdrawData, governance.address) ) } else { - // No event is emited otherwise - await displaySendTx('withdraw', withdrawTx) + // No event is emitted otherwise + await displayViemTx('withdraw', governance.withdraw(), publicClient) } }