Skip to content

feat(subgraph): add fee token to subgraph #1037

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

17 changes: 16 additions & 1 deletion subgraph/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,11 @@ type TokenAndETHShift @entity {
id: ID! # user.id-dispute.id
juror: User!
dispute: Dispute!
tokenAmount: BigInt!
pnkAmount: BigInt!
ethAmount: BigInt!
isNativeCurrency: Boolean!
feeTokenAmount: BigInt!
feeToken: FeeToken
}

type JurorTokensPerCourt @entity {
Expand Down Expand Up @@ -159,6 +162,7 @@ type Round @entity {
penalties: BigInt!
drawnJurors: [Draw!]! @derivedFrom(field: "round")
dispute: Dispute!
feeToken: FeeToken
}

type Draw @entity {
Expand Down Expand Up @@ -191,6 +195,17 @@ type Counter @entity {
casesRuled: BigInt!
}

type FeeToken @entity {
id: ID! # The address of the ERC20 token.
accepted: Boolean!
rateInEth: BigInt!
rateDecimals: Int!
totalPaid: BigInt!
totalPaidInETH: BigInt!
rounds: [Round!] @derivedFrom(field: "feeToken")
tokenAndETHShift: [TokenAndETHShift!] @derivedFrom(field: "feeToken")
}

#####################
# ClassicDisputeKit #
#####################
Expand Down
21 changes: 9 additions & 12 deletions subgraph/src/KlerosCore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,21 +12,23 @@ import {
TokenAndETHShift as TokenAndETHShiftEvent,
Ruling,
StakeDelayed,
AcceptedFeeToken,
} from "../generated/KlerosCore/KlerosCore";
import { ZERO, ONE } from "./utils";
import { createCourtFromEvent, getFeeForJuror } from "./entities/Court";
import { createDisputeKitFromEvent, filterSupportedDisputeKits } from "./entities/DisputeKit";
import { createDisputeFromEvent } from "./entities/Dispute";
import { createRoundFromRoundInfo } from "./entities/Round";
import { updateCases, updatePaidETH, updateRedistributedPNK, updateCasesRuled, updateCasesVoting } from "./datapoint";
import { updateCases, updateCasesRuled, updateCasesVoting } from "./datapoint";
import { addUserActiveDispute, ensureUser } from "./entities/User";
import { updateJurorDelayedStake, updateJurorStake } from "./entities/JurorTokensPerCourt";
import { createDrawFromEvent } from "./entities/Draw";
import { updateTokenAndEthShiftFromEvent } from "./entities/TokenAndEthShift";
import { updateArbitrableCases } from "./entities/Arbitrable";
import { Court, Dispute } from "../generated/schema";
import { Court, Dispute, FeeToken } from "../generated/schema";
import { BigInt } from "@graphprotocol/graph-ts";
import { updatePenalty } from "./entities/Penalty";
import { ensureFeeToken } from "./entities/FeeToken";

function getPeriodName(index: i32): string {
const periodArray = ["evidence", "commit", "vote", "appeal", "execution"];
Expand Down Expand Up @@ -156,22 +158,17 @@ export function handleStakeDelayed(event: StakeDelayed): void {
}

export function handleTokenAndETHShift(event: TokenAndETHShiftEvent): void {
updatePenalty(event);
updateTokenAndEthShiftFromEvent(event);
const jurorAddress = event.params._account.toHexString();
const disputeID = event.params._disputeID.toString();
const pnkAmount = event.params._pnkAmount;
const feeAmount = event.params._feeAmount;
const dispute = Dispute.load(disputeID);
if (!dispute) return;
const court = Court.load(dispute.court);
if (!court) return;
updateJurorStake(jurorAddress, court.id, KlerosCore.bind(event.address), event.block.timestamp);
court.paidETH = court.paidETH.plus(feeAmount);
if (pnkAmount.gt(ZERO)) {
court.paidPNK = court.paidPNK.plus(pnkAmount);
updateRedistributedPNK(pnkAmount, event.block.timestamp);
}
updatePaidETH(feeAmount, event.block.timestamp);
updatePenalty(event);
court.save();
}

export function handleAcceptedFeeToken(event: AcceptedFeeToken): void {
ensureFeeToken(event.params._token, event.address);
}
53 changes: 53 additions & 0 deletions subgraph/src/entities/FeeToken.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { BigInt, Address } from "@graphprotocol/graph-ts";
import { FeeToken } from "../../generated/schema";
import { KlerosCore } from "../../generated/KlerosCore/KlerosCore";
import { ZERO } from "../utils";

export function ensureFeeToken(tokenAddress: Address, klerosCoreAddress: Address): FeeToken {
const hexTokenAddress = tokenAddress.toHexString();
let feeToken = FeeToken.load(hexTokenAddress);
if (!feeToken) {
feeToken = new FeeToken(hexTokenAddress);
feeToken.totalPaid = ZERO;
feeToken.totalPaidInETH = ZERO;
}
const contract = KlerosCore.bind(klerosCoreAddress);
const currencyRate = contract.currencyRates(tokenAddress);
feeToken.accepted = currencyRate.value0;
feeToken.rateInEth = currencyRate.value1;
feeToken.rateDecimals = currencyRate.value2;
feeToken.save();
return feeToken;
}

export function updateFeeTokenRate(tokenAddress: Address, klerosCoreAddress: Address): void {
const feeToken = ensureFeeToken(tokenAddress, klerosCoreAddress);
const contract = KlerosCore.bind(klerosCoreAddress);
const currencyRate = contract.currencyRates(tokenAddress);
feeToken.accepted = currencyRate.value0;
feeToken.rateInEth = currencyRate.value1;
feeToken.rateDecimals = currencyRate.value2;
feeToken.save();
}

export function updateFeeTokenPaid(tokenAddress: Address, klerosCoreAddress: Address, amount: BigInt): void {
const feeToken = ensureFeeToken(tokenAddress, klerosCoreAddress);
const ethAmount = convertTokenAmountToEth(tokenAddress, amount, klerosCoreAddress);
feeToken.totalPaid = feeToken.totalPaid.plus(amount);
feeToken.totalPaidInETH = feeToken.totalPaidInETH.plus(ethAmount);
feeToken.save();
}

export function convertEthToTokenAmount(tokenAddress: Address, eth: BigInt, klerosCoreAddress: Address): BigInt {
const feeToken = ensureFeeToken(tokenAddress, klerosCoreAddress);
return eth.times(BigInt.fromI32(10 ** feeToken.rateDecimals)).div(feeToken.rateInEth);
}

export function convertTokenAmountToEth(
tokenAddress: Address,
tokenAmount: BigInt,
klerosCoreAddress: Address
): BigInt {
const feeToken = ensureFeeToken(tokenAddress, klerosCoreAddress);
return tokenAmount.times(feeToken.rateInEth).div(BigInt.fromI32(10 ** feeToken.rateDecimals));
}
3 changes: 3 additions & 0 deletions subgraph/src/entities/Round.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,15 @@ export function createRoundFromRoundInfo(
): void {
const roundID = `${disputeID.toString()}-${roundIndex.toString()}`;
const round = new Round(roundID);
const feeToken = roundInfo.getFeeToken();
round.disputeKit = roundInfo.getDisputeKitID.toString();
round.tokensAtStakePerJuror = roundInfo.getPnkAtStakePerJuror();
round.totalFeesForJurors = roundInfo.getTotalFeesForJurors();
round.nbVotes = roundInfo.getNbVotes();
round.repartitions = roundInfo.getRepartitions();
round.penalties = roundInfo.getPnkPenalties();
round.dispute = disputeID.toString();
round.feeToken =
feeToken.toHexString() === "0x0000000000000000000000000000000000000000" ? null : feeToken.toHexString();
round.save();
}
84 changes: 58 additions & 26 deletions subgraph/src/entities/TokenAndEthShift.ts
Original file line number Diff line number Diff line change
@@ -1,36 +1,68 @@
import { Address, BigInt } from "@graphprotocol/graph-ts";
import { TokenAndETHShift as TokenAndETHShiftEvent } from "../../generated/KlerosCore/KlerosCore";
import { TokenAndETHShift } from "../../generated/schema";
import { Court, Dispute, TokenAndETHShift } from "../../generated/schema";
import { updatePaidETH, updateRedistributedPNK } from "../datapoint";
import { ZERO } from "../utils";
import { convertTokenAmountToEth, updateFeeTokenPaid } from "./FeeToken";
import { resolveUserDispute } from "./User";

export function updateTokenAndEthShiftFromEvent(event: TokenAndETHShiftEvent): void {
const jurorAddress = event.params._account.toHexString();
const disputeID = event.params._disputeID.toString();
const shiftID = `${jurorAddress}-${disputeID}`;
const shift = TokenAndETHShift.load(shiftID);

if (!shift) {
createTokenAndEthShiftFromEvent(event);
resolveUserDispute(jurorAddress, ZERO, event.params._feeAmount, disputeID);
return;
const jurorAddress = event.params._account;
const disputeID = event.params._disputeID;
const dispute = Dispute.load(disputeID.toString());
if (!dispute) return;
const court = Court.load(dispute.court);
if (!court) return;
const roundIndex = event.params._roundID;
const feeTokenAddress = event.params._feeToken;
let shift = ensureTokenAndEthShift(jurorAddress, disputeID, roundIndex, feeTokenAddress);
const feeAmount = event.params._feeAmount;
const pnkAmount = event.params._pnkAmount;
let ethAmount: BigInt;
if (feeTokenAddress.toHexString() === "0x0000000000000000000000000000000000000000") {
updateFeeTokenPaid(feeTokenAddress, event.address, feeAmount);
ethAmount = convertTokenAmountToEth(feeTokenAddress, feeAmount, event.address);
shift.feeTokenAmount = shift.feeTokenAmount.plus(feeAmount);
} else {
ethAmount = feeAmount;
}

shift.tokenAmount = shift.tokenAmount.plus(event.params._pnkAmount);
const previousFeeAmount = shift.ethAmount;
const newFeeAmount = shift.ethAmount.plus(event.params._feeAmount);
shift.ethAmount = newFeeAmount;
const previousEthAmount = shift.ethAmount;
const newEthAmount = previousEthAmount.plus(ethAmount);
shift.ethAmount = newEthAmount;
resolveUserDispute(jurorAddress.toHexString(), previousEthAmount, newEthAmount, disputeID.toString());
court.paidETH = court.paidETH.plus(ethAmount);
updatePaidETH(ethAmount, event.block.timestamp);
if (pnkAmount.gt(ZERO)) {
court.paidPNK = court.paidPNK.plus(pnkAmount);
updateRedistributedPNK(pnkAmount, event.block.timestamp);
}
shift.pnkAmount = shift.pnkAmount.plus(pnkAmount);
shift.save();
resolveUserDispute(jurorAddress, previousFeeAmount, newFeeAmount, disputeID);
court.save();
}

export function createTokenAndEthShiftFromEvent(event: TokenAndETHShiftEvent): void {
const jurorAddress = event.params._account.toHexString();
const disputeID = event.params._disputeID.toString();
const shiftID = `${jurorAddress}-${disputeID}`;
const shift = new TokenAndETHShift(shiftID);
shift.juror = jurorAddress;
shift.dispute = disputeID;
shift.tokenAmount = event.params._pnkAmount;
shift.ethAmount = event.params._feeAmount;
shift.save();
export function ensureTokenAndEthShift(
jurorAddress: Address,
disputeID: BigInt,
roundIndex: BigInt,
feeTokenAddress: Address
): TokenAndETHShift {
const shiftID = `${jurorAddress.toHexString()}-${disputeID.toString()}-${roundIndex.toString()}`;
let shift = TokenAndETHShift.load(shiftID);
if (!shift) {
shift = new TokenAndETHShift(shiftID);
if (feeTokenAddress !== Address.fromI32(0)) {
shift.isNativeCurrency = false;
shift.feeToken = feeTokenAddress.toHexString();
} else {
shift.isNativeCurrency = true;
}
shift.feeTokenAmount = ZERO;
shift.ethAmount = ZERO;
shift.juror = jurorAddress.toHexString();
shift.dispute = disputeID.toString();
shift.pnkAmount = ZERO;
shift.save();
}
return shift;
}
2 changes: 2 additions & 0 deletions subgraph/subgraph.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ dataSources:
handler: handleTokenAndETHShift
- event: Ruling(indexed address,indexed uint256,uint256)
handler: handleRuling
- event: AcceptedFeeToken(indexed address,indexed bool)
handler: handleAcceptedFeeToken
file: ./src/KlerosCore.ts
- kind: ethereum
name: PolicyRegistry
Expand Down