Skip to content

Commit

Permalink
Add subgraph
Browse files Browse the repository at this point in the history
  • Loading branch information
sembrestels committed Sep 19, 2024
1 parent 648cc38 commit 56e2c48
Show file tree
Hide file tree
Showing 13 changed files with 343 additions and 1 deletion.
1 change: 1 addition & 0 deletions biome.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
},
"linter": {
"enabled": true,
"ignore": ["contracts/councilhaus-subgraph/generated/**"],
"rules": {
"recommended": true,
"suspicious": {
Expand Down
38 changes: 38 additions & 0 deletions contracts/councilhaus-subgraph/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Abis and subgraph.yaml
abis/
subgraph.yaml

# Graph CLI generated artifacts
build/
generated/

# Dependency directories
node_modules/
jspm_packages/

# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# Optional npm cache directory
.npm

# Optional eslint cache
.eslintcache

# dotenv environment variables file
.env

# Testing
coverage
coverage.json

# Typechain
typechain
typechain-types

# Hardhat files
cache
5 changes: 5 additions & 0 deletions contracts/councilhaus-subgraph/config/optimism.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"network": "optimism",
"address": "0x8e01ebaa4f4d660294e0d1301fe46ed96f4c44eb",
"startBlock": 125496765
}
50 changes: 50 additions & 0 deletions contracts/councilhaus-subgraph/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
version: "3"
services:
graph-node:
image: graphprotocol/graph-node
ports:
- "8000:8000"
- "8001:8001"
- "8020:8020"
- "8030:8030"
- "8040:8040"
depends_on:
- ipfs
- postgres
extra_hosts:
- host.docker.internal:host-gateway
environment:
postgres_host: postgres
postgres_user: graph-node
postgres_pass: let-me-in
postgres_db: graph-node
ipfs: "ipfs:5001"
ethereum: "mainnet:http://host.docker.internal:8545"
GRAPH_LOG: info
ipfs:
image: ipfs/kubo:v0.17.0
ports:
- "5001:5001"
volumes:
- ./data/ipfs:/data/ipfs
postgres:
image: postgres:14
ports:
- "5432:5432"
command:
[
"postgres",
"-cshared_preload_libraries=pg_stat_statements",
"-cmax_connections=200",
]
environment:
POSTGRES_USER: graph-node
POSTGRES_PASSWORD: let-me-in
POSTGRES_DB: graph-node
# FIXME: remove this env. var. which we shouldn't need. Introduced by
# <https://github.com/graphprotocol/graph-node/pull/3511>, maybe as a
# workaround for https://github.com/docker/for-mac/issues/6270?
PGDATA: "/var/lib/postgresql/data"
POSTGRES_INITDB_ARGS: "-E UTF8 --locale=C"
volumes:
- ./data/postgres:/var/lib/postgresql/data
8 changes: 8 additions & 0 deletions contracts/councilhaus-subgraph/networks.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"optimism": {
"CouncilFactory": {
"address": "0x8e01ebaa4f4d660294e0d1301fe46ed96f4c44eb",
"startBlock": 125496765
}
}
}
22 changes: 22 additions & 0 deletions contracts/councilhaus-subgraph/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"name": "councilhaus-subgraph",
"license": "AGPL-3.0-only",
"scripts": {
"codegen": "graph codegen",
"build": "graph build",
"deploy": "graph deploy --node https://api.studio.thegraph.com/deploy/ councilhaus-optimism",
"prepare:optimism": "mustache config/optimism.json subgraph.template.yaml > subgraph.yaml",
"create-local": "graph create --node http://localhost:8020/ councilhaus-optimism",
"remove-local": "graph remove --node http://localhost:8020/ councilhaus-optimism",
"deploy-local": "graph deploy --node http://localhost:8020/ --ipfs http://localhost:5001 councilhaus-optimism",
"test": "graph test"
},
"dependencies": {
"@graphprotocol/graph-cli": "0.82.0",
"@graphprotocol/graph-ts": "0.35.1"
},
"devDependencies": {
"matchstick-as": "0.5.0",
"mustache": "^4.0.1"
}
}
35 changes: 35 additions & 0 deletions contracts/councilhaus-subgraph/schema.graphql
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
type Council @entity {
id: ID!
councilName: String!
councilSymbol: String!
distributionToken: Bytes!
councilMembers: [CouncilMember!]! @derivedFrom(field: "council")
grantees: [Grantee!]! @derivedFrom(field: "council")
allocations: [Allocation!]! @derivedFrom(field: "council")
createdAt: BigInt!
}

