Skip to content

Commit

Permalink
GrantFund fixes and cleanup (#32)
Browse files Browse the repository at this point in the history
* fixed issues with array management

* Bytes.empty() is really just null, persist distributionPeriod mutations

* tests liked null, graph deploy did not

* add some logging

* added another log

* adjustments

* needed this to get matchstick working

* rename standardProposals to proposals

* more logging

* eliminated grantFundAddressTable

* debug conversion from u24 to bytes

* attempt to fix issue converting bigint to bytes

* try converting other way as signed

* wrote unit test which reproduced the problem

* hacked around oddball BigInt behavior

* fixed unit test

* tidy, remove debug logging
  • Loading branch information
EdNoepel authored Jul 20, 2023
1 parent f15f26d commit 2a90e09
Show file tree
Hide file tree
Showing 14 changed files with 232 additions and 191 deletions.
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@
},
"dependencies": {
"@graphprotocol/graph-cli": ">=0.37.6",
"@graphprotocol/graph-ts": ">=0.29.1"
"@graphprotocol/graph-ts": ">=0.29.1",
"@types/node": "*",
"typescript": "^5.1.6"
},
"devDependencies": {
"matchstick-as": "0.5.0"
Expand Down
7 changes: 3 additions & 4 deletions schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -766,12 +766,11 @@ type UpdateExchangeRates @entity(immutable: true) {
# GRANT FUND ENTITIES #
# # # # # # # # # # # #

# TODO: rename standardProposals to proposals
type GrantFund @entity {
id: Bytes! # address of the grant fund
treasury: BigDecimal! # Total ajna tokens in the grant fund treasure
standardProposals: [Proposal!]! # List of standard proposals submitted to the grant fund
standardProposalsExecuted: [Proposal!]! # List of standard proposals executed by the grant fund
proposals: [Proposal!]! # List of standard proposals submitted to the grant fund
proposalsExecuted: [Proposal!]! # List of standard proposals executed by the grant fund
distributionPeriods: [DistributionPeriod!]! # List of distribution periods
totalDelegationRewardsClaimed: BigDecimal! # Total delegation rewards claimed across all distribution periods
}
Expand All @@ -780,7 +779,7 @@ type DistributionPeriod @entity {
id: Bytes! # distribution period id converted to Bytes from uint
startBlock: BigInt! # block number the distribution period starts
endBlock: BigInt! # block number the distribution period ends
topSlate: FundedSlate! # The current top FundedSlate
topSlate: FundedSlate # The current top FundedSlate
slatesSubmitted: [FundedSlate!]! # FundedSlate[] slates submitted in the distribution period
delegationRewardsClaimed: BigDecimal! # Total delegation rewards claimed in the distribution period
fundingVotesCast: BigDecimal! # number of funding votes cast
Expand Down
57 changes: 24 additions & 33 deletions src/grant-fund.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Address, BigInt, Bytes, ethereum, log } from '@graphprotocol/graph-ts'
import { Address, Bytes, ethereum, log } from '@graphprotocol/graph-ts'

import {
DelegateRewardClaimed as DelegateRewardClaimedEvent,
Expand Down Expand Up @@ -27,9 +27,9 @@ import {

import { ZERO_ADDRESS, ZERO_BD } from './utils/constants'
import { addressArrayToBytesArray, addressToBytes, bigIntToBytes, bytesToBigInt, wadToDecimal } from "./utils/convert"
import { getProposalParamsId, getProposalsInSlate, loadOrCreateProposal, removeProposalFromList } from './utils/grants/proposal'
import { getProposalParamsId, getProposalsInSlate, removeProposalFromList } from './utils/grants/proposal'
import { getCurrentDistributionId, getCurrentStage, loadOrCreateDistributionPeriod } from './utils/grants/distribution'
import { getDistributionPeriodVoteId, getFundingStageVotingPower, getFundingVoteId, getScreeningStageVotingPower, getScreeningVoteId, loadOrCreateDistributionPeriodVote, loadOrCreateVoter } from './utils/grants/voter'
import { getFundingStageVotingPower, getFundingVoteId, getScreeningStageVotingPower, getScreeningVoteId, loadOrCreateDistributionPeriodVote, loadOrCreateVoter } from './utils/grants/voter'
import { loadOrCreateGrantFund } from './utils/grants/fund'

export function handleDelegateRewardClaimed(
Expand All @@ -48,7 +48,7 @@ export function handleDelegateRewardClaimed(
const rewardsClaimed = wadToDecimal(event.params.rewardClaimed)

// update DistributionPeriod entity
const distributionId = bigIntToBytes(getCurrentDistributionId())
const distributionId = bigIntToBytes(getCurrentDistributionId(event.address))
const distributionPeriod = loadOrCreateDistributionPeriod(distributionId)
distributionPeriod.delegationRewardsClaimed = distributionPeriod.delegationRewardsClaimed.plus(rewardsClaimed)

Expand Down Expand Up @@ -108,11 +108,13 @@ export function handleFundedSlateUpdated(event: FundedSlateUpdatedEvent): void {
fundedSlate.updateBlock = event.block.number

// get the list of proposals in the slate
const proposalsInSlate = getProposalsInSlate(fundedSlateUpdated.distributionId_)
const proposalsInSlate = getProposalsInSlate(event.address, fundedSlateUpdated.distributionId_)
const proposals = fundedSlate.proposals
for (let i = 0; i < proposalsInSlate.length; i++) {
const proposalId = proposalsInSlate[i]
fundedSlate.proposals.push(bigIntToBytes(proposalId))
proposals.push(bigIntToBytes(proposalId))
}
fundedSlate.proposals = proposals

// save entities to the store
distributionPeriod.save()
Expand Down Expand Up @@ -173,7 +175,7 @@ export function handleProposalCreated(event: ProposalCreatedEvent): void {
}

// add proposalParams information to proposal
proposal.params.push(proposalParams.id)
proposal.params = proposal.params.concat([proposalParams.id])
proposal.totalTokensRequested = totalTokensRequested

// save each proposalParams entity to the store
Expand All @@ -186,21 +188,19 @@ export function handleProposalCreated(event: ProposalCreatedEvent): void {
const grantFund = loadOrCreateGrantFund(event.address)

// update distribution entity
const distributionId = getCurrentDistributionId()
const distributionPeriod = loadOrCreateDistributionPeriod(bigIntToBytes(distributionId))
if (distributionPeriod != null) {
distributionPeriod.proposals.push(proposal.id)
distributionPeriod.totalTokensRequested = distributionPeriod.totalTokensRequested.plus(proposal.totalTokensRequested)
distributionPeriod.save()
}
const distributionId = bigIntToBytes(getCurrentDistributionId(event.address))
const distributionPeriod = DistributionPeriod.load(distributionId)!
distributionPeriod.proposals = distributionPeriod.proposals.concat([proposal.id])
distributionPeriod.totalTokensRequested = distributionPeriod.totalTokensRequested.plus(proposal.totalTokensRequested)

// record proposals distributionId
proposal.distribution = distributionPeriod.id

// record proposal in GrantFund entity
grantFund.standardProposals.push(proposal.id)
grantFund.proposals = grantFund.proposals.concat([proposal.id])

// save entities to the store
distributionPeriod.save()
grantFund.save()
proposal.save()
proposalCreated.save()
Expand All @@ -224,8 +224,8 @@ export function handleProposalExecuted(event: ProposalExecutedEvent): void {

// record proposal in GrantFund entity
const grantFund = loadOrCreateGrantFund(event.address)
grantFund.standardProposalsExecuted.push(proposal.id)
grantFund.standardProposals = removeProposalFromList(proposal.id, grantFund.standardProposals)
grantFund.proposalsExecuted = grantFund.proposalsExecuted.concat([proposal.id])
grantFund.proposals = removeProposalFromList(proposal.id, grantFund.proposals)

// save entities to the store
grantFund.save()
Expand All @@ -250,22 +250,13 @@ export function handleDistributionPeriodStarted(
distributionStarted.transactionHash = event.transaction.hash

// create DistributionPeriod entities
const distributionPeriod = new DistributionPeriod(distributionId) as DistributionPeriod

const distributionPeriod = loadOrCreateDistributionPeriod(distributionId)
distributionPeriod.startBlock = distributionStarted.startBlock
distributionPeriod.endBlock = distributionStarted.endBlock
distributionPeriod.topSlate = Bytes.empty()
distributionPeriod.delegationRewardsClaimed = ZERO_BD
distributionPeriod.totalTokensRequested = ZERO_BD
distributionPeriod.fundingVotesCast = ZERO_BD
distributionPeriod.fundingVotePowerUsed = ZERO_BD
distributionPeriod.screeningVotesCast = ZERO_BD
distributionPeriod.proposals = []
distributionPeriod.slatesSubmitted = []

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

// save entities to store
distributionPeriod.save()
Expand Down Expand Up @@ -296,7 +287,7 @@ export function handleVoteCast(event: VoteCastEvent): void {
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())
const distributionId = bigIntToBytes(getCurrentDistributionId(event.address))
const distributionPeriod = DistributionPeriod.load(distributionId) as DistributionPeriod

// load voter's distributionPeriodVotes
Expand All @@ -321,11 +312,11 @@ export function handleVoteCast(event: VoteCastEvent): void {

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

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

// save screeningVote to the store
screeningVote.save()
Expand All @@ -342,14 +333,14 @@ export function handleVoteCast(event: VoteCastEvent): void {

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

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

voter.distributionPeriodVotes.push(distributionPeriodVote.id)
voter.distributionPeriodVotes = voter.distributionPeriodVotes.concat([distributionPeriodVote.id])

// save entities to the store
distributionPeriod.save()
Expand Down
3 changes: 0 additions & 3 deletions src/utils/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,6 @@ poolInfoUtilsAddressTable.set('ganache', Address.fromString('0x4f05DA51eAAB00e58
export const positionManagerAddressTable = new TypedMap<string, Address>()
positionManagerAddressTable.set('goerli', Address.fromString('0x23E2EFF19bd50BfCF0364B7dCA01004D5cce41f9'))
positionManagerAddressTable.set('ganache', Address.fromString('0x6c5c7fD98415168ada1930d44447790959097482'))
export const grantFundAddressTable = new TypedMap<string, Address>()
grantFundAddressTable.set('goerli', Address.fromString('0x881b4dFF6C72babA6f5eA60f34A61410c1EA1ec2'))
grantFundAddressTable.set('ganache', Address.fromString('0xE340B87CEd1af1AbE1CE8D617c84B7f168e3b18b'))

// GrantFund constants
export const CHALLENGE_PERIOD_LENGTH = BigInt.fromI32(50400)
Expand Down
9 changes: 6 additions & 3 deletions src/utils/convert.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { BigInt, BigDecimal, Bytes, Address, log } from '@graphprotocol/graph-ts'
import { BigInt, BigDecimal, Bytes, Address, ByteArray } from '@graphprotocol/graph-ts'

import { EXP_18_BD, MAX_BUCKET_INDEX, MIN_BUCKET_INDEX, ONE_BI, ZERO_BD, ZERO_BI } from './constants'
import { prices } from './prices'
Expand All @@ -22,8 +22,11 @@ export function addressArrayToBytesArray(addresses: Address[]): Bytes[] {
return retval
}

export function bigIntToBytes(bi: BigInt): Bytes {
return Bytes.fromByteArray(Bytes.fromBigInt(bi))
export function bigIntToBytes(bi: BigInt): Bytes {
if (bi.isI32()) // HACK: handle padding oddities when converting BigInt which came from a signed number
return Bytes.fromByteArray(Bytes.fromBigInt(BigInt.fromI32(bi.toI32())))
else
return Bytes.fromByteArray(Bytes.fromBigInt(bi))
}

/***************************/
Expand Down
16 changes: 8 additions & 8 deletions src/utils/grants/distribution.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,27 @@
import { Address, BigInt, Bytes, dataSource } from "@graphprotocol/graph-ts"
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, grantFundAddressTable } from "../constants"
import { FUNDING_PERIOD_LENGTH, ONE_BI, ZERO_BD, ZERO_BI } from "../constants"
import { bigIntToBytes } from "../convert"

export function getDistributionIdAtBlock(blockNumber: BigInt): BigInt {
const currentDistributionId = getCurrentDistributionId()
export function getDistributionIdAtBlock(blockNumber: BigInt, grantFundAddress: Address): BigInt | null {
const currentDistributionId = getCurrentDistributionId(grantFundAddress)
for (let i = currentDistributionId.toI32(); i > 0; i--) {
const distributionPeriod = DistributionPeriod.load(Bytes.fromI32(i))!

if (blockNumber > distributionPeriod.startBlock && blockNumber < distributionPeriod.endBlock) {
return BigInt.fromI32(i)
}
}
return null
}

export function getCurrentDistributionId(): BigInt {
const grantFundAddress = grantFundAddressTable.get(dataSource.network())!
export function getCurrentDistributionId(grantFundAddress: Address): BigInt {
const grantFundContract = GrantFund.bind(grantFundAddress)
const distributionIdResult = grantFundContract.getDistributionId()
return BigInt.fromI32(distributionIdResult)
const distributionIdResult = BigInt.fromI32(grantFundContract.getDistributionId())
return distributionIdResult
}

export function getCurrentStage(currentBlockNumber: BigInt, distributionPeriod: DistributionPeriod): String {
Expand Down
4 changes: 2 additions & 2 deletions src/utils/grants/fund.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ export function loadOrCreateGrantFund(grantFundAddress: Address): GrantFund {
// create new grantFund if one hasn't already been stored
grantFund = new GrantFund(addressToBytes(grantFundAddress)) as GrantFund
grantFund.treasury = ZERO_BD
grantFund.standardProposals = []
grantFund.standardProposalsExecuted = []
grantFund.proposals = []
grantFund.proposalsExecuted = []
grantFund.distributionPeriods = []
grantFund.totalDelegationRewardsClaimed = ZERO_BD
}
Expand Down
5 changes: 2 additions & 3 deletions src/utils/grants/proposal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Address, BigInt, Bytes, dataSource, log } from "@graphprotocol/graph-ts
import { Proposal, ProposalParams } from "../../../generated/schema"
import { GrantFund } from "../../../generated/GrantFund/GrantFund"

import { ZERO_ADDRESS, ONE_BI, ZERO_BD, ZERO_BI, grantFundAddressTable } from "../constants"
import { ZERO_ADDRESS, ONE_BI, ZERO_BD, ZERO_BI } from "../constants"
import { bytesToBigInt } from "../convert"

export function getProposalParamsId(proposalId: Bytes, paramIndex: number): Bytes {
Expand Down Expand Up @@ -41,8 +41,7 @@ export function removeProposalFromList(proposalId: Bytes, proposalList: Array<By
/*** Contract Calls ***/
/**********************/

export function getProposalsInSlate(distributionId: BigInt): Array<BigInt> {
const grantFundAddress = grantFundAddressTable.get(dataSource.network())!
export function getProposalsInSlate(grantFundAddress: Address, distributionId: BigInt): Array<BigInt> {
const grantFundContract = GrantFund.bind(grantFundAddress)
const getProposalsInSlateResult = grantFundContract.getTopTenProposals(distributionId.toI32())

Expand Down
8 changes: 3 additions & 5 deletions src/utils/grants/voter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { Address, BigDecimal, BigInt, Bytes, dataSource } from "@graphprotocol/g
import { DistributionPeriodVote, Voter } from "../../../generated/schema"
import { GrantFund } from "../../../generated/GrantFund/GrantFund"

import { ZERO_BD, ZERO_BI, grantFundAddressTable } from "../constants"
import { ZERO_BD, ZERO_BI } from "../constants"
import { wadToDecimal } from "../convert"

export function getDistributionPeriodVoteId(distributionPeriodId: Bytes, voterId: Bytes): Bytes {
Expand Down Expand Up @@ -55,16 +55,14 @@ export function loadOrCreateDistributionPeriodVote(distributionPeriodId: Bytes,
/*** Contract Calls ***/
/**********************/

export function getFundingStageVotingPower(distributionId: BigInt, voter: Address): BigDecimal {
const grantFundAddress = grantFundAddressTable.get(dataSource.network())!
export function getFundingStageVotingPower(grantFundAddress: Address, distributionId: BigInt, voter: Address): BigDecimal {
const grantFundContract = GrantFund.bind(grantFundAddress)
const votingPower = grantFundContract.getVotesFunding(distributionId.toI32(), voter)

return wadToDecimal(votingPower)
}

export function getScreeningStageVotingPower(distributionId: BigInt, voter: Address): BigDecimal {
const grantFundAddress = grantFundAddressTable.get(dataSource.network())!
export function getScreeningStageVotingPower(grantFundAddress: Address, distributionId: BigInt, voter: Address): BigDecimal {
const grantFundContract = GrantFund.bind(grantFundAddress)
const votingPower = grantFundContract.getVotesScreening(distributionId.toI32(), voter)

Expand Down
23 changes: 23 additions & 0 deletions tests/convert.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { BigInt } from '@graphprotocol/graph-ts'
import { assert, describe, test } from "matchstick-as/assembly/index";
import { ONE_BI } from "../src/utils/constants";
import { bigIntToBytes, bytesToBigInt } from "../src/utils/convert";

describe("Conversions", () => {
test("Reliably convert integer contract values to bytes", () => {
const one_bi = BigInt.fromU32(1)
const one_number = 1
assert.bigIntEquals(one_bi, BigInt.fromI32(one_number));

const one_bytes_from_bi = bigIntToBytes(one_bi);
const one_bytes_from_number = bigIntToBytes(BigInt.fromI32(one_number));
assert.bytesEquals(one_bytes_from_bi, one_bytes_from_number);
});

test("Convert values which exceed 64 bits to bytes and back", () => {
const actually_big_number = BigInt.fromI32(2).pow(64)
const as_bytes = bigIntToBytes(actually_big_number);
const back_to_bigint = bytesToBigInt(as_bytes)
assert.bigIntEquals(actually_big_number, back_to_bigint);
});
});
Loading

0 comments on commit 2a90e09

Please sign in to comment.