From d5905216830399a2af8430508348f44db3a38df1 Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Thu, 16 Dec 2021 17:45:15 -0300 Subject: [PATCH 1/5] Add swr dependency --- package-lock.json | 15 +++++++++++++++ package.json | 1 + 2 files changed, 16 insertions(+) diff --git a/package-lock.json b/package-lock.json index cf4556c..2c79b40 100644 --- a/package-lock.json +++ b/package-lock.json @@ -46,6 +46,7 @@ "react-scripts": "4.0.3", "react-syntax-highlighter": "^15.4.5", "serve": "^13.0.2", + "swr": "^1.1.1", "typescript": "^4.5.4", "use-keyboard-shortcut": "^1.1.0", "web-vitals": "^1.0.1" @@ -17205,6 +17206,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/swr": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/swr/-/swr-1.1.1.tgz", + "integrity": "sha512-ZpUHyU3N3snj2QGFeE2Fd3BXl1CVS6YQIQGb1ttPAkTmvwZqDyV3GRMNPsaeAYCBM74tfn4XbKx28FVQR0mS7Q==", + "peerDependencies": { + "react": "^16.11.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/symbol-tree": { "version": "3.2.4", "license": "MIT" @@ -31069,6 +31078,12 @@ } } }, + "swr": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/swr/-/swr-1.1.1.tgz", + "integrity": "sha512-ZpUHyU3N3snj2QGFeE2Fd3BXl1CVS6YQIQGb1ttPAkTmvwZqDyV3GRMNPsaeAYCBM74tfn4XbKx28FVQR0mS7Q==", + "requires": {} + }, "symbol-tree": { "version": "3.2.4" }, diff --git a/package.json b/package.json index f738e5d..fc8845f 100644 --- a/package.json +++ b/package.json @@ -41,6 +41,7 @@ "react-scripts": "4.0.3", "react-syntax-highlighter": "^15.4.5", "serve": "^13.0.2", + "swr": "^1.1.1", "typescript": "^4.5.4", "use-keyboard-shortcut": "^1.1.0", "web-vitals": "^1.0.1" From d5ea295eecaa5e435e9ea1d3663c38023e0c6303 Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Thu, 16 Dec 2021 18:35:49 -0300 Subject: [PATCH 2/5] Rework use4Bytes hook to use swr; fix handling of 0x00 data --- src/use4Bytes.ts | 66 ++++++++++++++++++++---------------------------- 1 file changed, 28 insertions(+), 38 deletions(-) diff --git a/src/use4Bytes.ts b/src/use4Bytes.ts index 690d5a3..b619ca0 100644 --- a/src/use4Bytes.ts +++ b/src/use4Bytes.ts @@ -4,9 +4,10 @@ import { Interface, TransactionDescription, } from "@ethersproject/abi"; +import { BigNumberish } from "@ethersproject/bignumber"; +import useSWR from "swr"; import { RuntimeContext } from "./useRuntime"; import { fourBytesURL } from "./url"; -import { BigNumberish } from "@ethersproject/bignumber"; export type FourBytesEntry = { name: string; @@ -20,8 +21,6 @@ const simpleTransfer: FourBytesEntry = { signature: undefined, }; -const fullCache = new Map(); - export const extract4Bytes = (rawInput: string): string | null => { if (rawInput.length < 10) { return null; @@ -100,54 +99,45 @@ export const useBatch4Bytes = ( export const use4Bytes = ( rawFourBytes: string ): FourBytesEntry | null | undefined => { - if (rawFourBytes !== "0x") { - if (rawFourBytes.length !== 10 || !rawFourBytes.startsWith("0x")) { - throw new Error( - `rawFourBytes must contain a 4 bytes hex method signature starting with 0x; received value: "${rawFourBytes}"` - ); - } + if (!rawFourBytes.startsWith("0x")) { + throw new Error( + `rawFourBytes must contain a bytes hex string starting with 0x; received value: "${rawFourBytes}"` + ); } - const runtime = useContext(RuntimeContext); - const assetsURLPrefix = runtime.config?.assetsURLPrefix; + const { config } = useContext(RuntimeContext); + const assetsURLPrefix = config?.assetsURLPrefix; - const fourBytes = rawFourBytes.slice(2); - const [entry, setEntry] = useState( - fullCache.get(fourBytes) - ); - useEffect(() => { + const fourBytesFetcher = (key: string) => { + // TODO: throw error? if (assetsURLPrefix === undefined) { - return; + return undefined; } - if (fourBytes === "") { - return; + if (key === "0x") { + return undefined; } - const loadSig = async () => { - const entry = await fetch4Bytes(assetsURLPrefix, fourBytes); - fullCache.set(fourBytes, entry); - setEntry(entry); - }; - loadSig(); - }, [fourBytes, assetsURLPrefix]); + // Handle simple transfers with invalid selector like tx: + // 0x8bcbdcc1589b5c34c1e55909c8269a411f0267a4fed59a73dd4348cc71addbb9, + // which contains 0x00 as data + if (key.length !== 10) { + return undefined; + } + return fetch4Bytes(assetsURLPrefix, key.slice(2)); + }; + + const { data, error } = useSWR( + rawFourBytes, + fourBytesFetcher + ); if (rawFourBytes === "0x") { return simpleTransfer; } - if (assetsURLPrefix === undefined) { + if (error) { return undefined; } - - // Try to resolve 4bytes name - if (entry === null || entry === undefined) { - return entry; - } - - // Simulates LRU - // TODO: implement LRU purging - fullCache.delete(fourBytes); - fullCache.set(fourBytes, entry); - return entry; + return data; }; export const useTransactionDescription = ( From 478dd84710113f5f1fe44ac66b5f7a8ded4ec00f Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Thu, 16 Dec 2021 19:08:04 -0300 Subject: [PATCH 3/5] Eliminate simple transfer singleton entry --- src/components/MethodName.tsx | 10 ++++++---- src/use4Bytes.ts | 24 ++++++++++++------------ 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/src/components/MethodName.tsx b/src/components/MethodName.tsx index c965a81..f084fa5 100644 --- a/src/components/MethodName.tsx +++ b/src/components/MethodName.tsx @@ -1,15 +1,17 @@ import React from "react"; -import { rawInputTo4Bytes, use4Bytes } from "../use4Bytes"; +import { extract4Bytes, use4Bytes } from "../use4Bytes"; type MethodNameProps = { data: string; }; const MethodName: React.FC = ({ data }) => { - const rawFourBytes = rawInputTo4Bytes(data); + const rawFourBytes = extract4Bytes(data); const fourBytesEntry = use4Bytes(rawFourBytes); - const methodName = fourBytesEntry?.name ?? rawFourBytes; - const isSimpleTransfer = rawFourBytes === "0x"; + const isSimpleTransfer = data === "0x"; + const methodName = isSimpleTransfer + ? "transfer" + : fourBytesEntry?.name ?? rawFourBytes ?? "-"; const methodTitle = isSimpleTransfer ? "ETH Transfer" : methodName === rawFourBytes diff --git a/src/use4Bytes.ts b/src/use4Bytes.ts index b619ca0..c003bd3 100644 --- a/src/use4Bytes.ts +++ b/src/use4Bytes.ts @@ -16,11 +16,13 @@ export type FourBytesEntry = { export type FourBytesMap = Record; -const simpleTransfer: FourBytesEntry = { - name: "transfer", - signature: undefined, -}; - +/** + * Given a hex input data; extract the method selector + * + * @param rawInput Raw tx input including the "0x" + * @returns the first 4 bytes, including the "0x" or null if the input + * contains an invalid selector, e.g., txs with 0x00 data + */ export const extract4Bytes = (rawInput: string): string | null => { if (rawInput.length < 10) { return null; @@ -60,6 +62,7 @@ const fetch4Bytes = async ( } }; +// TODO: migrate to swr and merge with use4Bytes export const useBatch4Bytes = ( rawFourByteSigs: string[] | undefined ): FourBytesMap => { @@ -97,9 +100,9 @@ export const useBatch4Bytes = ( * @param rawFourBytes an hex string containing the 4bytes signature in the "0xXXXXXXXX" format. */ export const use4Bytes = ( - rawFourBytes: string + rawFourBytes: string | null ): FourBytesEntry | null | undefined => { - if (!rawFourBytes.startsWith("0x")) { + if (rawFourBytes !== null && !rawFourBytes.startsWith("0x")) { throw new Error( `rawFourBytes must contain a bytes hex string starting with 0x; received value: "${rawFourBytes}"` ); @@ -108,12 +111,12 @@ export const use4Bytes = ( const { config } = useContext(RuntimeContext); const assetsURLPrefix = config?.assetsURLPrefix; - const fourBytesFetcher = (key: string) => { + const fourBytesFetcher = (key: string | null) => { // TODO: throw error? if (assetsURLPrefix === undefined) { return undefined; } - if (key === "0x") { + if (key === null || key === "0x") { return undefined; } @@ -131,9 +134,6 @@ export const use4Bytes = ( rawFourBytes, fourBytesFetcher ); - if (rawFourBytes === "0x") { - return simpleTransfer; - } if (error) { return undefined; } From e9f32e3530882051f42cdc9dacf803d82fae64e1 Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Thu, 16 Dec 2021 19:12:55 -0300 Subject: [PATCH 4/5] Extract method formatter --- src/components/MethodName.tsx | 14 ++------------ src/use4Bytes.ts | 16 ++++++++++++++++ 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/src/components/MethodName.tsx b/src/components/MethodName.tsx index f084fa5..539db12 100644 --- a/src/components/MethodName.tsx +++ b/src/components/MethodName.tsx @@ -1,22 +1,12 @@ import React from "react"; -import { extract4Bytes, use4Bytes } from "../use4Bytes"; +import { useMethodSelector } from "../use4Bytes"; type MethodNameProps = { data: string; }; const MethodName: React.FC = ({ data }) => { - const rawFourBytes = extract4Bytes(data); - const fourBytesEntry = use4Bytes(rawFourBytes); - const isSimpleTransfer = data === "0x"; - const methodName = isSimpleTransfer - ? "transfer" - : fourBytesEntry?.name ?? rawFourBytes ?? "-"; - const methodTitle = isSimpleTransfer - ? "ETH Transfer" - : methodName === rawFourBytes - ? methodName - : `${methodName} [${rawFourBytes}]`; + const [isSimpleTransfer, methodName, methodTitle] = useMethodSelector(data); return (
{ + const rawFourBytes = extract4Bytes(data); + const fourBytesEntry = use4Bytes(rawFourBytes); + const isSimpleTransfer = data === "0x"; + const methodName = isSimpleTransfer + ? "transfer" + : fourBytesEntry?.name ?? rawFourBytes ?? "-"; + const methodTitle = isSimpleTransfer + ? "ETH Transfer" + : methodName === rawFourBytes + ? methodName + : `${methodName} [${rawFourBytes}]`; + + return [isSimpleTransfer, methodName, methodTitle]; +}; + export const useTransactionDescription = ( fourBytesEntry: FourBytesEntry | null | undefined, data: string | undefined, From 28a216ff5f5ea822c5114e64776b12ed022de8c8 Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Thu, 16 Dec 2021 19:22:13 -0300 Subject: [PATCH 5/5] Eliminate redundant function --- src/transaction/Details.tsx | 5 +++-- src/use4Bytes.ts | 5 ++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/transaction/Details.tsx b/src/transaction/Details.tsx index 66c34e9..d414d1e 100644 --- a/src/transaction/Details.tsx +++ b/src/transaction/Details.tsx @@ -37,7 +37,7 @@ import PercentagePosition from "../components/PercentagePosition"; import DecodedParamsTable from "./decoder/DecodedParamsTable"; import InputDecoder from "./decoder/InputDecoder"; import { - rawInputTo4Bytes, + extract4Bytes, use4Bytes, useTransactionDescription, } from "../use4Bytes"; @@ -74,7 +74,8 @@ const Details: React.FC = ({ txData.confirmedData?.blockBaseFeePerGas !== undefined && txData.confirmedData?.blockBaseFeePerGas !== null; - const fourBytes = txData.to !== null ? rawInputTo4Bytes(txData.data) : "0x"; + const fourBytes = + txData.to !== null ? extract4Bytes(txData.data) ?? "0x" : "0x"; const fourBytesEntry = use4Bytes(fourBytes); const fourBytesTxDesc = useTransactionDescription( fourBytesEntry, diff --git a/src/use4Bytes.ts b/src/use4Bytes.ts index 233fa3f..ea4f28c 100644 --- a/src/use4Bytes.ts +++ b/src/use4Bytes.ts @@ -21,7 +21,8 @@ export type FourBytesMap = Record; * * @param rawInput Raw tx input including the "0x" * @returns the first 4 bytes, including the "0x" or null if the input - * contains an invalid selector, e.g., txs with 0x00 data + * contains an invalid selector, e.g., txs with 0x00 data; simple transfers (0x) + * return null as well as it is not a method selector */ export const extract4Bytes = (rawInput: string): string | null => { if (rawInput.length < 10) { @@ -30,8 +31,6 @@ export const extract4Bytes = (rawInput: string): string | null => { return rawInput.slice(0, 10); }; -export const rawInputTo4Bytes = (rawInput: string) => rawInput.slice(0, 10); - const fetch4Bytes = async ( assetsURLPrefix: string, fourBytes: string