Skip to content

Commit

Permalink
Subgraph: add Governance (#563)
Browse files Browse the repository at this point in the history
  • Loading branch information
bpierre authored Nov 5, 2024
1 parent 79f4344 commit 506e987
Show file tree
Hide file tree
Showing 9 changed files with 272 additions and 12 deletions.
6 changes: 6 additions & 0 deletions subgraph/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,9 @@ cd subgraph
# subgraph.yaml file (after confirmation).
./deploy-subgraph local --version v1 --create
```

## Contracts addresses

The addresses and networks of the `subgraph.yaml` file are generated from the `networks.json` file.

Addresses of the zapper and initial initiatives should be updated in the `addresses.ts` file.
1 change: 1 addition & 0 deletions subgraph/abi/Governance.json

Large diffs are not rendered by default.

13 changes: 13 additions & 0 deletions subgraph/addresses.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Sepolia
export let leverageZappers = [
"0x709b1d298f202e0b0a72fd92769d7f81e9dc5911",
"0x75e7f939aec5ca455e4e32cd4b6567b70323bd4a",
"0x78b88fc6043810c4d3f0e6af84310620f91cf721",
];
export let governanceDeploymentInitiatives = [
"0x510f6888fd475A7b81C3652696eb807374d65400", // UniV4Donations
];

// Mainnet
// export let leverageZappers = [];
// export let governanceDeploymentInitiatives = [];
5 changes: 0 additions & 5 deletions subgraph/leverage-zappers.ts

This file was deleted.

17 changes: 14 additions & 3 deletions subgraph/networks.json
Original file line number Diff line number Diff line change
@@ -1,19 +1,30 @@
{
"local": {
"BoldToken": {
"address": "0x21a6370b245c05fade18c601f5efa23e1a3c9833"
"address": "0x324331e016663950856622e1d1a062cfe4df3006"
},
"Governance": {
"address": "0x79fdb65c20e75f8961d54f6714d2fcc4c30d1073"
}
},
"mainnet": {
"BoldToken": {
"address": "0x0000000000000000000000000000000000000000",
"startBlock": 0
},
"Governance": {
"address": "0x0000000000000000000000000000000000000000",
"startBlock": 0
}
},
"sepolia": {
"BoldToken": {
"address": "0x21a6370b245c05fade18c601f5efa23e1a3c9833",
"startBlock": 6684588
"address": "0x31764dcd10fff1514db117e3db84b48b30db5b43",
"startBlock": 6889790
},
"Governance": {
"address": "0x79fdb65c20e75f8961d54f6714d2fcc4c30d1073",
"startBlock": 6959313
}
}
}
38 changes: 38 additions & 0 deletions subgraph/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -109,3 +109,41 @@ type InterestBatch @entity {
annualManagementFee: BigInt!
troves: [Trove!]! @derivedFrom(field: "interestBatch")
}

type GovernanceUser @entity {
id: ID! # "userAddress", e.g. "0x0000000000000000000000000000000000000000"
allocatedLQTY: BigInt!
averageStakingTimestamp: BigInt!
allocations: [GovernanceAllocation!]! @derivedFrom(field: "user")
}

# Allocation of a user to a given initiative.
type GovernanceAllocation @entity {
id: ID! # "initiativeAddress:userAddress", e.g. "0x0000000000000000000000000000000000000000:0x0000000000000000000000000000000000000000"
user: GovernanceUser!
initiative: GovernanceInitiative!
voteLQTY: BigInt!
vetoLQTY: BigInt!
atEpoch: Int!
}

type GovernanceInitiative @entity {
id: ID! # "initiativeAddress", e.g. "0x0000000000000000000000000000000000000000"
lastClaimEpoch: Int
lastVoteSnapshotEpoch: Int
lastVoteSnapshotVotes: BigInt
registeredAt: BigInt!
registeredAtEpoch: Int!
registrant: Bytes!
totalBoldClaimed: BigInt!
totalVetos: BigInt!
totalVotes: BigInt!
unregisteredAt: BigInt
unregisteredAtEpoch: Int
}

type GovernanceStats @entity {
id: ID!
totalLQTYStaked: BigInt!
totalInitiatives: Int!
}
159 changes: 159 additions & 0 deletions subgraph/src/Governance.mapping.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
import { Address, BigInt, ethereum } from "@graphprotocol/graph-ts";
import { governanceDeploymentInitiatives } from "../addresses";
import {
AllocateLQTY as AllocateLQTYEvent,
ClaimForInitiative as ClaimForInitiativeEvent,
DepositLQTY as DepositLQTYEvent,
Governance as GovernanceContract,
RegisterInitiative as RegisterInitiativeEvent,
SnapshotVotesForInitiative as SnapshotVotesForInitiativeEvent,
UnregisterInitiative as UnregisterInitiativeEvent,
WithdrawLQTY as WithdrawLQTYEvent,
} from "../generated/Governance/Governance";
import { GovernanceAllocation, GovernanceInitiative, GovernanceStats, GovernanceUser } from "../generated/schema";

function initialize(block: ethereum.Block): void {
// Initial governance initiatives passed to the constructor (no event emitted)
for (let i = 0; i < governanceDeploymentInitiatives.length; i++) {
let initiative = new GovernanceInitiative(governanceDeploymentInitiatives[i]);
initiative.registeredAt = block.timestamp;
initiative.registeredAtEpoch = 1;
initiative.registrant = Address.zero();
initiative.totalBoldClaimed = BigInt.fromI32(0);
initiative.totalVetos = BigInt.fromI32(0);
initiative.totalVotes = BigInt.fromI32(0);
initiative.save();
}

// Create initial stats
let stats = new GovernanceStats("stats");
stats.totalLQTYStaked = BigInt.fromI32(0);
stats.totalInitiatives = 0;
stats.save();
}

export function handleBlock(block: ethereum.Block): void {
if (GovernanceStats.load("stats") === null) {
initialize(block);
}
}

export function handleRegisterInitiative(event: RegisterInitiativeEvent): void {
let initiative = new GovernanceInitiative(event.params.initiative.toHex());
initiative.registeredAt = event.block.timestamp;
initiative.registeredAtEpoch = event.params.atEpoch;
initiative.registrant = event.params.registrant;
initiative.totalBoldClaimed = BigInt.fromI32(0);
initiative.totalVetos = BigInt.fromI32(0);
initiative.totalVotes = BigInt.fromI32(0);
initiative.save();
}

export function handleUnregisterInitiative(event: UnregisterInitiativeEvent): void {
let initiative = GovernanceInitiative.load(event.params.initiative.toHex());
if (initiative) {
initiative.unregisteredAt = event.block.timestamp;
initiative.unregisteredAtEpoch = event.params.atEpoch;
initiative.save();
}
}

export function handleSnapshotVotesForInitiative(event: SnapshotVotesForInitiativeEvent): void {
let initiative = GovernanceInitiative.load(event.params.initiative.toHex());
if (initiative) {
initiative.lastVoteSnapshotEpoch = event.params.forEpoch;
initiative.lastVoteSnapshotVotes = event.params.votes;
initiative.save();
}
}

export function handleDepositLQTY(event: DepositLQTYEvent): void {
let user = GovernanceUser.load(event.params.user.toHex());
if (user === null) {
user = new GovernanceUser(event.params.user.toHex());
}

let governance = GovernanceContract.bind(event.address);
let userState = governance.userStates(event.params.user);

user.allocatedLQTY = userState.getAllocatedLQTY();
user.averageStakingTimestamp = userState.getAverageStakingTimestamp();
user.save();

let stats = getStats();
stats.totalLQTYStaked = stats.totalLQTYStaked.plus(event.params.depositedLQTY);
stats.save();
}

export function handleWithdrawLQTY(event: WithdrawLQTYEvent): void {
let user = GovernanceUser.load(event.params.user.toHex());
if (user === null) {
return;
}

let governance = GovernanceContract.bind(event.address);
let userState = governance.userStates(event.params.user);

user.allocatedLQTY = userState.getAllocatedLQTY();
user.averageStakingTimestamp = userState.getAverageStakingTimestamp();
user.save();

let stats = getStats();
stats.totalLQTYStaked = stats.totalLQTYStaked.minus(event.params.withdrawnLQTY);
stats.save();
}

export function handleAllocateLQTY(event: AllocateLQTYEvent): void {
let userId = event.params.user.toHex();
let initiativeId = event.params.initiative.toHex();
let allocationId = initiativeId + ":" + userId;

let user = GovernanceUser.load(userId);
let initiative = GovernanceInitiative.load(initiativeId);

if (!user || !initiative) {
return;
}

let allocation = GovernanceAllocation.load(allocationId);
if (allocation === null) {
allocation = new GovernanceAllocation(allocationId);
allocation.user = user.id;
allocation.initiative = initiative.id;
allocation.voteLQTY = BigInt.fromI32(0);
allocation.vetoLQTY = BigInt.fromI32(0);
}

// votes
allocation.voteLQTY = allocation.voteLQTY.plus(event.params.deltaVoteLQTY);
user.allocatedLQTY = user.allocatedLQTY.plus(event.params.deltaVoteLQTY);
initiative.totalVotes = initiative.totalVotes.plus(event.params.deltaVoteLQTY);

// vetos
allocation.vetoLQTY = allocation.vetoLQTY.plus(event.params.deltaVetoLQTY);
user.allocatedLQTY = user.allocatedLQTY.plus(event.params.deltaVetoLQTY);
initiative.totalVetos = initiative.totalVetos.plus(event.params.deltaVetoLQTY);

allocation.atEpoch = event.params.atEpoch;

allocation.save();
user.save();
initiative.save();
}

function getStats(): GovernanceStats {
let stats = GovernanceStats.load("stats");
if (stats === null) {
throw new Error("Stats entity not found");
}
return stats;
}

export function handleClaimForInitiative(event: ClaimForInitiativeEvent): void {
let initiative = GovernanceInitiative.load(event.params.initiative.toHex());
if (initiative) {
initiative.totalBoldClaimed = initiative.totalBoldClaimed.plus(event.params.bold);
initiative.lastClaimEpoch = event.params.forEpoch.toI32();
initiative.save();
}
}
4 changes: 2 additions & 2 deletions subgraph/src/TroveManager.mapping.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { Address, BigInt, ByteArray, crypto, dataSource, ethereum, log } from "@graphprotocol/graph-ts";
import { Address, BigInt, dataSource } from "@graphprotocol/graph-ts";
import { leverageZappers } from "../addresses";
import { BorrowerInfo, Collateral, InterestBatch, InterestRateBracket, Trove } from "../generated/schema";
import {
BatchUpdated as BatchUpdatedEvent,
TroveManager as TroveManagerContract,
TroveOperation as TroveOperationEvent,
} from "../generated/templates/TroveManager/TroveManager";
import { TroveNFT as TroveNFTContract } from "../generated/templates/TroveManager/TroveNFT";
import { leverageZappers } from "../leverage-zappers";

// see Operation enum in
// contracts/src/Interfaces/ITroveEvents.sol
Expand Down
41 changes: 39 additions & 2 deletions subgraph/subgraph.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ dataSources:
name: BoldToken
source:
abi: BoldToken
address: "0x21a6370b245c05fade18c601f5efa23e1a3c9833"
startBlock: 6684588
address: "0x31764dcd10fff1514db117e3db84b48b30db5b43"
startBlock: 6889790
mapping:
kind: ethereum/events
apiVersion: 0.0.9
Expand All @@ -32,6 +32,43 @@ dataSources:
handler: handleCollateralRegistryAddressChanged
file: ./src/BoldToken.mapping.ts
network: sepolia
- kind: ethereum/contract
name: Governance
source:
abi: Governance
address: "0x79fdb65c20e75f8961d54f6714d2fcc4c30d1073"
startBlock: 6959313
mapping:
kind: ethereum/events
apiVersion: 0.0.9
language: wasm/assemblyscript
entities:
- GovernanceAllocation
- GovernanceInitiative
- GovernanceStats
- GovernanceUser
abis:
- name: Governance
file: ./abi/Governance.json
eventHandlers:
- event: AllocateLQTY(address,address,int256,int256,uint16)
handler: handleAllocateLQTY
- event: ClaimForInitiative(address,uint256,uint256)
handler: handleClaimForInitiative
- event: DepositLQTY(address,uint256)
handler: handleDepositLQTY
- event: RegisterInitiative(address,address,uint16)
handler: handleRegisterInitiative
- event: SnapshotVotesForInitiative(address,uint240,uint16)
handler: handleSnapshotVotesForInitiative
- event: UnregisterInitiative(address,uint16)
handler: handleUnregisterInitiative
- event: WithdrawLQTY(address,uint256,uint256,uint256)
handler: handleWithdrawLQTY
blockHandlers:
- handler: handleBlock
file: ./src/Governance.mapping.ts
network: sepolia
templates:
- name: TroveManager
kind: ethereum/contract
Expand Down

0 comments on commit 506e987

Please sign in to comment.