type CouncilMember @entity {
id: ID!
account: Bytes!
votingPower: BigInt!
council: Council!
enabled: Boolean!
}

type Grantee @entity {
id: ID!
name: String!
account: Bytes!
council: Council!
enabled: Boolean!
}

type Allocation @entity {
id: ID!
council: Council!
councilMember: CouncilMember!
grantees: [Grantee!]!
amounts: [BigInt!]!
allocatedAt: BigInt!
}
20 changes: 20 additions & 0 deletions contracts/councilhaus-subgraph/src/council-factory.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import type { CouncilCreated as CouncilCreatedEvent } from "../generated/CouncilFactory/CouncilFactory";
import { Council } from "../generated/schema";
import { Council as CouncilTemplate } from "../generated/templates";
import { Council as CouncilContract } from "../generated/templates/Council/Council";

export function handleCouncilCreated(event: CouncilCreatedEvent): void {
const councilAddress = event.params.council;
const entity = new Council(event.params.council.toHex());

const councilContract = CouncilContract.bind(councilAddress);

entity.councilName = councilContract.name();
entity.councilSymbol = councilContract.symbol();
entity.distributionToken = councilContract.distributionToken();
entity.createdAt = event.block.timestamp;

entity.save();

CouncilTemplate.create(councilAddress);
}
89 changes: 89 additions & 0 deletions contracts/councilhaus-subgraph/src/council.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
// biome-ignore lint/suspicious/noShadowRestrictedNames: x
import { BigInt } from "@graphprotocol/graph-ts";
import { log } from "@graphprotocol/graph-ts";
import { Allocation, CouncilMember, Grantee } from "../generated/schema";
import type {
BudgetAllocated,
CouncilMemberAdded,
CouncilMemberRemoved,
GranteeAdded,
GranteeRemoved,
} from "../generated/templates/Council/Council";

// Handle council member added
export function handleCouncilMemberAdded(event: CouncilMemberAdded): void {
const councilMember = new CouncilMember(event.params.member.toHex());
councilMember.account = event.params.member;
councilMember.votingPower = event.params.votingPower;
councilMember.council = event.address.toHex(); // Linking to the council
councilMember.enabled = true;

councilMember.save();
}

// Handle council member removed
export function handleCouncilMemberRemoved(event: CouncilMemberRemoved): void {
const councilMemberId = event.params.member.toHex();
const councilMember = CouncilMember.load(councilMemberId);
if (councilMember) {
councilMember.votingPower = new BigInt(0);
councilMember.enabled = false;
councilMember.save();
} else {
log.warning("Council member not found, skipping removal", [
councilMemberId,
]);
}
}

// Handle grantee added
export function handleGranteeAdded(event: GranteeAdded): void {
const grantee = new Grantee(event.params.grantee.toHex());
grantee.name = event.params.name;
grantee.account = event.params.grantee;
grantee.council = event.address.toHex(); // Linking to the council
grantee.enabled = true;

grantee.save();
}

// Handle grantee removed
export function handleGranteeRemoved(event: GranteeRemoved): void {
const grantee = Grantee.load(event.params.grantee.toHex());
if (grantee) {
grantee.enabled = false;
grantee.save();
} else {
log.warning("Grantee not found, skipping removal", [
event.params.grantee.toHex(),
]);
}
}

