Skip to content

Commit

Permalink
US-322: Can now decode scilla logs. (#16)
Browse files Browse the repository at this point in the history
* (feat) Scilla log decodes now work.

* (fix) US-322: Fix package-lock.json
  • Loading branch information
rrw-zilliqa authored Nov 23, 2023
1 parent 8bb404d commit bf8b0c9
Show file tree
Hide file tree
Showing 5 changed files with 142 additions and 35 deletions.
13 changes: 6 additions & 7 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
"@types/react-syntax-highlighter": "^15.5.9",
"@zilliqa-js/zilliqa" : "^3.4.3",
"chart.js": "^4.4.0",
"ethers": "^6.8.0",
"ethers": "zilliqa/ethers.js#ethers-zq",
"highlightjs-solidity": "^2.0.6",
"prettier-plugin-organize-imports": "^3.2.3",
"react": "^18.2.0",
Expand Down
83 changes: 56 additions & 27 deletions src/execution/transaction/LogEntry.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import { RuntimeContext } from "../../useRuntime";
import TransactionAddressWithCopy from "../components/TransactionAddressWithCopy";
import DecodedLogSignature from "./decoder/DecodedLogSignature";
import DecodedParamsTable from "./decoder/DecodedParamsTable";
import DecodedScillaLogSignature from "./decoder/DecodedScillaLogSignature";
import DecodedScillaParamsTable from "./decoder/DecodedScillaParamsTable";
import LogIndex from "./log/LogIndex";
import RawLog from "./log/RawLog";
import TwoColumnPanel from "./log/TwoColumnPanel";
Expand All @@ -19,27 +21,65 @@ type LogEntryProps = {
log: Log;
};

type ScillaLog = {
eventName: string;
address: string;
params: object;
};

const EvmLogDisplay: FC<LogDisplayProps> = ( {resolvedLogDesc} ) => {
return ( <div> {resolvedLogDesc === undefined ? (
<TwoColumnPanel>Waiting for data...</TwoColumnPanel>
) : resolvedLogDesc === null ? (
<TwoColumnPanel>Cannot decode data</TwoColumnPanel>
) : (
<TwoColumnPanel>
<DecodedLogSignature event={resolvedLogDesc.fragment} />
<DecodedParamsTable
args={resolvedLogDesc.args}
paramTypes={resolvedLogDesc.fragment.inputs}
hasParamNames={resolvedLogDesc === logDesc || resolvedLogDesc === scillaLogDesc}
/>
</TwoColumnPanel>
)
} </div>);
};

/// Display a scilla log; if the log were null, we would have defaulted to
/// EvmLogDisplay, so no need to handle undefined or null.
const ScillaLogDisplay: FC<ScillaLog> = ( { scillaLogDesc} ) => {
let eventProps = { name: scillaLogDesc.eventName, address: scillaLogDesc.address }
return ( <div> <TwoColumnPanel>
<DecodedScillaLogSignature event={eventProps} />
<DecodedScillaParamsTable params={ scillaLogDesc.params } />
</TwoColumnPanel>
</div>
)
}



const LogEntry: FC<LogEntryProps> = ({ log }) => {
const { provider } = useContext(RuntimeContext);
const match = useSourcifyMetadata(log.address, provider?._network.chainId);

const scillaLogDesc = useMemo(() => {
const scillaLogDesc : ScillaLog | undefined = useMemo(() => {
// Scilla logs are encoded as a single JSON string.
try {
const data = JSON.parse(AbiCoder.defaultAbiCoder().decode(["string"], log.data)[0]);
const params: any[] = data.params;
return new LogDescription(
EventFragment.from({type: "event", name: data._eventname, inputs: params.map(p => ({ name: p.vname, type: p.type }))}),
"",
Result.fromItems(params.map(p => (p.value)), params.map(p=> (p.name)))
);
return {
eventName: data._eventname,
address: data.address,
params: data.params
}
} catch (err) {
// Silently ignore on purpose
return undefined;
}
}, [log]);

const logDesc = useMemo(() => {
const logDesc = scillaLogDesc ? undefined :
useMemo(() => {
if (!match) {
return match;
}
Expand All @@ -58,9 +98,9 @@ const LogEntry: FC<LogEntryProps> = ({ log }) => {
}, [log, match]);

const rawTopic0 = log.topics[0];
const topic0 = useTopic0(rawTopic0);
const topic0 = scillaLogDesc ? undefined : useTopic0(rawTopic0);

const topic0LogDesc = useMemo(() => {
const topic0LogDesc = scillaLogDesc ? undefined : useMemo(() => {
if (!topic0) {
return topic0;
}
Expand All @@ -84,7 +124,7 @@ const LogEntry: FC<LogEntryProps> = ({ log }) => {
return undefined;
}, [topic0, log]);

const resolvedLogDesc = scillaLogDesc ?? logDesc ?? topic0LogDesc;
const resolvedLogDesc = logDesc ?? topic0LogDesc;

return (
<div className="flex space-x-10 py-5">
Expand All @@ -105,22 +145,11 @@ const LogEntry: FC<LogEntryProps> = ({ log }) => {
</TwoColumnPanel>
</Tab.List>
<Tab.Panels as={React.Fragment}>
<Tab.Panel>
{resolvedLogDesc === undefined ? (
<TwoColumnPanel>Waiting for data...</TwoColumnPanel>
) : resolvedLogDesc === null ? (
<TwoColumnPanel>Can't decode data</TwoColumnPanel>
) : (
<TwoColumnPanel>
<DecodedLogSignature event={resolvedLogDesc.fragment} />
<DecodedParamsTable
args={resolvedLogDesc.args}
paramTypes={resolvedLogDesc.fragment.inputs}
hasParamNames={resolvedLogDesc === logDesc || resolvedLogDesc === scillaLogDesc}
/>
</TwoColumnPanel>
)}
</Tab.Panel>
<Tab.Panel>
({ scillaLogDesc !== undefined && scillaLogDesc !== null ?
<ScillaLogDisplay scillaLogDesc={scillaLogDesc} /> :
<EvmLogDisplay resolvedLogDesc={resolvedLogDesc} />})
</Tab.Panel>
<Tab.Panel as={React.Fragment}>
<RawLog topics={log.topics} data={log.data} />
</Tab.Panel>
Expand Down
21 changes: 21 additions & 0 deletions src/execution/transaction/decoder/DecodedScillaLogSignature.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { EventFragment } from "ethers";
import { FC, memo } from "react";

type DecodedScillaLogSignatureProps = {
name: string;
address: string;
};

const DecodedScillaLogSignature: FC<DecodedScillaLogSignatureProps> = ({ event }) => {
if (event) {
return (
<span className="font-mono">
<span className="font-italic">_eventName:</span> <span className="font-bold text-blue-900">{event.name}</span><br/>
<span className="font-italic">address:</span> <span className="font-bold text-blue-500">{event.address}</span><br/>
</span>)
} else {
return ( <div /> )
}
}

export default memo(DecodedScillaLogSignature);
58 changes: 58 additions & 0 deletions src/execution/transaction/decoder/DecodedScillaParamsTable.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { ParamType, Result } from "ethers";
import { FC, memo } from "react";
import { DevMethod, UserMethod } from "../../../sourcify/useSourcify";
import DecodedParamRow from "./DecodedParamRow";

type DecodedScillaParamsTableProps = {
params: object;
};

type DecodedScillaParamRowProps = {
name: string;
valueType: string;
value: string;
};

const DecodedScillaParamRow: FC<DecodedScillaParamRowProps> = ({
name,
valueType,
value }) => {
return (
<>
<tr className="grid grid-cols-12 gap-x-2 py-2 hover:bg-gray-100">
<td className="col-span-3 pl-1">
<span className="text-gray-600">{name}</span>
</td>
<td className="col-span-1 text-gray-500">{valueType}</td>
<td className="col-span-8 text-gray-500">{value}</td>
</tr>
</>
)};


const DecodedScillaParamsTable: FC<DecodedScillaParamsTableProps> = ({
params } ) => {
// params.map((val) => { alert(`X ${JSON.stringify(val)}`) });
return (
<table className="w-full border">
<thead>
<tr className="grid grid-cols-12 gap-x-2 bg-gray-100 py-2 text-left">
<th className="col-span-3 pl-1">
name
</th>
<th className="col-span-1 pl-1">
type
</th>
<th className="col-span-8 pr-1">value</th>
</tr>
</thead>
<tbody className="divide-y">
{params.map((val) => (
<DecodedScillaParamRow name={val["vname"]} valueType={val["type"]} value={val["value"]} />
))}
</tbody>
</table>
);
}

export default memo(DecodedScillaParamsTable);

0 comments on commit bf8b0c9

Please sign in to comment.