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

cartesi / rollups-explorer / 9950359198

16 Jul 2024 03:38AM UTC coverage: 94.504% (+0.2%) from 94.266%
9950359198

Pull #213

github

brunomenezes
feat: Add specification filtering and solidity-highlight-extensions for code-highlighting.
Pull Request #213: Feature/165 add decode specification v1

853 of 999 branches covered (85.39%)

Branch coverage included in aggregate %.

699 of 708 new or added lines in 8 files covered. (98.73%)

13 existing lines in 2 files now uncovered.

9602 of 10064 relevant lines covered (95.41%)

50.34 hits per line

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

94.22
/apps/web/src/components/specification/decoder.ts
1
import {
1✔
2
    T,
1✔
3
    cond,
1✔
4
    head,
1✔
5
    includes,
1✔
6
    isEmpty,
1✔
7
    isNil,
1✔
8
    pathEq,
1✔
9
    pathOr,
1✔
10
    pipe,
1✔
11
} from "ramda";
1✔
12
import {
1✔
13
    AbiDecodingDataSizeTooSmallError,
1✔
14
    AbiFunction,
1✔
15
    AbiFunctionSignatureNotFoundError,
1✔
16
    Hex,
1✔
17
    InvalidAbiParametersError,
1✔
18
    decodeAbiParameters,
1✔
19
    decodeFunctionData,
1✔
20
    parseAbiParameters,
1✔
21
    slice,
1✔
22
} from "viem";
1✔
23
import SpecificationModeNotSupportedError from "./errors/SpecificationModeNotSupported";
1✔
24
import { ABI_PARAMS, JSON_ABI, Specification, specModes } from "./types";
1✔
25

1✔
26
interface Piece {
1✔
27
    name: string;
1✔
28
    part: Hex | Uint8Array;
1✔
29
    decodedPart?: any;
1✔
30
}
1✔
31

1✔
32
interface Envelope {
1✔
33
    spec: Specification;
1✔
34
    input: Hex | Uint8Array;
1✔
35
    pieces: Piece[];
1✔
36
    result: Record<string, any>;
1✔
37
    error?: Error;
1✔
38
}
1✔
39

1✔
40
const getPieces = (
1✔
41
    abiParams: readonly string[],
7✔
42
    encodedData: Hex | Uint8Array,
7✔
43
): Piece[] => {
7✔
44
    const abiParameters = parseAbiParameters(abiParams);
7✔
45

7✔
46
    const resultList = decodeAbiParameters(
7✔
47
        // @ts-ignore dynamic type complains
7✔
48
        abiParameters,
7✔
49
        encodedData,
7✔
50
    );
7✔
51

7✔
52
    return resultList.map((decodedVal, index) => {
7✔
53
        // @ts-ignore
20✔
54
        const { name } = abiParameters[index] as {
20✔
55
            name: string;
20✔
56
        };
20✔
57

20✔
58
        return {
20✔
59
            name,
20✔
60
            part: encodedData,
20✔
61
            decodedPart: decodedVal,
20✔
62
        } as Piece;
20✔
63
    });
7✔
64
};
7✔
65

1✔
66
const addPiecesToEnvelope = (e: Envelope): Envelope => {
1✔
67
    if (!e.error && e.spec.mode === "abi_params") {
10✔
68
        try {
10✔
69
            if (e.spec.sliceInstructions?.length) {
10✔
70
                e.spec.sliceInstructions.forEach((instruction, index) => {
6✔
71
                    const { from, to, name, type } = instruction;
17✔
72
                    const part = slice(e.input, from, to);
17✔
73
                    const decodedPart =
17✔
74
                        !isNil(type) && !isEmpty(type)
17✔
75
                            ? head(decodeAbiParameters([{ type, name }], part))
4✔
76
                            : part;
13✔
77

17✔
78
                    e.pieces.push({
17✔
79
                        name: name ?? `param${index}`,
17!
80
                        part,
17✔
81
                        decodedPart,
17✔
82
                    });
17✔
83
                });
6✔
84
            } else {
10✔
85
                const pieces = getPieces(e.spec.abiParams, e.input);
4✔
86
                e.pieces.push(...pieces);
4✔
87
            }
4✔
88
        } catch (error: any) {
10✔
89
            const message = pathOr(error.message, ["shortMessage"], error);
1✔
90
            const errorMeta = pathOr([], ["metaMessages"], error).join("\n");
1✔
91
            e.error = new Error(`${message}\n\n${errorMeta}`);
1✔
92
        }
1✔
93
    }
10✔
94

10✔
95
    return e;
10✔
96
};
10✔
97

