• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

cartesi / rollups-explorer / 11826960107

13 Nov 2024 10:31PM CUT coverage: 85.956% (-0.6%) from 86.605%
11826960107

Pull #256

github

nevendyulgerov
test(apps/web): Tweak layout for tuple inputs
Pull Request #256: #251 Add ABI encoding for raw input form

1368 of 1630 branches covered (83.93%)

Branch coverage included in aggregate %.

793 of 984 new or added lines in 13 files covered. (80.59%)

9465 of 10973 relevant lines covered (86.26%)

47.16 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

89.08
/packages/ui/src/GenericInputForm/index.tsx
1
import {
1✔
2
    useSimulateInputBoxAddInput,
3
    useWriteInputBoxAddInput,
4
} from "@cartesi/rollups-wagmi";
1✔
5
import {
6
    Alert,
7
    Autocomplete,
8
    Button,
9
    Collapse,
10
    Group,
11
    Loader,
12
    SegmentedControl,
13
    Stack,
14
    Textarea,
15
} from "@mantine/core";
1✔
16
import { FC, useCallback, useEffect } from "react";
1✔
17
import { TbAlertCircle, TbCheck } from "react-icons/tb";
1✔
18
import {
19
    Abi,
20
    AbiFunction,
21
    BaseError,
22
    encodeAbiParameters,
23
    encodeFunctionData,
24
    stringToHex,
25
} from "viem";
1✔
26
import { useWaitForTransactionReceipt } from "wagmi";
1✔
27
import { TransactionProgress } from "../TransactionProgress";
1✔
28
import useUndeployedApplication from "../hooks/useUndeployedApplication";
1✔
29
import { TransactionFormSuccessData } from "../DepositFormTypes";
30
import { AbiFields } from "./AbiFields";
1✔
31
import { AbiValueParameter, FormMode, FormSpecification } from "./types";
32
import { FormProvider } from "./context";
1✔
33
import { useGenericInputForm } from "./useGenericInputForm";
1✔
34
import { useDebouncedCallback } from "@mantine/hooks";
1✔
35
import { generateFinalValues } from "./utils";
1✔
36

37
export interface GenericInputFormSpecification {
38
    id: string;
39
    name: string;
40
    abi: Abi;
41
}
42

43
export interface GenericInputFormProps {
44
    applications: string[];
45
    specifications: GenericInputFormSpecification[];
46
    isLoadingApplications: boolean;
47
    onSearchApplications: (applicationId: string) => void;
48
    onSuccess: (receipt: TransactionFormSuccessData) => void;
49
}
50

