Skip to content

Commit

Permalink
test: add governance entity tests
Browse files Browse the repository at this point in the history
  • Loading branch information
coreyar committed Oct 30, 2023
1 parent 2e94731 commit 25f225a
Show file tree
Hide file tree
Showing 12 changed files with 100 additions and 116 deletions.
19 changes: 1 addition & 18 deletions subgraphs/venus-governance/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ type Governance @entity {
id: ID!

"Number of proposals created"
proposals: BigInt!
totalProposals: BigInt!

"Total number of accounts delegates that can participate in governance by voting or creating proposals"
totalDelegates: BigInt!
Expand Down Expand Up @@ -135,23 +135,6 @@ type Governance @entity {
proposalMaxOperations: BigInt!
}

type GovernanceRoute @entity {
"Index of the governance route"
id: ID!
"Governor implementation the route belongs to"
governor: Bytes!
"Address of timelock contract for route"
timelock: Bytes!
"Queue execution delay in blocks"
queueDelayBlocks: BigInt!
"The delay before voting on a proposal may take place, once proposed, in blocks"
votingDelayBlocks: BigInt!
"The duration of voting on a proposal, in blocks"
votingPeriodBlocks: BigInt!
"The number of votes required in order for a voter to become a proposer"
proposalThresholdMantissa: BigInt!
}

enum PermissionStatus {
GRANTED,
REVOKED
Expand Down
4 changes: 4 additions & 0 deletions subgraphs/venus-governance/src/constants/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ export const BIGINT_ZERO = BigInt.fromI32(0);
export const BIGINT_ONE = BigInt.fromI32(1);
export const BIGDECIMAL_ZERO = new BigDecimal(BIGINT_ZERO);

// Ids
export const GOVERNANCE = 'GOVERNANCE';
export const SEPERATOR = '-';

// Vote support
export const FOR = 'FOR';
export const AGAINST = 'AGAINST';
Expand Down
2 changes: 2 additions & 0 deletions subgraphs/venus-governance/src/mappings/bravo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { createProposal, createVoteBravo } from '../operations/create';
import { getGovernanceEntity } from '../operations/get';
import { getOrCreateDelegate } from '../operations/getOrCreate';
import {
updateGovernanceEntity,
updateProposalCanceled,
updateProposalExecuted,
updateProposalQueued,
Expand Down Expand Up @@ -54,6 +55,7 @@ export function handleNewImplementation(event: NewImplementation): void {
const governance = getGovernanceEntity();
governance.implementation = event.params.newImplementation;
governance.save();
updateGovernanceEntity();
}

export function handleNewPendingAdmin(event: NewPendingAdmin): void {
Expand Down
2 changes: 1 addition & 1 deletion subgraphs/venus-governance/src/operations/create.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export function createProposal<E>(event: E): Proposal {

const governance = getGovernanceEntity();

governance.proposals = governance.proposals.plus(BIGINT_ONE);
governance.totalProposals = governance.totalProposals.plus(BIGINT_ONE);
governance.save();
const targets = event.params.targets.map<Bytes>((address: Address) =>
Bytes.fromHexString(address.toHexString()),
Expand Down
71 changes: 8 additions & 63 deletions subgraphs/venus-governance/src/operations/get.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,20 @@
import { Address, BigInt, log } from '@graphprotocol/graph-ts';
import { Address, log } from '@graphprotocol/graph-ts';

import { GovernorBravoDelegate2 } from '../../generated/GovernorBravoDelegate2/GovernorBravoDelegate2';
import { Timelock } from '../../generated/GovernorBravoDelegate2/Timelock';
import { Delegate, Governance, GovernanceRoute, Proposal } from '../../generated/schema';
import { Delegate, Governance, Proposal } from '../../generated/schema';
import { BIGINT_ZERO } from '../constants';
import { governorBravoDelegatorAddress, nullAddress } from '../constants/addresses';
import { getDelegateId } from '../utilities/ids';
import { nullAddress } from '../constants/addresses';
import { getDelegateId, getGovernanceId } from '../utilities/ids';

/**
* While technically this function does also create, we don't care because it only happens once as the id is a constant.
* The initial values are mocked because they are updated when an implementation is set
* @returns Governance
*/
export const getGovernanceEntity = (): Governance => {
let governance = Governance.load(governorBravoDelegatorAddress.toHex());
let governance = Governance.load(getGovernanceId());
if (!governance) {
const governorBravoDelegate2 = GovernorBravoDelegate2.bind(governorBravoDelegatorAddress);
governance = new Governance(governorBravoDelegatorAddress.toHex());
governance.proposals = BIGINT_ZERO;
governance = new Governance(getGovernanceId());
governance.totalProposals = BIGINT_ZERO;
governance.totalDelegates = BIGINT_ZERO;
governance.totalVoters = BIGINT_ZERO;
governance.totalVotesMantissa = BIGINT_ZERO;
Expand All @@ -26,59 +24,6 @@ export const getGovernanceEntity = (): Governance => {
governance.guardian = nullAddress;
governance.quorumVotesMantissa = BIGINT_ZERO;
governance.proposalMaxOperations = BIGINT_ZERO;

// There is only one active governance entity
// but while indexing proposals created with previous governance contracts all these calls will fail
// This method only exists on the latest governance interface so if it succeeds we can safely index the contract
const normalProposalConfigResult = governorBravoDelegate2.try_proposalConfigs(new BigInt(0));
if (normalProposalConfigResult.reverted === false) {
governance.admin = governorBravoDelegate2.admin();
governance.implementation = governorBravoDelegate2.implementation();
governance.guardian = governorBravoDelegate2.guardian();
governance.quorumVotesMantissa = governorBravoDelegate2.quorumVotes();
governance.proposalMaxOperations = governorBravoDelegate2.proposalMaxOperations();
// Governance Routes are set in initialization
// Normal
const normalProposalConfig = normalProposalConfigResult.value;
const normalTimelockAddress = governorBravoDelegate2.proposalTimelocks(new BigInt(0));
const normalTimelock = Timelock.bind(normalTimelockAddress);
const normalGovernanceRoute = new GovernanceRoute('0');
normalGovernanceRoute.governor = governorBravoDelegatorAddress;
normalGovernanceRoute.timelock = normalTimelockAddress;
normalGovernanceRoute.queueDelayBlocks = normalTimelock.delay();
normalGovernanceRoute.votingDelayBlocks = normalProposalConfig.getVotingDelay();
normalGovernanceRoute.votingPeriodBlocks = normalProposalConfig.getVotingPeriod();
normalGovernanceRoute.proposalThresholdMantissa = normalProposalConfig.getProposalThreshold();
normalGovernanceRoute.save();
// Fast track
const fastTrackProposalConfig = governorBravoDelegate2.proposalConfigs(new BigInt(1));
const fastTrackTimelockAddress = governorBravoDelegate2.proposalTimelocks(new BigInt(1));
const fastTrackTimelock = Timelock.bind(normalTimelockAddress);
const fastTrackGovernanceRoute = new GovernanceRoute('1');
fastTrackGovernanceRoute.governor = governorBravoDelegatorAddress;
fastTrackGovernanceRoute.timelock = fastTrackTimelockAddress;
fastTrackGovernanceRoute.queueDelayBlocks = fastTrackTimelock.delay();
fastTrackGovernanceRoute.votingDelayBlocks = fastTrackProposalConfig.getVotingDelay();
fastTrackGovernanceRoute.votingPeriodBlocks = fastTrackProposalConfig.getVotingPeriod();
fastTrackGovernanceRoute.proposalThresholdMantissa =
fastTrackProposalConfig.getProposalThreshold();
fastTrackGovernanceRoute.save();
// Critical
const criticalProposalConfig = governorBravoDelegate2.proposalConfigs(new BigInt(2));
const criticalTimelockAddress = governorBravoDelegate2.proposalTimelocks(new BigInt(2));
const criticalTimelock = Timelock.bind(normalTimelockAddress);
const criticalGovernanceRoute = new GovernanceRoute('2');
criticalGovernanceRoute.governor = governorBravoDelegatorAddress;
criticalGovernanceRoute.timelock = criticalTimelockAddress;
criticalGovernanceRoute.queueDelayBlocks = criticalTimelock.delay();
criticalGovernanceRoute.votingDelayBlocks = criticalProposalConfig.getVotingDelay();
criticalGovernanceRoute.votingPeriodBlocks = criticalProposalConfig.getVotingPeriod();
criticalGovernanceRoute.proposalThresholdMantissa =
criticalProposalConfig.getProposalThreshold();
criticalGovernanceRoute.save();
}

governance.save();
}

return governance as Governance;
Expand Down
23 changes: 20 additions & 3 deletions subgraphs/venus-governance/src/operations/update.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import { GovernorBravoDelegate2 } from '../../generated/GovernorBravoDelegate2/GovernorBravoDelegate2';
import { Governance } from '../../generated/schema';
import { BIGINT_ONE } from '../constants';
import { nullAddress } from '../constants/addresses';
import { governorBravoDelegatorAddress, nullAddress } from '../constants/addresses';
import { getGovernanceId } from '../utilities/ids';
import { getGovernanceEntity, getProposal } from './get';
import { getOrCreateDelegate } from './getOrCreate';

Expand Down Expand Up @@ -44,8 +47,10 @@ export function updateDelegateChanged<E>(event: E): void {
const oldDelegate = oldDelegateResult.entity;
oldDelegate.delegateCount = oldDelegate.delegateCount - 1;
oldDelegate.save();
}

governance.totalDelegates = governance.totalDelegates.minus(BIGINT_ONE);
if (fromDelegate == nullAddress.toHexString()) {
governance.totalDelegates = governance.totalDelegates.plus(BIGINT_ONE);
governance.save();
}

Expand All @@ -54,8 +59,10 @@ export function updateDelegateChanged<E>(event: E): void {
const newDelegate = newDelegateResult.entity;
newDelegate.delegateCount = newDelegate.delegateCount + 1;
newDelegate.save();
}

governance.totalDelegates = governance.totalDelegates.plus(BIGINT_ONE);
if (fromDelegate == nullAddress.toHexString()) {
governance.totalDelegates = governance.totalDelegates.minus(BIGINT_ONE);
governance.save();
}
}
Expand All @@ -76,3 +83,13 @@ export function updateDelegateVoteChanged<E>(event: E): void {
governance.totalVotesMantissa = governance.totalVotesMantissa.plus(votesDifference);
governance.save();
}

export function updateGovernanceEntity(): void {
const governorBravoDelegate2 = GovernorBravoDelegate2.bind(governorBravoDelegatorAddress);
const governance = Governance.load(getGovernanceId())!;
governance.quorumVotesMantissa = governorBravoDelegate2.quorumVotes();
governance.admin = governorBravoDelegate2.admin();
governance.guardian = governorBravoDelegate2.guardian();
governance.proposalMaxOperations = governorBravoDelegate2.proposalMaxOperations();
governance.save();
}
4 changes: 3 additions & 1 deletion subgraphs/venus-governance/src/utilities/ids.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Address, BigInt } from '@graphprotocol/graph-ts';

const SEPERATOR = '-';
import { GOVERNANCE, SEPERATOR } from '../constants';

export const getVoteId = (voter: Address, proposalId: BigInt): string =>
[voter.toHexString(), proposalId.toString()].join(SEPERATOR);
Expand All @@ -13,3 +13,5 @@ export const getPermissionId = (
[accountAddress.toHexString(), contractAddress.toHexString(), functionSig].join(SEPERATOR);

export const getDelegateId = (account: Address): string => account.toHexString();

export const getGovernanceId = (): string => GOVERNANCE;
6 changes: 6 additions & 0 deletions subgraphs/venus-governance/subgraph-client/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { Client as UrqlClient, createClient } from 'urql/core';
import {
DelegateByIdDocument,
DelegatesDocument,
GovernanceDocument,
PermissionsDocument,
ProposalByIdDocument,
ProposalsDocument,
Expand Down Expand Up @@ -51,6 +52,11 @@ class SubgraphClient {
const result = await this.query(PermissionsDocument, {});
return result;
}

async getGovernance() {
const result = await this.query(GovernanceDocument, {});
return result;
}
}

export default new SubgraphClient(
Expand Down
35 changes: 32 additions & 3 deletions subgraphs/venus-governance/tests/integration/bravo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -191,11 +191,28 @@ describe('GovernorBravo', function () {
});

describe('GovernorBravo2', function () {
before(async () => {
it('should update GovernorEntity when setting implementation', async function () {
const timelock = await ethers.getContract('Timelock');
const governorBravoDelegateV1 = await ethers.getContract('GovernorBravoDelegateV1');
// Assert original values
let {
data: { governance },
} = await subgraphClient.getGovernance();

expect(governance.totalProposals).to.equal('4');
expect(governance.totalDelegates).to.equal('4');
expect(governance.totalVoters).to.equal('4');
expect(governance.totalVotesMantissa).to.equal('1700000000000000000000000');
expect(governance.quorumVotesMantissa).to.equal('600000000000000000000000');
expect(governance.implementation).to.equal(governorBravoDelegateV1.address.toLowerCase());
expect(governance.pendingAdmin).to.equal(null);
expect(governance.admin).to.equal(signers[0].address.toLowerCase());
expect(governance.guardian).to.equal(signers[0].address.toLowerCase());
expect(governance.proposalMaxOperations).to.equal('10');

const governorBravoDelegatorV2 = await ethers.getContract('GovernorBravoDelegate');
const xvsVaultProxy = await ethers.getContract('XVSVaultProxy');
const xvsVault = await ethers.getContractAt('XVSVault', xvsVaultProxy.address);
const timelock = await ethers.getContract('Timelock');

await governorBravoDelegator._setImplementation(governorBravoDelegatorV2.address);
governorBravo = await ethers.getContractAt('GovernorBravoDelegate', governorBravo.address);
Expand Down Expand Up @@ -223,7 +240,19 @@ describe('GovernorBravo', function () {

const timelocks = [timelock.address, timelock.address, timelock.address];

governorBravo.initialize(xvsVault.address, proposalConfigs, timelocks, signers[0].address);
await governorBravo.initialize(
xvsVault.address,
proposalConfigs,
timelocks,
signers[0].address,
);
await waitForSubgraphToBeSynced(SYNC_DELAY);
// Assert updated values
({
data: { governance },
} = await subgraphClient.getGovernance());

expect(governance.implementation).to.equal(governorBravoDelegatorV2.address.toLowerCase());
});

it('should index created proposal with routes successfully', async function () {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
query Governance {
governance(id: "GOVERNANCE") {
id
totalProposals
totalDelegates
totalVoters
totalVotesMantissa
quorumVotesMantissa
implementation
pendingAdmin
admin
guardian
proposalMaxOperations
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ export const SUBGRAPH_NAME = 'venus-governance';
export const SYNC_DELAY = 2000;

export const mockAddress = '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266';
export const nullAddress = '0x0000000000000000000000000000000000000000';
34 changes: 7 additions & 27 deletions subgraphs/venus-governance/tests/unit/Bravo/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import {
ProposalQueued,
} from '../../../generated/GovernorBravoDelegate/GovernorBravoDelegate';
import { Delegate } from '../../../generated/schema';
import { governorBravoDelegatorAddress } from '../../../src/constants/addresses';
import { GOVERNANCE } from '../../../src/constants';
import {
handleBravoVoteCast,
handleNewAdmin,
Expand Down Expand Up @@ -267,12 +267,7 @@ describe('Bravo', () => {
);

handleNewImplementation(newImplementationEvent);
assert.fieldEquals(
'Governance',
governorBravoDelegatorAddress.toHex(),
'implementation',
newImplementation.toHexString(),
);
assert.fieldEquals('Governance', GOVERNANCE, 'implementation', newImplementation.toHexString());
});

test('registers new pending admin', () => {
Expand All @@ -285,12 +280,7 @@ describe('Bravo', () => {
);

handleNewPendingAdmin(pendingAdminEvent);
assert.fieldEquals(
'Governance',
governorBravoDelegatorAddress.toHex(),
'pendingAdmin',
newPendingAdmin.toHexString(),
);
assert.fieldEquals('Governance', GOVERNANCE, 'pendingAdmin', newPendingAdmin.toHexString());
});

test('registers new admin', () => {
Expand All @@ -299,13 +289,8 @@ describe('Bravo', () => {
const newAdminEvent = createNewAdminEvent(governanceAddress, oldAdmin, newAdmin);

handleNewAdmin(newAdminEvent);
assert.fieldEquals(
'Governance',
governorBravoDelegatorAddress.toHex(),
'admin',
newAdmin.toHexString(),
);
assert.fieldEquals('Governance', governorBravoDelegatorAddress.toHex(), 'pendingAdmin', 'null');
assert.fieldEquals('Governance', GOVERNANCE, 'admin', newAdmin.toHexString());
assert.fieldEquals('Governance', GOVERNANCE, 'pendingAdmin', 'null');
});

test('registers new guardian', () => {
Expand All @@ -314,12 +299,7 @@ describe('Bravo', () => {
const newGuardianEvent = createNewGuardianEvent(governanceAddress, oldGuardian, newGuardian);

handleNewGuardian(newGuardianEvent);
assert.fieldEquals(
'Governance',
governorBravoDelegatorAddress.toHex(),
'guardian',
newGuardian.toHexString(),
);
assert.fieldEquals('Governance', GOVERNANCE, 'guardian', newGuardian.toHexString());
});

test('registers new proposal max operations', () => {
Expand All @@ -334,7 +314,7 @@ describe('Bravo', () => {
handleProposalMaxOperationsUpdated(newProposalMaxOperationsEvent);
assert.fieldEquals(
'Governance',
governorBravoDelegatorAddress.toHex(),
GOVERNANCE,
'proposalMaxOperations',
newProposalMaxOperations.toString(),
);
Expand Down

0 comments on commit 25f225a

Please sign in to comment.