Skip to content

Commit 9f89478

Browse files
committed
feat(web): initial-subgraph-setup
1 parent 47eab5f commit 9f89478

File tree

10 files changed

+1021
-1
lines changed

10 files changed

+1021
-1
lines changed

subgraph/package.json

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,15 @@
33
"version": "2.0.0",
44
"license": "MIT",
55
"scripts": {
6-
"clean": "graph clean",
6+
"update:arbitrum-sepolia-devnet": "./scripts/update.sh arbitrumSepoliaDevnet arbitrum-sepolia",
7+
"update:arbitrum-sepolia": "./scripts/update.sh arbitrumSepolia arbitrum-sepolia",
8+
"update:arbitrum": "./scripts/update.sh arbitrum arbitrum",
9+
"update:local": "./scripts/update.sh localhost mainnet",
710
"codegen": "graph codegen",
811
"build": "graph build",
12+
"test": "graph test",
13+
"clean": "graph clean && rm subgraph.yaml.bak.*",
14+
" ": "----------------------------------------------------------------------------------------",
915
"create-local": "graph create --node http://localhost:8020/ kleros/curate-v2-local",
1016
"remove-local": "graph remove --node http://localhost:8020/ kleros/curate-v2-local",
1117
"deploy-local": "graph deploy --node http://localhost:8020/ --ipfs http://localhost:5001 kleros/curate-v2-local --version-label v$(date +%s)",

subgraph/schema.graphql

Lines changed: 247 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,247 @@
1+
enum Status {
2+
"The item is not registered on the TCR and there are no pending requests."
3+
Absent
4+
"The item is registered and there are no pending requests."
5+
Registered
6+
"The item is not registered on the TCR, but there is a pending registration request."
7+
RegistrationRequested
8+
"The item is registered on the TCR, but there is a pending removal request. These are sometimes also called removal requests."
9+
ClearingRequested
10+
}
11+
12+
enum Ruling {
13+
"The arbitrator did not rule or refused to rule."
14+
None
15+
"The arbitrator ruled in favor of the requester."
16+
Accept
17+
"The arbitrator in favor of the challenger."
18+
Reject
19+
}
20+
21+
type EvidenceGroup @entity {
22+
"evidenceGroupId@tcrAddress"
23+
id: ID!
24+
"Evidences posted to this evidenceGroupId"
25+
evidences: [Evidence!]! @derivedFrom(field: "evidenceGroup")
26+
"Number of evidences posted in this group"
27+
numberOfEvidence: BigInt!
28+
}
29+
30+
type Evidence @entity {
31+
"evidenceGroupId@tcrAddress-number"
32+
id: ID!
33+
"The arbitrator's address."
34+
arbitrator: Bytes!
35+
"The evidence group"
36+
evidenceGroup: EvidenceGroup!
37+
"The address of the party that sent this piece of evidence."
38+
party: Bytes!
39+
"The URI of the evidence file."
40+
URI: String!
41+
"This is the <number>th evidence submitted (starting at 0) for <request>."
42+
number: BigInt!
43+
"When was this evidence posted"
44+
timestamp: BigInt!
45+
"Tx hash of the evidence submission"
46+
txHash: Bytes!
47+
"Name of the evidence"
48+
name: String
49+
"Title of the evidence"
50+
title: String
51+
"Description of the evidence"
52+
description: String
53+
"URI of the attached file"
54+
fileURI: String
55+
"File extension of the attached file"
56+
fileTypeExtension: String
57+
}
58+
59+
type Arbitrator @entity {
60+
"The address of the arbitrator"
61+
id: ID!
62+
}
63+
64+
type Registry @entity {
65+
"The registry address"
66+
id: ID!
67+
"The items submitted to this list"
68+
items: [Item!]! @derivedFrom(field: "registry")
69+
"The requests submitted to this list"
70+
requests: [Request!]! @derivedFrom(field: "registry")
71+
"The total number of items in absent state."
72+
numberOfAbsent: BigInt!
73+
"The total number of items in registered state."
74+
numberOfRegistered: BigInt!
75+
"The total number of items in the registration requested state."
76+
numberOfRegistrationRequested: BigInt!
77+
numberOfClearingRequested: BigInt!
78+
"The total number of items in the challenged registration state."
79+
numberOfChallengedRegistrations: BigInt!
80+
"The total number of items in the challenged removal state."
81+
numberOfChallengedClearing: BigInt!
82+
"Connected TCR. Can be the 0 address. In practice, will never be null."
83+
connectedTCR: Bytes
84+
}
85+
86+
type Item @entity {
87+
"The id of the item in the subgraph entity. Format: <itemID>@<listaddress_lowercase>"
88+
id: ID!
89+
"The ID of the item in the registry. Also the keccak256 hash of the data."
90+
itemID: Bytes!
91+
"The data describing the item."
92+
data: String!
93+
"The parsed data describing the item."
94+
props: [ItemProp!]! @derivedFrom(field: "item")
95+
"First indexable value of the json file."
96+
key0: String
97+
"Second indexable value of the json file."
98+
key1: String
99+
"Third indexable value of the json file."
100+
key2: String
101+
"Fourth indexable value of the json file."
102+
key3: String
103+
"Fifth indexable value of the json file."
104+
key4: String
105+
"The item identifiers combined as a single string."
106+
keywords: String
107+
"The current status of the item."
108+
status: Status!
109+
"List of status change requests made for the item in the form requests[requestID]."
110+
requests: [Request!]! @derivedFrom(field: "item")
111+
"The total number of requests for this item."
112+
numberOfRequests: BigInt!
113+
"The registry where this item was submitted."
114+
registry: Registry!
115+
"The address of the registry this item was submitted. Redundant with registry field to allow use in conditionals."
116+
registryAddress: Bytes!
117+
"Time when the latest request was made."
118+
latestRequestSubmissionTime: BigInt!
119+
"The time the latest request was resolved."
120+
latestRequestResolutionTime: BigInt!
121+
"Whether the item is currently disputed."
122+
disputed: Boolean!
123+
"The account that made the latest request to the item."
124+
latestRequester: Bytes!
125+
"The account that challenged the latest request, if any."
126+
latestChallenger: Bytes!
127+
}
128+
129+
type _Schema_
130+
@fulltext(
131+
name: "itemSearch"
132+
language: en
133+
algorithm: rank
134+
include: [{ entity: "Item", fields: [{ name: "keywords" }] }]
135+
)
136+
137+
type ItemProp @entity {
138+
id: ID!
139+
type: String!
140+
label: String!
141+
description: String!
142+
isIdentifier: Boolean!
143+
value: String
144+
item: Item!
145+
}
146+
147+
type Request @entity {
148+
"The item ID (which is the keccak256 hash of its data)."
149+
id: ID!
150+
"True if a dispute was raised."
151+
disputed: Boolean!
152+
"ID of the dispute, if any."
153+
disputeID: BigInt!
154+
"Time when the request was made. Used to track when the challenge period ends."
155+
submissionTime: BigInt!
156+
"True if the request was executed and/or any raised disputes were resolved."
157+
resolved: Boolean!
158+
"The address of the party that made a request"
159+
requester: Bytes!
160+
"The address of the party that challenged the request"
161+
challenger: Bytes!
162+
"The arbitrator trusted to solve disputes for this request."
163+
arbitrator: Bytes!
164+
"The extra data for the trusted arbitrator of this request."
165+
arbitratorExtraData: Bytes!
166+
"The deposit that would be awarded to the challenger if challenge is successful"
167+
deposit: BigInt!
168+
"The outcome of the dispute, if any. Note that unsuccessful appeal fundings can invert the arbitrator ruling (so this may differ from the ruling given by the arbitrator)."
169+
disputeOutcome: Ruling!
170+
"Tracks each round of a dispute in the form rounds[roundID]."
171+
rounds: [Round!]! @derivedFrom(field: "request")
172+
"The evidence group for this request."
173+
evidenceGroup: EvidenceGroup!
174+
"The total number of rounds on this request."
175+
numberOfRounds: BigInt!
176+
"Whether it was requested to add or remove the item to/from the list."
177+
requestType: Status!
178+
"The item this request belongs to."
179+
item: Item!
180+
"The registry where this request was submitted."
181+
registry: Registry!
182+
"The address of the registry this item was submitted. Redundant with registry field to allow use in conditionals."
183+
registryAddress: Bytes!
184+
"The time the request was resolved."
185+
resolutionTime: BigInt!
186+
"Only set if the request was settled by a dispute. Used by the twitter bot"
187+
finalRuling: BigInt
188+
"The hash of the transaction that created this request."
189+
creationTx: Bytes!
190+
"The hash of the transaction that solved this request."
191+
resolutionTx: Bytes
192+
}
193+
194+
type Round @entity {
195+
id: ID!
196+
"The total amount of appeal fees contributed to the requester in this round."
197+
amountPaidRequester: BigInt!
198+
"The total amount of appeal fees contributed to the challenger in this round."
199+
amountPaidChallenger: BigInt!
200+
"Whether the requester is fully funded."
201+
hasPaidRequester: Boolean!
202+
"Whether the challenger is fully funded."
203+
hasPaidChallenger: Boolean!
204+
"When was the last contribution for requester (hack for curate bot)"
205+
lastFundedRequester: BigInt!
206+
"When was the last contribution for challenger (hack for curate bot)"
207+
lastFundedChallenger: BigInt!
208+
"Sum of reimbursable fees and stake rewards available to the parties that made contributions to the side that ultimately wins a dispute."
209+
feeRewards: BigInt!
210+
"The request to which this round belongs."
211+
request: Request!
212+
"The time the appeal period starts, if in the appeal period."
213+
appealPeriodStart: BigInt!
214+
"The time the appeal period ends, if in the appeal period."
215+
appealPeriodEnd: BigInt!
216+
"The time the round received the ruling."
217+
rulingTime: BigInt!
218+
"The ruling given by the arbitrator."
219+
ruling: Ruling!
220+
"The tx hash of the moment appealing became possible"
221+
txHashAppealPossible: Bytes
222+
"The tx hash of the moment the round was appealed"
223+
txHashAppealDecision: Bytes
224+
"The moment the round was created."
225+
creationTime: BigInt!
226+
"The contributions made to this round."
227+
contributions: [Contribution!]! @derivedFrom(field: "round")
228+
"The number of contributions made to this round"
229+
numberOfContributions: BigInt!
230+
"Whether this round was appealed."
231+
appealed: Boolean!
232+
"When this round was appealed, if it was appealed"
233+
appealedAt: BigInt
234+
}
235+
236+
type Contribution @entity {
237+
"The contribution ID."
238+
id: ID!
239+
"The round the contribution was made to."
240+
round: Round!
241+
"The address that made the contribution."
242+
contributor: Bytes!
243+
"Whether there are any withdrawable contributions."
244+
withdrawable: Boolean!
245+
"To which side the contribution was made."
246+
side: BigInt!
247+
}

subgraph/scripts/update.sh

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
#!/usr/bin/env bash
2+
3+
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
4+
5+
function update() #hardhatNetwork #graphNetwork #dataSourceIndex #contract
6+
{
7+
local hardhatNetwork="$1"
8+
local graphNetwork="$2"
9+
local dataSourceIndex="$3"
10+
local contract="$4"
11+
local artifact="$SCRIPT_DIR/../../contracts/deployments/$hardhatNetwork/$contract.json"
12+
13+
# Set the address
14+
address=$(cat "$artifact" | jq '.address')
15+
yq -i ".dataSources[$dataSourceIndex].source.address=$address" "$SCRIPT_DIR"/../subgraph.yaml
16+
17+
# Set the start block
18+
blockNumber="$(cat "$artifact" | jq '.receipt.blockNumber')"
19+
yq -i ".dataSources[$dataSourceIndex].source.startBlock=$blockNumber" "$SCRIPT_DIR"/../subgraph.yaml
20+
21+
# Set the Graph network
22+
graphNetwork=$graphNetwork yq -i ".dataSources[$dataSourceIndex].network=env(graphNetwork)" "$SCRIPT_DIR"/../subgraph.yaml
23+
24+
# Set the ABIs path for this Hardhat network
25+
abiIndex=0
26+
for f in $(yq e .dataSources[$dataSourceIndex].mapping.abis[].file subgraph.yaml -o json -I 0 | jq -sr '.[]')
27+
do
28+
f2=$(echo $f | sed "s|\(.*\/deployments\/\).*\/|\1$hardhatNetwork\/|")
29+
yq -i ".dataSources[$dataSourceIndex].mapping.abis[$abiIndex].file=\"$f2\"" "$SCRIPT_DIR"/../subgraph.yaml
30+
(( ++abiIndex ))
31+
done
32+
}
33+
34+
# as per ../contracts/hardhat.config.js
35+
hardhatNetwork=${1:-arbitrumSepolia}
36+
37+
# as per https://thegraph.com/docs/en/developing/supported-networks/
38+
graphNetwork=${2:-arbitrum\-sepolia}
39+
i=0
40+
41+
# backup
42+
cp "$SCRIPT_DIR"/../subgraph.yaml "$SCRIPT_DIR"/../subgraph.yaml.bak.$(date +%s)
43+
44+
for contract in $(yq .dataSources[].name "$SCRIPT_DIR"/../subgraph.yaml)
45+
do
46+
update $hardhatNetwork $graphNetwork $i $contract
47+
(( ++i ))
48+
done

subgraph/src/Counters.ts

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import { Address, log } from "@graphprotocol/graph-ts";
2+
import { Registry } from "../generated/schema";
3+
import {
4+
ABSENT_CODE,
5+
CHALLENGED_CLEARING_REQUEST_CODE,
6+
CHALLENGED_REGISTRATION_REQUEST_CODE,
7+
CLEARING_REQUESTED_CODE,
8+
ONE,
9+
REGISTERED_CODE,
10+
REGISTRATION_REQUESTED_CODE,
11+
} from "./utils";
12+
13+
/**
14+
* Decrements and increments registry counters based on item status change.
15+
*
16+
* The user should ensure that this function is called once and only once for
17+
* each status update. What handlers were called before and which will be called
18+
* after the one this is being called on? Do they call updateCounters?
19+
* @param previousStatus The previous extended status of the item.
20+
* @param newStatus The new extended status of the item.
21+
* @param registry The registry to which update the counters.
22+
*/
23+
export function updateCounters(previousStatus: number, newStatus: number, registryAddress: Address): void {
24+
let registry = Registry.load(registryAddress.toHexString());
25+
if (!registry) {
26+
log.error(`Registry at {} not found.`, [registryAddress.toHexString()]);
27+
return;
28+
}
29+
30+
if (previousStatus == ABSENT_CODE) {
31+
registry.numberOfAbsent = registry.numberOfAbsent.minus(ONE);
32+
} else if (previousStatus == REGISTERED_CODE) {
33+
registry.numberOfRegistered = registry.numberOfRegistered.minus(ONE);
34+
} else if (previousStatus == REGISTRATION_REQUESTED_CODE) {
35+
registry.numberOfRegistrationRequested = registry.numberOfRegistrationRequested.minus(ONE);
36+
} else if (previousStatus == CLEARING_REQUESTED_CODE) {
37+
registry.numberOfClearingRequested = registry.numberOfClearingRequested.minus(ONE);
38+
} else if (previousStatus == CHALLENGED_REGISTRATION_REQUEST_CODE) {
39+
registry.numberOfChallengedRegistrations = registry.numberOfChallengedRegistrations.minus(ONE);
40+
} else if (previousStatus == CHALLENGED_CLEARING_REQUEST_CODE) {
41+
registry.numberOfChallengedClearing = registry.numberOfChallengedClearing.minus(ONE);
42+
}
43+
44+
if (newStatus == ABSENT_CODE) {
45+
registry.numberOfAbsent = registry.numberOfAbsent.plus(ONE);
46+
} else if (newStatus == REGISTERED_CODE) {
47+
registry.numberOfRegistered = registry.numberOfRegistered.plus(ONE);
48+
} else if (newStatus == REGISTRATION_REQUESTED_CODE) {
49+
registry.numberOfRegistrationRequested = registry.numberOfRegistrationRequested.plus(ONE);
50+
} else if (newStatus == CLEARING_REQUESTED_CODE) {
51+
registry.numberOfClearingRequested = registry.numberOfClearingRequested.plus(ONE);
52+
} else if (newStatus == CHALLENGED_REGISTRATION_REQUEST_CODE) {
53+
registry.numberOfChallengedRegistrations = registry.numberOfChallengedRegistrations.plus(ONE);
54+
} else if (newStatus == CHALLENGED_CLEARING_REQUEST_CODE) {
55+
registry.numberOfChallengedClearing = registry.numberOfChallengedClearing.plus(ONE);
56+
}
57+
58+
registry.save();
59+
}

0 commit comments

Comments
 (0)