Skip to content

Commit 037bc7f

Browse files
committed
feat(web): transaction-batcher
1 parent 2d2c605 commit 037bc7f

File tree

2 files changed

+110
-16
lines changed

2 files changed

+110
-16
lines changed
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import { encodeFunctionData, type SimulateContractParameters } from "viem";
2+
import { useAccount, useWriteContract } from "wagmi";
3+
import { createUseSimulateContract, createUseWriteContract } from "wagmi/codegen";
4+
5+
import { DEFAULT_CHAIN } from "consts/chains";
6+
7+
import { isUndefined } from "src/utils";
8+
9+
const batcherAbi = [
10+
{
11+
inputs: [
12+
{
13+
internalType: "address[]",
14+
name: "targets",
15+
type: "address[]",
16+
},
17+
{
18+
internalType: "uint256[]",
19+
name: "values",
20+
type: "uint256[]",
21+
},
22+
{
23+
internalType: "bytes[]",
24+
name: "datas",
25+
type: "bytes[]",
26+
},
27+
],
28+
name: "batchSend",
29+
outputs: [],
30+
stateMutability: "payable",
31+
type: "function",
32+
},
33+
] as const;
34+
35+
const useBatchSimulate = createUseSimulateContract({
36+
abi: batcherAbi,
37+
functionName: "batchSend",
38+
});
39+
40+
export const useBatchWrite = createUseWriteContract({
41+
abi: batcherAbi,
42+
functionName: "batchSend",
43+
});
44+
45+
const batcherAddress = {
46+
421614: "0xe8061d185D865ce2B2FbCfDa628b5F147d8eB8Ab",
47+
};
48+
49+
export type TransactionBatcherConfig = SimulateContractParameters[];
50+
51+
const useTransactionBatcher = (configs?: TransactionBatcherConfig) => {
52+
const { chainId } = useAccount();
53+
54+
const validConfigs = configs ?? [];
55+
const {
56+
data: batchConfig,
57+
isLoading,
58+
isError,
59+
} = useBatchSimulate({
60+
query: {
61+
enabled: !isUndefined(configs),
62+
},
63+
address: batcherAddress[chainId ?? DEFAULT_CHAIN],
64+
args: [
65+
validConfigs.map((config) => config?.address),
66+
validConfigs.map((config) => config?.value ?? BigInt(0)),
67+
validConfigs.map((config) => encodeFunctionData(config)),
68+
],
69+
});
70+
const { writeContractAsync } = useWriteContract();
71+
72+
const executeBatch = () => batchConfig && writeContractAsync(batchConfig.request);
73+
return { executeBatch, batchConfig, isError, isLoading };
74+
};
75+
76+
export default useTransactionBatcher;

web/src/pages/Cases/CaseDetails/MaintenanceButtons/DistributeRewards.tsx

Lines changed: 34 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
1-
import React, { useMemo, useState } from "react";
1+
import React, { useEffect, useMemo, useState } from "react";
22
import styled from "styled-components";
33

4-
import { usePublicClient } from "wagmi";
4+
import { useAccount, usePublicClient } from "wagmi";
55

66
import { Button } from "@kleros/ui-components-library";
77

8-
import { useSimulateKlerosCoreExecute, useWriteKlerosCoreExecute } from "hooks/contracts/generated";
8+
import { DEFAULT_CHAIN } from "consts/chains";
9+
import { klerosCoreAbi, klerosCoreAddress } from "hooks/contracts/generated";
10+
import useTransactionBatcher, { type TransactionBatcherConfig, useBatchWrite } from "hooks/useTransactionBatcher";
911
import { wrapWithToast } from "utils/wrapWithToast";
1012

1113
import { isUndefined } from "src/utils";
@@ -23,32 +25,48 @@ interface IDistributeRewards extends IBaseMaintenaceButton {
2325

2426
const DistributeRewards: React.FC<IDistributeRewards> = ({ id, numberOfVotes, roundIndex, setIsOpen }) => {
2527
const [isSending, setIsSending] = useState(false);
28+
const [contractConfigs, setContractConfigs] = useState<TransactionBatcherConfig>();
2629
const publicClient = usePublicClient();
30+
const { chainId } = useAccount();
2731

28-
const {
29-
data: executeConfig,
30-
isLoading: isLoadingConfig,
31-
isError,
32-
} = useSimulateKlerosCoreExecute({
33-
query: {
34-
enabled: !isUndefined(id) && !isUndefined(numberOfVotes) && !isUndefined(roundIndex),
35-
},
36-
args: [BigInt(id ?? 0), BigInt(roundIndex ?? 0), BigInt(numberOfVotes ?? 0)],
37-
});
32+
useEffect(() => {
33+
if (!id || !roundIndex || !numberOfVotes) return;
3834

39-
const { writeContractAsync: execute } = useWriteKlerosCoreExecute();
35+
const baseArgs = {
36+
abi: klerosCoreAbi,
37+
address: klerosCoreAddress[chainId ?? DEFAULT_CHAIN],
38+
functionName: "execute",
39+
};
40+
41+
const argsArr: TransactionBatcherConfig = [];
42+
let nbVotes = parseInt(numberOfVotes);
43+
44+
// each previous round has (n - 1)/2 jurors
45+
for (let i = parseInt(roundIndex); i >= 0; i--) {
46+
argsArr.push({ ...baseArgs, args: [BigInt(id), BigInt(i), BigInt(nbVotes)] });
47+
48+
nbVotes = (nbVotes - 1) / 2;
49+
}
50+
51+
setContractConfigs(argsArr);
52+
}, [id, roundIndex, numberOfVotes, chainId]);
53+
54+
const { batchConfig, isLoading: isLoadingConfig, isError } = useTransactionBatcher(contractConfigs);
55+
56+
const { writeContractAsync: executeBatch } = useBatchWrite();
4057

4158
const isLoading = useMemo(() => isLoadingConfig || isSending, [isLoadingConfig, isSending]);
4259
const isDisabled = useMemo(
4360
() => isUndefined(id) || isUndefined(numberOfVotes) || isError || isLoading,
4461
[id, numberOfVotes, isError, isLoading]
4562
);
63+
4664
const handleClick = () => {
47-
if (!executeConfig) return;
65+
if (!batchConfig) return;
4866

4967
setIsSending(true);
5068

51-
wrapWithToast(async () => await execute(executeConfig.request), publicClient).finally(() => {
69+
wrapWithToast(async () => await executeBatch(batchConfig.request), publicClient).finally(() => {
5270
setIsOpen(false);
5371
});
5472
};

0 commit comments

Comments
 (0)