diff --git a/schemas/request.gql b/schemas/request.gql index 3504c54..db31168 100644 --- a/schemas/request.gql +++ b/schemas/request.gql @@ -69,6 +69,7 @@ query Request($id: ID!) { } nbRounds rounds(orderBy: creationTime) { + creationTime requesterFund { amount } diff --git a/src/app/[pohid]/[chain]/[request]/ActionBar.tsx b/src/app/[pohid]/[chain]/[request]/ActionBar.tsx index a6a052a..95ef1f0 100644 --- a/src/app/[pohid]/[chain]/[request]/ActionBar.tsx +++ b/src/app/[pohid]/[chain]/[request]/ActionBar.tsx @@ -24,6 +24,7 @@ import RemoveVouch from "./RemoveVouch"; import Vouch from "./Vouch"; import { getMyData } from "data/user"; import useSWR from "swr"; +import Appeal from "./Appeal"; enableReactUse(); @@ -425,6 +426,24 @@ export default withClientConnected(function ActionBar({ )} . + +
+ 1 && currentChallenge.rounds.at(0)?.challengerFund?.amount} + claimerFunds={currentChallenge.rounds.length>1 && currentChallenge.rounds.at(0)?.requesterFund.amount} */ + chainId={chain.id} + /> (function ActionBar({ > View case #{currentChallenge.disputeId} +
)} diff --git a/src/app/[pohid]/[chain]/[request]/Appeal.tsx b/src/app/[pohid]/[chain]/[request]/Appeal.tsx index a9872bf..c84ae67 100644 --- a/src/app/[pohid]/[chain]/[request]/Appeal.tsx +++ b/src/app/[pohid]/[chain]/[request]/Appeal.tsx @@ -3,44 +3,231 @@ import Accordion from "components/Accordion"; import Identicon from "components/Identicon"; import Progress from "components/Progress"; -import { formatEth } from "utils/misc"; +import { eth2Wei, formatEth } from "utils/misc"; import usePoHWrite from "contracts/hooks/usePoHWrite"; -import { Address, Hash } from "viem"; +import { Address } from "viem"; +import { useMemo, useRef, useState } from "react"; +import Field from "components/Field"; +import Modal from "components/Modal"; +import { SupportedChainId } from "config/chains"; +import { toast } from "react-toastify"; +import { useEffectOnce } from "@legendapp/state/react"; +import TimeAgo from "components/TimeAgo"; +import { useLoading } from "hooks/useLoading"; +import { APIPoH, ArbitratorFromRequest, CurrentRoundSideFunds, StakeMultipliers } from "contracts/apis/APIPoH"; +import { APIArbitrator, ArbitratorsData, DisputeStatusEnum, SideEnum } from "contracts/apis/APIArbitrator"; -interface AppealProps { - pohId: Hash; + +interface SideFundingProps { + side: SideEnum; + arbitrator: Address; + disputeId: bigint; + contributor: Address; requester: Address; + requesterFunds: bigint; + appealCost: bigint; +} + +const SideFunding: React.FC = ({ + side, + disputeId, + arbitrator, + contributor, + requester, + requesterFunds, + appealCost, +}) => { + + const title = side === SideEnum.claimer? 'Claimer' : 'Challenger'; + const [requesterInput, setRequesterInput] = useState(0n); + const loading = useLoading(); + const errorRef = useRef(false); + + const value = formatEth(requesterFunds)*100/formatEth(appealCost); + const valueProgress = value > 100? 100 : value; + + const [prepareFundAppeal] = usePoHWrite( + "fundAppeal", + useMemo( + () => ({ + onLoading() { + loading.start(); + }, + onReady(fire) { + fire(); + }, + onFail() { + !errorRef.current && toast.info("Transaction is not possible! Do you have enough funds?"); + errorRef.current = true; + } + }), + [loading] + ) + ); + + return ( + +
+
+ +
+ {requester} + {title} +
+
+
+ setRequesterInput(eth2Wei(+v.target.value))} + /> + +
+ +
+
+ ) +} + +interface AppealProps { + pohId: Address; + requestIndex: number; + /* arbitrator: Address; + extraData: any, */ + contributor: Address; + claimer: Address; challenger: Address; disputeId: bigint; - requestIndex: bigint; - challengeIndex: bigint; - start: number; - end: number; - challengerFunds: bigint; - requesterFunds: bigint; + /* challengerFunds: bigint; + claimerFunds: bigint; */ + chainId: SupportedChainId; } const Appeal: React.FC = ({ pohId, requestIndex, - challengeIndex, disputeId, - requester, + /* arbitrator, + extraData, */ + contributor, + chainId, + claimer, challenger, - start, - end, - challengerFunds, - requesterFunds, + /* claimerFunds, + challengerFunds, */ }) => { - const [fundAppeal] = usePoHWrite("fundAppeal"); + const [totalClaimerCost, setTotalClaimerCost] = useState(0n); + const [totalChallengerCost, setTotalChallengerCost] = useState(0n); + const [formatedCurrentRuling, setFormatedCurrentRuling] = useState(""); + const defaultPeriod = [0n,0n]; + const [period, setPeriod] = useState(defaultPeriod); + const [disputeStatus, setDisputeStatus] = useState(DisputeStatusEnum.Appealable); + const [error, setError] = useState(false); + const errorRef = useRef(false); + const [loading, setLoading] = useState(true); + const [arbitrator, setArbitrator] = useState(null); + const [claimerFunds, setClaimerFunds] = useState(0n); + const [challengerFunds, setChallengerFunds] = useState(0n); + + useEffectOnce(() => { + const formatCurrentRuling = (currentRuling: SideEnum) => { + var text = "Undecided"; + switch (currentRuling) { + case SideEnum.claimer: + text = "Claimer wins"; + break; + case SideEnum.challenger: + text = "Challenger wins"; + break; + case SideEnum.shared: + text = "Shared"; + } + setFormatedCurrentRuling(text); + } - // const [requesterInput, setRequesterInput] = useState(0); - // const [challengerInput, setChallengerInput] = useState(0); + const calculateTotalCost = (appealCost: bigint, currentRuling: SideEnum, winnerMult: number, loserMult: number, sharedMult: number) => { + const getSideTotalCost = (sideMultiplier: number) => { + return Number(appealCost) + ((Number(appealCost) * sideMultiplier) / MULTIPLIER_DIVISOR); + } + const MULTIPLIER_DIVISOR = 10000; + + const claimerMultiplier = currentRuling === SideEnum.shared?sharedMult: currentRuling === SideEnum.claimer? winnerMult : loserMult; + const totalClaimerCost = getSideTotalCost(Number(claimerMultiplier)); + setTotalClaimerCost(BigInt(totalClaimerCost)); + + const challengerMultiplier = currentRuling === SideEnum.shared?sharedMult: currentRuling === SideEnum.claimer? loserMult : winnerMult; + const totalChallengerCost = getSideTotalCost(Number(challengerMultiplier)); + setTotalChallengerCost(BigInt(totalChallengerCost)); + } + const getAppealData = async () => { + try{ + const arbitratorFromRequest: ArbitratorFromRequest = await APIPoH.getArbitratorFromRequest(chainId, pohId, requestIndex); + const arbitrator = arbitratorFromRequest.arbitrator!; + const extraData = arbitratorFromRequest.extraData!; + setArbitrator(arbitrator as any); + + const currentRoundSideFunds: CurrentRoundSideFunds = await APIPoH.getCurrentRoundSideFunds(chainId, pohId, requestIndex, arbitrator, disputeId); + setClaimerFunds(currentRoundSideFunds.claimerFunds!); + setChallengerFunds(currentRoundSideFunds.challengerFunds!); + + const stakeMultipliers: StakeMultipliers = await APIPoH.getStakeMultipliers(chainId); + const winnerMult = stakeMultipliers.winnerStakeMultiplier; + const loserMult = stakeMultipliers.looserStakeMultiplier; + const sharedMult = stakeMultipliers.sharedStakeMultiplier; + + const arbitratorsData: ArbitratorsData = await APIArbitrator.getArbitratorsData(chainId, arbitrator, disputeId, extraData); + const status = arbitratorsData.status; + const cost = arbitratorsData.cost; + const period = arbitratorsData.period; + const currentRuling = arbitratorsData.currentRuling; + + + setPeriod(period as any); + setDisputeStatus(Number(status) as DisputeStatusEnum); + //setDisputeStatus(1 as DisputeStatusEnum); // -------------------------------- JUST FOR TESTING + formatCurrentRuling(Number(currentRuling) as SideEnum); + calculateTotalCost(cost as any, Number(currentRuling) as SideEnum, Number(winnerMult), Number(loserMult), Number(sharedMult)); + + setLoading(false); + } catch (e) { + !errorRef.current && toast.info("Unexpected error while reading appelate round info. Come back later"); + setError(true); + errorRef.current = true; + console.log(e); + } + } + getAppealData(); + }); + return ( - -
-

Appeal the decision

+ disputeStatus === DisputeStatusEnum.Appealable && !error && !loading? + + Fund Appeal (ends ) + } + > +
+

Appeal the decision: {formatedCurrentRuling}

In order to appeal the decision, you need to fully fund the crowdfunding deposit. The dispute will be sent to the jurors when the @@ -48,85 +235,32 @@ const Appeal: React.FC = ({ its side, the previous round winner should also fully fund its side, in order not to lose the case.

+
+ +
+ -
-
- -
- {requester} - Claimer -
-
-
- {/* setRequesterInput(+v.target.value)} - /> */} - -
- -
- -
-
- -
- {challenger} - Challenger -
-
-
- {/* setChallengerInput(+v.target.value)} - /> */} - -
- -
-
+ requester={challenger} + requesterFunds={challengerFunds} + appealCost={totalChallengerCost} + />
-
+ + : null ); }; -export default Appeal; +export default Appeal; \ No newline at end of file diff --git a/src/app/[pohid]/[chain]/[request]/Info.tsx b/src/app/[pohid]/[chain]/[request]/Info.tsx index 4a5eec2..c865922 100644 --- a/src/app/[pohid]/[chain]/[request]/Info.tsx +++ b/src/app/[pohid]/[chain]/[request]/Info.tsx @@ -31,7 +31,7 @@ export default function Info({ nbRequests }: InfoProps) { unique human registered on Proof of Humanity.

- This POH ID had {nbRequests} requests in total + This POH ID had {nbRequests} requests claimed in this chain

); diff --git a/src/components/Attachment.tsx b/src/components/Attachment.tsx index 870e325..9dd83e9 100644 --- a/src/components/Attachment.tsx +++ b/src/components/Attachment.tsx @@ -1,8 +1,8 @@ "use client"; -import Image from "next/image"; import Link from "next/link"; import { ipfs } from "utils/ipfs"; +import AttachmentIcon from "icons/AttachmentMajor.svg"; interface AttachmentProps { uri: string; @@ -13,13 +13,7 @@ const Attachment: React.FC = ({ uri }) => { return ( - Attachment Icon + ); }; diff --git a/src/config/subgraph.ts b/src/config/subgraph.ts index e5653e4..738e05d 100644 --- a/src/config/subgraph.ts +++ b/src/config/subgraph.ts @@ -34,16 +34,16 @@ export const subgraph_url = { ), [sepolia.id]: ( (configSetSelection.id === configSets.testOld.id) ? - "https://api.studio.thegraph.com/query/64099/proof-of-humanity-sepolia-test/v0.0.14" // OLD - //"https://api.studio.thegraph.com/query/64099/proof-of-humanity-sepolia-test/version/latest" // OLD + //"https://api.studio.thegraph.com/query/64099/proof-of-humanity-sepolia-test/v0.0.14" // TESTNETS + "https://api.studio.thegraph.com/query/64099/proof-of-humanity-sepolia-test/version/latest" // Development : (configSetSelection.id === configSets.testNew.id) ? "https://api.studio.thegraph.com/query/64099/proof-of-humanity-sepolia/version/latest" : "" ), [gnosisChiado.id]: ( (configSetSelection.id === configSets.testOld.id) ? - "https://api.goldsky.com/api/public/project_cluh21be5gq0o01u27olk4rwl/subgraphs/proof-of-humanity-chiado/1.0.2/gn" // OLD - //"https://api.goldsky.com/api/public/project_cluh21be5gq0o01u27olk4rwl/subgraphs/proof-of-humanity-chiado/1.0.0/gn" // OLD + //"https://api.goldsky.com/api/public/project_cluh21be5gq0o01u27olk4rwl/subgraphs/proof-of-humanity-chiado/1.0.2/gn" // TESTNETS + "https://api.goldsky.com/api/public/project_cluh21be5gq0o01u27olk4rwl/subgraphs/proof-of-humanity-chiado/1.0.0/gn" // Development : (configSetSelection.id === configSets.testNew.id) ? "https://api.goldsky.com/api/public/project_cluh21be5gq0o01u27olk4rwl/subgraphs/proof-of-humanity-chiado/1.0.1/gn" : "" diff --git a/src/contracts/apis/APIArbitrator.ts b/src/contracts/apis/APIArbitrator.ts new file mode 100644 index 0000000..f93e5d0 --- /dev/null +++ b/src/contracts/apis/APIArbitrator.ts @@ -0,0 +1,77 @@ +import { SupportedChainId, getChainRpc, supportedChains } from "config/chains"; +import { Address, createPublicClient, http } from "viem"; +import Error from "next/error"; +import klerosLiquid from "contracts/abis/kleros-liquid"; + +export enum DisputeStatusEnum { + Waiting, + Appealable, + Solved +}; + +export enum SideEnum { + shared, + claimer, + challenger +}; + +export interface ArbitratorsData { + status: DisputeStatusEnum | undefined; + cost: bigint | undefined; + period: bigint[] | undefined; + currentRuling: SideEnum | undefined; +}; + +export class APIArbitrator { + + private static apiReader: APIArbitrator; // Singleton + + private publicClient: any; + private chainId: SupportedChainId; + private address: Address; + + constructor(_chainId: SupportedChainId, _arbitrator: Address) { + this.chainId = _chainId; + this.address = _arbitrator; + this.publicClient = createPublicClient({ + chain: supportedChains[_chainId], + transport: http(getChainRpc(_chainId)), + }); + } + private static getApiReader(_chainId: SupportedChainId, _arbitrator: Address) { + if (!APIArbitrator.apiReader || APIArbitrator.apiReader.chainId !== _chainId || APIArbitrator.apiReader.address !== _arbitrator) + APIArbitrator.apiReader = new APIArbitrator(_chainId, _arbitrator); + return APIArbitrator.apiReader; + } + + private async get(func: string, args: Array = []) { + return await this.publicClient.readContract({ + address: this.address, + abi: klerosLiquid as any, + functionName: func, + args: args + }) + .catch(() => { + throw new Error({statusCode: 521, title: "Error while reading Arbitrator"}); + }); + } + + public static async getArbitratorsData(chainId: SupportedChainId, _arbitrator: Address, disputeId: bigint, extraData: bigint) : Promise { + const apiReader = APIArbitrator.getApiReader(chainId, _arbitrator); + const out: ArbitratorsData = { + status: undefined, + cost: undefined, + currentRuling: undefined, + period: undefined + }; + try { + out.status = await apiReader.get('disputeStatus', [disputeId]); + out.cost = await apiReader.get('appealCost', [disputeId, extraData]); + out.period = await apiReader.get('appealPeriod', [disputeId]); + out.currentRuling = await apiReader.get('currentRuling', [disputeId]); + return out; + } catch (error) { + throw new Error({statusCode: 521, title: "Error while reading Arbitrator"}); + } + } +} \ No newline at end of file diff --git a/src/contracts/apis/APIPoH.ts b/src/contracts/apis/APIPoH.ts new file mode 100644 index 0000000..aa6e96c --- /dev/null +++ b/src/contracts/apis/APIPoH.ts @@ -0,0 +1,110 @@ +import { SupportedChainId, getChainRpc, supportedChains } from "config/chains"; +import { Contract } from "contracts"; +import abis from "contracts/abis"; +import { Address, createPublicClient, http } from "viem"; +import Error from "next/error"; + +export interface ArbitratorFromRequest { + arbitrator: Address | undefined; + extraData: bigint | undefined; +} + +export interface CurrentRoundSideFunds { + claimerFunds: bigint | undefined; + challengerFunds: bigint | undefined; +} + +export interface StakeMultipliers { + winnerStakeMultiplier: bigint | undefined; + looserStakeMultiplier: bigint | undefined; + sharedStakeMultiplier: bigint | undefined; +} + +export class APIPoH { + + private static apiReader: APIPoH; // Singleton + + private publicClient: any; + private chainId: SupportedChainId; + private address: Address; + + constructor(_chainId: SupportedChainId) { + this.chainId = _chainId; + this.address = Contract.ProofOfHumanity[_chainId] as Address; + this.publicClient = createPublicClient({ + chain: supportedChains[_chainId], + transport: http(getChainRpc(_chainId)), + }); + } + private static getApiReader(_chainId: SupportedChainId) { + if (!APIPoH.apiReader || APIPoH.apiReader.chainId !== _chainId) + APIPoH.apiReader = new APIPoH(_chainId); + return APIPoH.apiReader; + } + + private async get(func: string, args: Array = []) { + return await this.publicClient.readContract({ + address: this.address, + abi: abis.ProofOfHumanity as any, + functionName: func, + args: args + }) + .catch(() => { + throw new Error({statusCode: 520, title: "Error while reading ProofOfHumanity"}); + }); + } + + public static async getArbitratorFromRequest(_chainId: SupportedChainId, pohId: Address, requestIndex: number): Promise { + const apiReader = APIPoH.getApiReader(_chainId); + const out: ArbitratorFromRequest = { + arbitrator: undefined, + extraData: undefined + }; + try { + const data = await apiReader.get('getRequestInfo', [pohId, requestIndex]); + const arbitratorDataHistory = await apiReader.get('arbitratorDataHistory', [data[2]]); + out.arbitrator = arbitratorDataHistory[1]; + out.extraData = arbitratorDataHistory[2]; + return out; + } catch (error) { + throw new Error({statusCode: 520, title: "Error while reading ProofOfHumanity"}); + } + } + + public static async getCurrentRoundSideFunds(_chainId: SupportedChainId, pohId: Address, requestIndex: number, arbitrator: Address, disputeId: bigint): Promise { + const apiReader = APIPoH.getApiReader(_chainId); + const out: CurrentRoundSideFunds = { + claimerFunds: undefined, + challengerFunds: undefined + }; + try { + const data = await apiReader.get('disputeIdToData', [arbitrator, disputeId]); + const challengeId = data[1]; + const challengeInfo = await apiReader.get('getChallengeInfo', [pohId, requestIndex, challengeId]); + const lastRoundId = challengeInfo[0]; + const funds = await apiReader.get('getRoundInfo', [pohId, requestIndex, challengeId, lastRoundId]); + out.claimerFunds = funds[1]; + out.challengerFunds = funds[2]; + return out; + } catch (error) { + throw new Error({statusCode: 520, title: "Error while reading ProofOfHumanity"}); + } + } + + public static async getStakeMultipliers(_chainId: SupportedChainId) : Promise { + const apiReader = APIPoH.getApiReader(_chainId); + const out: StakeMultipliers = { + winnerStakeMultiplier: undefined, + looserStakeMultiplier: undefined, + sharedStakeMultiplier: undefined + }; + try { + out.winnerStakeMultiplier = await apiReader.get('winnerStakeMultiplier'); + out.looserStakeMultiplier = await apiReader.get('loserStakeMultiplier'); + out.sharedStakeMultiplier = await apiReader.get('sharedStakeMultiplier'); + return out; + } catch (error) { + throw new Error({statusCode: 520, title: "Error while reading ProofOfHumanity"}); + } + } +} \ No newline at end of file diff --git a/src/contracts/index.ts b/src/contracts/index.ts index 854973a..50da60b 100644 --- a/src/contracts/index.ts +++ b/src/contracts/index.ts @@ -31,12 +31,18 @@ export const Contract = { } : (configSetSelection.id === configSets.main.id)? { [gnosis.id]: "0xa4AC94C4fa65Bb352eFa30e3408e64F72aC857bc", [mainnet.id]: "0xbE9834097A4E97689d9B667441acafb456D0480A", + [sepolia.id]: "0x", + [gnosisChiado.id]: "0x", } : (configSetSelection.id === configSets.mainPreAudit.id)? { [gnosis.id]: "0xECd1823b3087acEE3C77928b1959c08d31A8F20e", [mainnet.id]: "0x87c5c294C9d0ACa6b9b2835A99FE0c9A444Aacc1", + [sepolia.id]: "0x", + [gnosisChiado.id]: "0x", } : (configSetSelection.id === configSets.mainOld.id)? { [gnosis.id]: "0xe6573F65efAbc351b69F9b73ed8e95772698938b", [mainnet.id]: "0x6cbEdC1920090EA4F28A38C1CD61c8D37b2cc323", + [sepolia.id]: "0x", + [gnosisChiado.id]: "0x", } : { [mainnet.id]: "0x", [gnosis.id]: "0x", diff --git a/src/generated/graphql.ts b/src/generated/graphql.ts index edda89c..3f2ae41 100644 --- a/src/generated/graphql.ts +++ b/src/generated/graphql.ts @@ -3397,7 +3397,7 @@ export type RequestQueryVariables = Exact<{ }>; -export type RequestQuery = { __typename?: 'Query', request?: { __typename?: 'Request', index: any, revocation: boolean, registrationEvidenceRevokedReq: string, requester: any, creationTime: any, lastStatusChange: any, status: { __typename?: 'Status', id: string }, vouches: Array<{ __typename?: 'VouchInProcess', voucher: { __typename?: 'Humanity', id: any } }>, humanity: { __typename?: 'Humanity', id: any, nbRequests: any, nbLegacyRequests: any, registration?: { __typename?: 'Registration', expirationTime: any, claimer: { __typename?: 'Claimer', id: any } } | null, winnerClaim: Array<{ __typename?: 'Request', index: any, resolutionTime: any, evidenceGroup: { __typename?: 'EvidenceGroup', evidence: Array<{ __typename?: 'Evidence', uri: string }> } }> }, claimer: { __typename?: 'Claimer', id: any, name?: string | null, vouchesReceived: Array<{ __typename?: 'Vouch', from: { __typename?: 'Claimer', id: any, registration?: { __typename?: 'Registration', expirationTime: any, humanity: { __typename?: 'Humanity', vouching: boolean } } | null }, humanity: { __typename?: 'Humanity', id: any } }>, vouches: Array<{ __typename?: 'Vouch', for: { __typename?: 'Claimer', id: any, name?: string | null } }> }, evidenceGroup: { __typename?: 'EvidenceGroup', evidence: Array<{ __typename?: 'Evidence', id: any, uri: string, creationTime: any, submitter: any }> }, challenges: Array<{ __typename?: 'Challenge', id: any, disputeId: any, nbRounds: any, reason: { __typename?: 'Reason', id: string }, challenger?: { __typename?: 'Challenger', id: any } | null, rounds: Array<{ __typename?: 'Round', requesterFund: { __typename?: 'RequesterFund', amount: any }, challengerFund?: { __typename?: 'ChallengerFund', amount: any } | null }> }>, arbitratorHistory: { __typename?: 'ArbitratorHistory', updateTime: any, registrationMeta: string } } | null }; +export type RequestQuery = { __typename?: 'Query', request?: { __typename?: 'Request', index: any, revocation: boolean, registrationEvidenceRevokedReq: string, requester: any, creationTime: any, lastStatusChange: any, status: { __typename?: 'Status', id: string }, vouches: Array<{ __typename?: 'VouchInProcess', voucher: { __typename?: 'Humanity', id: any } }>, humanity: { __typename?: 'Humanity', id: any, nbRequests: any, nbLegacyRequests: any, registration?: { __typename?: 'Registration', expirationTime: any, claimer: { __typename?: 'Claimer', id: any } } | null, winnerClaim: Array<{ __typename?: 'Request', index: any, resolutionTime: any, evidenceGroup: { __typename?: 'EvidenceGroup', evidence: Array<{ __typename?: 'Evidence', uri: string }> } }> }, claimer: { __typename?: 'Claimer', id: any, name?: string | null, vouchesReceived: Array<{ __typename?: 'Vouch', from: { __typename?: 'Claimer', id: any, registration?: { __typename?: 'Registration', expirationTime: any, humanity: { __typename?: 'Humanity', vouching: boolean } } | null }, humanity: { __typename?: 'Humanity', id: any } }>, vouches: Array<{ __typename?: 'Vouch', for: { __typename?: 'Claimer', id: any, name?: string | null } }> }, evidenceGroup: { __typename?: 'EvidenceGroup', evidence: Array<{ __typename?: 'Evidence', id: any, uri: string, creationTime: any, submitter: any }> }, challenges: Array<{ __typename?: 'Challenge', id: any, disputeId: any, nbRounds: any, reason: { __typename?: 'Reason', id: string }, challenger?: { __typename?: 'Challenger', id: any } | null, rounds: Array<{ __typename?: 'Round', creationTime: any, requesterFund: { __typename?: 'RequesterFund', amount: any }, challengerFund?: { __typename?: 'ChallengerFund', amount: any } | null }> }>, arbitratorHistory: { __typename?: 'ArbitratorHistory', updateTime: any, registrationMeta: string } } | null }; export type RequestsQueryVariables = Exact<{ skip?: InputMaybe; @@ -3664,6 +3664,7 @@ export const RequestDocument = gql` } nbRounds rounds(orderBy: creationTime) { + creationTime requesterFund { amount } diff --git a/src/utils/misc.ts b/src/utils/misc.ts index 987e280..47aa9ed 100644 --- a/src/utils/misc.ts +++ b/src/utils/misc.ts @@ -41,6 +41,9 @@ export const randomString = (length: number) => export const formatEth = (wei: bigint, precision: number = 4) => +parseFloat(formatEther(wei)).toFixed(precision); +export const eth2Wei = (ethAmount: number): bigint => + BigInt(ethAmount * Math.pow(10, 18)); + export function romanize(n: number): string { if (n < 1) return ""; if (n >= 40) return `XL${romanize(n - 40)}`;