Skip to content

Commit

Permalink
chore(governance):Refactor makeGovernanceDriver methods to be contrac…
Browse files Browse the repository at this point in the history
…t agnostic
  • Loading branch information
Jorge-Lopes committed Dec 9, 2024
1 parent 58e2b53 commit db0cd37
Show file tree
Hide file tree
Showing 3 changed files with 105 additions and 67 deletions.
68 changes: 12 additions & 56 deletions a3p-integration/proposals/z:acceptance/governance.test.js
Original file line number Diff line number Diff line change
@@ -1,53 +1,36 @@
/* global fetch setTimeout */
/* global fetch */

import test from 'ava';

import { makeWalletUtils } from '@agoric/client-utils';
import { GOV1ADDR, GOV2ADDR } from '@agoric/synthetic-chain';
import { makeGovernanceDriver } from './test-lib/governance.js';
import { networkConfig } from './test-lib/index.js';
import { makeTimerUtils } from './test-lib/utils.js';
import { networkConfig, walletUtils } from './test-lib/index.js';

const GOV4ADDR = 'agoric1c9gyu460lu70rtcdp95vummd6032psmpdx7wdy';
const governanceAddresses = [GOV4ADDR, GOV2ADDR, GOV1ADDR];

// TODO test-lib export `walletUtils` with this ambient authority like s:stake-bld has
/** @param {number} ms */
const delay = ms =>
new Promise(resolve => setTimeout(() => resolve(undefined), ms));
const { getLastUpdate } = walletUtils;
const governanceDriver = await makeGovernanceDriver(fetch, networkConfig);

