Skip to content

Commit c4419cb

Browse files
committed
feat: add record after challenge success
1 parent 72c75a6 commit c4419cb

File tree

2 files changed

+161
-71
lines changed

2 files changed

+161
-71
lines changed

src/components/Charts/ClaimChart/index.tsx

Lines changed: 128 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,9 @@ import { useEthersSigner } from "@/hooks/useEthersSigner";
2020
import { getChallengeContract } from "@/service/contract";
2121
import { XMarkIcon } from "@heroicons/react/24/solid";
2222
import useAutoSwitchNetwork from "@/hooks/useAutoSwitchNetwork";
23-
import { EthersError, formatUnits } from 'ethers'
23+
import { EthersError, formatUnits } from "ethers";
2424
import { useEthersProvider } from "@/hooks/useEthersProvider";
25+
import { useFrontendMoveMutation } from "@/hooks/useFrontendMove";
2526

2627
const xGap = 45;
2728
const yGap = 50;
@@ -32,8 +33,8 @@ type Node = {
3233
claim: string;
3334
position: string;
3435
value: string;
35-
parentIndex: number,
36-
isRoot?: boolean,
36+
parentIndex: number;
37+
isRoot?: boolean;
3738
itemStyle: {
3839
color: string;
3940
};
@@ -138,9 +139,13 @@ const genNodesAndLinks = (data: ClaimData[]): any => {
138139
};
139140
};
140141

141-
const ClaimChart: FC<{ claimData: ClaimData[], address: string, resolved: boolean }> = ({ claimData, address, resolved }) => {
142+
const ClaimChart: FC<{
143+
claimData: ClaimData[];
144+
address: string;
145+
resolved: boolean;
146+
}> = ({ claimData, address, resolved }) => {
142147
const { nodes, links, maxDepth } = genNodesAndLinks(claimData);
143-
useAutoSwitchNetwork()
148+
useAutoSwitchNetwork();
144149
const { isMutating, trigger } = useCalculateClaim();
145150
const options: EChartOption<EChartOption.SeriesGraph> = {
146151
tooltip: {
@@ -196,111 +201,155 @@ const ClaimChart: FC<{ claimData: ClaimData[], address: string, resolved: boolea
196201
],
197202
};
198203
const { openConnectModal } = useConnectModal();
199-
const { isConnected } = useAccount()
204+
const { address: addr, isConnected } = useAccount();
200205
const [showModal, setShowModal] = useState(false);
201206
const [modalData, setModalData] = useState<Node>();
202207
const [val, setVal] = useState("");
203-
const [recommendAttackClaim, setAttackClaim] = useState("")
208+
const [recommendAttackClaim, setAttackClaim] = useState("");
204209
const [attackLoading, setAttackLoading] = useState(false);
205210
const [defendLoading, setDefendLoading] = useState(false);
206-
const signer = useEthersSigner()
207-
const provider = useEthersProvider()
211+
const signer = useEthersSigner();
212+
const provider = useEthersProvider();
208213
const [gas, setGas] = useState({
209-
attackGas: '',
210-
defendGas: ''
211-
})
214+
attackGas: "",
215+
defendGas: "",
216+
});
217+
218+
const { trigger: frontMove } = useFrontendMoveMutation();
212219

213220
const attackPosition = useMemo(() => {
214221
if (modalData) {
215-
return 2 * Number(modalData.position)
222+
return 2 * Number(modalData.position);
216223
}
217-
}, [modalData])
224+
}, [modalData]);
218225
const defendPosition = useMemo(() => {
219226
if (modalData) {
220-
return 2 * (Number(modalData.position) + 1)
227+
return 2 * (Number(modalData.position) + 1);
221228
}
222-
}, [modalData])
229+
}, [modalData]);
223230

224231
const handleClick = (e: any) => {
225232
if (!isConnected) {
226-
openConnectModal && openConnectModal()
227-
return
233+
openConnectModal && openConnectModal();
234+
return;
228235
}
229236
if (resolved) {
230-
toast.warning('This game has already resolved!')
231-
return
237+
toast.warning("This game has already resolved!");
238+
return;
232239
}
233240
setShowModal(true);
234241
setModalData(e.data);
235242
};
236243

237244
useEffect(() => {
238245
if (attackPosition) {
239-
trigger({ disputeGame: address, position: attackPosition }).then((res) => {
240-
setAttackClaim(res.claims)
241-
})
246+
trigger({ disputeGame: address, position: attackPosition }).then(
247+
(res) => {
248+
setAttackClaim(res.claims);
249+
}
250+
);
242251
}
243-
}, [attackPosition])
252+
}, [attackPosition]);
244253

