Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

feat: evm integration tests using tilt #206

Merged
merged 110 commits into from
Dec 19, 2024
Merged
Changes from 1 commit
Commits
Show all changes
110 commits
Select commit Hold shift + click to select a range
e2da93e
feat: update hub deploy script to incliude agg proposer, decoder, and…
marcomariscal Oct 8, 2024
4333264
fix: build warnings/errors
marcomariscal Oct 8, 2024
d277791
fix: handle stack too deep
marcomariscal Oct 8, 2024
399b376
feat: update spoke deploy script to use create2
marcomariscal Oct 8, 2024
e2655e6
fix: salts
marcomariscal Oct 8, 2024
0785352
chore: runs
marcomariscal Oct 9, 2024
49a2fa4
fix: init after
marcomariscal Oct 9, 2024
be934f3
fix: contract addr refs
marcomariscal Oct 9, 2024
f979f99
fix: contract addr ref
marcomariscal Oct 9, 2024
cfa3e93
chore: latest run
marcomariscal Oct 9, 2024
e4d9e1d
feat: holesky as hub deploy script
marcomariscal Oct 11, 2024
1e4330a
feat: add etherscan
marcomariscal Oct 11, 2024
53c43e6
WIP
alexkeating Oct 15, 2024
1eb6650
WIP: changes
alexkeating Oct 15, 2024
e01b274
Remove old deployments
alexkeating Oct 15, 2024
0b5aefb
Add formatting
alexkeating Oct 15, 2024
623e735
Actual deployments
alexkeating Oct 17, 2024
e7131d4
Remove dry runs
alexkeating Oct 17, 2024
58fcc51
Merge branch 'main' into feat/deploy-testing-3
marcomariscal Oct 17, 2024
87629f2
fix: compile
marcomariscal Oct 17, 2024
5842165
feat: deploy scripts for devnet
marcomariscal Oct 18, 2024
ddd7fd4
feat: script to output abis
marcomariscal Oct 21, 2024
a35e669
feat: runs
marcomariscal Oct 28, 2024
1ebced2
feat: handle mock token deploy for devnet
marcomariscal Oct 28, 2024
e347fc6
feat: integration test structure wip
marcomariscal Oct 28, 2024
151b5aa
feat: handle auto addresses from deploys
marcomariscal Oct 28, 2024
40788bb
feat: flesh out proposal helpers
marcomariscal Oct 29, 2024
a838438
fix: trying to make proposals unique
marcomariscal Oct 29, 2024
f91e234
fix: queue proposal
marcomariscal Oct 29, 2024
983b4c6
fix: wait for tx
marcomariscal Oct 29, 2024
622a3ae
chore: check
marcomariscal Oct 29, 2024
ff3254c
fix: wait for tx
marcomariscal Oct 29, 2024
59a8706
feat: get proposal data
marcomariscal Oct 29, 2024
69a0d9f
chore: cleaning/organizing
marcomariscal Oct 29, 2024
b4a698c
fix: move handleNoAccount to a more appropriate place
marcomariscal Oct 29, 2024
ed916d1
fix: naming clarity
marcomariscal Oct 29, 2024
70e501a
chore: check
marcomariscal Oct 29, 2024
8c91a20
feat: biome at root to fix format on save issues
marcomariscal Oct 30, 2024
57bcdec
chore: comment
marcomariscal Oct 30, 2024
dd1dc3f
feat: handle bridging votes to hub
marcomariscal Oct 30, 2024
3d84f98
fix: await tx's
marcomariscal Oct 30, 2024
868a512
fix: helpers for registering on hub vote pool
marcomariscal Oct 30, 2024
b797cf2
chore: runs
marcomariscal Oct 30, 2024
0a6e8cb
fix: to wormhole format
marcomariscal Oct 30, 2024
e45829d
Merge branch 'main' into feat/integration-test-w-main
marcomariscal Oct 30, 2024
e5f77d8
fix: params
marcomariscal Oct 30, 2024
6f31c1b
fix: handle conflicting env vars
marcomariscal Oct 30, 2024
a5899eb
chore: remove logs
marcomariscal Oct 30, 2024
d8fe042
fix: transfer ownership of hub vote pool to timelock
marcomariscal Nov 4, 2024
d4f7c23
feat: executeCrossChain
marcomariscal Nov 5, 2024
d87b8b7
feat: deploy from within integrations test folder
marcomariscal Nov 7, 2024
49f8eb8
chore: remove unused
marcomariscal Nov 7, 2024
3310a45
fix: remove etherscan
marcomariscal Oct 31, 2024
c00fe42
fix: evm gitignore to relevant folder
marcomariscal Oct 31, 2024
e5b9130
fix: setup
marcomariscal Nov 7, 2024
f73fc23
fix: better logging, wormhole format the hub message dispatcher in co…
marcomariscal Nov 8, 2024
332e575
fix: scripts
marcomariscal Nov 8, 2024
a063108
chore: check
marcomariscal Nov 8, 2024
382181c
fix: setup
marcomariscal Nov 8, 2024
75d3914
fix: setup
marcomariscal Nov 8, 2024
8fa9d5d
fix: setup
marcomariscal Nov 8, 2024
2267ee5
feat: deployment cache and setup check locally
marcomariscal Nov 8, 2024
c79c3e1
fix: improve setup handling
marcomariscal Nov 8, 2024
40d6eb0
fix: testing structure to one file since tests are dependent on each …
marcomariscal Nov 8, 2024
8661e03
fix: contract addr
marcomariscal Nov 8, 2024
cec1018
fix: setup
marcomariscal Nov 8, 2024
f564969
chore: logs and inc timeout
marcomariscal Nov 8, 2024
4c1dfa4
chore: check
marcomariscal Nov 8, 2024
e79bd4a
chore: remove broadcast
marcomariscal Nov 8, 2024
4e54de9
revert: dont need fs permish now
marcomariscal Nov 8, 2024
22fdd89
fix: revert to original deploy wallet logic
marcomariscal Nov 8, 2024
ae8a916
chore: remove unused
marcomariscal Nov 8, 2024
b433650
chore: remove unused
marcomariscal Nov 8, 2024
a7aabd0
fix: use /// for comments
marcomariscal Nov 8, 2024
e73756c
chore: comment for token creation
marcomariscal Nov 8, 2024
3cc031b
fix: revert deploy wallet logic
marcomariscal Nov 8, 2024
cfbc2bc
chore: dupe line
marcomariscal Nov 8, 2024
2bc916e
fix: remove unused default deploy version
marcomariscal Nov 8, 2024
dbe1725
fix: remove unused deploy version
marcomariscal Nov 8, 2024
8122b3c
fix: revert
marcomariscal Nov 8, 2024
89ca469
fix: revert
marcomariscal Nov 8, 2024
4a75ae4
fix: foundry toml for import lint errors
marcomariscal Nov 8, 2024
c4c2e0d
fix: build
marcomariscal Nov 8, 2024
300e8df
chore: remove unused
marcomariscal Nov 8, 2024
a1ffad9
feat: readme
marcomariscal Nov 8, 2024
ef7073e
fix: readme
marcomariscal Nov 8, 2024
04faa35
feat: gen abis lang
marcomariscal Nov 8, 2024
bb82f3a
chore: remove unused file
marcomariscal Nov 8, 2024
5bcfed6
fix: name
marcomariscal Nov 8, 2024
3358713
fix: abs paths and no forge clean
marcomariscal Nov 8, 2024
0710cca
fix: remove cache oldness check
marcomariscal Nov 11, 2024
4e3d2d9
fix: deployment cache handling
marcomariscal Nov 11, 2024
1ad2023
fix: init ci workflow for integration tests
marcomariscal Nov 11, 2024
d059401
fix: node
marcomariscal Nov 11, 2024
48b7e65
chore: check
marcomariscal Nov 11, 2024
a3aecdd
fix: remove redundant wrapping call to create proposal via agg proposer
marcomariscal Nov 11, 2024
9ca6ff8
fix: remove unused
marcomariscal Nov 11, 2024
02203d3
fix: check for proposal state pending
marcomariscal Nov 11, 2024
45cc584
fix: name
marcomariscal Nov 11, 2024
f8b01d3
chore: check
marcomariscal Nov 11, 2024
1dca00c
fix: name
marcomariscal Nov 11, 2024
a512f3c
fix: use token mint for proposal execution data
marcomariscal Nov 11, 2024
205dd32
chore: check
marcomariscal Nov 11, 2024
0a05d64
Merge branch 'main' into feat/integration-test-at-prev-commit
marcomariscal Nov 11, 2024
bbd86a5
chore: format
marcomariscal Nov 11, 2024
127d7e2
fix: name
marcomariscal Nov 12, 2024
dd816c3
fix: restore broadcast files
marcomariscal Nov 12, 2024
64fc164
fix: handle broadcast files
marcomariscal Nov 12, 2024
36253aa
fix: comment wrapping
marcomariscal Nov 12, 2024
dee495b
Add EVM tilt to CI (#208)
alexkeating Dec 16, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
feat: flesh out proposal helpers
marcomariscal committed Oct 29, 2024

Verified

This commit was signed with the committer’s verified signature.
commit 40788bbf317f4adb81c2e912ccf95878e79c69cf
57 changes: 34 additions & 23 deletions integration-tests/test/config/addresses.ts
Original file line number Diff line number Diff line change
@@ -1,46 +1,51 @@
import { join } from "path";
import { readFileSync } from "fs";
import { readFileSync } from 'node:fs';
import { join } from 'node:path';
import type { Address } from 'viem';

// Chain IDs for the different networks
const CHAIN_IDS = {
HUB: 1337, // EthDevnet1 (Hub)
SPOKE: 1397, // EthDevnet2 (Spoke)
HUB: 1337, // EthDevnet1 (Hub)
SPOKE: 1397, // EthDevnet2 (Spoke)
} as const;

function getDeploymentAddresses(deploymentFile: string, chainId: number = CHAIN_IDS.HUB) {
function getDeploymentAddresses(
deploymentFile: string,
chainId: number = CHAIN_IDS.HUB,
) {
const projectRoot = join(__dirname, '..', '..', '..', '..');

const artifactPath = join(
projectRoot,
'example-cross-chain-governance',
'evm',
'broadcast',
deploymentFile,
chainId.toString(),
"run-latest.json"
'run-latest.json',
);

try {
console.log('Attempting to read deployment from:', artifactPath);
const deployment = JSON.parse(
readFileSync(artifactPath, "utf-8")
);
const deployment = JSON.parse(readFileSync(artifactPath, 'utf-8'));

const addresses: Record<string, Address> = {};

deployment.transactions
.filter((tx: any) => tx.contractAddress)
.forEach((tx: any) => {

for (const tx of deployment.transactions) {
if (tx.contractAddress) {
addresses[tx.contractName] = tx.contractAddress as Address;
});
}
}

return addresses;
} catch (error) {
if ((error as any).code === 'ENOENT') {
console.error(`Deployment file not found: ${artifactPath}`);
console.error('Make sure you have run the deployments for both hub and spoke chains');
console.error(`Expected chainIds: Hub=${CHAIN_IDS.HUB}, Spoke=${CHAIN_IDS.SPOKE}`);
console.error(
'Make sure you have run the deployments for both hub and spoke chains',
);
console.error(
`Expected chainIds: Hub=${CHAIN_IDS.HUB}, Spoke=${CHAIN_IDS.SPOKE}`,
);
console.error(`Current directory: ${__dirname}`);
console.error(`Project root: ${projectRoot}`);
}
@@ -49,9 +54,15 @@ function getDeploymentAddresses(deploymentFile: string, chainId: number = CHAIN_
}

// Get Hub contract addresses from EthDevnet1 deployment
const hubAddresses = getDeploymentAddresses("DeployHubContractsEthDevnet1.sol", CHAIN_IDS.HUB);
const hubAddresses = getDeploymentAddresses(
'DeployHubContractsEthDevnet1.sol',
CHAIN_IDS.HUB,
);
// Get Spoke contract addresses from EthDevnet2 deployment
const spokeAddresses = getDeploymentAddresses("DeploySpokeContractsEthDevnet2.sol", CHAIN_IDS.SPOKE);
const spokeAddresses = getDeploymentAddresses(
'DeploySpokeContractsEthDevnet2.sol',
CHAIN_IDS.SPOKE,
);

const ContractAddressesEnum = {
// Hub contracts (deployed on EthDevnet1)
@@ -65,7 +76,7 @@ const ContractAddressesEnum = {
HUB_PROPOSAL_EXTENDER: hubAddresses.HubProposalExtender,
HUB_SOLANA_MESSAGE_DISPATCHER: hubAddresses.HubSolanaMessageDispatcher,
HUB_SOLANA_SPOKE_VOTE_DECODER: hubAddresses.HubSolanaSpokeVoteDecoder,

// Spoke contracts (deployed on EthDevnet2)
SPOKE_VOTE_AGGREGATOR: spokeAddresses.SpokeVoteAggregator,
SPOKE_MESSAGE_EXECUTOR: spokeAddresses.SpokeMessageExecutor,
@@ -80,10 +91,10 @@ export type Addresses = {
};

// Validate all addresses are defined
Object.entries(ContractAddressesEnum).forEach(([key, value]) => {
for (const [key, value] of Object.entries(ContractAddressesEnum)) {
if (!value) {
throw new Error(`Missing address for ${key}`);
}
});
}

export { ContractAddressesEnum as ContractAddresses };
4 changes: 2 additions & 2 deletions integration-tests/test/config/clients.ts
Original file line number Diff line number Diff line change
@@ -29,13 +29,13 @@ export const createClients = () => {
account,
chain: ethDevnet,
transport: http(ETH_DEVNET_NODE_URL),
});
}).extend(publicActions);

const eth2Wallet = createWalletClient({
account,
chain: eth2Devnet,
transport: http(ETH2_DEVNET_NODE_URL),
});
}).extend(publicActions);

return { ethClient, eth2Client, ethWallet, eth2Wallet, account };
};
9 changes: 9 additions & 0 deletions integration-tests/test/config/types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,13 @@
import type { createClients } from './clients';

