Skip to content

Commit

Permalink
Merge pull request #802 from NewShadesDAO/add-prop-status-event-times…
Browse files Browse the repository at this point in the history
…tamps

Add timestamps to proposal status changes in subgraph
  • Loading branch information
solimander authored Oct 9, 2023
2 parents 2607b4b + e47c915 commit ec6f6c1
Show file tree
Hide file tree
Showing 4 changed files with 230 additions and 0 deletions.
24 changes: 24 additions & 0 deletions packages/nouns-subgraph/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,30 @@ type Proposal @entity {

"The block at which voting balance snapshots are taken for this proposal"
voteSnapshotBlock: BigInt!

"The block number at which this proposal was canceled"
canceledBlock: BigInt

"The timestamp when this proposal was canceled"
canceledTimestamp: BigInt

"The block number at which this proposal was executed"
executedBlock: BigInt

"The timestamp when this proposal was executed"
executedTimestamp: BigInt

"The block number at which this proposal was vetoed"
vetoedBlock: BigInt

"The timestamp when this proposal was vetoed"
vetoedTimestamp: BigInt

"The block number at which this proposal was queued"
queuedBlock: BigInt

"The timestamp when this proposal was queued"
queuedTimestamp: BigInt
}

type ProposalVersion @entity(immutable: true) {
Expand Down
8 changes: 8 additions & 0 deletions packages/nouns-subgraph/src/nouns-dao.ts
Original file line number Diff line number Diff line change
Expand Up @@ -219,13 +219,17 @@ export function handleProposalCanceled(event: ProposalCanceled): void {
let proposal = getOrCreateProposal(event.params.id.toString());

proposal.status = STATUS_CANCELLED;
proposal.canceledBlock = event.block.number;
proposal.canceledTimestamp = event.block.timestamp;
proposal.save();
}

export function handleProposalVetoed(event: ProposalVetoed): void {
let proposal = getOrCreateProposal(event.params.id.toString());

proposal.status = STATUS_VETOED;
proposal.vetoedBlock = event.block.number;
proposal.vetoedTimestamp = event.block.timestamp;
proposal.save();
}

Expand All @@ -235,6 +239,8 @@ export function handleProposalQueued(event: ProposalQueued): void {

proposal.status = STATUS_QUEUED;
proposal.executionETA = event.params.eta;
proposal.queuedBlock = event.block.number;
proposal.queuedTimestamp = event.block.timestamp;
proposal.save();

governance.proposalsQueued = governance.proposalsQueued.plus(BIGINT_ONE);
Expand All @@ -247,6 +253,8 @@ export function handleProposalExecuted(event: ProposalExecuted): void {

proposal.status = STATUS_EXECUTED;
proposal.executionETA = null;
proposal.executedBlock = event.block.number;
proposal.executedTimestamp = event.block.timestamp;
proposal.save();

governance.proposalsQueued = governance.proposalsQueued.minus(BIGINT_ONE);
Expand Down
103 changes: 103 additions & 0 deletions packages/nouns-subgraph/tests/nouns-dao.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ import {
handleProposalTransactionsUpdated,
handleEscrowedToFork,
handleWithdrawFromForkEscrow,
handleProposalCanceled,
handleProposalVetoed,
handleProposalExecuted,
handleProposalQueued,
} from '../src/nouns-dao';
import {
createProposalCreatedWithRequirementsEventV1,
Expand All @@ -41,13 +45,21 @@ import {
createProposalTransactionsUpdatedEvent,
createEscrowedToForkEvent,
createWithdrawFromForkEscrowEvent,
createProposalCanceledEvent,
createProposalVetoedEvent,
createProposalExecutedEvent,
createProposalQueuedEvent,
} from './utils';
import {
BIGINT_10K,
BIGINT_ONE,
BIGINT_ZERO,
STATUS_ACTIVE,
STATUS_CANCELLED,
STATUS_EXECUTED,
STATUS_PENDING,
STATUS_QUEUED,
STATUS_VETOED,
} from '../src/utils/constants';
import {
getOrCreateDynamicQuorumParams,
Expand Down Expand Up @@ -752,3 +764,94 @@ describe('forking', () => {
});
});
});

describe('Proposal status changes', () => {
beforeEach(() => {
const proposalEvent = new ParsedProposalV3();
proposalEvent.id = proposalId.toString();
proposalEvent.proposer = proposerWithDelegate.toHexString();
proposalEvent.targets = changetype<Bytes[]>([Address.fromString(SOME_ADDRESS)]);
proposalEvent.values = [BigInt.fromI32(123)];
proposalEvent.signatures = ['some signature'];
proposalEvent.signers = [proposerWithDelegate.toHexString()];
proposalEvent.calldatas = [Bytes.fromI32(312)];
proposalEvent.createdTimestamp = updateBlockTimestamp.minus(BIGINT_ONE);
proposalEvent.createdBlock = updateBlockNumber.minus(BIGINT_ONE);
proposalEvent.createdTransactionHash = Bytes.fromI32(11);
proposalEvent.description = 'some description';
proposalEvent.title = 'some title';

handleProposalCreated(proposalEvent);
});

test('handleProposalCanceled', () => {
handleProposalCanceled(
createProposalCanceledEvent(
txHash,
logIndex,
updateBlockTimestamp,
updateBlockNumber,
proposalId,
),
);

const proposal = Proposal.load(proposalId.toString())!;
assert.stringEquals(STATUS_CANCELLED, proposal.status);
assert.bigIntEquals(updateBlockTimestamp, proposal.canceledTimestamp!);
assert.bigIntEquals(updateBlockNumber, proposal.canceledBlock!);
});

test('handleProposalVetoed', () => {
handleProposalVetoed(
createProposalVetoedEvent(
txHash,
logIndex,
updateBlockTimestamp,
updateBlockNumber,
proposalId,
),
);

const proposal = Proposal.load(proposalId.toString())!;
assert.stringEquals(STATUS_VETOED, proposal.status);
assert.bigIntEquals(updateBlockTimestamp, proposal.vetoedTimestamp!);
assert.bigIntEquals(updateBlockNumber, proposal.vetoedBlock!);
});

test('handleProposalQueued', () => {
const eta = updateBlockTimestamp.plus(BigInt.fromI32(100));
handleProposalQueued(
createProposalQueuedEvent(
txHash,
logIndex,
updateBlockTimestamp,
updateBlockNumber,
proposalId,
eta,
),
);

const proposal = Proposal.load(proposalId.toString())!;
assert.stringEquals(STATUS_QUEUED, proposal.status);
assert.bigIntEquals(updateBlockTimestamp, proposal.queuedTimestamp!);
assert.bigIntEquals(updateBlockNumber, proposal.queuedBlock!);
assert.bigIntEquals(eta, proposal.executionETA!);
});

test('handleProposalExecuted', () => {
handleProposalExecuted(
createProposalExecutedEvent(
txHash,
logIndex,
updateBlockTimestamp,
updateBlockNumber,
proposalId,
),
);

const proposal = Proposal.load(proposalId.toString())!;
assert.stringEquals(STATUS_EXECUTED, proposal.status);
assert.bigIntEquals(updateBlockTimestamp, proposal.executedTimestamp!);
assert.bigIntEquals(updateBlockNumber, proposal.executedBlock!);
});
});
95 changes: 95 additions & 0 deletions packages/nouns-subgraph/tests/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ import {
ProposalTransactionsUpdated,
EscrowedToFork,
WithdrawFromForkEscrow,
ProposalCanceled,
ProposalVetoed,
ProposalExecuted,
ProposalQueued,
} from '../src/types/NounsDAO/NounsDAO';
import {
handleMinQuorumVotesBPSSet,
Expand Down Expand Up @@ -562,3 +566,94 @@ export function createWithdrawFromForkEscrowEvent(

return newEvent;
}

export function createProposalCanceledEvent(
txHash: Bytes,
logIndex: BigInt,
blockTimestamp: BigInt,
blockNumber: BigInt,
proposalId: BigInt,
): ProposalCanceled {
let newEvent = changetype<ProposalCanceled>(newMockEvent());

newEvent.transaction.hash = txHash;
newEvent.logIndex = logIndex;
newEvent.block.timestamp = blockTimestamp;
newEvent.block.number = blockNumber;

newEvent.parameters = new Array();
newEvent.parameters.push(
new ethereum.EventParam('id', ethereum.Value.fromUnsignedBigInt(proposalId)),
);

return newEvent;
}

export function createProposalVetoedEvent(
txHash: Bytes,
logIndex: BigInt,
blockTimestamp: BigInt,
blockNumber: BigInt,
proposalId: BigInt,
): ProposalVetoed {
let newEvent = changetype<ProposalVetoed>(newMockEvent());

newEvent.transaction.hash = txHash;
newEvent.logIndex = logIndex;
newEvent.block.timestamp = blockTimestamp;
newEvent.block.number = blockNumber;

newEvent.parameters = new Array();
newEvent.parameters.push(
new ethereum.EventParam('id', ethereum.Value.fromUnsignedBigInt(proposalId)),
);

return newEvent;
}

export function createProposalExecutedEvent(
txHash: Bytes,
logIndex: BigInt,
blockTimestamp: BigInt,
blockNumber: BigInt,
proposalId: BigInt,
): ProposalExecuted {
let newEvent = changetype<ProposalExecuted>(newMockEvent());

newEvent.transaction.hash = txHash;
newEvent.logIndex = logIndex;
newEvent.block.timestamp = blockTimestamp;
newEvent.block.number = blockNumber;

newEvent.parameters = new Array();
newEvent.parameters.push(
new ethereum.EventParam('id', ethereum.Value.fromUnsignedBigInt(proposalId)),
);

return newEvent;
}

export function createProposalQueuedEvent(
txHash: Bytes,
logIndex: BigInt,
blockTimestamp: BigInt,
blockNumber: BigInt,
proposalId: BigInt,
eta: BigInt,
): ProposalQueued {
let newEvent = changetype<ProposalQueued>(newMockEvent());

newEvent.transaction.hash = txHash;
newEvent.logIndex = logIndex;
newEvent.block.timestamp = blockTimestamp;
newEvent.block.number = blockNumber;

newEvent.parameters = new Array();
newEvent.parameters.push(
new ethereum.EventParam('id', ethereum.Value.fromUnsignedBigInt(proposalId)),
);

newEvent.parameters.push(new ethereum.EventParam('eta', ethereum.Value.fromUnsignedBigInt(eta)));

return newEvent;
}

0 comments on commit ec6f6c1

Please sign in to comment.