245254
useEffect(() => {
246255
const getGas = async () => {
247256
if (attackPosition && defendPosition && provider) {
248-
const contract = getChallengeContract(address, provider)
249-
const attackGas = await contract.getRequiredBond(attackPosition)
250-
const defendGas = await contract.getRequiredBond(defendPosition)
257+
const contract = getChallengeContract(address, provider);
258+
const attackGas = await contract.getRequiredBond(attackPosition);
259+
const defendGas = await contract.getRequiredBond(defendPosition);
251260
setGas({
252261
attackGas: formatUnits(attackGas, 18),
253-
defendGas: formatUnits(defendGas, 18)
254-
})
262+
defendGas: formatUnits(defendGas, 18),
263+
});
255264
}
256-
}
257-
getGas()
258-
}, [attackPosition, defendPosition, provider])
259-
265+
};
266+
getGas();
267+
}, [attackPosition, defendPosition, provider]);
260268

261269
const handleAttack = async () => {
262270
if (!signer) return;
263271
if (!val) {
264-
toast.error('Challenge claim required!')
265-
return
266-
};
267-
const contract = getChallengeContract(address, signer)
272+
toast.error("Challenge claim required!");
273+
return;
274+
}
275+
const contract = getChallengeContract(address, signer);
276+
const game_contract = contract.getAddress;
268277
try {
269-
setAttackLoading(true)
270-
const gas = await contract.getRequiredBond(attackPosition)
271-
const tx = await contract.attack('0x' + modalData?.claim, modalData?.parentIndex, val, { value: gas })
272-
const res = await tx.wait()
273-
setAttackLoading(false)
278+
setAttackLoading(true);
279+
const gas = await contract.getRequiredBond(attackPosition);
280+
const tx = await contract.attack(
281+
"0x" + modalData?.claim,
282+
modalData?.parentIndex,
283+
val,
284+
{ value: gas }
285+
);
286+
const res = await tx.wait();
287+
setAttackLoading(false);
274288
if (res.status === 1) {
275-
toast.success('Transaction receipt!')
289+
toast.success("Transaction receipt!");
290+
const data = {
291+
game_contract: game_contract,
292+
tx_hash: tx.hash,
293+
claimant: addr,
294+
parent_index: modalData?.parentIndex,
295+
challenge_index: modalData?.position,
296+
disputed_claim: modalData?.claim,
297+
claim: val,
298+
is_attack: true,
299+
};
300+
await frontMove(data);
276301
}
277302
} catch (error: any) {
278-
setAttackLoading(false)
279-
toast.error(error?.shortMessage || error?.reason || 'Transaction error!')
303+
setAttackLoading(false);
304+
toast.error(error?.shortMessage || error?.reason || "Transaction error!");
280305
}
281306
};
282307

283308
const handleDefend = async () => {
284309
if (!val) {
285-
toast.error('Challenge claim required!')
286-
return
287-
};
310+
toast.error("Challenge claim required!");
311+
return;
312+
}
288313
if (!signer) return;
289-
const contract = getChallengeContract(address, signer)
314+
const contract = getChallengeContract(address, signer);
315+
const game_contract = contract.getAddress;
290316
try {
291-
setDefendLoading(true)
292-
const gas = await contract.getRequiredBond(defendPosition)
293-
const tx = await contract.defend('0x' + modalData?.claim, modalData?.parentIndex, val, { value: gas })
294-
const res = await tx.wait()
317+
setDefendLoading(true);
318+
const gas = await contract.getRequiredBond(defendPosition);
319+
const tx = await contract.defend(
320+
"0x" + modalData?.claim,
321+
modalData?.parentIndex,
322+
val,
323+
{ value: gas }
324+
);
325+
const res = await tx.wait();
295326
if (res.status === 1) {
296-
toast.success('Transaction receipt!')
327+
toast.success("Transaction receipt!");
328+
const data = {
329+
game_contract: game_contract,
330+
tx_hash: tx.hash,
331+
claimant: addr,
332+
parent_index: modalData?.parentIndex,
333+
challenge_index: modalData?.position,
334+
disputed_claim: modalData?.claim,
335+
claim: val,
336+
is_attack: false,
337+
};
338+
await frontMove(data);
297339
}
298-
setDefendLoading(false)
340+
setDefendLoading(false);
299341
} catch (error: any) {
300-
setDefendLoading(false)
301-
toast.error(error?.shortMessage || error?.reason || error?.msg || error?.data || error?.message || 'Transaction error!')
342+
setDefendLoading(false);
343+
toast.error(
344+
error?.shortMessage ||
345+
error?.reason ||
346+
error?.msg ||
347+
error?.data ||
348+
error?.message ||
349+
"Transaction error!"
350+
);
302351
}
303-
}
352+
};
304353

