Merge branch 'feature/topic0-event-decoding' into develop
This commit is contained in:
commit
6fa78bcdc6
|
@ -4,3 +4,6 @@
|
||||||
[submodule "trustwallet"]
|
[submodule "trustwallet"]
|
||||||
path = trustwallet
|
path = trustwallet
|
||||||
url = https://github.com/trustwallet/assets.git
|
url = https://github.com/trustwallet/assets.git
|
||||||
|
[submodule "topic0"]
|
||||||
|
path = topic0
|
||||||
|
url = https://github.com/wmitsuda/topic0.git
|
||||||
|
|
|
@ -19,8 +19,13 @@ WORKDIR /signatures
|
||||||
COPY 4bytes/signatures /signatures/
|
COPY 4bytes/signatures /signatures/
|
||||||
COPY 4bytes/with_parameter_names /signatures/
|
COPY 4bytes/with_parameter_names /signatures/
|
||||||
|
|
||||||
|
FROM alpine:3.14.0 AS topic0builder
|
||||||
|
WORKDIR /topic0
|
||||||
|
COPY topic0/with_parameter_names /topic0/
|
||||||
|
|
||||||
FROM nginx:1.21.1-alpine
|
FROM nginx:1.21.1-alpine
|
||||||
RUN apk add jq
|
RUN apk add jq
|
||||||
|
COPY --from=topic0builder /topic0 /usr/share/nginx/html/topic0/
|
||||||
COPY --from=fourbytesbuilder /signatures /usr/share/nginx/html/signatures/
|
COPY --from=fourbytesbuilder /signatures /usr/share/nginx/html/signatures/
|
||||||
COPY --from=logobuilder /assets /usr/share/nginx/html/assets/
|
COPY --from=logobuilder /assets /usr/share/nginx/html/assets/
|
||||||
COPY nginx.conf /etc/nginx/conf.d/default.conf
|
COPY nginx.conf /etc/nginx/conf.d/default.conf
|
||||||
|
|
34
nginx.conf
34
nginx.conf
|
@ -43,6 +43,40 @@ server {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
location /topic0 {
|
||||||
|
root /usr/share/nginx/html;
|
||||||
|
expires 30d;
|
||||||
|
|
||||||
|
# Base on: https://michielkalkman.com/snippets/nginx-cors-open-configuration/
|
||||||
|
if ($request_method = 'OPTIONS') {
|
||||||
|
add_header 'Access-Control-Allow-Origin' '*';
|
||||||
|
#
|
||||||
|
# Om nom nom cookies
|
||||||
|
#
|
||||||
|
add_header 'Access-Control-Allow-Credentials' 'true';
|
||||||
|
add_header 'Access-Control-Allow-Methods' 'GET, OPTIONS';
|
||||||
|
|
||||||
|
#
|
||||||
|
# Custom headers and headers various browsers *should* be OK with but aren't
|
||||||
|
#
|
||||||
|
add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';
|
||||||
|
|
||||||
|
#
|
||||||
|
# Tell client that this pre-flight info is valid for 20 days
|
||||||
|
#
|
||||||
|
add_header 'Access-Control-Max-Age' 1728000;
|
||||||
|
add_header 'Content-Type' 'text/plain charset=UTF-8';
|
||||||
|
add_header 'Content-Length' 0;
|
||||||
|
return 204;
|
||||||
|
}
|
||||||
|
if ($request_method = 'GET') {
|
||||||
|
add_header 'Access-Control-Allow-Origin' '*' always;
|
||||||
|
add_header 'Access-Control-Allow-Credentials' 'true' always;
|
||||||
|
add_header 'Access-Control-Allow-Methods' 'GET, OPTIONS' always;
|
||||||
|
add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type' always;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
location /assets {
|
location /assets {
|
||||||
root /usr/share/nginx/html;
|
root /usr/share/nginx/html;
|
||||||
expires 30d;
|
expires 30d;
|
||||||
|
|
|
@ -52,8 +52,8 @@
|
||||||
"test": "craco test",
|
"test": "craco test",
|
||||||
"eject": "react-scripts eject",
|
"eject": "react-scripts eject",
|
||||||
"source-map-explorer": "source-map-explorer build/static/js/*.js",
|
"source-map-explorer": "source-map-explorer build/static/js/*.js",
|
||||||
"assets-start": "docker run --rm -p 3001:80 --name otterscan-assets -d -v$(pwd)/4bytes/signatures:/usr/share/nginx/html/signatures/ -v$(pwd)/trustwallet/blockchains/ethereum/assets:/usr/share/nginx/html/assets -v$(pwd)/nginx.conf:/etc/nginx/conf.d/default.conf nginx:1.21.1-alpine",
|
"assets-start": "docker run --rm -p 3001:80 --name otterscan-assets -d -v$(pwd)/4bytes/signatures:/usr/share/nginx/html/signatures/ -v$(pwd)/trustwallet/blockchains/ethereum/assets:/usr/share/nginx/html/assets -v$(pwd)/topic0/with_parameter_names:/usr/share/nginx/html/topic0/ -v$(pwd)/nginx.conf:/etc/nginx/conf.d/default.conf nginx:1.21.1-alpine",
|
||||||
"assets-start-with-param-names": "docker run --rm -p 3001:80 --name otterscan-assets -d -v$(pwd)/4bytes/with_parameter_names:/usr/share/nginx/html/signatures/ -v$(pwd)/trustwallet/blockchains/ethereum/assets:/usr/share/nginx/html/assets -v$(pwd)/nginx.conf:/etc/nginx/conf.d/default.conf nginx:1.21.1-alpine",
|
"assets-start-with-param-names": "docker run --rm -p 3001:80 --name otterscan-assets -d -v$(pwd)/4bytes/with_parameter_names:/usr/share/nginx/html/signatures/ -v$(pwd)/trustwallet/blockchains/ethereum/assets:/usr/share/nginx/html/assets -v$(pwd)/topic0/with_parameter_names:/usr/share/nginx/html/topic0/ -v$(pwd)/nginx.conf:/etc/nginx/conf.d/default.conf nginx:1.21.1-alpine",
|
||||||
"assets-stop": "docker stop otterscan-assets",
|
"assets-stop": "docker stop otterscan-assets",
|
||||||
"docker-build": "DOCKER_BUILDKIT=1 docker build -t otterscan -f Dockerfile .",
|
"docker-build": "DOCKER_BUILDKIT=1 docker build -t otterscan -f Dockerfile .",
|
||||||
"docker-start": "docker run --rm -p 5000:80 --name otterscan -d otterscan",
|
"docker-start": "docker run --rm -p 5000:80 --name otterscan -d otterscan",
|
||||||
|
|
|
@ -0,0 +1,63 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
width="15.875mm"
|
||||||
|
height="15.875mm"
|
||||||
|
viewBox="0 0 15.875 15.875"
|
||||||
|
version="1.1"
|
||||||
|
id="svg990"
|
||||||
|
inkscape:version="1.1.1 (c3084ef, 2021-09-22)"
|
||||||
|
sodipodi:docname="sourcify.svg"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg">
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="namedview992"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1.0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
inkscape:pagecheckerboard="0"
|
||||||
|
inkscape:document-units="mm"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="0.57905443"
|
||||||
|
inkscape:cx="384.24712"
|
||||||
|
inkscape:cy="511.1782"
|
||||||
|
inkscape:window-width="1440"
|
||||||
|
inkscape:window-height="872"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="28"
|
||||||
|
inkscape:window-maximized="0"
|
||||||
|
inkscape:current-layer="layer1" />
|
||||||
|
<defs
|
||||||
|
id="defs987" />
|
||||||
|
<g
|
||||||
|
inkscape:label="Layer 1"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer1"
|
||||||
|
transform="translate(-3.0286537,-13.080962)">
|
||||||
|
<g
|
||||||
|
id="g972"
|
||||||
|
transform="matrix(0.26458333,0,0,0.26458333,3.0286537,13.080962)">
|
||||||
|
<path
|
||||||
|
d="M 0,30 C 0,46.5685 13.4315,60 30,60 46.5685,60 60,46.5685 60,30 60,13.4315 46.5685,0 30,0 13.4315,0 0,13.4315 0,30 Z"
|
||||||
|
fill="#2b50aa"
|
||||||
|
id="path874" />
|
||||||
|
<path
|
||||||
|
d="m 30.0587,59.413 c 16.2119,0 29.3542,-13.1423 29.3542,-29.3542 0,-16.2119 -13.1423,-29.35421 -29.3542,-29.35421 -16.2118,0 -29.354171,13.14231 -29.354171,29.35421 0,16.2119 13.142371,29.3542 29.354171,29.3542 z"
|
||||||
|
fill="#2b50aa"
|
||||||
|
id="path876" />
|
||||||
|
<path
|
||||||
|
d="m 21.9326,42.1567 c 0.1284,0.1176 0.2889,0.1819 0.4387,0.1819 0.1497,0 0.3209,-0.0643 0.4387,-0.1819 l 1.4765,-1.4123 c 0.1283,-0.1178 0.2032,-0.289 0.2032,-0.4602 0,-0.1712 -0.0749,-0.3424 -0.2032,-0.46 L 14.0366,30.0343 24.2865,20.2446 c 0.1283,-0.1178 0.2032,-0.289 0.2032,-0.4602 0,-0.1712 -0.0749,-0.3422 -0.2032,-0.46 L 22.81,17.9121 c -0.1285,-0.1176 -0.2782,-0.1819 -0.4387,-0.1819 -0.1605,0 -0.321,0.0643 -0.4387,0.1819 L 9.71403,29.5743 c -0.1284,0.1178 -0.20329,0.2888 -0.20329,0.46 0,0.1712 0.07489,0.3424 0.20329,0.4602 z M 46.0808,30.0343 35.8309,39.8242 c -0.1283,0.1176 -0.2033,0.2888 -0.2033,0.46 0,0.1712 0.075,0.3424 0.2033,0.4602 l 1.4765,1.4123 c 0.1285,0.1176 0.289,0.1819 0.4387,0.1819 0.1498,0 0.321,-0.0643 0.4387,-0.1819 L 50.4034,30.4945 c 0.1283,-0.1178 0.2032,-0.289 0.2032,-0.4602 0,-0.1712 -0.0749,-0.3422 -0.2032,-0.46 L 38.1848,17.9121 c -0.1284,-0.1176 -0.2889,-0.1819 -0.4387,-0.1819 -0.1605,0 -0.3209,0.0643 -0.4387,0.1819 l -1.4765,1.4123 c -0.1283,0.1178 -0.2033,0.2888 -0.2033,0.46 0,0.1712 0.075,0.3424 0.2033,0.4602 z"
|
||||||
|
fill="#c5d5ea"
|
||||||
|
id="path878" />
|
||||||
|
<path
|
||||||
|
d="m 21.8471,28.8355 c -0.0107,0.4172 0.1498,0.8131 0.4493,1.1126 l 0.0107,0.0107 c 0.2888,0.289 0.6634,0.4495 1.0699,0.4495 0.3746,0 0.7276,-0.1391 1.0058,-0.3853 l 4.1619,-3.7019 V 37.673 c 0,0.8346 0.6848,1.5194 1.5194,1.5194 0.8345,0 1.5193,-0.6848 1.5193,-1.5194 V 26.3104 l 4.1619,3.702 c 0.2783,0.2461 0.6421,0.3851 1.0058,0.3851 0.4066,0 0.7918,-0.1604 1.0806,-0.4494 l 0.0107,-0.0107 c 0.2996,-0.2995 0.4495,-0.6847 0.4495,-1.1127 -0.0108,-0.4172 -0.182,-0.8024 -0.4922,-1.0913 L 31.0805,21.56 c -0.2782,-0.2568 -0.6419,-0.3959 -1.0164,-0.3959 -0.3745,0 -0.7383,0.1391 -1.0165,0.3959 l -6.7191,6.1734 c -0.2996,0.2995 -0.4814,0.6848 -0.4814,1.1021 z"
|
||||||
|
fill="#c5d5ea"
|
||||||
|
id="path880" />
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 3.6 KiB |
|
@ -13,7 +13,6 @@ import queryString from "query-string";
|
||||||
import Blockies from "react-blockies";
|
import Blockies from "react-blockies";
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
import { faCircleNotch } from "@fortawesome/free-solid-svg-icons/faCircleNotch";
|
import { faCircleNotch } from "@fortawesome/free-solid-svg-icons/faCircleNotch";
|
||||||
import { faCheckCircle } from "@fortawesome/free-regular-svg-icons/faCheckCircle";
|
|
||||||
import { faQuestionCircle } from "@fortawesome/free-regular-svg-icons/faQuestionCircle";
|
import { faQuestionCircle } from "@fortawesome/free-regular-svg-icons/faQuestionCircle";
|
||||||
import StandardFrame from "./StandardFrame";
|
import StandardFrame from "./StandardFrame";
|
||||||
import StandardSubtitle from "./StandardSubtitle";
|
import StandardSubtitle from "./StandardSubtitle";
|
||||||
|
@ -240,7 +239,13 @@ const AddressTransactions: React.FC = () => {
|
||||||
</span>
|
</span>
|
||||||
) : (
|
) : (
|
||||||
<span className="self-center text-green-500">
|
<span className="self-center text-green-500">
|
||||||
<FontAwesomeIcon icon={faCheckCircle} />
|
<img
|
||||||
|
src="/sourcify.svg"
|
||||||
|
alt="Sourcify logo"
|
||||||
|
title="Verified by Sourcify"
|
||||||
|
width={16}
|
||||||
|
height={16}
|
||||||
|
/>
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
</span>
|
</span>
|
||||||
|
@ -300,10 +305,6 @@ const AddressTransactions: React.FC = () => {
|
||||||
disabled={controller === undefined}
|
disabled={controller === undefined}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<ResultHeader
|
|
||||||
feeDisplay={feeDisplay}
|
|
||||||
feeDisplayToggler={feeDisplayToggler}
|
|
||||||
/>
|
|
||||||
</SelectionContext.Provider>
|
</SelectionContext.Provider>
|
||||||
) : (
|
) : (
|
||||||
<PendingResults />
|
<PendingResults />
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import React, { Fragment } from "react";
|
import React, { useMemo } from "react";
|
||||||
import { Log } from "@ethersproject/abstract-provider";
|
import { Log } from "@ethersproject/abstract-provider";
|
||||||
import { LogDescription } from "@ethersproject/abi";
|
import { Fragment, Interface, LogDescription } from "@ethersproject/abi";
|
||||||
import { Tab } from "@headlessui/react";
|
import { Tab } from "@headlessui/react";
|
||||||
import AddressHighlighter from "../components/AddressHighlighter";
|
import AddressHighlighter from "../components/AddressHighlighter";
|
||||||
import DecoratedAddressLink from "../components/DecoratedAddressLink";
|
import DecoratedAddressLink from "../components/DecoratedAddressLink";
|
||||||
|
@ -9,6 +9,7 @@ import ModeTab from "../components/ModeTab";
|
||||||
import DecodedParamsTable from "./decoder/DecodedParamsTable";
|
import DecodedParamsTable from "./decoder/DecodedParamsTable";
|
||||||
import DecodedLogSignature from "./decoder/DecodedLogSignature";
|
import DecodedLogSignature from "./decoder/DecodedLogSignature";
|
||||||
import { TransactionData } from "../types";
|
import { TransactionData } from "../types";
|
||||||
|
import { useTopic0 } from "../useTopic0";
|
||||||
|
|
||||||
type LogEntryProps = {
|
type LogEntryProps = {
|
||||||
txData: TransactionData;
|
txData: TransactionData;
|
||||||
|
@ -16,7 +17,31 @@ type LogEntryProps = {
|
||||||
logDesc: LogDescription | null | undefined;
|
logDesc: LogDescription | null | undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
const LogEntry: React.FC<LogEntryProps> = ({ txData, log, logDesc }) => (
|
const LogEntry: React.FC<LogEntryProps> = ({ txData, log, logDesc }) => {
|
||||||
|
const rawTopic0 = log.topics[0];
|
||||||
|
const topic0 = useTopic0(rawTopic0);
|
||||||
|
|
||||||
|
const topic0LogDesc = useMemo(() => {
|
||||||
|
if (!topic0?.signatures) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const sigs = topic0.signatures;
|
||||||
|
for (const sig of sigs) {
|
||||||
|
const logFragment = Fragment.fromString(`event ${sig}`);
|
||||||
|
const intf = new Interface([logFragment]);
|
||||||
|
try {
|
||||||
|
return intf.parseLog(log);
|
||||||
|
} catch (err) {
|
||||||
|
// Ignore on purpose; try to match other sigs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}, [topic0, log]);
|
||||||
|
|
||||||
|
const resolvedLogDesc = logDesc ?? topic0LogDesc;
|
||||||
|
|
||||||
|
return (
|
||||||
<div className="flex space-x-10 py-5">
|
<div className="flex space-x-10 py-5">
|
||||||
<div>
|
<div>
|
||||||
<span className="rounded-full w-12 h-12 flex items-center justify-center bg-green-50 text-green-500">
|
<span className="rounded-full w-12 h-12 flex items-center justify-center bg-green-50 text-green-500">
|
||||||
|
@ -48,15 +73,15 @@ const LogEntry: React.FC<LogEntryProps> = ({ txData, log, logDesc }) => (
|
||||||
<ModeTab>Raw</ModeTab>
|
<ModeTab>Raw</ModeTab>
|
||||||
</div>
|
</div>
|
||||||
</Tab.List>
|
</Tab.List>
|
||||||
<Tab.Panels as={Fragment}>
|
<Tab.Panels as={React.Fragment}>
|
||||||
<Tab.Panel className="space-y-2">
|
<Tab.Panel className="space-y-2">
|
||||||
{logDesc === undefined ? (
|
{resolvedLogDesc === undefined ? (
|
||||||
<div className="grid grid-cols-12 gap-x-3 gap-y-5 text-sm">
|
<div className="grid grid-cols-12 gap-x-3 gap-y-5 text-sm">
|
||||||
<div className="col-start-2 flex space-x-2 items-center col-span-11">
|
<div className="col-start-2 flex space-x-2 items-center col-span-11">
|
||||||
Waiting for data...
|
Waiting for data...
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : logDesc === null ? (
|
) : resolvedLogDesc === null ? (
|
||||||
<div className="grid grid-cols-12 gap-x-3 gap-y-5 text-sm">
|
<div className="grid grid-cols-12 gap-x-3 gap-y-5 text-sm">
|
||||||
<div className="col-start-2 flex space-x-2 items-center col-span-11">
|
<div className="col-start-2 flex space-x-2 items-center col-span-11">
|
||||||
No decoded data
|
No decoded data
|
||||||
|
@ -66,15 +91,18 @@ const LogEntry: React.FC<LogEntryProps> = ({ txData, log, logDesc }) => (
|
||||||
<>
|
<>
|
||||||
<div className="grid grid-cols-12 gap-x-3 gap-y-5 text-sm">
|
<div className="grid grid-cols-12 gap-x-3 gap-y-5 text-sm">
|
||||||
<div className="col-start-2 flex space-x-2 items-center col-span-11 font-mono">
|
<div className="col-start-2 flex space-x-2 items-center col-span-11 font-mono">
|
||||||
<DecodedLogSignature event={logDesc.eventFragment} />
|
<DecodedLogSignature
|
||||||
|
event={resolvedLogDesc.eventFragment}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="grid grid-cols-12 gap-x-3 gap-y-5 text-sm">
|
<div className="grid grid-cols-12 gap-x-3 gap-y-5 text-sm">
|
||||||
<div className="col-start-2 flex space-x-2 items-center col-span-11">
|
<div className="col-start-2 flex space-x-2 items-center col-span-11">
|
||||||
<DecodedParamsTable
|
<DecodedParamsTable
|
||||||
args={logDesc.args}
|
args={resolvedLogDesc.args}
|
||||||
paramTypes={logDesc.eventFragment.inputs}
|
paramTypes={resolvedLogDesc.eventFragment.inputs}
|
||||||
txData={txData}
|
txData={txData}
|
||||||
|
hasParamNames={resolvedLogDesc === logDesc}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -111,6 +139,7 @@ const LogEntry: React.FC<LogEntryProps> = ({ txData, log, logDesc }) => (
|
||||||
</Tab.Group>
|
</Tab.Group>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export default React.memo(LogEntry);
|
export default React.memo(LogEntry);
|
||||||
|
|
|
@ -5,6 +5,9 @@ export const fourBytesURL = (
|
||||||
fourBytes: string
|
fourBytes: string
|
||||||
): string => `${assetsURLPrefix}/signatures/${fourBytes}`;
|
): string => `${assetsURLPrefix}/signatures/${fourBytes}`;
|
||||||
|
|
||||||
|
export const topic0URL = (assetsURLPrefix: string, topic0: string): string =>
|
||||||
|
`${assetsURLPrefix}/topic0/${topic0}`;
|
||||||
|
|
||||||
export const tokenLogoURL = (
|
export const tokenLogoURL = (
|
||||||
assetsURLPrefix: string,
|
assetsURLPrefix: string,
|
||||||
address: string
|
address: string
|
||||||
|
|
|
@ -0,0 +1,77 @@
|
||||||
|
import { useState, useEffect, useContext } from "react";
|
||||||
|
import { RuntimeContext } from "./useRuntime";
|
||||||
|
import { topic0URL } from "./url";
|
||||||
|
|
||||||
|
export type Topic0Entry = {
|
||||||
|
signatures: string[] | undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
const fullCache = new Map<string, Topic0Entry | null>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extract topic0 DB info
|
||||||
|
*
|
||||||
|
* @param rawTopic0 an hex string containing the keccak256 of event signature
|
||||||
|
*/
|
||||||
|
export const useTopic0 = (
|
||||||
|
rawTopic0: string
|
||||||
|
): Topic0Entry | null | undefined => {
|
||||||
|
if (rawTopic0.length !== 66 || !rawTopic0.startsWith("0x")) {
|
||||||
|
throw new Error(
|
||||||
|
`rawTopic0 must contain a 32 bytes hex event signature starting with 0x; received value: "${rawTopic0}"`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const runtime = useContext(RuntimeContext);
|
||||||
|
const assetsURLPrefix = runtime.config?.assetsURLPrefix;
|
||||||
|
|
||||||
|
const topic0 = rawTopic0.slice(2);
|
||||||
|
const [entry, setEntry] = useState<Topic0Entry | null | undefined>(
|
||||||
|
fullCache.get(topic0)
|
||||||
|
);
|
||||||
|
useEffect(() => {
|
||||||
|
if (assetsURLPrefix === undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const signatureURL = topic0URL(assetsURLPrefix, topic0);
|
||||||
|
fetch(signatureURL)
|
||||||
|
.then(async (res) => {
|
||||||
|
if (!res.ok) {
|
||||||
|
console.error(`Signature does not exist in topic0 DB: ${topic0}`);
|
||||||
|
fullCache.set(topic0, null);
|
||||||
|
setEntry(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get only the first occurrence, for now ignore alternative param names
|
||||||
|
const sig = await res.text();
|
||||||
|
const sigs = sig.split(";");
|
||||||
|
const entry: Topic0Entry = {
|
||||||
|
signatures: sigs,
|
||||||
|
};
|
||||||
|
setEntry(entry);
|
||||||
|
fullCache.set(topic0, entry);
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.error(`Couldn't fetch signature URL ${signatureURL}`, err);
|
||||||
|
setEntry(null);
|
||||||
|
fullCache.set(topic0, null);
|
||||||
|
});
|
||||||
|
}, [topic0, assetsURLPrefix]);
|
||||||
|
|
||||||
|
if (assetsURLPrefix === undefined) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to resolve topic0 name
|
||||||
|
if (entry === null || entry === undefined) {
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Simulates LRU
|
||||||
|
// TODO: implement LRU purging
|
||||||
|
fullCache.delete(topic0);
|
||||||
|
fullCache.set(topic0, entry);
|
||||||
|
return entry;
|
||||||
|
};
|
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 52559d5690d491f8191a2d3fdb3c037516adc68f
|
Loading…
Reference in New Issue