export enum VoteType {
FOR = 1,
AGAINST = 0,
}

export type Client =
| ReturnType<typeof createClients>['ethClient']
| ReturnType<typeof createClients>['eth2Client'];
export type Wallet =
| ReturnType<typeof createClients>['ethWallet']
| ReturnType<typeof createClients>['eth2Wallet'];
8 changes: 5 additions & 3 deletions integration-tests/test/createProposalFromHub/helpers.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { createArbitraryProposalData, createProposal } from 'test/helpers';
import {
createArbitraryProposalData,
createProposalViaAggregateProposer,
} from 'test/helpers';

export const createProposalFromHub = async () => {
const proposalData = await createArbitraryProposalData();
const proposalId = await createProposal({
const proposalId = await createProposalViaAggregateProposer({
proposalData,
});
return proposalId;
// TODO: Add the rest of the logic to create a proposal from hub
};
7 changes: 7 additions & 0 deletions integration-tests/test/createProposalFromHub/index.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
import { afterAll, beforeAll, describe, expect, test } from 'bun:test';
import { ContractAddresses } from 'test/config/addresses';
import { getWhitelistedProposer } from 'test/helpers/governance/registrationHelpers';
import { setupTestEnvironment, teardownTestEnvironment } from '../setup';
import { createProposalFromHub } from './helpers';

describe('Create proposal from hub', () => {
beforeAll(async () => {
await setupTestEnvironment();
// check to make sure hubevmspokeaggregate proposer is whitelisted
const isWhitelisted = await getWhitelistedProposer();
expect(isWhitelisted).toBe(
ContractAddresses.HUB_EVM_SPOKE_AGGREGATE_PROPOSER,
);
});

afterAll(async () => {
149 changes: 126 additions & 23 deletions integration-tests/test/helpers/governance/proposalHelpers.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { VoteType, type Wallet } from 'test/config/types';
import {
type Address,
type WalletClient,
encodeFunctionData,
keccak256,
@@ -8,20 +8,25 @@ import {
import { HubEvmSpokeAggregateProposerAbi, HubGovernorAbi } from '../../../abis';
import { ContractAddresses } from '../../config/addresses';
import { createClients } from '../../config/clients';
import { getWormholeGetVotesQueryResponse } from '../wormhole/wormholeHelpers';
import { getVotingPower } from './votingHelpers';
import { mineToTimestamp } from '../time/timeHelpers';
import {
getMaxQueryTimestampOffset,
getWormholeGetVotesQueryResponse,
} from '../wormhole/wormholeHelpers';
import type { ProposalData } from './types';
import {
getVoteEnd,
getVoteStart,
getVotingPower,
voteOnProposal,
} from './votingHelpers';

export function createProposalData({
targets,
values,
calldatas,
description,
}: {
targets: Address[];
values: bigint[];
calldatas: `0x${string}`[];
description: string;
}) {
}: ProposalData) {
return {
targets,
values,
@@ -30,41 +35,59 @@ export function createProposalData({
};
}

export const createProposal = async ({
export const createProposalViaAggregateProposer = async ({
proposalData,
}: {
proposalData: ReturnType<typeof createProposalData>;
proposalData: ProposalData;
}) => {
const { ethClient, eth2Client, ethWallet, account } = createClients();

// Use a past timestamp to account for limitations in the query server
// Use 5 minutes ago
const timestampHub = (await ethClient.getBlock()).timestamp - 300n;
const timestampSpoke = (await eth2Client.getBlock()).timestamp - 300n;
// Get current block timestamps
const hubBlock = await ethClient.getBlock();
const spokeBlock = await eth2Client.getBlock();
const maxQueryTimestampOffset = await getMaxQueryTimestampOffset();

// Use timestamp from 5 minutes ago for query server compatibility
const FIVE_MINUTES = 300n;
const timestamp = hubBlock.timestamp - FIVE_MINUTES;

// Debug: Check voting power
// Verify we're still within maxQueryTimestampOffset
if (hubBlock.timestamp - timestamp > BigInt(maxQueryTimestampOffset)) {
throw new Error('Timestamp too old for maxQueryTimestampOffset');
}

console.log('Timestamps after sync:', {
hubBlock: Number(hubBlock.timestamp),
spokeBlock: Number(spokeBlock.timestamp),
queryTimestamp: Number(timestamp),
maxOffset: maxQueryTimestampOffset,
offsetFromCurrent: Number(hubBlock.timestamp - timestamp),
});

// Debug: Check voting power using block timestamp
const hubVotingPower = await getVotingPower({
account: account.address,
isHub: true,
timestamp: timestampHub,
timestamp,
});
const spokeVotingPower = await getVotingPower({
account: account.address,
isHub: false,
timestamp: timestampSpoke,
timestamp,
});
console.log(`Hub voting power: ${hubVotingPower}`);
console.log(`Spoke voting power: ${spokeVotingPower}`);

// Pass block timestamp - the Wormhole query will handle microsecond conversion
const { queryResponseBytes, queryResponseSignatures } =
await getWormholeGetVotesQueryResponse({
account: account.address,
timestampSpoke,
timestampSpoke: timestamp,
});

// Debug: Log query response
console.log('Query response bytes:', queryResponseBytes);
console.log('Query response signatures:', queryResponseSignatures);
console.log('🦄 ~ account:', account.address);

try {
const { result: proposalId } = await ethClient.simulateContract({
@@ -111,11 +134,24 @@ export const executeProposal = async ({
proposalId,
proposalData,
}: {
wallet: WalletClient;
wallet: Wallet;
proposalId: bigint;
proposalData: ReturnType<typeof createProposalData>;
proposalData: ProposalData;
}) => {
const descriptionHash = keccak256(toBytes(proposalData.description));

await wallet.simulateContract({
address: ContractAddresses.HUB_GOVERNOR,
abi: HubGovernorAbi,
functionName: 'execute',
args: [
proposalData.targets,
proposalData.values,
proposalData.calldatas,
descriptionHash,
],
});

const hash = await wallet.writeContract({
address: ContractAddresses.HUB_GOVERNOR,
abi: HubGovernorAbi,
@@ -129,8 +165,9 @@ export const executeProposal = async ({
account: handleNoAccount(wallet),
chain: wallet.chain,
});

console.log(`Executed proposal ${proposalId}. Transaction hash: ${hash}`);
return hash;
return proposalId;
};

export const createArbitraryProposalData = async () => {
@@ -165,3 +202,69 @@ const handleNoAccount = (wallet: WalletClient) => {
}
return wallet.account;
};

// Creates and executes a proposal via the HubGovernor directly
export async function createAndExecuteProposal(proposalData: ProposalData) {
const { ethWallet } = createClients();

const proposalId = await createProposalViaHubGovernor({
targets: proposalData.targets,
values: proposalData.values,
calldatas: proposalData.calldatas,
description: proposalData.description,
});

await passProposal({
proposalId,
});

await executeProposal({
wallet: ethWallet,
proposalId,
proposalData,
});

return proposalId;
}

export const createProposalViaHubGovernor = async ({
targets,
values,
calldatas,
description,
}: ProposalData) => {
const { ethClient, ethWallet } = createClients();

const { result: proposalId } = await ethClient.simulateContract({
address: ContractAddresses.HUB_GOVERNOR,
abi: HubGovernorAbi,
functionName: 'propose',
args: [targets, values, calldatas, description],
});

const hash = await ethWallet.writeContract({
address: ContractAddresses.HUB_GOVERNOR,
abi: HubGovernorAbi,
functionName: 'propose',
args: [targets, values, calldatas, description],
account: handleNoAccount(ethWallet),
chain: ethWallet.chain,
});

console.log(`Created proposal ${proposalId}. Transaction hash: ${hash}`);

return proposalId;
};

// Go through the proposal state flow to make it pass
export const passProposal = async ({ proposalId }: { proposalId: bigint }) => {
const { ethClient } = createClients();

const voteStart = await getVoteStart({ proposalId });

await mineToTimestamp({ client: ethClient, timestamp: voteStart });
await voteOnProposal({ proposalId, isHub: true, voteType: VoteType.FOR });

const voteEnd = await getVoteEnd({ proposalId });
await mineToTimestamp({ client: ethClient, timestamp: voteEnd + 1n });
};
105 changes: 21 additions & 84 deletions integration-tests/test/helpers/governance/registrationHelpers.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,15 @@
import {
type Account,
type Address,
encodeFunctionData,
zeroAddress,
} from 'viem';
import { type Address, encodeFunctionData, zeroAddress } from 'viem';
import {
HubEvmSpokeAggregateProposerAbi,
HubGovernorAbi,
HubVotePoolAbi,
} from '../../../abis';
import { ContractAddresses } from '../../config/addresses';
import { createClients } from '../../config/clients';
import { VoteType } from '../../config/types';
import {
createProposal,
createAndExecuteProposal,
createProposalData,
executeProposal,
} from './proposalHelpers';
import { voteOnProposal } from './votingHelpers';

export const getWhitelistedProposer = async () => {
const { ethClient } = createClients();
@@ -135,7 +127,6 @@ export const registerSpokeOnHubVotePool = async ({
chainId: number;
spokeAddress: Address;
}) => {
const { ethClient, ethWallet } = createClients();
const registerSpokeCalldata = encodeFunctionData({
abi: HubVotePoolAbi,
functionName: 'registerSpoke',
@@ -149,89 +140,35 @@ export const registerSpokeOnHubVotePool = async ({
description: `Register spoke for chain ${chainId} at address ${spokeAddress}`,
});

const proposalId = await createProposal({
proposalData,
});
const proposalId = await createAndExecuteProposal(proposalData);

console.log(
`Created proposal to register spoke for chain ${chainId} at address ${spokeAddress}. Proposal ID: ${proposalId}`,
`Registered spoke for chain ${chainId} at address ${spokeAddress}. Proposal ID: ${proposalId}`,
);

// Fast forward to the vote start
const voteStart = await getVoteStart({ proposalId });
await ethClient.setNextBlockTimestamp({ timestamp: voteStart });

// Vote on the proposal to make it pass
await voteOnProposal({
isHub: true,
proposalId,
voteType: VoteType.FOR,
});

// Fast forward to the end of voting period
const voteEnd = await getVoteEnd({ proposalId });
await ethClient.setNextBlockTimestamp({ timestamp: voteEnd + 1n });
await ethClient.mine({ blocks: 1 });

// Execute the proposal
const hash = await executeProposal({
wallet: ethWallet,
proposalId,
proposalData,
});

return hash;
return proposalId;
};

export const registerWhitelistedProposer = async ({
export async function registerWhitelistedProposer({
proposerAddress,
}: {
proposerAddress: Address;
account: Account;
}) => {
const { ethClient, ethWallet } = createClients();

// Impersonate the HubGovernor
await ethClient.impersonateAccount({
address: ContractAddresses.HUB_GOVERNOR,
});

const hash = await ethWallet.writeContract({
address: ContractAddresses.HUB_GOVERNOR,
abi: HubGovernorAbi,
functionName: 'setWhitelistedProposer',
args: [proposerAddress],
account: ContractAddresses.HUB_GOVERNOR,
chain: ethWallet.chain,
}) {
const proposalData = createProposalData({
targets: [ContractAddresses.HUB_GOVERNOR],
values: [0n],
calldatas: [
encodeFunctionData({
abi: HubGovernorAbi,
functionName: 'setWhitelistedProposer',
args: [proposerAddress],
}),
],
description: `Set whitelisted proposer to ${proposerAddress}`,
});

await ethClient.stopImpersonatingAccount({
address: ContractAddresses.HUB_GOVERNOR,
});
const proposalId = await createAndExecuteProposal(proposalData);

console.log(
`Registered whitelisted proposer at address ${proposerAddress}. Transaction hash: ${hash}`,
`Set whitelisted proposer to ${proposerAddress}. Proposal ID: ${proposalId}`,
);
return hash;
};

const getVoteStart = async ({ proposalId }: { proposalId: bigint }) => {
const { ethClient } = createClients();

return await ethClient.readContract({
address: ContractAddresses.HUB_GOVERNOR,
abi: HubGovernorAbi,
functionName: 'proposalSnapshot',
args: [proposalId],
});
};

const getVoteEnd = async ({ proposalId }: { proposalId: bigint }) => {
const { ethClient } = createClients();
return await ethClient.readContract({
address: ContractAddresses.HUB_GOVERNOR,
abi: HubGovernorAbi,
functionName: 'proposalDeadline',
args: [proposalId],
});
};
}
8 changes: 8 additions & 0 deletions integration-tests/test/helpers/governance/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import type { Address } from 'viem';

export type ProposalData = {
targets: Address[];
values: bigint[];
calldatas: `0x${string}`[];
description: string;
};
21 changes: 21 additions & 0 deletions integration-tests/test/helpers/governance/votingHelpers.ts
Original file line number Diff line number Diff line change
@@ -139,3 +139,24 @@ export const getProposalVotes = async ({
abstainVotes: votes[3],
};
};

export const getVoteStart = async ({ proposalId }: { proposalId: bigint }) => {
const { ethClient } = createClients();

return await ethClient.readContract({
address: ContractAddresses.HUB_GOVERNOR,
abi: HubGovernorAbi,
functionName: 'proposalSnapshot',
args: [proposalId],
});
};

export const getVoteEnd = async ({ proposalId }: { proposalId: bigint }) => {
const { ethClient } = createClients();
return await ethClient.readContract({
address: ContractAddresses.HUB_GOVERNOR,
abi: HubGovernorAbi,
functionName: 'proposalDeadline',
args: [proposalId],
});
};
21 changes: 0 additions & 21 deletions integration-tests/test/helpers/index.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,3 @@
import type { Address } from 'viem';

export function createProposalData({
targets,
values,
calldatas,
description,
}: {
targets: Address[];
values: bigint[];
calldatas: `0x${string}`[];
description: string;
}) {
return {
targets,
values,
calldatas,
description,
};
}

// Export all helpers from their respective files
export * from './governance/proposalHelpers';
export * from './governance/votingHelpers';
10 changes: 10 additions & 0 deletions integration-tests/test/helpers/time/timeHelpers.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { Client } from 'test/config/types';
import { createClients } from '../../config/clients';

export const syncTime = async () => {
@@ -13,3 +14,12 @@ export const syncTime = async () => {
await ethClient.mine({ blocks: 1 });
await eth2Client.mine({ blocks: 1 });
};

export const mineToTimestamp = async ({
client,
timestamp,
}: { client: Client; timestamp: bigint }) => {
await client.setNextBlockTimestamp({ timestamp });
await client.mine({ blocks: 1 });
await syncTime();
};
3 changes: 2 additions & 1 deletion integration-tests/test/setup.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { ContractAddresses } from './config/addresses';
import { ETH2_DEVNET_WORMHOLE_CHAIN_ID } from './config/chains';
import { createClients } from './config/clients';
import { syncTime } from './helpers';
import {
handleRegisterSpokeOnAggProposer,
handleRegisterSpokeOnHubVotePool,
@@ -48,9 +49,9 @@ export async function setupTestEnvironment() {
// 5. Register whitelisted proposer (HubEvmSpokeAggregateProposer)
await registerWhitelistedProposer({
proposerAddress: ContractAddresses.HUB_EVM_SPOKE_AGGREGATE_PROPOSER,
account,
});

await syncTime();
console.log('Test environment setup completed');
}