305354
return (
306355
<>
@@ -309,8 +358,8 @@ const ClaimChart: FC<{ claimData: ClaimData[], address: string, resolved: boolea
309358
as="div"
310359
className="relative z-10 focus:outline-none"
311360
onClose={() => {
312-
setShowModal(false)
313-
setVal('')
361+
setShowModal(false);
362+
setVal("");
314363
}}
315364
>
316365
<DialogBackdrop
@@ -323,17 +372,23 @@ const ClaimChart: FC<{ claimData: ClaimData[], address: string, resolved: boolea
323372
transition
324373
className="w-full max-w-md rounded-xl dark:bg-surface-dark bg-white p-6 backdrop-blur-2xl duration-300 ease-out data-[closed]:transform-[scale(95%)] data-[closed]:opacity-0"
325374
>
326-
<DialogTitle as="h3" className="text-base/7 font-medium flex justify-between">
375+
<DialogTitle
376+
as="h3"
377+
className="text-base/7 font-medium flex justify-between"
378+
>
327379
Challenge
328-
<XMarkIcon onClick={() => setShowModal(false)} className="w-6 cursor-pointer" />
380+
<XMarkIcon
381+
onClick={() => setShowModal(false)}
382+
className="w-6 cursor-pointer"
383+
/>
329384
</DialogTitle>
330385
<div className="mt-4 text-sm/6 text-white/50">
331386
<div>
332387
<div className="text-sm font-semibold text-contentSecondary-light dark:text-warmGray-300 mb-1">
333388
Claim:
334389
</div>
335390
<div className="text-sm text-contentSecondary-light dark:text-warmGray-300 mb-2 break-all">
336-
{'0x' + modalData?.claim}
391+
{"0x" + modalData?.claim}
337392
</div>
338393
</div>
339394
<div>
@@ -345,16 +400,16 @@ const ClaimChart: FC<{ claimData: ClaimData[], address: string, resolved: boolea
345400
</div>
346401
</div>
347402

348-
{
349-
!modalData?.isRoot && <div>
403+
{!modalData?.isRoot && (
404+
<div>
350405
<div className="text-sm font-semibold text-contentSecondary-light dark:text-warmGray-300 mb-1">
351406
Defend Required Bond
352407
</div>
353408
<div className="text-sm text-contentSecondary-light dark:text-warmGray-300 mb-2 break-all">
354409
{gas.defendGas} ETH
355410
</div>
356411
</div>
357-
}
412+
)}
358413
<div>
359414
<div className="text-sm font-semibold text-contentSecondary-light dark:text-warmGray-300 mb-1">
360415
Attack Required Bond
@@ -373,20 +428,22 @@ const ClaimChart: FC<{ claimData: ClaimData[], address: string, resolved: boolea
373428
id="search"
374429
value={val}
375430
onChange={(e) => setVal(e.target.value)}
376-
className={"rounded-none rounded-l-md text-black dark:text-warmGray-300 h-10 "}
431+
className={
432+
"rounded-none rounded-l-md text-black dark:text-warmGray-300 h-10 "
433+
}
377434
placeholder={"challenge string"}
378435
/>
379436
</div>
380437
</div>
381438
<div className="mt-4 flex justify-end gap-4">
382-
{
383-
!modalData?.isRoot && <Button
439+
{!modalData?.isRoot && (
440+
<Button
384441
label="defend"
385442
variant="outline"
386443
icon={defendLoading ? <Spinner /> : undefined}
387444
onClick={handleDefend}
388445
></Button>
389-
}
446+
)}
390447
<Button
391448
icon={attackLoading ? <Spinner /> : undefined}
392449
label="attack"
@@ -402,7 +459,7 @@ const ClaimChart: FC<{ claimData: ClaimData[], address: string, resolved: boolea
402459
title={
403460
<div className="flex items-center gap-10">
404461
<div>Fault Dispute Game Graph</div>
405-
<ConnectButton chainStatus={'none'} showBalance={false} />
462+
<ConnectButton chainStatus={"none"} showBalance={false} />
406463
</div>
407464
}
408465
handleClick={handleClick}

src/hooks/useFrontendMove.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import useSWR, { SWRResponse } from "swr";
2+
import { post, get } from "@/service/index";
3+
import { ClaimData, ListResponse } from "@/types";
4+
5+
import useSWRMutation from "swr/mutation";
6+
7+
async function postFetcher(url: string, { arg }: { arg: any }) {
8+
return await post(url, arg);
9+
}
10+
11+
export const useFrontendMoveMutation = () => {
12+
return useSWRMutation("/api/disputegames/frontend-move", postFetcher);
13+
};
14+
15+
export const useGetFrontendMoves = (address: string) => {
16+
const url = `/api/disputegames/${address}/frontend-moves`;
17+
const params = {};
18+
const fetcher = async (): Promise<ListResponse<ClaimData>> => {
19+
return await get(url, params);
20+
};
21+
const res = useSWR(url, fetcher);
22+
return res;
23+
};
24+
25+
export const useGetFrontendMovesByTx = (txHash: string) => {
26+
const url = `/api/disputegames/frontend-move/{txHash}`;
27+
const params = {};
28+
const fetcher = async (): Promise<ListResponse<ClaimData>> => {
29+
return await get(url, params);
30+
};
31+
const res = useSWR(url, fetcher);
32+
return res;
33+
};

0 commit comments

Comments
 (0)