From 9b3faf0d628a55d6e9d1098f64b0a18be065cd7f Mon Sep 17 00:00:00 2001 From: Mike Date: Thu, 27 Jul 2023 15:02:04 -0400 Subject: [PATCH 1/4] fix proposal calldata decoding --- src/grant-fund.ts | 19 +++----- tests/grant-fund.test.ts | 102 ++++++++++++++++++++++++++++++++++----- 2 files changed, 95 insertions(+), 26 deletions(-) diff --git a/src/grant-fund.ts b/src/grant-fund.ts index 0c41a9c..d40d561 100644 --- a/src/grant-fund.ts +++ b/src/grant-fund.ts @@ -1,4 +1,4 @@ -import { Address, BigInt, Bytes, ethereum } from '@graphprotocol/graph-ts' +import { Address, BigInt, Bytes, ethereum, log } from '@graphprotocol/graph-ts' import { DelegateRewardClaimed as DelegateRewardClaimedEvent, @@ -182,18 +182,11 @@ export function handleProposalCreated(event: ProposalCreatedEvent): void { proposalParams.calldata = event.params.calldatas[i] // decode the calldata to get the recipient and tokens requested - const dataWithoutFunctionSelector = Bytes.fromUint8Array(proposalParams.calldata.subarray(3)) - const decoded = ethereum.decode('(address,uint256)', dataWithoutFunctionSelector) - if (decoded != null) { - proposalParams.recipient = decoded.toTuple()[0].toAddress() - const tokensRequested = decoded.toTuple()[1].toBigInt().toBigDecimal() - proposalParams.tokensRequested = tokensRequested - totalTokensRequested = totalTokensRequested.plus(tokensRequested) - } - else { - proposalParams.recipient = ZERO_ADDRESS - proposalParams.tokensRequested = ZERO_BD - } + const decoded = ethereum.decode('(address,uint256)', proposalParams.calldata)! + proposalParams.recipient = decoded.toTuple()[0].toAddress() + const tokensRequested = decoded.toTuple()[1].toBigInt().toBigDecimal() + proposalParams.tokensRequested = tokensRequested + totalTokensRequested = totalTokensRequested.plus(tokensRequested) // add proposalParams information to proposal proposal.params = proposal.params.concat([proposalParams.id]) diff --git a/tests/grant-fund.test.ts b/tests/grant-fund.test.ts index 0660d1e..210d7a7 100644 --- a/tests/grant-fund.test.ts +++ b/tests/grant-fund.test.ts @@ -7,7 +7,7 @@ import { log, logStore, } from "matchstick-as/assembly/index"; -import { Address, BigInt, Bytes, dataSource } from "@graphprotocol/graph-ts"; +import { Address, BigInt, Bytes, dataSource, ethereum } from "@graphprotocol/graph-ts"; import { handleDelegateRewardClaimed, handleFundTreasury, @@ -183,16 +183,26 @@ describe("Grant Fund assertions", () => { const proposer = Address.fromString( "0x0000000000000000000000000000000000000025" ); + + // encode mock calldatas const targets = [ajnaTokenAddress, ajnaTokenAddress]; const values = [ZERO_BI, ZERO_BI]; const signatures = [ "transfer(address,uint256)", "transfer(address,uint256)", ]; + const paramsArray: Array = [ + ethereum.Value.fromAddress(proposer), + ethereum.Value.fromUnsignedBigInt(ONE_BI), + ]; + const params = changetype(paramsArray) + const encodedparamsOne = ethereum.encode(ethereum.Value.fromTuple(params))!; + const encodedparamsTwo = ethereum.encode(ethereum.Value.fromTuple(params))!; const calldatas = [ - Bytes.fromHexString("0x000000"), - Bytes.fromHexString("0x000000"), + encodedparamsOne, + encodedparamsTwo, ]; + const distributionId = BigInt.fromI32(234); const startBlock = ONE_BI; const endBlock = startBlock.plus(DISTRIBUTION_PERIOD_LENGTH); @@ -227,12 +237,30 @@ describe("Grant Fund assertions", () => { // check Proposal attributes assert.entityCount("Proposal", 1); + assert.entityCount("ProposalParams", 2); // check DistributionPeriod attributes assert.entityCount("DistributionPeriod", 1); // check DistributionPeriod attributes assert.entityCount("GrantFund", 1); + + const expectedDistributionId = bigIntToBytes(distributionId).toHexString(); + + assert.fieldEquals( + "Proposal", + `${bigIntToBytes(proposalId).toHexString()}`, + "distribution", + `${expectedDistributionId}` + ); + + assert.fieldEquals( + "Proposal", + `${bigIntToBytes(proposalId).toHexString()}`, + "totalTokensRequested", + `${BigInt.fromI32(2)}` + ); + }); test("ProposalExecuted", () => { @@ -247,16 +275,26 @@ describe("Grant Fund assertions", () => { const proposer = Address.fromString( "0x0000000000000000000000000000000000000025" ); + + // encode mock calldatas const targets = [ajnaTokenAddress, ajnaTokenAddress]; const values = [ZERO_BI, ZERO_BI]; const signatures = [ "transfer(address,uint256)", "transfer(address,uint256)", ]; + const paramsArray: Array = [ + ethereum.Value.fromAddress(proposer), + ethereum.Value.fromUnsignedBigInt(ONE_BI), + ]; + const params = changetype(paramsArray) + const encodedparamsOne = ethereum.encode(ethereum.Value.fromTuple(params))!; + const encodedparamsTwo = ethereum.encode(ethereum.Value.fromTuple(params))!; const calldatas = [ - Bytes.fromHexString("0x000000"), - Bytes.fromHexString("0x000000"), + encodedparamsOne, + encodedparamsTwo, ]; + const distributionId = BigInt.fromI32(234); const startBlock = ONE_BI; const endBlock = startBlock.plus(DISTRIBUTION_PERIOD_LENGTH); @@ -307,6 +345,7 @@ describe("Grant Fund assertions", () => { // check Proposal attributes assert.entityCount("Proposal", 1); + assert.entityCount("ProposalParams", 2); // check ProposalExecuted attributes assert.entityCount("ProposalExecuted", 1); @@ -324,16 +363,26 @@ describe("Grant Fund assertions", () => { const proposer = Address.fromString( "0x0000000000000000000000000000000000000025" ); + + // encode mock calldatas const targets = [ajnaTokenAddress, ajnaTokenAddress]; const values = [ZERO_BI, ZERO_BI]; const signatures = [ "transfer(address,uint256)", "transfer(address,uint256)", ]; + const paramsArray: Array = [ + ethereum.Value.fromAddress(proposer), + ethereum.Value.fromUnsignedBigInt(ONE_BI), + ]; + const params = changetype(paramsArray) + const encodedparamsOne = ethereum.encode(ethereum.Value.fromTuple(params))!; + const encodedparamsTwo = ethereum.encode(ethereum.Value.fromTuple(params))!; const calldatas = [ - Bytes.fromHexString("0x000000"), - Bytes.fromHexString("0x000000"), + encodedparamsOne, + encodedparamsTwo, ]; + const distributionId = ONE_BI; const startBlock = ONE_BI; const endBlock = startBlock.plus(DISTRIBUTION_PERIOD_LENGTH); @@ -390,6 +439,7 @@ describe("Grant Fund assertions", () => { // check Proposal attributes assert.entityCount("Proposal", 1); + assert.entityCount("ProposalParams", 2); // check Proposal attributes assert.entityCount("DistributionPeriodVote", 1); @@ -426,16 +476,26 @@ describe("Grant Fund assertions", () => { const proposer = Address.fromString( "0x0000000000000000000000000000000000000025" ); + + // encode mock calldatas const targets = [ajnaTokenAddress, ajnaTokenAddress]; const values = [ZERO_BI, ZERO_BI]; const signatures = [ "transfer(address,uint256)", "transfer(address,uint256)", ]; + const paramsArray: Array = [ + ethereum.Value.fromAddress(proposer), + ethereum.Value.fromUnsignedBigInt(ONE_BI), + ]; + const params = changetype(paramsArray) + const encodedparamsOne = ethereum.encode(ethereum.Value.fromTuple(params))!; + const encodedparamsTwo = ethereum.encode(ethereum.Value.fromTuple(params))!; const calldatas = [ - Bytes.fromHexString("0x000000"), - Bytes.fromHexString("0x000000"), + encodedparamsOne, + encodedparamsTwo, ]; + const distributionId = ONE_BI; const startBlock = ONE_BI; const endBlock = startBlock.plus(DISTRIBUTION_PERIOD_LENGTH); @@ -505,6 +565,7 @@ describe("Grant Fund assertions", () => { // check Proposal attributes assert.entityCount("Proposal", 1); + assert.entityCount("ProposalParams", 2); assert.entityCount("DistributionPeriod", 1); assert.entityCount("DistributionPeriodVote", 1); @@ -604,15 +665,24 @@ describe("Grant Fund assertions", () => { const proposer = Address.fromString( "0x0000000000000000000000000000000000000025" ); + + // encode mock calldatas const targets = [ajnaTokenAddress, ajnaTokenAddress]; const values = [ZERO_BI, ZERO_BI]; const signatures = [ "transfer(address,uint256)", "transfer(address,uint256)", ]; + const paramsArray: Array = [ + ethereum.Value.fromAddress(proposer), + ethereum.Value.fromUnsignedBigInt(ONE_BI), + ]; + const params = changetype(paramsArray) + const encodedparamsOne = ethereum.encode(ethereum.Value.fromTuple(params))!; + const encodedparamsTwo = ethereum.encode(ethereum.Value.fromTuple(params))!; const calldatas = [ - Bytes.fromHexString("0x000000"), - Bytes.fromHexString("0x000000"), + encodedparamsOne, + encodedparamsTwo, ]; const distributionId = ONE_BI; const startBlock = ONE_BI; @@ -687,19 +757,25 @@ describe("Grant Fund assertions", () => { // check Proposal attributes assert.entityCount("Proposal", 1); + assert.entityCount("ProposalParams", 2); assert.entityCount("DistributionPeriod", 1); assert.entityCount("FundedSlate", 1); logStore(); - // // check FundedSlate attributes + // check FundedSlate attributes // assert.fieldEquals( // "FundedSlate", // `${fundedSlateHash.toHexString()}`, // "totalFundingVotesReceived", // `${votesCast}` // ); - + assert.fieldEquals( + "FundedSlate", + `${fundedSlateHash.toHexString()}`, + "totalTokensRequested", + `${BigInt.fromI32(2)}` + ); }); }); From c9061e5d898491a1162b3ea08d6289a74e60e5df Mon Sep 17 00:00:00 2001 From: Mike Date: Thu, 27 Jul 2023 15:15:09 -0400 Subject: [PATCH 2/4] remove succeeded attribute of Proposals; expand proposalExecuted testing --- schema.graphql | 1 - src/grant-fund.ts | 19 +++++-------------- src/utils/grants/proposal.ts | 1 - tests/grant-fund.test.ts | 24 +++++++++++++++++++++++- 4 files changed, 28 insertions(+), 17 deletions(-) diff --git a/schema.graphql b/schema.graphql index b7fa6df..f265128 100644 --- a/schema.graphql +++ b/schema.graphql @@ -808,7 +808,6 @@ type Proposal @entity { description: String! # proposal description hashed as part of proposalId distribution: DistributionPeriod # distributionPeriod in which the proposal was submitted if Standard, null otherwise executed: Boolean! # bool - successful: Boolean! # bool screeningVotesReceived: BigDecimal! # uint256 fundingVotesReceived: BigDecimal! # uint256 totalTokensRequested: BigDecimal! # uint256 diff --git a/src/grant-fund.ts b/src/grant-fund.ts index d40d561..382a3ff 100644 --- a/src/grant-fund.ts +++ b/src/grant-fund.ts @@ -162,14 +162,8 @@ export function handleProposalCreated(event: ProposalCreatedEvent): void { // create Proposal entity const proposalId = bigIntToBytes(event.params.proposalId) - const proposal = new Proposal(proposalId) as Proposal + const proposal = loadOrCreateProposal(proposalId) proposal.description = event.params.description - proposal.distribution = Bytes.empty() - proposal.executed = false - proposal.successful = false - proposal.screeningVotesReceived = ZERO_BD - proposal.fundingVotesReceived = ZERO_BD - proposal.params = [] let totalTokensRequested = ZERO_BD @@ -224,14 +218,11 @@ export function handleProposalExecuted(event: ProposalExecutedEvent): void { proposalExecuted.transactionHash = event.transaction.hash // update proposal entity - const proposal = Proposal.load(bigIntToBytes(event.params.proposalId)) as Proposal - if (proposal != null) { - proposal.executed = true - proposal.successful = true + const proposal = loadOrCreateProposal(bigIntToBytes(event.params.proposalId)) + proposal.executed = true - // save entities to the store - proposal.save() - } + // save entities to the store + proposal.save() proposalExecuted.save() } diff --git a/src/utils/grants/proposal.ts b/src/utils/grants/proposal.ts index 72c3d2c..0dbdbcd 100644 --- a/src/utils/grants/proposal.ts +++ b/src/utils/grants/proposal.ts @@ -19,7 +19,6 @@ export function loadOrCreateProposal(proposalId: Bytes): Proposal { proposal.description = "" proposal.distribution = Bytes.empty() proposal.executed = false - proposal.successful = false proposal.screeningVotesReceived = ZERO_BD proposal.fundingVotesReceived = ZERO_BD proposal.totalTokensRequested = ZERO_BD diff --git a/tests/grant-fund.test.ts b/tests/grant-fund.test.ts index 210d7a7..aa3a95c 100644 --- a/tests/grant-fund.test.ts +++ b/tests/grant-fund.test.ts @@ -349,6 +349,28 @@ describe("Grant Fund assertions", () => { // check ProposalExecuted attributes assert.entityCount("ProposalExecuted", 1); + + assert.fieldEquals( + "Proposal", + `${bigIntToBytes(proposalId).toHexString()}`, + "totalTokensRequested", + `${BigInt.fromI32(2)}` + ); + + assert.fieldEquals( + "Proposal", + `${bigIntToBytes(proposalId).toHexString()}`, + "executed", + `${true}` + ); + + assert.fieldEquals( + "ProposalExecuted", + `0xa16081f360e3847006db660bae1c6d1b2e17ec2a01000000`, + "proposalId", + `${proposalId}` + ); + }); test("ScreeningVote", () => { @@ -762,7 +784,7 @@ describe("Grant Fund assertions", () => { assert.entityCount("DistributionPeriod", 1); assert.entityCount("FundedSlate", 1); - logStore(); + // logStore(); // check FundedSlate attributes // assert.fieldEquals( From 8f4f57077bb5c512ec33229e5618e952ce3e2525 Mon Sep 17 00:00:00 2001 From: Mike Date: Thu, 27 Jul 2023 15:26:35 -0400 Subject: [PATCH 3/4] record fundingVotesReceived on proposal --- src/grant-fund.ts | 3 +++ src/utils/grants/voter.ts | 2 -- tests/grant-fund.test.ts | 41 ++++++++++++++++++++++++++++----------- 3 files changed, 33 insertions(+), 13 deletions(-) diff --git a/src/grant-fund.ts b/src/grant-fund.ts index 382a3ff..7089a13 100644 --- a/src/grant-fund.ts +++ b/src/grant-fund.ts @@ -340,6 +340,9 @@ export function handleVoteCast(event: VoteCastEvent): void { distributionPeriodVote.estimatedRemainingFundingStageVotingPowerForCalculatingRewards = getFundingStageVotingPower(event.address, bytesToBigInt(distributionId), Address.fromBytes(voter.id)) } + // record votes cast on the Proposal entity + proposal.fundingVotesReceived = proposal.fundingVotesReceived.plus(fundingVote.votesCast) + // save fundingVote to the store fundingVote.save() } diff --git a/src/utils/grants/voter.ts b/src/utils/grants/voter.ts index 8841d5a..2070242 100644 --- a/src/utils/grants/voter.ts +++ b/src/utils/grants/voter.ts @@ -31,8 +31,6 @@ export function getFundingVotesByProposalId(distributionPeriodVote: Distribution 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)) { diff --git a/tests/grant-fund.test.ts b/tests/grant-fund.test.ts index aa3a95c..fc7e117 100644 --- a/tests/grant-fund.test.ts +++ b/tests/grant-fund.test.ts @@ -350,6 +350,13 @@ describe("Grant Fund assertions", () => { // check ProposalExecuted attributes assert.entityCount("ProposalExecuted", 1); + assert.fieldEquals( + "Proposal", + `${bigIntToBytes(proposalId).toHexString()}`, + "description", + `${description}` + ); + assert.fieldEquals( "Proposal", `${bigIntToBytes(proposalId).toHexString()}`, @@ -598,8 +605,24 @@ describe("Grant Fund assertions", () => { const distributionPeriodVoteId = getDistributionPeriodVoteId(bigIntToBytes(distributionId), addressToBytes(voter)); const fundingVoteId = getFundingVoteId(bigIntToBytes(proposalId), addressToBytes(voter), BigInt.fromI32(2)); const screeningVoteId = getScreeningVoteId(bigIntToBytes(proposalId), addressToBytes(voter), BigInt.fromI32(1)); + const expectedProposalId = bigIntToBytes(proposalId).toHexString(); const expectedDistributionId = bigIntToBytes(distributionId).toHexString(); const expectedVotingPowerUsed = wadToDecimal(votesCast.times(votesCast)); + const expectedScreeningVotesReceived = wadToDecimal(votesCast.times(BigInt.fromI32(-1))); + + assert.fieldEquals( + "Proposal", + `${expectedProposalId}`, + "screeningVotesReceived", + `${expectedScreeningVotesReceived}` + ); + + assert.fieldEquals( + "Proposal", + `${expectedProposalId}`, + "fundingVotesReceived", + `${wadToDecimal(votesCast)}` + ); assert.fieldEquals( "DistributionPeriodVote", @@ -613,7 +636,7 @@ describe("Grant Fund assertions", () => { "ScreeningVote", `${screeningVoteId.toHexString()}`, "votesCast", - `${wadToDecimal(votesCast.times(BigInt.fromI32(-1)))}` + `${expectedScreeningVotesReceived}` ); // check DistributionPeriodVote attributes @@ -761,8 +784,6 @@ describe("Grant Fund assertions", () => { /********************/ const fundedProposalSlate = [proposalId] - - // TODO: need to determine how to best hash the proposalId array const fundedSlateHash = Bytes.fromHexString("0x000010") mockGetFundedProposalSlate(grantFundAddress, fundedSlateHash, fundedProposalSlate); @@ -784,15 +805,13 @@ describe("Grant Fund assertions", () => { assert.entityCount("DistributionPeriod", 1); assert.entityCount("FundedSlate", 1); - // logStore(); - // check FundedSlate attributes - // assert.fieldEquals( - // "FundedSlate", - // `${fundedSlateHash.toHexString()}`, - // "totalFundingVotesReceived", - // `${votesCast}` - // ); + assert.fieldEquals( + "FundedSlate", + `${fundedSlateHash.toHexString()}`, + "totalFundingVotesReceived", + `${wadToDecimal(votesCast)}` + ); assert.fieldEquals( "FundedSlate", `${fundedSlateHash.toHexString()}`, From 1d24fc9ecd1e87fc6bfcce973618aefe325cd185 Mon Sep 17 00:00:00 2001 From: Mike Date: Thu, 27 Jul 2023 15:32:25 -0400 Subject: [PATCH 4/4] add assertion --- tests/grant-fund.test.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/grant-fund.test.ts b/tests/grant-fund.test.ts index fc7e117..cf34b54 100644 --- a/tests/grant-fund.test.ts +++ b/tests/grant-fund.test.ts @@ -171,6 +171,12 @@ describe("Grant Fund assertions", () => { "treasury", `${wadToDecimal(treasuryBalance)}` ); + assert.fieldEquals( + "FundTreasury", + `0xa16081f360e3847006db660bae1c6d1b2e17ec2a01000000`, + "treasuryBalance", + `${treasuryBalance}` + ); }); test("ProposalCreated", () => {