Skip to content

Commit

Permalink
update FundingVote entity (#33)
Browse files Browse the repository at this point in the history
* update FundingVote entity

* fix todo

* update FundingVote entity

* fix todo

* expand vote unit testing

* fix fundingVote flow

* fix votingPowerUsed calculation

* cleanups

* expand test assertions

* wip distributionPeriod start screening power tracking

* expand fundedSlate entity

* add funds available to DistributionPeriod; improve treasury tracking

* remove screeningStageVotingPower; rename fundingStage votingPower attributes

* delete unused file

* use wmul for fundsAvailable

* fix FundedSlate bugs

---------

Co-authored-by: Mike <mikehathaway@makerdao.com>
  • Loading branch information
MikeHathaway and Mike committed Jul 27, 2023
1 parent 44af871 commit 91e0728
Show file tree
Hide file tree
Showing 10 changed files with 599 additions and 60 deletions.
12 changes: 7 additions & 5 deletions schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -794,8 +794,8 @@ type DistributionPeriod @entity {
endBlock: BigInt! # block number the distribution period ends
topSlate: FundedSlate # The current top FundedSlate
slatesSubmitted: [FundedSlate!]! # FundedSlate[] slates submitted in the distribution period
fundsAvailable: BigDecimal! # Total ajna tokens available for distribution in the distribution period
delegationRewardsClaimed: BigDecimal! # Total delegation rewards claimed in the distribution period
fundingVotesCast: BigDecimal! # number of funding votes cast
fundingVotePowerUsed: BigDecimal! # Total funding vote power used
screeningVotesCast: BigDecimal! # number of screening votes cast
votes: [DistributionPeriodVote!]! # Voter info for the distribution period
Expand Down Expand Up @@ -828,8 +828,8 @@ type DistributionPeriodVote @entity {
id: Bytes! # $accountId + '|'' + $distributionId
voter: Account!
distribution: DistributionPeriod! # uint256
screeningStageVotingPower: BigDecimal! # uint256
fundingStageVotingPower: BigDecimal! # uint256
estimatedInitialFundingStageVotingPowerForCalculatingRewards: BigDecimal! # uint256
estimatedRemainingFundingStageVotingPowerForCalculatingRewards: BigDecimal! # uint256
screeningVotes: [ScreeningVote!]! # ScreeningVote[]
fundingVotes: [FundingVote!]! # FundingVote[]
}
Expand All @@ -848,15 +848,17 @@ type FundingVote @entity {
distribution: DistributionPeriod! # distribution period the vote was cast in
voter: Account! # actor who cast votes
proposal: Proposal! # proposal being voted on
votesCast: BigDecimal! # uint256 # TODO: can this be negative?
votingPowerUsed: BigDecimal! # uint256
votesCast: BigDecimal! # uint256
votingPowerUsed: BigDecimal! # uint256 # cost of the incremetnal funding vote to the voter's voting power
blockNumber: BigInt! # block number the vote was cast
}

type FundedSlate @entity {
id: Bytes! # bytes32 hash of slate proposalIds
distribution: DistributionPeriod! # distribution period the vote was cast in
proposals: [Proposal!]! # uint256[] list of proposalIds
totalTokensRequested: BigDecimal! # total number of tokens requested by all proposals in the slate
totalFundingVotesReceived: BigDecimal! # net funding votes received by all proposals in the slate
updateBlock: BigInt! # block number the slate was updated
}

Expand Down
84 changes: 52 additions & 32 deletions src/grant-fund.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Address, Bytes, ethereum, log } from '@graphprotocol/graph-ts'
import { Address, BigInt, Bytes, ethereum } from '@graphprotocol/graph-ts'

import {
DelegateRewardClaimed as DelegateRewardClaimedEvent,
Expand Down Expand Up @@ -26,13 +26,14 @@ import {
DistributionPeriodVote
} from "../generated/schema"

import { ZERO_ADDRESS, ZERO_BD } from './utils/constants'
import { ONE_BI, THREE_PERCENT_BI, ZERO_ADDRESS, ZERO_BD } from './utils/constants'
import { addressArrayToBytesArray, addressToBytes, bigIntToBytes, bytesToBigInt, wadToDecimal } from "./utils/convert"
import { getProposalParamsId, getProposalsInSlate, removeProposalFromList } from './utils/grants/proposal'
import { getProposalParamsId, getProposalsInSlate, loadOrCreateProposal, removeProposalFromList } from './utils/grants/proposal'
import { getCurrentDistributionId, getCurrentStage, loadOrCreateDistributionPeriod } from './utils/grants/distribution'
import { getFundingStageVotingPower, getFundingVoteId, getScreeningStageVotingPower, getScreeningVoteId, loadOrCreateDistributionPeriodVote } from './utils/grants/voter'
import { loadOrCreateGrantFund } from './utils/grants/fund'
import { getFundingStageVotingPower, getFundingVoteId, getFundingVotingPowerUsed, getScreeningStageVotingPower, getScreeningVoteId, loadOrCreateDistributionPeriodVote } from './utils/grants/voter'
import { getTreasury, loadOrCreateGrantFund } from './utils/grants/fund'
import { loadOrCreateAccount } from './utils/account'
import { wmul } from './utils/math'

export function handleDelegateRewardClaimed(
event: DelegateRewardClaimedEvent
Expand Down Expand Up @@ -86,8 +87,7 @@ export function handleFundTreasury(event: FundTreasuryEvent): void {

// update GrantFund entity
const grantFund = loadOrCreateGrantFund(event.address)
// TODO: simply set this to treasuryBalance?
grantFund.treasury = grantFund.treasury.plus(wadToDecimal(event.params.amount))
grantFund.treasury = wadToDecimal(getTreasury(event.address))

// save entities to the store
grantFund.save()
Expand All @@ -111,17 +111,30 @@ export function handleFundedSlateUpdated(event: FundedSlateUpdatedEvent): void {
distributionPeriod.topSlate = event.params.fundedSlateHash

// create FundedSlate entity
const fundedSlate = new FundedSlate(distributionId) as FundedSlate
const fundedSlate = new FundedSlate(fundedSlateUpdated.fundedSlateHash_) as FundedSlate
fundedSlate.distribution = distributionId
fundedSlate.updateBlock = event.block.number

// get the list of proposals in the slate
const proposalsInSlate = getProposalsInSlate(event.address, fundedSlateUpdated.distributionId_)
const proposals = fundedSlate.proposals
const proposalsInSlate = getProposalsInSlate(event.address, fundedSlateUpdated.fundedSlateHash_)
const proposals: Bytes[] = []
let totalTokensRequested = ZERO_BD
let totalFundingVotesReceived = ZERO_BD

for (let i = 0; i < proposalsInSlate.length; i++) {
const proposalId = proposalsInSlate[i]
const proposal = loadOrCreateProposal(bigIntToBytes(proposalId))

totalTokensRequested = totalTokensRequested.plus(proposal.totalTokensRequested)
totalFundingVotesReceived = totalFundingVotesReceived.plus(proposal.fundingVotesReceived)

// add proposal information to fundedSlate
proposals.push(bigIntToBytes(proposalId))
}

// record proposal information in fundedSlate entity
fundedSlate.totalTokensRequested = totalTokensRequested
fundedSlate.totalFundingVotesReceived = totalFundingVotesReceived
fundedSlate.proposals = proposals

// save entities to the store
Expand Down Expand Up @@ -249,18 +262,13 @@ export function handleDistributionPeriodStarted(
distributionPeriod.startBlock = distributionStarted.startBlock
distributionPeriod.endBlock = distributionStarted.endBlock

// loop through DistributionPeriodVotes for the current period and update voting power
const votes = distributionPeriod.votes
for (var i=0; i<votes.length; ++i) {
const vote = DistributionPeriodVote.load(votes[i])!
vote.screeningStageVotingPower = getScreeningStageVotingPower(event.address, bytesToBigInt(distributionId), Address.fromBytes(vote.voter))
vote.fundingStageVotingPower = getFundingStageVotingPower(event.address, bytesToBigInt(distributionId), Address.fromBytes(vote.voter))
vote.save()
}

// update GrantFund entity
const grantFund = loadOrCreateGrantFund(event.address)
const treasury = getTreasury(event.address)
grantFund.distributionPeriods = grantFund.distributionPeriods.concat([distributionPeriod.id])
grantFund.treasury = wadToDecimal(treasury)

distributionPeriod.fundsAvailable = wadToDecimal(wmul(treasury, THREE_PERCENT_BI))

// save entities to store
distributionPeriod.save()
Expand Down Expand Up @@ -289,9 +297,8 @@ export function handleVoteCast(event: VoteCastEvent): void {
const proposalId = bigIntToBytes(event.params.proposalId)
const proposal = Proposal.load(proposalId) as Proposal
if (proposal != null) {
// TODO: need to be able to access the distributionId at that block height or call getDistributionIdAtBlock()?
// load distribution entity
const distributionId = bigIntToBytes(getCurrentDistributionId(event.address))
// load distribution entity using the distributionId from the proposal
const distributionId = proposal.distribution!
const distributionPeriod = DistributionPeriod.load(distributionId) as DistributionPeriod

// load voter's distributionPeriodVotes
Expand All @@ -314,11 +321,6 @@ export function handleVoteCast(event: VoteCastEvent): void {
screeningVote.votesCast = screeningVotesCast
screeningVote.blockNumber = voteCast.blockNumber

// update voter's distributionPeriodVote entity if it hasn't been recorded yet
if (distributionPeriodVote.screeningStageVotingPower === ZERO_BD) {
distributionPeriodVote.screeningStageVotingPower = getScreeningStageVotingPower(event.address, bytesToBigInt(distributionId), Address.fromBytes(voter.id))
}

// add additional screening votes to voter's distributionPeriodVote entity
distributionPeriodVote.screeningVotes = distributionPeriodVote.screeningVotes.concat([screeningVote.id])

Expand All @@ -332,19 +334,37 @@ export function handleVoteCast(event: VoteCastEvent): void {
fundingVote.voter = voter.id
fundingVote.proposal = proposalId
fundingVote.votesCast = wadToDecimal(event.params.weight)
// fundingVote.votingPowerUsed = ZERO_BD TODO: need to calculate this
fundingVote.blockNumber = voteCast.blockNumber

// update voter's distributionPeriodVote entity if it hasn't been recorded yet
if (distributionPeriodVote.screeningStageVotingPower === ZERO_BD) {
distributionPeriodVote.fundingStageVotingPower = getFundingStageVotingPower(event.address, bytesToBigInt(distributionId), Address.fromBytes(voter.id))
// save initial fundingVote information to enable usage in calculation of votingPowerUsed
fundingVote.votingPowerUsed = ZERO_BD
fundingVote.save()

// add additional funding votes to voter's distributionPeriodVote entity
distributionPeriodVote.fundingVotes = distributionPeriodVote.fundingVotes.concat([fundingVote.id])

// calculate and record the voting power cost of this funding vote
fundingVote.votingPowerUsed = getFundingVotingPowerUsed(distributionPeriodVote, proposalId);
distributionPeriod.fundingVotePowerUsed = distributionPeriod.fundingVotePowerUsed.plus(fundingVote.votingPowerUsed)

// update voter's distributionPeriodVote entity voting power tracking if it hasn't been recorded yet
if (distributionPeriodVote.estimatedInitialFundingStageVotingPowerForCalculatingRewards.equals(ZERO_BD)) {
distributionPeriodVote.estimatedInitialFundingStageVotingPowerForCalculatingRewards = getFundingStageVotingPower(event.address, bytesToBigInt(distributionId), Address.fromBytes(voter.id))
distributionPeriodVote.estimatedRemainingFundingStageVotingPowerForCalculatingRewards = distributionPeriodVote.estimatedInitialFundingStageVotingPowerForCalculatingRewards.minus(fundingVote.votingPowerUsed)
}
else {
distributionPeriodVote.estimatedRemainingFundingStageVotingPowerForCalculatingRewards = getFundingStageVotingPower(event.address, bytesToBigInt(distributionId), Address.fromBytes(voter.id))
}

// save fundingVote to the store
fundingVote.save()
}

voter.distributionPeriodVotes = voter.distributionPeriodVotes.concat([distributionPeriodVote.id])
// check if the account has already voted in this distribution period
if (!voter.distributionPeriodVotes.includes(distributionPeriodVote.id)) {
// associate the distributionPeriodVote entity with the voter
voter.distributionPeriodVotes = voter.distributionPeriodVotes.concat([distributionPeriodVote.id])
}

// save entities to the store
distributionPeriod.save()
Expand Down
17 changes: 9 additions & 8 deletions src/utils/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@ import { BigInt, Address, BigDecimal, TypedMap } from '@graphprotocol/graph-ts'
export const ZERO_ADDRESS = Address.fromString('0x0000000000000000000000000000000000000000')

// BigInt constants
export const ZERO_BI = BigInt.zero()
export const ONE_BI = BigInt.fromI32(1)
export const TEN_BI = BigInt.fromI32(10)
export const ONE_PERCENT_BI = BigInt.fromString("10000000000000000") // 0.01 * 1e18
export const FIVE_PERCENT_BI = BigInt.fromString("50000000000000000") // 0.05 * 1e18
export const HALF_WAD_BI = BigInt.fromString("500000000000000000")
export const ONE_WAD_BI = BigInt.fromString("1000000000000000000")
export const ZERO_BI = BigInt.zero()
export const ONE_BI = BigInt.fromI32(1)
export const TEN_BI = BigInt.fromI32(10)
export const ONE_PERCENT_BI = BigInt.fromString("10000000000000000") // 0.01 * 1e18
export const THREE_PERCENT_BI = BigInt.fromString("30000000000000000") // 0.03 * 1e18
export const FIVE_PERCENT_BI = BigInt.fromString("50000000000000000") // 0.05 * 1e18
export const HALF_WAD_BI = BigInt.fromString("500000000000000000")
export const ONE_WAD_BI = BigInt.fromString("1000000000000000000")

// BigDecimal constants
export const ZERO_BD = BigDecimal.zero()
Expand All @@ -36,4 +37,4 @@ positionManagerAddressTable.set('ganache', Address.fromString('0x6c5c7fD98415168
export const CHALLENGE_PERIOD_LENGTH = BigInt.fromI32(50400)
export const DISTRIBUTION_PERIOD_LENGTH = BigInt.fromI32(648000)
export const FUNDING_PERIOD_LENGTH = BigInt.fromI32(72000)
export const MAX_EFM_PROPOSAL_LENGTH = BigInt.fromI32(216000)
export const SCREENING_PERIOD_LENGTH = BigInt.fromI32(525600)
12 changes: 8 additions & 4 deletions src/utils/grants/distribution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { Address, BigInt, Bytes, dataSource, log } from "@graphprotocol/graph-ts
import { GrantFund } from "../../../generated/GrantFund/GrantFund"
import { DistributionPeriod } from "../../../generated/schema"

import { FUNDING_PERIOD_LENGTH, ONE_BI, ZERO_BD, ZERO_BI } from "../constants"
import { FUNDING_PERIOD_LENGTH, SCREENING_PERIOD_LENGTH, ONE_BI, ZERO_BD, ZERO_BI, CHALLENGE_PERIOD_LENGTH } from "../constants"
import { bigIntToBytes } from "../convert"

export function getDistributionIdAtBlock(blockNumber: BigInt, grantFundAddress: Address): BigInt | null {
Expand All @@ -25,9 +25,13 @@ export function getCurrentDistributionId(grantFundAddress: Address): BigInt {
}

export function getCurrentStage(currentBlockNumber: BigInt, distributionPeriod: DistributionPeriod): String {
if (currentBlockNumber.lt(distributionPeriod.endBlock.minus(FUNDING_PERIOD_LENGTH))) {
if (currentBlockNumber.lt(distributionPeriod.startBlock.plus(SCREENING_PERIOD_LENGTH))) {
return "SCREENING"
} else if (currentBlockNumber.gt(distributionPeriod.endBlock.minus(FUNDING_PERIOD_LENGTH)) && currentBlockNumber.lt(distributionPeriod.endBlock)) {
} else if (
currentBlockNumber.ge(distributionPeriod.startBlock.plus(SCREENING_PERIOD_LENGTH))
&&
currentBlockNumber.le(distributionPeriod.endBlock.minus(CHALLENGE_PERIOD_LENGTH))
) {
return "FUNDING"
} else {
return "CHALLENGE"
Expand All @@ -44,7 +48,7 @@ export function loadOrCreateDistributionPeriod(distributionId: Bytes): Distribut
distributionPeriod.topSlate = Bytes.empty()
distributionPeriod.slatesSubmitted = []
distributionPeriod.delegationRewardsClaimed = ZERO_BD
distributionPeriod.fundingVotesCast = ZERO_BD
distributionPeriod.fundsAvailable = ZERO_BD
distributionPeriod.fundingVotePowerUsed = ZERO_BD
distributionPeriod.screeningVotesCast = ZERO_BD
distributionPeriod.votes = []
Expand Down
7 changes: 7 additions & 0 deletions src/utils/grants/fund.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,10 @@ export function loadOrCreateGrantFund(grantFundAddress: Address): GrantFund {
}
return grantFund
}

export function getTreasury(grantFundAddress: Address): BigInt {
const grantFundContract = GrantFundContract.bind(grantFundAddress)
const getTreasuryResult = grantFundContract.treasury()

return getTreasuryResult
}
4 changes: 2 additions & 2 deletions src/utils/grants/proposal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,9 @@ export function removeProposalFromList(proposalId: Bytes, proposalList: Array<By
/*** Contract Calls ***/
/**********************/

export function getProposalsInSlate(grantFundAddress: Address, distributionId: BigInt): Array<BigInt> {
export function getProposalsInSlate(grantFundAddress: Address, slateHash: Bytes): Array<BigInt> {
const grantFundContract = GrantFund.bind(grantFundAddress)
const getProposalsInSlateResult = grantFundContract.getTopTenProposals(distributionId.toI32())
const getProposalsInSlateResult = grantFundContract.getFundedProposalSlate(slateHash)

return getProposalsInSlateResult
}
62 changes: 57 additions & 5 deletions src/utils/grants/voter.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { Address, BigDecimal, BigInt, Bytes, dataSource } from "@graphprotocol/graph-ts"
import { Address, BigDecimal, BigInt, Bytes, dataSource, log } from "@graphprotocol/graph-ts"

import { DistributionPeriodVote } from "../../../generated/schema"
import { DistributionPeriodVote, FundingVote } from "../../../generated/schema"
import { GrantFund } from "../../../generated/GrantFund/GrantFund"

import { ZERO_BD, ZERO_BI } from "../constants"
import { EXP_18_BD, ZERO_BD, ZERO_BI } from "../constants"
import { wadToDecimal } from "../convert"
import { loadOrCreateDistributionPeriod } from "./distribution"

Expand All @@ -27,6 +27,58 @@ export function getScreeningVoteId(proposalId: Bytes, voterId: Bytes, logIndex:
.concat(Bytes.fromUTF8(logIndex.toString()))
}

export function getFundingVotesByProposalId(distributionPeriodVote: DistributionPeriodVote, proposalId: Bytes): Bytes[] {
const filteredVotes: Bytes[] = [];
const fundingVotes = distributionPeriodVote.fundingVotes;

log.info("getFundingVotesByProposalId: {} {}", [distributionPeriodVote.fundingVotes.length.toString(), proposalId.toString()])

for (let i = 0; i < fundingVotes.length; i++) {
const proposal = loadOrCreateFundingVote(fundingVotes[i]).proposal;
if (proposal.equals(proposalId)) {
filteredVotes.push(fundingVotes[i]);
}
}
return filteredVotes;
}

// calculate the amount of funding voting power used on an individual FundingVote
export function getFundingVotingPowerUsed(distributionPeriodVote: DistributionPeriodVote, proposalId: Bytes): BigDecimal {
const votes = getFundingVotesByProposalId(distributionPeriodVote, proposalId);

// accumulate the squared votes from each separate vote on the proposal
const squaredAmount: BigDecimal[] = [];
for (let i = 0; i < votes.length; i++) {
const vote = loadOrCreateFundingVote(votes[i]);
// convert back from wad before squaring
const decimalVotesCast = vote.votesCast.times(EXP_18_BD)
squaredAmount.push(decimalVotesCast.times(decimalVotesCast).div(EXP_18_BD));
}

// sum the squared amounts
let sum = ZERO_BD;
for (let i = 0; i < squaredAmount.length; i++) {
sum = sum.plus(squaredAmount[i]);
}

return sum;
}

export function loadOrCreateFundingVote(fundingVoteId: Bytes): FundingVote {
let fundingVote = FundingVote.load(fundingVoteId)
if (fundingVote == null) {
// create new fundingVote if one hasn't already been stored
fundingVote = new FundingVote(fundingVoteId) as FundingVote
fundingVote.distribution = Bytes.empty()
fundingVote.voter = Bytes.empty()
fundingVote.proposal = Bytes.empty()
fundingVote.votesCast = ZERO_BD
fundingVote.votingPowerUsed = ZERO_BD
fundingVote.blockNumber = ZERO_BI
}
return fundingVote
}

export function loadOrCreateDistributionPeriodVote(distributionPeriodId: Bytes, voterId: Bytes): DistributionPeriodVote {
const distributionPeriodVotesId = getDistributionPeriodVoteId(distributionPeriodId, voterId)
let distributionPeriodVotes = DistributionPeriodVote.load(distributionPeriodVotesId)
Expand All @@ -35,8 +87,8 @@ export function loadOrCreateDistributionPeriodVote(distributionPeriodId: Bytes,
distributionPeriodVotes = new DistributionPeriodVote(distributionPeriodVotesId) as DistributionPeriodVote
distributionPeriodVotes.voter = voterId
distributionPeriodVotes.distribution = distributionPeriodId
distributionPeriodVotes.screeningStageVotingPower = ZERO_BD
distributionPeriodVotes.fundingStageVotingPower = ZERO_BD
distributionPeriodVotes.estimatedInitialFundingStageVotingPowerForCalculatingRewards = ZERO_BD
distributionPeriodVotes.estimatedRemainingFundingStageVotingPowerForCalculatingRewards = ZERO_BD
distributionPeriodVotes.screeningVotes = []
distributionPeriodVotes.fundingVotes = []

Expand Down
Loading

0 comments on commit 91e0728

Please sign in to comment.