// Handle budget allocated
export function handleBudgetAllocated(event: BudgetAllocated): void {
const allocation = new Allocation(
`${event.transaction.hash.toHex()}-${event.logIndex.toString()}`,
);

allocation.council = event.address.toHex();
allocation.councilMember = event.params.member.toHex();
allocation.allocatedAt = event.block.timestamp;
allocation.amounts = event.params.allocation.amounts;

// Check all grantees are valid
const grantees: string[] = [];
const accounts = event.params.allocation.accounts;
for (let i = 0; i < accounts.length; i++) {
const grantee = Grantee.load(accounts[i].toHex());
if (!grantee) {
log.warning("Not all grantees found, skipping allocation", [
accounts[i].toHex(),
]);
return;
}
grantees.push(grantee.id);
}
allocation.grantees = grantees;
allocation.save();
}
62 changes: 62 additions & 0 deletions contracts/councilhaus-subgraph/subgraph.template.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
specVersion: 1.0.0
description: Track councils, council members, grantees, and allocations created by the CouncilFactory.
repository: "https://github.com/BlossomLabs/councilhaus/tree/master/packages/councilhaus-subgraph"

indexerHints:
prune: auto

schema:
file: ./schema.graphql

dataSources:
- kind: ethereum
name: CouncilFactory
network: {{ network }}
source:
address: "{{ address }}"
abi: CouncilFactory
startBlock: {{ startBlock }}
mapping:
kind: ethereum/events
apiVersion: 0.0.6
language: wasm/assemblyscript
entities:
- Council
abis:
- name: CouncilFactory
file: ./abis/CouncilFactory.json
- name: Council
file: ./abis/Council.json
eventHandlers:
- event: CouncilCreated(address,address)
handler: handleCouncilCreated
file: ./src/council-factory.ts
templates:
- kind: ethereum/contract
name: Council
network: {{ network }}
source:
abi: Council
mapping:
kind: ethereum/events
apiVersion: 0.0.6
language: wasm/assemblyscript
entities:
- CouncilMember
- Grantee
- Budget
abis:
- name: Council
file: ./abis/Council.json
eventHandlers:
- event: CouncilMemberAdded(address,uint256)
handler: handleCouncilMemberAdded
- event: CouncilMemberRemoved(address)
handler: handleCouncilMemberRemoved
- event: GranteeAdded(string,address)
handler: handleGranteeAdded
- event: GranteeRemoved(address)
handler: handleGranteeRemoved
- event: BudgetAllocated(address,(address[],uint128[]))
handler: handleBudgetAllocated
file: ./src/council.ts
4 changes: 4 additions & 0 deletions contracts/councilhaus-subgraph/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"extends": "@graphprotocol/graph-ts/types/tsconfig.base.json",
"include": ["src", "tests"]
}
7 changes: 7 additions & 0 deletions contracts/councilhaus/hardhat.config.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { HardhatUserConfig } from "hardhat/config";
import "@nomicfoundation/hardhat-toolbox-viem";
import "hardhat-abi-exporter";

const config: HardhatUserConfig = {
solidity: {
Expand Down Expand Up @@ -36,6 +37,12 @@ const config: HardhatUserConfig = {
sourcify: {
enabled: false,
},
abiExporter: {
path: "../councilhaus-subgraph/abis",
runOnCompile: true,
clear: true,
flat: true,
},
};

export default config;
3 changes: 2 additions & 1 deletion contracts/councilhaus/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
"hardhat": "^2.22.7"
},
"dependencies": {
"@openzeppelin/contracts": "^5.0.2"
"@openzeppelin/contracts": "^5.0.2",
"hardhat-abi-exporter": "^2.10.1"
},
"scripts": {
"compile": "hardhat compile",
Expand Down

0 comments on commit 56e2c48

Please sign in to comment.