diff --git a/.github/workflows/deploy-subgraph.yml b/.github/workflows/deploy-subgraph.yml
index b31ab20c1..4b32f972b 100644
--- a/.github/workflows/deploy-subgraph.yml
+++ b/.github/workflows/deploy-subgraph.yml
@@ -3,15 +3,15 @@ name: Deploy the Subgraph
on:
workflow_dispatch:
inputs:
- network:
- description: The network to deploy the subgraph to
+ graph_environment:
+ description: The Graph environment to deploy to
required: true
- default: 'arbitrum-sepolia'
+ default: 'graph-studio-devnet'
type: choice
options:
- - arbitrum-sepolia-devnet
- - arbitrum-sepolia
- - arbitrum
+ - graph-studio-devnet
+ - graph-studio-testnet
+ - graph-studio-mainnet
subgraph:
description: The name of the subgraph to deploy
required: true
@@ -32,13 +32,17 @@ permissions:
jobs:
buildAndDeploy:
runs-on: ubuntu-latest
- environment: kleros-org-subgraph
+ environment: ${{ inputs.graph_environment }}
steps:
- name: Harden Runner
uses: step-security/harden-runner@1b05615854632b887b69ae1be8cbefe72d3ae423 # v2.5.0
with:
egress-policy: audit
+ - name: Validate Network environment variable
+ if: ${{!startsWith(vars.NETWORK, 'arbitrum')}}
+ run: echo ${{vars.NETWORK}} && exit 1
+
- name: Checkout code
uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
@@ -66,7 +70,7 @@ jobs:
if: ${{ inputs.update }}
run: |
export PATH=$PWD/../bin:$PATH
- yarn update:${{ inputs.subgraph }}:${{ inputs.network }}
+ yarn update:${{ inputs.subgraph }}:${{ vars.NETWORK }}
working-directory: subgraph
- name: Build the subgraph
@@ -75,10 +79,10 @@ jobs:
yarn build:${{ inputs.subgraph }}
working-directory: subgraph
- - name: Authenticate with TheGraph
- run: yarn graph auth "${{ secrets.SUBGRAPH_AUTH_TOKEN }}" --product hosted-service
+ - name: Authenticate with TheGraph Studio
+ run: yarn graph auth "${{ secrets.SUBGRAPH_AUTH_TOKEN }}" --studio
working-directory: subgraph
- name: Deploy the subgraph
- run: yarn deploy:${{ inputs.subgraph }}:${{ inputs.network }}
+ run: yarn deploy:${{ inputs.subgraph }}:${{ vars.NETWORK }}
working-directory: subgraph
diff --git a/contracts/deploy/utils/deployERC20AndFaucet.ts b/contracts/deploy/utils/deployERC20AndFaucet.ts
index d3b9b2e77..ed0ce1a50 100644
--- a/contracts/deploy/utils/deployERC20AndFaucet.ts
+++ b/contracts/deploy/utils/deployERC20AndFaucet.ts
@@ -9,11 +9,7 @@ export const deployERC20AndFaucet = async (
ticker: string,
faucetFundingAmount: BigNumber = hre.ethers.utils.parseUnits("100000")
): Promise => {
- let erc20 = await hre.ethers.getContractOrNull(ticker);
- if (erc20) {
- return erc20;
- }
- erc20 = await getContractOrDeploy(hre, ticker, {
+ const erc20 = await getContractOrDeploy(hre, ticker, {
from: deployer,
contract: "TestERC20",
args: [ticker, ticker],
@@ -27,7 +23,8 @@ export const deployERC20AndFaucet = async (
});
const faucetBalance = await erc20.balanceOf(faucet.address);
const deployerBalance = await erc20.balanceOf(deployer);
- if (deployerBalance.gte(faucetFundingAmount) && faucetBalance.isZero()) {
+ if (deployerBalance.gte(faucetFundingAmount) && faucetBalance.lt(faucetFundingAmount.div(5))) {
+ // Fund the faucet if deployer has enough tokens and if the faucet has less than 20% of the faucetFundingAmount
console.log(`funding ${ticker}Faucet with ${faucetFundingAmount}`);
await erc20.transfer(faucet.address, faucetFundingAmount);
}
diff --git a/contracts/scripts/keeperBot.ts b/contracts/scripts/keeperBot.ts
index 4ae827f4c..792a13f55 100644
--- a/contracts/scripts/keeperBot.ts
+++ b/contracts/scripts/keeperBot.ts
@@ -1,4 +1,4 @@
-import { DisputeKitClassic, KlerosCore, PNK, RandomizerRNG, SortitionModule } from "../typechain-types";
+import { DisputeKitClassic, KlerosCore, PNK, RandomizerRNG, BlockHashRNG, SortitionModule } from "../typechain-types";
import request from "graphql-request";
import env from "./utils/env";
import loggerFactory from "./utils/logger";
@@ -36,9 +36,10 @@ const getContracts = async () => {
const core = (await ethers.getContract("KlerosCore")) as KlerosCore;
const sortition = (await ethers.getContract("SortitionModule")) as SortitionModule;
const randomizerRng = (await ethers.getContract("RandomizerRNG")) as RandomizerRNG;
+ const blockHashRNG = (await ethers.getContract("BlockHashRNG")) as BlockHashRNG;
const disputeKitClassic = (await ethers.getContract("DisputeKitClassic")) as DisputeKitClassic;
const pnk = (await ethers.getContract("PNK")) as PNK;
- return { core, sortition, randomizerRng, disputeKitClassic, pnk };
+ return { core, sortition, randomizerRng, blockHashRNG, disputeKitClassic, pnk };
};
type Contribution = {
@@ -150,15 +151,32 @@ const handleError = (e: any) => {
};
const isRngReady = async () => {
- const { randomizerRng, sortition } = await getContracts();
- const requesterID = await randomizerRng.requesterToID(sortition.address);
- const n = await randomizerRng.randomNumbers(requesterID);
- if (Number(n) === 0) {
- logger.info("RandomizerRNG is NOT ready yet");
- return false;
+ const { randomizerRng, blockHashRNG, sortition } = await getContracts();
+ const currentRng = await sortition.rng();
+ if (currentRng === randomizerRng.address) {
+ const requesterID = await randomizerRng.requesterToID(sortition.address);
+ const n = await randomizerRng.randomNumbers(requesterID);
+ if (Number(n) === 0) {
+ logger.info("RandomizerRNG is NOT ready yet");
+ return false;
+ } else {
+ logger.info(`RandomizerRNG is ready: ${n.toString()}`);
+ return true;
+ }
+ } else if (currentRng === blockHashRNG.address) {
+ const requestBlock = await sortition.randomNumberRequestBlock();
+ const lookahead = await sortition.rngLookahead();
+ const n = await blockHashRNG.callStatic.receiveRandomness(requestBlock.add(lookahead));
+ if (Number(n) === 0) {
+ logger.info("BlockHashRNG is NOT ready yet");
+ return false;
+ } else {
+ logger.info(`BlockHashRNG is ready: ${n.toString()}`);
+ return true;
+ }
} else {
- logger.info(`RandomizerRNG is ready: ${n.toString()}`);
- return true;
+ logger.error("Unknown RNG at ", currentRng);
+ return false;
}
};
diff --git a/scripts/act-subgraph.yml b/scripts/act-subgraph.yml
index 08b4c4d6d..392575317 100755
--- a/scripts/act-subgraph.yml
+++ b/scripts/act-subgraph.yml
@@ -1,3 +1,3 @@
#!/usr/bin/env bash
-act workflow_dispatch -j buildAndDeploy --input network=arbitrum-sepolia,update=true
+act workflow_dispatch -j buildAndDeploy --input graph_environment=graph-studio-devnet,update=true --env network=arbitrum-sepolia-devnet
diff --git a/subgraph/README.md b/subgraph/README.md
index 25a69b51b..843c3ce21 100644
--- a/subgraph/README.md
+++ b/subgraph/README.md
@@ -40,7 +40,14 @@ $ yarn run graph auth --studio
#### Deployment
```bash
+# bump the package version number
+yarn version patch
+
+# deploy the new version
yarn deploy:arbitrum-sepolia-devnet
+
+# commit the new version number
+git commit -m "chore: subgraph deployment"
```
### Using the Kleros organization account
diff --git a/subgraph/core/schema.graphql b/subgraph/core/schema.graphql
index e8715ab2d..e0acc6811 100644
--- a/subgraph/core/schema.graphql
+++ b/subgraph/core/schema.graphql
@@ -181,6 +181,7 @@ type Round @entity {
penalties: BigInt!
drawnJurors: [Draw!]! @derivedFrom(field: "round")
dispute: Dispute!
+ court: Court!
feeToken: FeeToken
}
diff --git a/subgraph/core/src/KlerosCore.ts b/subgraph/core/src/KlerosCore.ts
index a8dee3fdc..389572dfb 100644
--- a/subgraph/core/src/KlerosCore.ts
+++ b/subgraph/core/src/KlerosCore.ts
@@ -78,7 +78,7 @@ export function handleDisputeCreation(event: DisputeCreation): void {
court.save();
createDisputeFromEvent(event);
const roundInfo = contract.getRoundInfo(disputeID, ZERO);
- createRoundFromRoundInfo(disputeID, ZERO, roundInfo);
+ createRoundFromRoundInfo(KlerosCore.bind(event.address), disputeID, ZERO, roundInfo);
const arbitrable = event.params._arbitrable.toHexString();
updateArbitrableCases(arbitrable, ONE);
updateCases(ONE, event.block.timestamp);
@@ -163,7 +163,7 @@ export function handleAppealDecision(event: AppealDecision): void {
dispute.currentRound = roundID;
dispute.save();
const roundInfo = contract.getRoundInfo(disputeID, newRoundIndex);
- createRoundFromRoundInfo(disputeID, newRoundIndex, roundInfo);
+ createRoundFromRoundInfo(KlerosCore.bind(event.address), disputeID, newRoundIndex, roundInfo);
}
export function handleCourtJump(event: CourtJump): void {
diff --git a/subgraph/core/src/entities/Round.ts b/subgraph/core/src/entities/Round.ts
index 5ffb518e6..47380099e 100644
--- a/subgraph/core/src/entities/Round.ts
+++ b/subgraph/core/src/entities/Round.ts
@@ -1,8 +1,9 @@
import { BigInt } from "@graphprotocol/graph-ts";
-import { KlerosCore__getRoundInfoResultValue0Struct } from "../../generated/KlerosCore/KlerosCore";
+import { KlerosCore, KlerosCore__getRoundInfoResultValue0Struct } from "../../generated/KlerosCore/KlerosCore";
import { Round } from "../../generated/schema";
export function createRoundFromRoundInfo(
+ contract: KlerosCore,
disputeID: BigInt,
roundIndex: BigInt,
roundInfo: KlerosCore__getRoundInfoResultValue0Struct
@@ -19,5 +20,7 @@ export function createRoundFromRoundInfo(
round.repartitions = roundInfo.repartitions;
round.penalties = roundInfo.pnkPenalties;
round.dispute = disputeID.toString();
+ const courtID = contract.disputes(disputeID).value0.toString();
+ round.court = courtID;
round.save();
}
diff --git a/subgraph/package.json b/subgraph/package.json
index c8cb22540..147dd0100 100644
--- a/subgraph/package.json
+++ b/subgraph/package.json
@@ -1,5 +1,6 @@
{
"name": "@kleros/kleros-v2-subgraph",
+ "version": "0.3.1",
"license": "MIT",
"scripts": {
"update:core:arbitrum-sepolia-devnet": "./scripts/update.sh arbitrumSepoliaDevnet arbitrum-sepolia core/subgraph.yaml",
@@ -10,9 +11,9 @@
"build:core": "graph build --output-dir core/build/ core/subgraph.yaml",
"test:core": "cd core && graph test",
"clean:core": "graph clean --codegen-dir core/generated/ --build-dir core/build/ && rm core/subgraph.yaml.bak.*",
- "deploy:core:arbitrum-sepolia-devnet": "graph deploy --product subgraph-studio kleros-v2-core-devnet -l v0.0.2 core/subgraph.yaml",
- "deploy:core:arbitrum-sepolia": "graph deploy --product subgraph-studio kleros-v2-core-testnet -l v0.0.2 core/subgraph.yaml",
- "deploy:core:arbitrum": "graph deploy --product subgraph-studio kleros-v2-core -l v0.0.2 core/subgraph.yaml",
+ "deploy:core:arbitrum-sepolia-devnet": "graph deploy --product subgraph-studio kleros-v2-core-devnet -l v$npm_package_version core/subgraph.yaml",
+ "deploy:core:arbitrum-sepolia": "graph deploy --product subgraph-studio kleros-v2-core-testnet -l v$npm_package_version core/subgraph.yaml",
+ "deploy:core:arbitrum": "graph deploy --product subgraph-studio kleros-v2-core -l v$npm_package_version core/subgraph.yaml",
"": "------------------------------------------------------------------------------------------",
"update:drt:arbitrum-sepolia-devnet": "./scripts/update.sh arbitrumSepoliaDevnet arbitrum-sepolia dispute-template-registry/subgraph.yaml",
"update:drt:arbitrum-sepolia": "./scripts/update.sh arbitrumSepolia arbitrum-sepolia dispute-template-registry/subgraph.yaml",
@@ -22,8 +23,8 @@
"build:drt": "graph build --output-dir dispute-template-registry/generated/ dispute-template-registry/subgraph.yaml",
"test:drt": "cd dispute-template-registry && graph test ",
"clean:drt": "graph clean --codegen-dir dispute-template-registry/generated/ --build-dir dispute-template-registry/build/ && rm dispute-template-registry/subgraph.yaml.bak.*",
- "deploy:drt:arbitrum-sepolia-devnet": "graph deploy --product subgraph-studio kleros-v2-drt-arbisep-devnet -l v0.0.2 dispute-template-registry/subgraph.yaml",
- "deploy:drt:arbitrum-sepolia": "graph deploy --product subgraph-studio kleros-v2-drt-arbisep-testnet -l v0.0.2 dispute-template-registry/subgraph.yaml",
+ "deploy:drt:arbitrum-sepolia-devnet": "graph deploy --product subgraph-studio kleros-v2-drt-arbisep-devnet -l v$npm_package_version dispute-template-registry/subgraph.yaml",
+ "deploy:drt:arbitrum-sepolia": "graph deploy --product subgraph-studio kleros-v2-drt-arbisep-testnet -l v$npm_package_version dispute-template-registry/subgraph.yaml",
" ": "-----------------------------------------------------------------------------------------",
"update:arbitrum-sepolia-devnet": "./scripts/all.sh update arbitrum-sepolia-devnet",
"update:arbitrum-sepolia": "./scripts/all.sh update arbitrum-sepolia",
diff --git a/web/src/assets/svgs/icons/paperclip.svg b/web/src/assets/svgs/icons/paperclip.svg
new file mode 100644
index 000000000..733c87215
--- /dev/null
+++ b/web/src/assets/svgs/icons/paperclip.svg
@@ -0,0 +1,17 @@
+
\ No newline at end of file
diff --git a/web/src/components/Verdict/DisputeTimeline.tsx b/web/src/components/Verdict/DisputeTimeline.tsx
index ec085ce2a..1d3601e98 100644
--- a/web/src/components/Verdict/DisputeTimeline.tsx
+++ b/web/src/components/Verdict/DisputeTimeline.tsx
@@ -87,7 +87,7 @@ const useItems = (disputeDetails?: DisputeDetailsQuery, arbitrable?: `0x${string
acc.push({
title: `Jury Decision - Round ${index + 1}`,
party: isOngoing ? "Voting is ongoing" : getVoteChoice(parsedRoundChoice, answers),
- subtitle: eventDate,
+ subtitle: `${eventDate} / ${votingHistory?.dispute?.rounds.at(index)?.court.name}`,
rightSided: true,
variant: theme.secondaryPurple,
Icon: icon !== "" ? icon : undefined,
diff --git a/web/src/hooks/queries/useVotingHistory.ts b/web/src/hooks/queries/useVotingHistory.ts
index 8ba728f9d..e021ee418 100644
--- a/web/src/hooks/queries/useVotingHistory.ts
+++ b/web/src/hooks/queries/useVotingHistory.ts
@@ -10,6 +10,10 @@ const votingHistoryQuery = graphql(`
id
rounds {
nbVotes
+ court {
+ id
+ name
+ }
}
disputeKitDispute {
localRounds {
diff --git a/web/src/pages/Cases/CaseDetails/Overview/Policies.tsx b/web/src/pages/Cases/CaseDetails/Overview/Policies.tsx
index 460dcee41..8e0155adc 100644
--- a/web/src/pages/Cases/CaseDetails/Overview/Policies.tsx
+++ b/web/src/pages/Cases/CaseDetails/Overview/Policies.tsx
@@ -5,6 +5,7 @@ import { IPFS_GATEWAY } from "consts/index";
import PolicyIcon from "svgs/icons/policy.svg";
import { isUndefined } from "utils/index";
import { responsiveSize } from "styles/responsiveSize";
+import PaperclipIcon from "svgs/icons/paperclip.svg";
const ShadeArea = styled.div`
display: flex;
@@ -46,21 +47,38 @@ const StyledPolicyIcon = styled(PolicyIcon)`
fill: ${({ theme }) => theme.primaryBlue};
`;
+const StyledPaperclipIcon = styled(PaperclipIcon)`
+ width: 16px;
+ fill: ${({ theme }) => theme.primaryBlue};
+`;
+
const LinkContainer = styled.div`
display: flex;
- gap: ${responsiveSize(8, 24)};
+ gap: ${responsiveSize(16, 24)};
+ flex-wrap: wrap;
`;
+type Attachment = {
+ label?: string;
+ uri: string;
+};
interface IPolicies {
disputePolicyURI?: string;
courtId?: string;
+ attachment?: Attachment;
}
-export const Policies: React.FC = ({ disputePolicyURI, courtId }) => {
+export const Policies: React.FC = ({ disputePolicyURI, courtId, attachment }) => {
return (
Make sure you read and understand the Policies
+ {!isUndefined(attachment) && !isUndefined(attachment.uri) ? (
+
+
+ {attachment.label ?? "Attachment"}
+
+ ) : null}
{isUndefined(disputePolicyURI) ? null : (
diff --git a/web/src/pages/Cases/CaseDetails/Overview/index.tsx b/web/src/pages/Cases/CaseDetails/Overview/index.tsx
index ef9a003d5..84f22374b 100644
--- a/web/src/pages/Cases/CaseDetails/Overview/index.tsx
+++ b/web/src/pages/Cases/CaseDetails/Overview/index.tsx
@@ -73,7 +73,11 @@ const Overview: React.FC = ({ arbitrable, courtID, currentPeriodIndex
{...{ rewards, category }}
/>
-
+
>
);
};
diff --git a/web/src/pages/Cases/CaseDetails/Voting/VotingHistory.tsx b/web/src/pages/Cases/CaseDetails/Voting/VotingHistory.tsx
index 9aed8c3c4..eedc86cb1 100644
--- a/web/src/pages/Cases/CaseDetails/Voting/VotingHistory.tsx
+++ b/web/src/pages/Cases/CaseDetails/Voting/VotingHistory.tsx
@@ -133,7 +133,8 @@ const VotingHistory: React.FC<{ arbitrable?: `0x${string}`; isQuestion: boolean
? "All jurors voted"
: localRounds.at(currentTab)?.totalVoted.toString() +
` vote${localRounds.at(currentTab)?.totalVoted.toString() === "1" ? "" : "s"} cast out of ` +
- rounds.at(currentTab)?.nbVotes}
+ rounds.at(currentTab)?.nbVotes}{" "}
+ - {rounds.at(currentTab)?.court.name}