Add custom error decoding

This commit is contained in:
Willian Mitsuda 2021-12-13 21:03:13 -03:00
parent f22199d7d5
commit da50c7b4ac
3 changed files with 85 additions and 20 deletions

View File

@ -113,6 +113,7 @@ const Transaction: React.FC = () => {
<Details <Details
txData={txData} txData={txData}
txDesc={txDesc} txDesc={txDesc}
toMetadata={metadata}
userDoc={metadata?.output.userdoc} userDoc={metadata?.output.userdoc}
devDoc={metadata?.output.devdoc} devDoc={metadata?.output.devdoc}
internalOps={internalOps} internalOps={internalOps}

View File

@ -1,5 +1,6 @@
import { useState, useEffect, useMemo } from "react"; import { useState, useEffect, useMemo } from "react";
import { Interface } from "@ethersproject/abi"; import { Interface } from "@ethersproject/abi";
import { ErrorDescription } from "@ethersproject/abi/lib/interface";
import { ChecksummedAddress, TransactionData } from "../types"; import { ChecksummedAddress, TransactionData } from "../types";
import { sourcifyMetadata, SourcifySource, sourcifySourceFile } from "../url"; import { sourcifyMetadata, SourcifySource, sourcifySourceFile } from "../url";
@ -11,12 +12,19 @@ export type UserEvent = {
notice?: string | undefined; notice?: string | undefined;
}; };
export type UserError = [
{
notice?: string | undefined;
}
];
export type UserDoc = { export type UserDoc = {
kind: "user"; kind: "user";
version?: number | undefined; version?: number | undefined;
notice?: string | undefined; notice?: string | undefined;
methods: Record<string, UserMethod>; methods: Record<string, UserMethod>;
events: Record<string, UserEvent>; events: Record<string, UserEvent>;
errors?: Record<string, UserError> | undefined;
}; };
export type DevMethod = { export type DevMethod = {
@ -24,10 +32,17 @@ export type DevMethod = {
returns?: Record<string, string>; returns?: Record<string, string>;
}; };
export type DevError = [
{
params?: Record<string, string>;
}
];
export type DevDoc = { export type DevDoc = {
kind: "dev"; kind: "dev";
version?: number | undefined; version?: number | undefined;
methods: Record<string, DevMethod>; methods: Record<string, DevMethod>;
errors?: Record<string, DevError> | undefined;
}; };
export type Metadata = { export type Metadata = {
@ -236,3 +251,25 @@ export const useTransactionDescription = (
return txDesc; return txDesc;
}; };
export const useError = (
metadata: Metadata | null | undefined,
output: string | null | undefined
): ErrorDescription | null | undefined => {
const err = useMemo(() => {
if (!metadata || !output) {
return undefined;
}
const abi = metadata.output.abi;
const intf = new Interface(abi as any);
try {
return intf.parseError(output);
} catch (err) {
console.warn("Couldn't find error signature", err);
return null;
}
}, [metadata, output]);
return err;
};

View File

@ -34,13 +34,14 @@ import PercentageBar from "../components/PercentageBar";
import ExternalLink from "../components/ExternalLink"; import ExternalLink from "../components/ExternalLink";
import RelativePosition from "../components/RelativePosition"; import RelativePosition from "../components/RelativePosition";
import PercentagePosition from "../components/PercentagePosition"; import PercentagePosition from "../components/PercentagePosition";
import DecodedParamsTable from "./decoder/DecodedParamsTable";
import InputDecoder from "./decoder/InputDecoder"; import InputDecoder from "./decoder/InputDecoder";
import { import {
rawInputTo4Bytes, rawInputTo4Bytes,
use4Bytes, use4Bytes,
useTransactionDescription, useTransactionDescription,
} from "../use4Bytes"; } from "../use4Bytes";
import { DevDoc, UserDoc } from "../sourcify/useSourcify"; import { DevDoc, Metadata, useError, UserDoc } from "../sourcify/useSourcify";
import { ResolvedAddresses } from "../api/address-resolver"; import { ResolvedAddresses } from "../api/address-resolver";
import { RuntimeContext } from "../useRuntime"; import { RuntimeContext } from "../useRuntime";
import { useContractsMetadata } from "../hooks"; import { useContractsMetadata } from "../hooks";
@ -49,6 +50,7 @@ import { useTransactionError } from "../useErigonHooks";
type DetailsProps = { type DetailsProps = {
txData: TransactionData; txData: TransactionData;
txDesc: TransactionDescription | null | undefined; txDesc: TransactionDescription | null | undefined;
toMetadata: Metadata | null | undefined;
userDoc?: UserDoc | undefined; userDoc?: UserDoc | undefined;
devDoc?: DevDoc | undefined; devDoc?: DevDoc | undefined;
internalOps?: InternalOperation[]; internalOps?: InternalOperation[];
@ -60,6 +62,7 @@ type DetailsProps = {
const Details: React.FC<DetailsProps> = ({ const Details: React.FC<DetailsProps> = ({
txData, txData,
txDesc, txDesc,
toMetadata,
userDoc, userDoc,
devDoc, devDoc,
internalOps, internalOps,
@ -104,6 +107,16 @@ const Details: React.FC<DetailsProps> = ({
provider, provider,
txData.transactionHash txData.transactionHash
); );
const errorDescription = useError(
toMetadata,
isCustomError ? outputData : undefined
);
const userError = errorDescription
? userDoc?.errors?.[errorDescription.signature]?.[0]
: undefined;
const devError = errorDescription
? devDoc?.errors?.[errorDescription.signature]?.[0]
: undefined;
const [expanded, setExpanded] = useState<boolean>(false); const [expanded, setExpanded] = useState<boolean>(false);
return ( return (
@ -129,7 +142,7 @@ const Details: React.FC<DetailsProps> = ({
) : ( ) : (
<> <>
<div className="flex space-x-1 items-baseline"> <div className="flex space-x-1 items-baseline">
<div className="flex rounded-lg space-x-1 px-3 py-1 bg-red-50 text-red-500 text-xs"> <div className="flex items-baseline rounded-lg space-x-1 px-3 py-1 bg-red-50 text-red-500 text-xs">
<FontAwesomeIcon <FontAwesomeIcon
className="self-center" className="self-center"
icon={faTimesCircle} icon={faTimesCircle}
@ -144,7 +157,21 @@ const Details: React.FC<DetailsProps> = ({
<span className="font-bold underline">{errorMsg}</span>' <span className="font-bold underline">{errorMsg}</span>'
</> </>
)} )}
{isCustomError && <> with custom error</>} {isCustomError && (
<>
{" "}
with custom error
{errorDescription && (
<>
{" '"}
<span className="font-code font-bold underline">
{errorDescription.name}
</span>
{"'"}
</>
)}
</>
)}
</span> </span>
</div> </div>
{isCustomError && ( {isCustomError && (
@ -154,27 +181,27 @@ const Details: React.FC<DetailsProps> = ({
{expanded && ( {expanded && (
<Tab.Group> <Tab.Group>
<Tab.List className="flex space-x-1 mt-2 mb-1"> <Tab.List className="flex space-x-1 mt-2 mb-1">
<ModeTab disabled={!resolvedTxDesc}>Decoded</ModeTab> <ModeTab disabled={!errorDescription}>Decoded</ModeTab>
<ModeTab>Raw</ModeTab> <ModeTab>Raw</ModeTab>
</Tab.List> </Tab.List>
<Tab.Panels> <Tab.Panels>
<Tab.Panel> <Tab.Panel>
{/* {fourBytes === "0x" ? ( {errorDescription === undefined ? (
<>No parameters</>
) : resolvedTxDesc === undefined ? (
<>Waiting for data...</> <>Waiting for data...</>
) : resolvedTxDesc === null ? ( ) : errorDescription === null ? (
<>Can't decode data</> <>Can't decode data</>
) : errorDescription.args.length === 0 ? (
<>No parameters</>
) : ( ) : (
<DecodedParamsTable <DecodedParamsTable
args={resolvedTxDesc.args} args={errorDescription.args}
paramTypes={resolvedTxDesc.functionFragment.inputs} paramTypes={errorDescription.errorFragment.inputs}
hasParamNames={hasParamNames} hasParamNames
userMethod={userMethod} userMethod={userError}
devMethod={devMethod} devMethod={devError}
resolvedAddresses={resolvedAddresses} resolvedAddresses={resolvedAddresses}
/> />
)} */} )}
</Tab.Panel> </Tab.Panel>
<Tab.Panel> <Tab.Panel>
<textarea <textarea