A comprehensive library for interacting with Reality.eth questions and disputes across multiple blockchains.
Reality Data Service provides React hooks and utilities for fetching, filtering, and displaying Reality.eth questions and their associated dispute data. The library supports multiple blockchains including Ethereum, Gnosis Chain, and Polygon.
npm install reality-data-service
A React hook for fetching and managing Reality.eth questions.
function useQuestions({
chain,
searchTerm,
filters
}: UseQuestionsConfig): UseQuestionsResult
Parameters:
-
chain
: The blockchain to fetch questions from (seeChain
type) -
searchTerm
(optional): String to filter questions by title/content -
filters
(optional): Object with additional filtering criteria
Returns:
-
questions
: Array of all fetched questions -
filteredQuestions
: Array of questions after filters are applied -
loading
: Boolean indicating if questions are being loaded -
error
: Error message if fetch failed, null otherwise -
hasMore
: Boolean indicating if more questions can be loaded -
loadMore
: Function to load more questions -
refresh
: Function to refresh the questions -
loadingState
: Object with detailed loading state information -
status
: Current fetch status ('idle', 'loading', 'success', 'error')
Example:
function QuestionsList() {
const {
questions,
loading,
error,
loadMore,
hasMore
} = useQuestions({
chain: SUPPORTED_CHAINS[0], // Ethereum
searchTerm: "climate"
});
if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error}</div>;
return (
<>
<ul>
{questions.map(q => (
<li key={q.id}>{q.title}</li>
))}
</ul>
{hasMore && <button onClick={loadMore}>Load More</button>}
</>
);
}
A React hook for fetching dispute data for a specific Reality.eth question.
function useDisputeData(question: Question): {
disputeId: string | null;
dispute: Dispute | undefined;
disputeLoading: boolean;
evidences: Evidence[];
metaEvidence: any;
arbitrableContractAddress: string | undefined;
}
Parameters:
-
question
: A Question object (must include thechain
property)
Returns:
-
disputeId
: The ID of the dispute, or null if no dispute exists -
dispute
: The dispute data, or undefined if not loaded -
disputeLoading
: Boolean indicating if dispute data is being loaded -
evidences
: Array of evidence submitted for the dispute -
metaEvidence
: Metadata about the dispute -
arbitrableContractAddress
: The address of the arbitrable contract
Example:
function DisputeView({ question }) {
const {
dispute,
disputeLoading,
evidences
} = useDisputeData(question);
if (disputeLoading) return <div>Loading dispute data...</div>;
if (!dispute) return <div>No dispute found</div>;
return (
<div>
<h2>Dispute #{dispute.id}</h2>
<p>Status: {dispute.ruled ? 'Ruled' : 'Pending'}</p>
<h3>Evidence ({evidences.length})</h3>
{evidences.map(e => (
<div key={e.id}>
<h4>{e.URI_contents.name}</h4>
<p>{e.URI_contents.description}</p>
<small>Submitted by: {e.sender}</small>
</div>
))}
</div>
);
}
Represents a Reality.eth question.
interface Question {
id: string; // Unique identifier for the question
title: string; // Question title
description: string; // Detailed description
arbitrator: string; // Address of the arbitrator
options: string[]; // Possible answer options
qType: string; // Question type (binary, multiple-choice, etc.)
phase: QuestionPhase; // Current phase of the question
currentAnswer: string; // Current answer value
openingTimestamp: number; // When the question opened for answering
arbitrationStatus?: ArbitrationStatus; // Status of arbitration if requested
currentBond: string; // Current bond amount
timeToOpen: number; // Time until question opens (ms)
timeRemaining: number; // Time remaining until finalization (ms)
answers: Answer[]; // Ignore, use responses instead for answer history
contract: string; // Contract address
createdTimestamp: number; // When the question was created
currentScheduledFinalizationTimestamp?: string; // When finalization is scheduled
finalAnswer?: string; // Final answer if finalized
disputeId?: number; // ID of associated dispute
appealPeriodEnd?: number; // When appeal period ends
minimumBond: string; // Minimum bond required
arbitrationRequestedBy?: string; // Who requested arbitration
responses: { // User responses, should be used to construct the answer history
value: string;
timestamp: number;
bond: string;
user: string;
}[];
chain: Chain; // Blockchain the question is on
template?: Template; // Template used to create the question
}
Enum representing the possible phases of a question.
enum QuestionPhase {
NOT_CREATED = 'NOT_CREATED',
OPEN = 'OPEN',
UPCOMING = 'UPCOMING',
PENDING_ARBITRATION = 'PENDING_ARBITRATION',
FINALIZED = 'FINALIZED',
SETTLED_TOO_SOON = 'SETTLED_TOO_SOON'
}
Enum representing the possible statuses of arbitration.
enum ArbitrationStatus {
WAITING = 'WAITING',
APPEALABLE = 'APPEALABLE',
SOLVED = 'SOLVED'
}
Represents an answer to a question.
interface Answer {
value: string; // Answer value
bond: string; // Bond amount
timestamp: number; // When the answer was provided
provider: string; // Address of the answer provider
}
Represents evidence submitted for a dispute.
interface Evidence {
id: string; // Unique identifier
URI: string; // URI pointing to evidence
URI_contents: { // Contents of the evidence
name: string;
description: string;
};
creationTime: string; // When the evidence was created
sender: string; // Address of the evidence submitter
}
Note: When working with evidence URIs from subgraphs, you must prepend
https://cdn.kleros.link
to the URI to form the complete URL. For example, if the URI is/ipfs/QmRXgRaKkcKKfXjTFozwoWLosFHmtcEaeuj1cL1G8xKHJd/file.pdf
, the complete URL would behttps://cdn.kleros.link/ipfs/QmRXgRaKkcKKfXjTFozwoWLosFHmtcEaeuj1cL1G8xKHJd/file.pdf
.
Evidence JSON Format: The JSON retrieved from an evidence URI typically follows this structure:
{ "name": "Response to \"The new candidate' justification for Invalid stands\"", "description": "The response argues against the claim that a \"new pool of candidates\" makes an election invalid...", "fileURI": "/ipfs/QmRXgRaKkcKKfXjTFozwoWLosFHmtcEaeuj1cL1G8xKHJd/Response.pdf", "evidenceSide": 0 }The
fileURI
field may point to additional documents, which also need to be prefixed withhttps://cdn.kleros.link
.
Represents a template used to create a Reality.eth question.
interface Template {
templateId: string; // Unique identifier for the template
questionText: string; // Text of the template with placeholders
creationTimestamp: string; // When the template was created
creator: string; // Address of the template creator
}
Note: Templates contain placeholders like
${0}
,${1}
, etc. that are replaced with actual values when a question is created. The template system allows for standardized question formats across the Reality.eth ecosystem.
Represents a dispute for a question.
interface Dispute {
id: string; // Unique identifier
period: number; // Current period of the dispute
periodDeadline: string; // Deadline for the current period
nbRounds: string; // Number of rounds
nbChoices: string; // Number of choices
rounds: { // Information about rounds
jurors: string;
isCurrentRound: boolean;
}[];
lastPeriodChangeTs: string; // Timestamp of last period change
arbitrableHistory: { // History of arbitrable events
id: string;
metaEvidence: string;
}[];
arbitrated: string; // Address of arbitrated contract
ruled: boolean; // Whether the dispute has been ruled
ruling: string; // The ruling
evidenceGroup: { // Group of evidence
id: string;
length: string;
evidence: Evidence[];
};
}
Represents a blockchain network.
interface Chain {
id: string; // Unique identifier (e.g., "ethereum")
name: string; // Display name (e.g., "Ethereum")
subgraphUrl: string; // URL for The Graph subgraph
public_rpc_url: string; // Public RPC endpoint
native_currency: string; // The native currency of the chain, used for the bonds of each question
}
Represents a bridge between chains for Reality.eth questions.
interface Bridge {
Name: string;
Comment: string | null;
"Home Chain": string;
"Home Proxy": string | null;
"Foreign Chain": string;
"Foreign Proxy": string | null;
Testnet: "Yes" | "No";
Oracle: string | null;
Bond: string | null;
Appeals: boolean | null;
Governor: boolean | null;
Bot: boolean | false | "?" | null;
Dispute: string | null;
}
Array of supported blockchain networks.
const SUPPORTED_CHAINS: Chain[] = [
{
id: "ethereum",
name: "Ethereum",
subgraphUrl: "https://gateway-arbitrum.network.thegraph.com/api/9a2c1f62c4da897f79c95a6a67600891/subgraphs/id/AGLkTv6eaW7JhQsLgB6SMzo43uM9V12ZoNkjAw7uijra",
public_rpc_url: "https://rpc.ankr.com/eth",
},
{
id: "gnosis",
name: "Gnosis",
subgraphUrl: "https://gateway-arbitrum.network.thegraph.com/api/9a2c1f62c4da897f79c95a6a67600891/subgraphs/id/E7ymrCnNcQdAAgLbdFWzGE5mvr5Mb5T9VfT43FqA7bNh",
public_rpc_url: "https://rpc.ankr.com/gnosis",
},
{
id: "polygon",
name: "Polygon",
subgraphUrl: "https://gateway-arbitrum.network.thegraph.com/api/9a2c1f62c4da897f79c95a6a67600891/subgraphs/id/AWx6jkeKZ3xKRzkrzgfCAMPT7d6Jc3AMcqB8koN3QEqE",
public_rpc_url: "https://rpc.ankr.com/polygon",
},
]
Array of bridges between chains for Reality.eth questions.
The useQuestions
hook supports filtering questions by various criteria:
const { filteredQuestions } = useQuestions({
chain: SUPPORTED_CHAINS[0],
filters: {
phase: QuestionPhase.OPEN,
// Add any property from the Question type
}
});
The useQuestions
hook supports pagination through the loadMore
function:
function QuestionsList() {
const { questions, hasMore, loadMore, loadingState } = useQuestions({
chain: SUPPORTED_CHAINS[0]
});
return (
<div>
<ul>{questions.map(q => <li key={q.id}>{q.title}</li>)}</ul>
{loadingState.loadingMore && <div>Loading more...</div>}
{hasMore && (
<button onClick={loadMore} disabled={loadingState.loadingMore}>
Load More
</button>
)}
</div>
);
}
This example shows how to list questions and then view details for a selected question:
function RealityApp() {
const [selectedChain, setSelectedChain] = useState(SUPPORTED_CHAINS[0]);
const [selectedQuestion, setSelectedQuestion] = useState(null);
const { questions, loading } = useQuestions({
chain: selectedChain
});
if (loading) return <div>Loading questions...</div>;
if (selectedQuestion) {
return (
<QuestionDetail
question={selectedQuestion}
onBack={() => setSelectedQuestion(null)}
/>
);
}
return (
<div>
<h1>Reality.eth Questions</h1>
<select onChange={e => {
const chain = SUPPORTED_CHAINS.find(c => c.id === e.target.value);
if (chain) setSelectedChain(chain);
}}>
{SUPPORTED_CHAINS.map(chain => (
<option key={chain.id} value={chain.id}>
{chain.name}
</option>
))}
</select>
<ul>
{questions.map(question => (
<li key={question.id}>
<h3>{question.title}</h3>
<p>Phase: {question.phase}</p>
<button onClick={() => setSelectedQuestion(question)}>
View Details
</button>
</li>
))}
</ul>
</div>
);
}
function QuestionDetail({ question, onBack }) {
const { dispute, disputeLoading, evidences } = useDisputeData(question);
return (
<div>
<button onClick={onBack}>Back to List</button>
<h2>{question.title}</h2>
<p>{question.description}</p>
<h3>Options:</h3>
<ul>
{question.options.map((option, i) => (
<li key={i}>{option}</li>
))}
</ul>
<h3>Current Answer: {question.currentAnswer}</h3>
<p>Current Bond: {question.currentBond}</p>
<h3>Dispute Information:</h3>
{disputeLoading ? (
<p>Loading dispute data...</p>
) : dispute ? (
<div>
<p>Dispute ID: {dispute.id}</p>
<p>Status: {dispute.ruled ? 'Ruled' : 'Pending'}</p>
<p>Ruling: {dispute.ruling}</p>
<h4>Evidence:</h4>
{evidences.length > 0 ? (
<ul>
{evidences.map(e => (
<li key={e.id}>
<h5>{e.URI_contents.name}</h5>
<p>{e.URI_contents.description}</p>
<small>From: {e.sender}</small>
</li>
))}
</ul>
) : (
<p>No evidence submitted</p>
)}
</div>
) : (
<p>No dispute found for this question</p>
)}
</div>
);
}
-
useQuestions
fetches questions from The Graph subgraphs for the selected chain - Questions are transformed and cached for performance
- When a specific question is selected,
useDisputeData
fetches dispute information - Dispute data is retrieved through a combination of:
- The Graph subgraphs for dispute details
- RPC calls to determine dispute IDs
- Bridge information to handle cross-chain disputes
Reality.eth questions can be created on one chain and have their disputes resolved on another. The library handles this complexity through:
- Bridge configurations that map home chains to foreign chains
- Automatic detection of the appropriate chain for dispute resolution
- Proper routing of RPC calls to the correct endpoints
- No questions loading: Verify the chain's subgraph URL is correct and accessible
- Dispute data not loading: Check that the question has an arbitrator assigned and that arbitration has been requested
- Missing evidence: Evidence may take time to be indexed by The Graph