1✔
98
const decodeTargetSliceAndAddToPieces = (e: Envelope): Envelope => {
1✔
99
    if (!e.error && e.spec.mode === "abi_params") {
10✔
100
        const targetName = e.spec.sliceTarget;
9✔
101
        const piece = e.pieces.find((piece) => piece.name === targetName);
9✔
102

9✔
103
        try {
9✔
104
            if (piece && piece.part) {
9✔
105
                const pieces = getPieces(e.spec.abiParams, piece.part);
3✔
106
                e.pieces.push(...pieces);
3✔
107
            }
3✔
108
        } catch (error: any) {
9✔
109
            const message = pathOr(error.message, ["shortMessage"], error);
1✔
110
            let errorMeta;
1✔
111
            let extra;
1✔
112

1✔
113
            if (error instanceof AbiDecodingDataSizeTooSmallError) {
1✔
114
                errorMeta = pathOr([], ["metaMessages"], error).join("\n");
1✔
115
                extra = `Slice name: "${targetName}" (Is it the right one?)`;
1✔
116
            }
1✔
117

1✔
118
            if (error instanceof InvalidAbiParametersError) {
1!
NEW
119
                errorMeta = `ABI Parameters : [ ${e.spec.abiParams.join(
×
NEW
120
                    ",",
×
NEW
121
                )} ]`;
×
NEW
122
                extra = "Check the ABI parameters defined.";
×
NEW
123
            }
×
124

1✔
125
            const errorMessage = `${message}\n\n${errorMeta ?? ""}\n${
1!
126
                extra ?? ""
1!
127
            }`;
1✔
128

1✔
129
            e.error = new Error(errorMessage);
1✔
130
        }
1✔
131
    }
9✔
132
    return e;
10✔
133
};
10✔
134

1✔
135
const prepareResultFromPieces = (e: Envelope): Envelope => {
1✔
136
    if (!e.error && e.spec.mode === "abi_params") {
10✔
137
        const sliceTarget = e.spec.sliceTarget;
8✔
138
        e.result = e.pieces.reduce((prev, { name, decodedPart }, index) => {
8✔
139
            /**
33✔
140
             * Adding a unwrap effect
33✔
141
             * decoded target is not included in the result
33✔
142
             */
33✔
143
            if (sliceTarget === name) return prev;
33✔
144

31✔
145
            const key = name ?? `params${index}`;
33!
146
            return { ...prev, [key]: decodedPart };
33✔
147
        }, {});
8✔
148
    }
8✔
149

10✔
150
    return e;
10✔
151
};
10✔
152

1✔
153
const prepareResultForJSONABI = (e: Envelope): Envelope => {
1✔
154
    if (e.spec.mode === "json_abi") {
2✔
155
        try {
2✔
156
            const { functionName, args } = decodeFunctionData({
2✔
157
                abi: e.spec.abi,
2✔
158
                data: e.input as Hex,
2✔
159
            });
2✔
160

2✔
161
            const orderedNamedArgs: [string, any][] = [];
2✔
162

2✔
163
            if (args && args?.length > 0) {
2✔
164
                const abiItem = e.spec.abi.find(
1✔
165
                    (item) =>
1✔
166
                        item.type === "function" && item.name === functionName,
2✔
167
                ) as AbiFunction;
1✔
168

1✔
169
                // respecting order of arguments but including abi names
1✔
170
                abiItem.inputs.forEach((param, index) => {
1✔
171
                    const name = param.name ?? `param${0}`;
6!
172
                    orderedNamedArgs.push([name, args[index]]);
6✔
173
                });
1✔
174
            }
1✔
175

1✔
176
            e.result = {
1✔
177
                functionName,
1✔
178
                args,
1✔
179
                orderedNamedArgs,
1✔
180
            };
1✔
181
        } catch (err: any) {
1✔
182
            const message =
1✔
183
                err instanceof AbiFunctionSignatureNotFoundError
1✔
184
                    ? err.shortMessage
1!
NEW
185
                    : err.message;
×
186
            e.error = new Error(message);
1✔
187
        }
1✔
188
    }
2✔
189

2✔
190
    return e;
2✔
191
};
2✔
192

1✔
193
const transform: (e: Envelope) => Envelope = cond([
1✔
194
    [
1✔
195
        pathEq(ABI_PARAMS, ["spec", "mode"]),
1✔
196
        pipe(
1✔
197
            addPiecesToEnvelope,
1✔
198
            decodeTargetSliceAndAddToPieces,
1✔
199
            prepareResultFromPieces,
1✔
200
        ),
1✔
201
    ],
1✔
202
    [pathEq(JSON_ABI, ["spec", "mode"]), prepareResultForJSONABI],
1✔
203
    [
1✔
204
        T,
1✔
205
        (e: Envelope) => {
1✔
NEW
206
            e.error = new SpecificationModeNotSupportedError(e.spec);
×
NEW
207
            return e;
×
NEW
208
        },
×
209
    ],
1✔
210
]);
1✔
211

1✔
212
/**
1✔
213
 * Decode the payload data based on the specification passed. The return
1✔
214
 * contains the result but also could contain an error as a value, therefore
1✔
215
 * the callee should decide what to do with it.
1✔
216
 * @param spec {Specification}
1✔
217
 * @param payload {Hex | Uint8Array}
1✔
218
 * @throws {SpecificationModeNotSupportedError}
1✔
219
 * @returns {Envelope}
1✔
220
 */
1✔
221
export function decodePayload(
1✔
222
    spec: Specification,
13✔
223
    input: Hex | Uint8Array,
13✔
224
): Envelope {
13✔
225
    if (!includes(spec.mode, specModes)) {
13✔
226
        throw new SpecificationModeNotSupportedError(spec);
1✔
227
    }
1✔
228

12✔
229
    const envelope: Envelope = {
12✔
230
        spec,
12✔
231
        input,
12✔
232
        pieces: [],
12✔
233
        result: {},
12✔
234
    };
12✔
235

12✔
236
    return transform(envelope);
12✔
237
}
12✔
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