test.serial(
'economic committee can make governance proposal and vote on it',
async t => {
const { waitUntil } = makeTimerUtils({ setTimeout });
const { readLatestHead, getLastUpdate, getCurrentWalletRecord } =
await makeWalletUtils({ delay, fetch }, networkConfig);
const governanceDriver = await makeGovernanceDriver(fetch, networkConfig);

/** @type {any} */
const instance = await readLatestHead(`published.agoricNames.instance`);
const instances = Object.fromEntries(instance);

const wallet = await getCurrentWalletRecord(governanceAddresses[0]);
const usedInvitations = wallet.offerToUsedInvitation;

const charterInvitation = usedInvitations.find(
v =>
v[1].value[0].instance.getBoardId() ===
instances.econCommitteeCharter.getBoardId(),
const charterInvitation = await governanceDriver.getCharterInvitation(
governanceAddresses[0],
);
assert(charterInvitation, 'missing charter invitation');

const params = {
ChargingPeriod: 400n,
};
const path = { paramPath: { key: 'governedParams' } };
t.log('Proposing param change', { params, path });
const instanceName = 'VaultFactory';

await governanceDriver.proposeVaultDirectorParamChange(
await governanceDriver.proposeParamChange(
governanceAddresses[0],
params,
path,
instanceName,
charterInvitation[0],
);

Expand All @@ -59,22 +42,9 @@ test.serial(

t.log('Voting on param change');
for (const address of governanceAddresses) {
const voteWallet =
/** @type {import('@agoric/smart-wallet/src/smartWallet.js').CurrentWalletRecord} */ (
await readLatestHead(`published.wallet.${address}.current`)
);

const usedInvitationsForVoter = voteWallet.offerToUsedInvitation;
const committeeInvitationForVoter =
await governanceDriver.getCommitteeInvitation(address);

const committeeInvitationForVoter = usedInvitationsForVoter.find(
v =>
v[1].value[0].instance.getBoardId() ===
instances.economicCommittee.getBoardId(),
);
assert(
committeeInvitationForVoter,
`${address} must have committee invitation`,
);
await governanceDriver.voteOnProposedChanges(
address,
committeeInvitationForVoter[0],
Expand All @@ -87,21 +57,7 @@ test.serial(
});
}

const latestQuestion =
/** @type {import('@agoric/governance/src/types.js').QuestionSpec} */ (
await readLatestHead(
'published.committees.Economic_Committee.latestQuestion',
)
);
t.log('Waiting for deadline', latestQuestion);
/** @type {bigint} */
// @ts-expect-error assume POSIX seconds since epoch
const deadline = latestQuestion.closingRule.deadline;
await waitUntil(deadline);

const latestOutcome = await readLatestHead(
'published.committees.Economic_Committee.latestOutcome',
);
const { latestOutcome } = await governanceDriver.getLatestQuestion();
t.log('Verifying latest outcome', latestOutcome);
t.like(latestOutcome, { outcome: 'win' });
},
Expand Down
102 changes: 92 additions & 10 deletions a3p-integration/proposals/z:acceptance/test-lib/governance.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,18 @@
/* global fetch setTimeout */

import { agops, agoric, executeOffer } from '@agoric/synthetic-chain';
import { makeVstorageKit } from '@agoric/client-utils';
import {
boardSlottingMarshaller,
makeFromBoard,
makeVstorageKit,
retryUntilCondition,
} from '@agoric/client-utils';
import { makeVStorage } from './rpc.js';
import { walletUtils } from './index.js';
import {
checkCommitteeElectionResult,
fetchLatestEcQuestion,
} from './psm-lib.js';

/**
* @param {typeof window.fetch} fetch
Expand All @@ -11,6 +24,8 @@ export const makeGovernanceDriver = async (fetch, networkConfig) => {
networkConfig,
);

let deadline;

/** @param {string} previousOfferId */
const generateVoteOffer = async previousOfferId => {
const latestQuestionRecord =
Expand Down Expand Up @@ -63,7 +78,7 @@ export const makeGovernanceDriver = async (fetch, networkConfig) => {
};

/**
* Generates a vault director parameter change proposal as a `executeOffer` message
* Generates a parameter change proposal as a `executeOffer` message
* body.
*
* @param {string} previousOfferId - the `id` of the offer that this proposal is
Expand All @@ -72,13 +87,15 @@ export const makeGovernanceDriver = async (fetch, networkConfig) => {
* be open for (in seconds)
* @param {any} params
* @param {{ paramPath: any; }} paramsPath
* @param {string} instanceName
* @returns {Promise<string>} - the `executeOffer` message body as a JSON string
*/
const generateVaultDirectorParamChange = async (
const generateParamChange = async (
previousOfferId,
voteDur,
params,
paramsPath,
instanceName,
) => {
const instancesRaw = await agoric.follow(
'-lF',
Expand All @@ -89,12 +106,12 @@ export const makeGovernanceDriver = async (fetch, networkConfig) => {
const instances = Object.fromEntries(
marshaller.fromCapData(JSON.parse(instancesRaw)),
);
const { VaultFactory } = instances;
assert(VaultFactory);
const instance = instances[instanceName];
assert(instance);

const msSinceEpoch = Date.now();
const id = `propose-${msSinceEpoch}`;
const deadline = BigInt(Math.ceil(msSinceEpoch / 1000)) + BigInt(voteDur);
deadline = BigInt(Math.ceil(msSinceEpoch / 1000)) + BigInt(voteDur);
const body = {
method: 'executeOffer',
offer: {
Expand All @@ -106,7 +123,7 @@ export const makeGovernanceDriver = async (fetch, networkConfig) => {
},
offerArgs: {
deadline,
instance: VaultFactory,
instance,
params,
path: paramsPath,
},
Expand All @@ -123,12 +140,14 @@ export const makeGovernanceDriver = async (fetch, networkConfig) => {
* @param {string} address
* @param {any} params
* @param {{paramPath: any}} path
* @param {string} instanceName
* @param {string} charterAcceptOfferId
*/
const proposeVaultDirectorParamChange = async (
const proposeParamChange = async (
address,
params,
path,
instanceName,
charterAcceptOfferId,
) => {
await null;
Expand All @@ -144,12 +163,75 @@ export const makeGovernanceDriver = async (fetch, networkConfig) => {

return executeOffer(
address,
generateVaultDirectorParamChange(offerId, 10, params, path),
generateParamChange(offerId, 30, params, path, instanceName),
);
};

const getCharterInvitation = async address => {
const { getCurrentWalletRecord } = walletUtils;

/** @type {any} */
const instance = await readLatestHead(`published.agoricNames.instance`);
const instances = Object.fromEntries(instance);

const wallet = await getCurrentWalletRecord(address);
const usedInvitations = wallet.offerToUsedInvitation;

const charterInvitation = usedInvitations.find(
v =>
v[1].value[0].instance.getBoardId() ===
instances.econCommitteeCharter.getBoardId(),
);
assert(charterInvitation, 'missing charter invitation');

return charterInvitation;
};

const getCommitteeInvitation = async address => {
/** @type {any} */
const instance = await readLatestHead(`published.agoricNames.instance`);
const instances = Object.fromEntries(instance);

const voteWallet =
/** @type {import('@agoric/smart-wallet/src/smartWallet.js').CurrentWalletRecord} */ (
await readLatestHead(`published.wallet.${address}.current`)
);

const usedInvitationsForVoter = voteWallet.offerToUsedInvitation;

const committeeInvitationForVoter = usedInvitationsForVoter.find(
v =>
v[1].value[0].instance.getBoardId() ===
instances.economicCommittee.getBoardId(),
);
assert(
committeeInvitationForVoter,
`${address} must have committee invitation`,
);

return committeeInvitationForVoter;
};

const getLatestQuestion = async () => {
const { latestOutcome, latestQuestion } = await await retryUntilCondition(
() => fetchLatestEcQuestion({ follow: agoric.follow }),
electionResult =>
checkCommitteeElectionResult(electionResult, {
outcome: 'win',
deadline,
}),
'Governed param change election failed',
{ setTimeout, retryIntervalMs: 5000, maxRetries: 15 },
);

return { latestOutcome, latestQuestion };
};

return {
voteOnProposedChanges,
proposeVaultDirectorParamChange,
proposeParamChange,
getCharterInvitation,
getCommitteeInvitation,
getLatestQuestion,
};
};
2 changes: 1 addition & 1 deletion a3p-integration/proposals/z:acceptance/test-lib/psm-lib.js
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@ export const fetchLatestEcQuestion = async io => {
return { latestOutcome, latestQuestion };
};

const checkCommitteeElectionResult = (
export const checkCommitteeElectionResult = (
/** @type {{ latestOutcome: { outcome: any; question: any; }; latestQuestion: { closingRule: { deadline: any; }; questionHandle: any; }; }} */ electionResult,
/** @type {{ outcome: any; deadline: any; }} */ expectedResult,
) => {
Expand Down

0 comments on commit db0cd37

Please sign in to comment.