51
export const GenericInputForm: FC<GenericInputFormProps> = (props) => {
1✔
52
    const {
116✔
53
        applications,
116✔
54
        specifications,
116✔
55
        isLoadingApplications,
116✔
56
        onSearchApplications,
116✔
57
        onSuccess,
116✔
58
    } = props;
116✔
59
    const form = useGenericInputForm(specifications);
116✔
60
    const {
116✔
61
        address,
116✔
62
        rawInput,
116✔
63
        mode,
116✔
64
        abiFunction,
116✔
65
        selectedSpecification,
116✔
66
        specificationMode,
116✔
67
        abiFunctionName,
116✔
68
    } = form.getTransformedValues();
116✔
69
    const prepare = useSimulateInputBoxAddInput({
116✔
70
        args: [address, rawInput],
116✔
71
        query: {
116✔
72
            enabled: form.isValid(),
116✔
73
        },
116✔
74
    });
116✔
75

76
    const execute = useWriteInputBoxAddInput();
116✔
77
    const wait = useWaitForTransactionReceipt({
116✔
78
        hash: execute.data,
116✔
79
    });
116✔
80
    const loading = execute.isPending || wait.isLoading;
116✔
81
    const isFormValid = form.isValid();
116✔
82
    const canSubmit = isFormValid && prepare.error === null;
116✔
83
    const isUndeployedApp = useUndeployedApplication(address, applications);
116✔
84
    const abiFunctionParams = form.getInputProps("abiFunctionParams");
116✔
85

86
    const onChangeFormMode = useCallback(
116✔
87
        (mode: string | null) => {
116✔
88
            const application = form.getInputProps("application");
8✔
89
            form.reset();
8✔
90
            form.setFieldValue("mode", mode as FormMode);
8✔
91
            form.setFieldValue("application", application.value);
8✔
92
        },
8✔
93
        [form],
116✔
94
    );
116✔
95

96
    const encodeFunctionParamsDebounced = useDebouncedCallback(
116✔
97
        (params: AbiValueParameter[]) => {
116✔
98
            const abiFunctions =
1✔
99
                (selectedSpecification?.abi as AbiFunction[]) ?? [];
1!
100
            const nextAbiFunction = abiFunctions.find(
1✔
101
                (f) => f.name === abiFunctionName,
1✔
102
            );
1✔
103

104
            if (nextAbiFunction) {
1!
NEW
105
                const finalValues = generateFinalValues(
×
NEW
106
                    nextAbiFunction.inputs.slice(),
×
NEW
107
                    params,
×
NEW
108
                );
×
109

NEW
110
                if (specificationMode === "json_abi") {
×
NEW
111
                    const payload = encodeFunctionData({
×
NEW
112
                        abi: (selectedSpecification as FormSpecification)?.abi,
×
NEW
113
                        functionName: (abiFunction as AbiFunction)?.name,
×
NEW
114
                        args: finalValues,
×
NEW
115
                    });
×
NEW
116
                    form.setFieldValue("rawInput", payload);
×
NEW
117
                } else if (specificationMode === "abi_params") {
×
NEW
118
                    const payload = encodeAbiParameters(
×
NEW
119
                        nextAbiFunction.inputs,
×
NEW
120
                        finalValues,
×
NEW
121
                    );
×
NEW
122
                    form.setFieldValue("rawInput", payload);
×
NEW
123
                }
×
NEW
124
            }
×
125
        },
1✔
126
        400,
116✔
127
    );
116✔
128

129
    useEffect(() => {
116✔
130
        if (wait.isSuccess) {
37✔
131
            onSuccess({ receipt: wait.data, type: "RAW" });
11✔
132
            form.reset();
11✔
133
            execute.reset();
11✔
134
            onSearchApplications("");
11✔
135
        }
11✔
136
        // eslint-disable-next-line react-hooks/exhaustive-deps
137
    }, [wait, onSearchApplications, onSuccess]);
116✔
138

139
    useEffect(() => {
116✔
140
        if (isFormValid) {
94✔
141
            encodeFunctionParamsDebounced(abiFunctionParams.value);
12✔
142
        }
12✔
143
    }, [abiFunctionParams.value, isFormValid, encodeFunctionParamsDebounced]);
116✔
144

145
    return (
116✔
146
        <FormProvider form={form}>
116✔
147
            <form data-testid="raw-input-form">
116✔
148
                <Stack>
116✔
149
                    <Autocomplete
116✔
150
                        label="Application"
116✔
151
                        description="The application smart contract address"
116✔
152
                        placeholder="0x"
116✔
153
                        data={applications}
116✔
154
                        withAsterisk
116✔
155
                        data-testid="application-autocomplete"
116✔
156
                        rightSection={
116✔
157
                            (prepare.isLoading || isLoadingApplications) && (
116!
NEW
158
                                <Loader size="xs" />
×
159
                            )
160
                        }
161
                        {...form.getInputProps("application")}
116✔
162
                        error={
116✔
163
                            form.errors.application ||
116✔
164
                            (prepare.error as BaseError)?.shortMessage
115!
165
                        }
166
                        onChange={(nextValue) => {
116✔
167
                            form.setFieldValue("application", nextValue);
13✔
168
                            onSearchApplications(nextValue);
13✔
169
                        }}
13✔
170
                    />
116✔
171

172
                    {!form.errors.application && isUndeployedApp && (
116✔
173
                        <Alert
1✔
174
                            variant="light"
1✔
175
                            color="yellow"
1✔
176
                            icon={<TbAlertCircle />}
1✔
177
                        >
1✔
178
                            This is an undeployed application.
179
                        </Alert>
1✔
180
                    )}
181

182
                    <SegmentedControl
116✔
183
                        value={mode}
116✔
184
                        onChange={onChangeFormMode}
116✔
185
                        data={[
116✔
186
                            { label: "Hex", value: "hex" },
116✔
187
                            { label: "String to Hex", value: "string" },
116✔
188
                            { label: "ABI to Hex", value: "abi" },
116✔
189
                        ]}
116✔
190
                    />
116✔
191

192
                    {mode === "hex" ? (
116✔
193
                        <Textarea
53✔
194
                            label="Hex input"
53✔
195
                            description="Hex input for the application"
53✔
196
                            withAsterisk
53✔
197
                            data-testid="hex-textarea"
53✔
198
                            {...form.getInputProps("rawInput")}
53✔
199
                        />
53✔
200
                    ) : mode === "string" ? (
63✔
201
                        <>
3✔
202
                            <Textarea
3✔
203
                                label="String input"
3✔
204
                                description="String input for the application"
3✔
205
                                {...form.getInputProps("stringInput")}
3✔
206
                                onChange={(event) => {
3✔
207
                                    const nextValue = event.target.value;
1✔
208
                                    form.setFieldValue(
1✔
209
                                        "stringInput",
1✔
210
                                        nextValue,
1✔
211
                                    );
1✔
212

213
                                    form.setFieldValue(
1✔
214
                                        "rawInput",
1✔
215
                                        stringToHex(nextValue),
1✔
216
                                    );
1✔
217
                                }}
1✔
218
                            />
3✔
219

220
                            <Textarea
3✔
221
                                label="Hex value"
3✔
222
                                description="Encoded hex value for the application"
3✔
223
                                readOnly
3✔
224
                                {...form.getInputProps("rawInput")}
3✔
225
                            />
3✔
226
                        </>
3✔
227
                    ) : mode === "abi" ? (
60✔
228
                        <AbiFields specifications={specifications} />
59✔
229
                    ) : null}
1✔
230

231
                    <Collapse
116✔
232
                        in={
116✔
233
                            execute.isPending ||
116✔
234
                            wait.isLoading ||
116✔
235
                            execute.isSuccess ||
116✔
236
                            execute.isError
116✔
237
                        }
238
                    >
239
                        <TransactionProgress
116✔
240
                            prepare={prepare}
116✔
241
                            execute={execute}
116✔
242
                            wait={wait}
116✔
243
                            confirmationMessage="Raw input sent successfully!"
116✔
244
                            defaultErrorMessage={execute.error?.message}
116!
245
                        />
116✔
246
                    </Collapse>
116✔
247

248
                    <Group justify="right">
116✔
249
                        <Button
116✔
250
                            variant="filled"
116✔
251
                            disabled={!canSubmit}
116✔
252
                            leftSection={<TbCheck />}
116✔
253
                            loading={loading}
116✔
254
                            data-testid="generic-input-submit-button"
116✔
255
                            onClick={() =>
116✔
256
                                execute.writeContract(prepare.data!.request)
7✔
257
                            }
258
                        >
116✔
259
                            Send
260
                        </Button>
116✔
261
                    </Group>
116✔
262
                </Stack>
116✔
263
            </form>
116✔
264
        </FormProvider>
116✔
265
    );
266
};
116✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc