Merge branch 'feature/tx-by-nonce' into develop
This commit is contained in:
commit
e45ea7db12
|
@ -1,9 +1,12 @@
|
||||||
[submodule "4bytes"]
|
[submodule "4bytes"]
|
||||||
path = 4bytes
|
path = 4bytes
|
||||||
url = https://github.com/ethereum-lists/4bytes.git
|
url = https://github.com/ethereum-lists/4bytes.git
|
||||||
|
ignore = dirty
|
||||||
[submodule "trustwallet"]
|
[submodule "trustwallet"]
|
||||||
path = trustwallet
|
path = trustwallet
|
||||||
url = https://github.com/trustwallet/assets.git
|
url = https://github.com/trustwallet/assets.git
|
||||||
|
ignore = dirty
|
||||||
[submodule "topic0"]
|
[submodule "topic0"]
|
||||||
path = topic0
|
path = topic0
|
||||||
url = https://github.com/wmitsuda/topic0.git
|
url = https://github.com/wmitsuda/topic0.git
|
||||||
|
ignore = dirty
|
||||||
|
|
|
@ -13,6 +13,7 @@ import { faCircleNotch } from "@fortawesome/free-solid-svg-icons/faCircleNotch";
|
||||||
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";
|
||||||
|
import AddressOrENSNameNotFound from "./components/AddressOrENSNameNotFound";
|
||||||
import Copy from "./components/Copy";
|
import Copy from "./components/Copy";
|
||||||
import NavTab from "./components/NavTab";
|
import NavTab from "./components/NavTab";
|
||||||
import SourcifyLogo from "./sourcify/SourcifyLogo";
|
import SourcifyLogo from "./sourcify/SourcifyLogo";
|
||||||
|
@ -20,12 +21,19 @@ import AddressTransactionResults from "./address/AddressTransactionResults";
|
||||||
import Contracts from "./address/Contracts";
|
import Contracts from "./address/Contracts";
|
||||||
import { RuntimeContext } from "./useRuntime";
|
import { RuntimeContext } from "./useRuntime";
|
||||||
import { useAppConfigContext } from "./useAppConfig";
|
import { useAppConfigContext } from "./useAppConfig";
|
||||||
import { useAddressOrENSFromURL } from "./useResolvedAddresses";
|
import { useAddressOrENS } from "./useResolvedAddresses";
|
||||||
import { useMultipleMetadata } from "./sourcify/useSourcify";
|
import { useMultipleMetadata } from "./sourcify/useSourcify";
|
||||||
import { ChecksummedAddress } from "./types";
|
import { ChecksummedAddress } from "./types";
|
||||||
import { useAddressesWithCode } from "./useErigonHooks";
|
import { useAddressesWithCode } from "./useErigonHooks";
|
||||||
|
|
||||||
const AddressTransactions: React.FC = () => {
|
const AddressTransactionByNonce = React.lazy(
|
||||||
|
() =>
|
||||||
|
import(
|
||||||
|
/* webpackChunkName: "addresstxbynonce", webpackPrefetch: true */ "./AddressTransactionByNonce"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
const Address: React.FC = () => {
|
||||||
const { provider } = useContext(RuntimeContext);
|
const { provider } = useContext(RuntimeContext);
|
||||||
const { addressOrName, direction } = useParams();
|
const { addressOrName, direction } = useParams();
|
||||||
if (addressOrName === undefined) {
|
if (addressOrName === undefined) {
|
||||||
|
@ -45,7 +53,7 @@ const AddressTransactions: React.FC = () => {
|
||||||
},
|
},
|
||||||
[navigate, direction, searchParams]
|
[navigate, direction, searchParams]
|
||||||
);
|
);
|
||||||
const [checksummedAddress, isENS, error] = useAddressOrENSFromURL(
|
const [checksummedAddress, isENS, error] = useAddressOrENS(
|
||||||
addressOrName,
|
addressOrName,
|
||||||
urlFixer
|
urlFixer
|
||||||
);
|
);
|
||||||
|
@ -78,12 +86,21 @@ const AddressTransactions: React.FC = () => {
|
||||||
? metadatas[checksummedAddress]
|
? metadatas[checksummedAddress]
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
|
// Search address by nonce === transaction @ nonce
|
||||||
|
const rawNonce = searchParams.get("nonce");
|
||||||
|
if (rawNonce !== null) {
|
||||||
|
return (
|
||||||
|
<AddressTransactionByNonce
|
||||||
|
checksummedAddress={checksummedAddress}
|
||||||
|
rawNonce={rawNonce}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StandardFrame>
|
<StandardFrame>
|
||||||
{error ? (
|
{error ? (
|
||||||
<span className="text-base">
|
<AddressOrENSNameNotFound addressOrENSName={addressOrName} />
|
||||||
"{addressOrName}" is not an ETH address or ENS name.
|
|
||||||
</span>
|
|
||||||
) : (
|
) : (
|
||||||
checksummedAddress && (
|
checksummedAddress && (
|
||||||
<>
|
<>
|
||||||
|
@ -175,4 +192,4 @@ const AddressTransactions: React.FC = () => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default AddressTransactions;
|
export default Address;
|
|
@ -0,0 +1,106 @@
|
||||||
|
import React, { useContext, useEffect, useState } from "react";
|
||||||
|
import { useNavigate } from "react-router-dom";
|
||||||
|
import StandardFrame from "./StandardFrame";
|
||||||
|
import AddressOrENSNameInvalidNonce from "./components/AddressOrENSNameInvalidNonce";
|
||||||
|
import AddressOrENSNameNoTx from "./components/AddressOrENSNameNoTx";
|
||||||
|
import { ChecksummedAddress } from "./types";
|
||||||
|
import { transactionURL } from "./url";
|
||||||
|
import { useTransactionBySenderAndNonce } from "./useErigonHooks";
|
||||||
|
import { RuntimeContext } from "./useRuntime";
|
||||||
|
|
||||||
|
type AddressTransactionByNonceProps = {
|
||||||
|
checksummedAddress: ChecksummedAddress | undefined;
|
||||||
|
rawNonce: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const AddressTransactionByNonce: React.FC<AddressTransactionByNonceProps> = ({
|
||||||
|
checksummedAddress,
|
||||||
|
rawNonce,
|
||||||
|
}) => {
|
||||||
|
const { provider } = useContext(RuntimeContext);
|
||||||
|
|
||||||
|
// Calculate txCount ONLY when asked for latest nonce
|
||||||
|
const [txCount, setTxCount] = useState<number | undefined>();
|
||||||
|
useEffect(() => {
|
||||||
|
if (!provider || !checksummedAddress || rawNonce !== "latest") {
|
||||||
|
setTxCount(undefined);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const readTxCount = async () => {
|
||||||
|
const count = await provider.getTransactionCount(checksummedAddress);
|
||||||
|
setTxCount(count);
|
||||||
|
};
|
||||||
|
readTxCount();
|
||||||
|
}, [provider, checksummedAddress, rawNonce]);
|
||||||
|
|
||||||
|
// Determine desired nonce from parse int query param or txCount - 1 nonce
|
||||||
|
// in case of latest
|
||||||
|
let nonce: number | undefined;
|
||||||
|
if (rawNonce === "latest") {
|
||||||
|
if (txCount !== undefined) {
|
||||||
|
nonce = txCount - 1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
nonce = parseInt(rawNonce, 10);
|
||||||
|
if (nonce < 0) {
|
||||||
|
nonce = NaN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Given all base params are determined, get the corresponding tx
|
||||||
|
const txHash = useTransactionBySenderAndNonce(
|
||||||
|
provider,
|
||||||
|
checksummedAddress,
|
||||||
|
nonce !== undefined && isNaN(nonce) ? undefined : nonce
|
||||||
|
);
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
// Loading...
|
||||||
|
if (
|
||||||
|
checksummedAddress === undefined ||
|
||||||
|
nonce === undefined ||
|
||||||
|
txHash === undefined
|
||||||
|
) {
|
||||||
|
return <StandardFrame />;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Address hasn't made the first outbound tx yet
|
||||||
|
if (nonce < 0) {
|
||||||
|
return (
|
||||||
|
<StandardFrame>
|
||||||
|
<AddressOrENSNameNoTx addressOrENSName={checksummedAddress} />
|
||||||
|
</StandardFrame>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Garbage nonce
|
||||||
|
if (isNaN(nonce)) {
|
||||||
|
return (
|
||||||
|
<StandardFrame>
|
||||||
|
<AddressOrENSNameInvalidNonce
|
||||||
|
addressOrENSName={checksummedAddress}
|
||||||
|
nonce={rawNonce}
|
||||||
|
/>
|
||||||
|
</StandardFrame>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Valid nonce, but no tx found
|
||||||
|
if (txHash === null) {
|
||||||
|
return (
|
||||||
|
<StandardFrame>
|
||||||
|
<AddressOrENSNameInvalidNonce
|
||||||
|
addressOrENSName={checksummedAddress}
|
||||||
|
nonce={nonce.toString()}
|
||||||
|
/>
|
||||||
|
</StandardFrame>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Success; replace and render filler
|
||||||
|
navigate(transactionURL(txHash), { replace: true });
|
||||||
|
return <StandardFrame />;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default AddressTransactionByNonce;
|
16
src/App.tsx
16
src/App.tsx
|
@ -17,11 +17,9 @@ const BlockTransactions = React.lazy(
|
||||||
/* webpackChunkName: "blocktxs", webpackPrefetch: true */ "./BlockTransactions"
|
/* webpackChunkName: "blocktxs", webpackPrefetch: true */ "./BlockTransactions"
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
const AddressTransactions = React.lazy(
|
const Address = React.lazy(
|
||||||
() =>
|
() =>
|
||||||
import(
|
import(/* webpackChunkName: "address", webpackPrefetch: true */ "./Address")
|
||||||
/* webpackChunkName: "address", webpackPrefetch: true */ "./AddressTransactions"
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
const Transaction = React.lazy(
|
const Transaction = React.lazy(
|
||||||
() =>
|
() =>
|
||||||
|
@ -33,6 +31,12 @@ const London = React.lazy(
|
||||||
/* webpackChunkName: "london", webpackPrefetch: true */ "./special/london/London"
|
/* webpackChunkName: "london", webpackPrefetch: true */ "./special/london/London"
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
const PageNotFound = React.lazy(
|
||||||
|
() =>
|
||||||
|
import(
|
||||||
|
/* webpackChunkName: "notfound", webpackPrefetch: true */ "./PageNotFound"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
const App = () => {
|
const App = () => {
|
||||||
const runtime = useRuntime();
|
const runtime = useRuntime();
|
||||||
|
@ -61,9 +65,9 @@ const App = () => {
|
||||||
<Route path="tx/:txhash/*" element={<Transaction />} />
|
<Route path="tx/:txhash/*" element={<Transaction />} />
|
||||||
<Route
|
<Route
|
||||||
path="address/:addressOrName/*"
|
path="address/:addressOrName/*"
|
||||||
element={<AddressTransactions />}
|
element={<Address />}
|
||||||
/>
|
/>
|
||||||
<Route path="*" element={<Home />} />
|
<Route path="*" element={<PageNotFound />} />
|
||||||
</Route>
|
</Route>
|
||||||
</Routes>
|
</Routes>
|
||||||
</Router>
|
</Router>
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
import React from "react";
|
||||||
|
import { NavLink } from "react-router-dom";
|
||||||
|
import StandardFrame from "./StandardFrame";
|
||||||
|
|
||||||
|
const PageNotFound: React.FC = () => (
|
||||||
|
<StandardFrame>
|
||||||
|
<div className="border h-full m-auto flex flex-col justify-center items-center space-y-10">
|
||||||
|
<span className="text-4xl">Page not found!</span>
|
||||||
|
<NavLink className="text-link-blue hover:text-link-blue-hover" to="/">
|
||||||
|
Click here to go to home
|
||||||
|
</NavLink>
|
||||||
|
</div>
|
||||||
|
</StandardFrame>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default PageNotFound;
|
|
@ -1,129 +1,13 @@
|
||||||
import React, { useMemo, useContext } from "react";
|
import React from "react";
|
||||||
import { useParams, Routes, Route } from "react-router-dom";
|
import { useParams } from "react-router-dom";
|
||||||
import { Tab } from "@headlessui/react";
|
import TransactionPageContent from "./TransactionPageContent";
|
||||||
import StandardFrame from "./StandardFrame";
|
|
||||||
import StandardSubtitle from "./StandardSubtitle";
|
|
||||||
import ContentFrame from "./ContentFrame";
|
|
||||||
import NavTab from "./components/NavTab";
|
|
||||||
import { RuntimeContext } from "./useRuntime";
|
|
||||||
import { SelectionContext, useSelection } from "./useSelection";
|
|
||||||
import { useInternalOperations, useTxData } from "./useErigonHooks";
|
|
||||||
import { useETHUSDOracle } from "./usePriceOracle";
|
|
||||||
import { useAppConfigContext } from "./useAppConfig";
|
|
||||||
import { useSourcify, useTransactionDescription } from "./sourcify/useSourcify";
|
|
||||||
import { SelectedTransactionContext } from "./useSelectedTransaction";
|
|
||||||
|
|
||||||
const Details = React.lazy(
|
|
||||||
() =>
|
|
||||||
import(
|
|
||||||
/* webpackChunkName: "txdetails", webpackPrefetch: true */
|
|
||||||
"./transaction/Details"
|
|
||||||
)
|
|
||||||
);
|
|
||||||
const Logs = React.lazy(
|
|
||||||
() =>
|
|
||||||
import(
|
|
||||||
/* webpackChunkName: "txlogs", webpackPrefetch: true */ "./transaction/Logs"
|
|
||||||
)
|
|
||||||
);
|
|
||||||
const Trace = React.lazy(
|
|
||||||
() =>
|
|
||||||
import(
|
|
||||||
/* webpackChunkName: "txtrace", webpackPrefetch: true */ "./transaction/Trace"
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
const Transaction: React.FC = () => {
|
const Transaction: React.FC = () => {
|
||||||
const { provider } = useContext(RuntimeContext);
|
|
||||||
const { txhash } = useParams();
|
const { txhash } = useParams();
|
||||||
if (txhash === undefined) {
|
if (txhash === undefined) {
|
||||||
throw new Error("txhash couldn't be undefined here");
|
throw new Error("txhash couldn't be undefined here");
|
||||||
}
|
}
|
||||||
|
return <TransactionPageContent txHash={txhash} />;
|
||||||
const txData = useTxData(provider, txhash);
|
|
||||||
const internalOps = useInternalOperations(provider, txData);
|
|
||||||
const sendsEthToMiner = useMemo(() => {
|
|
||||||
if (!txData || !internalOps) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const t of internalOps) {
|
|
||||||
if (t.to === txData.confirmedData?.miner) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}, [txData, internalOps]);
|
|
||||||
|
|
||||||
const selectionCtx = useSelection();
|
|
||||||
|
|
||||||
const blockETHUSDPrice = useETHUSDOracle(
|
|
||||||
provider,
|
|
||||||
txData?.confirmedData?.blockNumber
|
|
||||||
);
|
|
||||||
|
|
||||||
const { sourcifySource } = useAppConfigContext();
|
|
||||||
const metadata = useSourcify(
|
|
||||||
txData?.to,
|
|
||||||
provider?.network.chainId,
|
|
||||||
sourcifySource
|
|
||||||
);
|
|
||||||
const txDesc = useTransactionDescription(metadata, txData);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<SelectedTransactionContext.Provider value={txData}>
|
|
||||||
<StandardFrame>
|
|
||||||
<StandardSubtitle>Transaction Details</StandardSubtitle>
|
|
||||||
{txData === null && (
|
|
||||||
<ContentFrame>
|
|
||||||
<div className="py-4 text-sm">
|
|
||||||
Transaction <span className="font-hash">{txhash}</span> not found.
|
|
||||||
</div>
|
|
||||||
</ContentFrame>
|
|
||||||
)}
|
|
||||||
{txData && (
|
|
||||||
<SelectionContext.Provider value={selectionCtx}>
|
|
||||||
<Tab.Group>
|
|
||||||
<Tab.List className="flex space-x-2 border-l border-r border-t rounded-t-lg bg-white">
|
|
||||||
<NavTab href=".">Overview</NavTab>
|
|
||||||
{txData.confirmedData?.blockNumber !== undefined && (
|
|
||||||
<NavTab href="logs">
|
|
||||||
Logs
|
|
||||||
{txData && ` (${txData.confirmedData?.logs?.length ?? 0})`}
|
|
||||||
</NavTab>
|
|
||||||
)}
|
|
||||||
<NavTab href="trace">Trace</NavTab>
|
|
||||||
</Tab.List>
|
|
||||||
</Tab.Group>
|
|
||||||
<React.Suspense fallback={null}>
|
|
||||||
<Routes>
|
|
||||||
<Route
|
|
||||||
index
|
|
||||||
element={
|
|
||||||
<Details
|
|
||||||
txData={txData}
|
|
||||||
txDesc={txDesc}
|
|
||||||
toMetadata={metadata}
|
|
||||||
userDoc={metadata?.output.userdoc}
|
|
||||||
devDoc={metadata?.output.devdoc}
|
|
||||||
internalOps={internalOps}
|
|
||||||
sendsEthToMiner={sendsEthToMiner}
|
|
||||||
ethUSDPrice={blockETHUSDPrice}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<Route
|
|
||||||
path="logs"
|
|
||||||
element={<Logs txData={txData} metadata={metadata} />}
|
|
||||||
/>
|
|
||||||
<Route path="trace" element={<Trace txData={txData} />} />
|
|
||||||
</Routes>
|
|
||||||
</React.Suspense>
|
|
||||||
</SelectionContext.Provider>
|
|
||||||
)}
|
|
||||||
</StandardFrame>
|
|
||||||
</SelectedTransactionContext.Provider>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Transaction;
|
export default Transaction;
|
||||||
|
|
|
@ -0,0 +1,131 @@
|
||||||
|
import React, { useContext, useMemo } from "react";
|
||||||
|
import { Route, Routes } from "react-router-dom";
|
||||||
|
import { Tab } from "@headlessui/react";
|
||||||
|
import StandardFrame from "./StandardFrame";
|
||||||
|
import StandardSubtitle from "./StandardSubtitle";
|
||||||
|
import ContentFrame from "./ContentFrame";
|
||||||
|
import NavTab from "./components/NavTab";
|
||||||
|
import { RuntimeContext } from "./useRuntime";
|
||||||
|
import { useInternalOperations, useTxData } from "./useErigonHooks";
|
||||||
|
import { SelectionContext, useSelection } from "./useSelection";
|
||||||
|
import { SelectedTransactionContext } from "./useSelectedTransaction";
|
||||||
|
import { useETHUSDOracle } from "./usePriceOracle";
|
||||||
|
import { useAppConfigContext } from "./useAppConfig";
|
||||||
|
import { useSourcify, useTransactionDescription } from "./sourcify/useSourcify";
|
||||||
|
|
||||||
|
const Details = React.lazy(
|
||||||
|
() =>
|
||||||
|
import(
|
||||||
|
/* webpackChunkName: "txdetails", webpackPrefetch: true */
|
||||||
|
"./transaction/Details"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
const Logs = React.lazy(
|
||||||
|
() =>
|
||||||
|
import(
|
||||||
|
/* webpackChunkName: "txlogs", webpackPrefetch: true */ "./transaction/Logs"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
const Trace = React.lazy(
|
||||||
|
() =>
|
||||||
|
import(
|
||||||
|
/* webpackChunkName: "txtrace", webpackPrefetch: true */ "./transaction/Trace"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
type TransactionPageContentProps = {
|
||||||
|
txHash: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const TransactionPageContent: React.FC<TransactionPageContentProps> = ({
|
||||||
|
txHash,
|
||||||
|
}) => {
|
||||||
|
const { provider } = useContext(RuntimeContext);
|
||||||
|
|
||||||
|
const txData = useTxData(provider, txHash);
|
||||||
|
const internalOps = useInternalOperations(provider, txData);
|
||||||
|
const sendsEthToMiner = useMemo(() => {
|
||||||
|
if (!txData || !internalOps) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const t of internalOps) {
|
||||||
|
if (t.to === txData.confirmedData?.miner) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}, [txData, internalOps]);
|
||||||
|
|
||||||
|
const selectionCtx = useSelection();
|
||||||
|
|
||||||
|
const blockETHUSDPrice = useETHUSDOracle(
|
||||||
|
provider,
|
||||||
|
txData?.confirmedData?.blockNumber
|
||||||
|
);
|
||||||
|
|
||||||
|
const { sourcifySource } = useAppConfigContext();
|
||||||
|
const metadata = useSourcify(
|
||||||
|
txData?.to,
|
||||||
|
provider?.network.chainId,
|
||||||
|
sourcifySource
|
||||||
|
);
|
||||||
|
const txDesc = useTransactionDescription(metadata, txData);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SelectedTransactionContext.Provider value={txData}>
|
||||||
|
<StandardFrame>
|
||||||
|
<StandardSubtitle>Transaction Details</StandardSubtitle>
|
||||||
|
{txData === null && (
|
||||||
|
<ContentFrame>
|
||||||
|
<div className="py-4 text-sm">
|
||||||
|
Transaction <span className="font-hash">{txHash}</span> not found.
|
||||||
|
</div>
|
||||||
|
</ContentFrame>
|
||||||
|
)}
|
||||||
|
{txData && (
|
||||||
|
<SelectionContext.Provider value={selectionCtx}>
|
||||||
|
<Tab.Group>
|
||||||
|
<Tab.List className="flex space-x-2 border-l border-r border-t rounded-t-lg bg-white">
|
||||||
|
<NavTab href=".">Overview</NavTab>
|
||||||
|
{txData.confirmedData?.blockNumber !== undefined && (
|
||||||
|
<NavTab href="logs">
|
||||||
|
Logs
|
||||||
|
{txData && ` (${txData.confirmedData?.logs?.length ?? 0})`}
|
||||||
|
</NavTab>
|
||||||
|
)}
|
||||||
|
<NavTab href="trace">Trace</NavTab>
|
||||||
|
</Tab.List>
|
||||||
|
</Tab.Group>
|
||||||
|
<React.Suspense fallback={null}>
|
||||||
|
<Routes>
|
||||||
|
<Route
|
||||||
|
index
|
||||||
|
element={
|
||||||
|
<Details
|
||||||
|
txData={txData}
|
||||||
|
txDesc={txDesc}
|
||||||
|
toMetadata={metadata}
|
||||||
|
userDoc={metadata?.output.userdoc}
|
||||||
|
devDoc={metadata?.output.devdoc}
|
||||||
|
internalOps={internalOps}
|
||||||
|
sendsEthToMiner={sendsEthToMiner}
|
||||||
|
ethUSDPrice={blockETHUSDPrice}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<Route
|
||||||
|
path="logs"
|
||||||
|
element={<Logs txData={txData} metadata={metadata} />}
|
||||||
|
/>
|
||||||
|
<Route path="trace" element={<Trace txData={txData} />} />
|
||||||
|
</Routes>
|
||||||
|
</React.Suspense>
|
||||||
|
</SelectionContext.Provider>
|
||||||
|
)}
|
||||||
|
</StandardFrame>
|
||||||
|
</SelectedTransactionContext.Provider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default TransactionPageContent;
|
|
@ -1,11 +1,12 @@
|
||||||
import React, { useContext } from "react";
|
import React, { useContext } from "react";
|
||||||
|
import PlainAddress from "./PlainAddress";
|
||||||
import { resolverRendererRegistry } from "../api/address-resolver";
|
import { resolverRendererRegistry } from "../api/address-resolver";
|
||||||
import { useResolvedAddress } from "../useResolvedAddresses";
|
import { useResolvedAddress } from "../useResolvedAddresses";
|
||||||
import { RuntimeContext } from "../useRuntime";
|
import { RuntimeContext } from "../useRuntime";
|
||||||
import PlainAddress from "./PlainAddress";
|
import { ChecksummedAddress } from "../types";
|
||||||
|
|
||||||
type AddressOrENSNameProps = {
|
type AddressOrENSNameProps = {
|
||||||
address: string;
|
address: ChecksummedAddress;
|
||||||
selectedAddress?: string;
|
selectedAddress?: string;
|
||||||
dontOverrideColors?: boolean;
|
dontOverrideColors?: boolean;
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
import React from "react";
|
||||||
|
import StandardSubtitle from "../StandardSubtitle";
|
||||||
|
import ContentFrame from "../ContentFrame";
|
||||||
|
import AddressOrENSName from "./AddressOrENSName";
|
||||||
|
|
||||||
|
type AddressOrENSNameInvalidNonceProps = {
|
||||||
|
addressOrENSName: string;
|
||||||
|
nonce: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const AddressOrENSNameInvalidNonce: React.FC<
|
||||||
|
AddressOrENSNameInvalidNonceProps
|
||||||
|
> = ({ addressOrENSName, nonce }) => (
|
||||||
|
<>
|
||||||
|
<StandardSubtitle>Transaction Details</StandardSubtitle>
|
||||||
|
<ContentFrame>
|
||||||
|
<div className="flex py-4 text-sm">
|
||||||
|
<AddressOrENSName address={addressOrENSName} />
|
||||||
|
<span>: no transaction found for nonce="{nonce}".</span>
|
||||||
|
</div>
|
||||||
|
</ContentFrame>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default React.memo(AddressOrENSNameInvalidNonce);
|
|
@ -0,0 +1,24 @@
|
||||||
|
import React from "react";
|
||||||
|
import StandardSubtitle from "../StandardSubtitle";
|
||||||
|
import ContentFrame from "../ContentFrame";
|
||||||
|
import AddressOrENSName from "./AddressOrENSName";
|
||||||
|
|
||||||
|
type AddressOrENSNameNoTxProps = {
|
||||||
|
addressOrENSName: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const AddressOrENSNameNoTx: React.FC<AddressOrENSNameNoTxProps> = ({
|
||||||
|
addressOrENSName,
|
||||||
|
}) => (
|
||||||
|
<>
|
||||||
|
<StandardSubtitle>Transaction Details</StandardSubtitle>
|
||||||
|
<ContentFrame>
|
||||||
|
<div className="flex py-4 text-sm">
|
||||||
|
<AddressOrENSName address={addressOrENSName} />
|
||||||
|
<span>: no outbound transactions found.</span>
|
||||||
|
</div>
|
||||||
|
</ContentFrame>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default React.memo(AddressOrENSNameNoTx);
|
|
@ -0,0 +1,22 @@
|
||||||
|
import React from "react";
|
||||||
|
import StandardSubtitle from "../StandardSubtitle";
|
||||||
|
import ContentFrame from "../ContentFrame";
|
||||||
|
|
||||||
|
type AddressOrENSNameNotFoundProps = {
|
||||||
|
addressOrENSName: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const AddressOrENSNameNotFound: React.FC<AddressOrENSNameNotFoundProps> = ({
|
||||||
|
addressOrENSName,
|
||||||
|
}) => (
|
||||||
|
<>
|
||||||
|
<StandardSubtitle>Transaction Details</StandardSubtitle>
|
||||||
|
<ContentFrame>
|
||||||
|
<div className="py-4 text-sm">
|
||||||
|
"{addressOrENSName}" is not an ETH address or ENS name.
|
||||||
|
</div>
|
||||||
|
</ContentFrame>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default React.memo(AddressOrENSNameNotFound);
|
|
@ -1,4 +1,5 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
import { commify } from "@ethersproject/units";
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
import { faArrowUp } from "@fortawesome/free-solid-svg-icons/faArrowUp";
|
import { faArrowUp } from "@fortawesome/free-solid-svg-icons/faArrowUp";
|
||||||
|
|
||||||
|
@ -14,7 +15,7 @@ const Nonce: React.FC<NonceProps> = ({ value }) => (
|
||||||
<span className="text-green-400">
|
<span className="text-green-400">
|
||||||
<FontAwesomeIcon icon={faArrowUp} size="1x" />
|
<FontAwesomeIcon icon={faArrowUp} size="1x" />
|
||||||
</span>
|
</span>
|
||||||
<span className="text-green-600">{value}</span>
|
<span className="text-green-600">{commify(value)}</span>
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { NavLink } from "react-router-dom";
|
import { NavLink } from "react-router-dom";
|
||||||
|
import { transactionURL } from "../url";
|
||||||
|
|
||||||
type TransactionLinkProps = {
|
type TransactionLinkProps = {
|
||||||
txHash: string;
|
txHash: string;
|
||||||
|
@ -8,7 +9,7 @@ type TransactionLinkProps = {
|
||||||
const TransactionLink: React.FC<TransactionLinkProps> = ({ txHash }) => (
|
const TransactionLink: React.FC<TransactionLinkProps> = ({ txHash }) => (
|
||||||
<NavLink
|
<NavLink
|
||||||
className="text-link-blue hover:text-link-blue-hover font-hash"
|
className="text-link-blue hover:text-link-blue-hover font-hash"
|
||||||
to={`/tx/${txHash}`}
|
to={transactionURL(txHash)}
|
||||||
>
|
>
|
||||||
<p className="truncate">{txHash}</p>
|
<p className="truncate">{txHash}</p>
|
||||||
</NavLink>
|
</NavLink>
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
export const MIN_API_LEVEL = 5;
|
export const MIN_API_LEVEL = 6;
|
||||||
|
|
||||||
export const PAGE_SIZE = 25;
|
export const PAGE_SIZE = 25;
|
||||||
|
|
|
@ -206,17 +206,36 @@ export class SearchController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const doSearch = (q: string, navigate: NavigateFunction) => {
|
const doSearch = async (q: string, navigate: NavigateFunction) => {
|
||||||
if (isAddress(q)) {
|
// Cleanup
|
||||||
navigate(`/address/${q}`, { replace: true });
|
q = q.trim();
|
||||||
|
|
||||||
|
let maybeAddress = q;
|
||||||
|
let maybeIndex = "";
|
||||||
|
const sepIndex = q.lastIndexOf(":");
|
||||||
|
if (sepIndex !== -1) {
|
||||||
|
maybeAddress = q.substring(0, sepIndex);
|
||||||
|
maybeIndex = q.substring(sepIndex + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Plain address?
|
||||||
|
if (isAddress(maybeAddress)) {
|
||||||
|
navigate(
|
||||||
|
`/address/${maybeAddress}${
|
||||||
|
maybeIndex !== "" ? `?nonce=${maybeIndex}` : ""
|
||||||
|
}`,
|
||||||
|
{ replace: true }
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Tx hash?
|
||||||
if (isHexString(q, 32)) {
|
if (isHexString(q, 32)) {
|
||||||
navigate(`/tx/${q}`, { replace: true });
|
navigate(`/tx/${q}`, { replace: true });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Block number?
|
||||||
const blockNumber = parseInt(q);
|
const blockNumber = parseInt(q);
|
||||||
if (!isNaN(blockNumber)) {
|
if (!isNaN(blockNumber)) {
|
||||||
navigate(`/block/${blockNumber}`, { replace: true });
|
navigate(`/block/${blockNumber}`, { replace: true });
|
||||||
|
@ -224,7 +243,12 @@ const doSearch = (q: string, navigate: NavigateFunction) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Assume it is an ENS name
|
// Assume it is an ENS name
|
||||||
navigate(`/address/${q}`);
|
navigate(
|
||||||
|
`/address/${maybeAddress}${
|
||||||
|
maybeIndex !== "" ? `?nonce=${maybeIndex}` : ""
|
||||||
|
}`,
|
||||||
|
{ replace: true }
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useGenericSearch = (): [
|
export const useGenericSearch = (): [
|
||||||
|
|
|
@ -15,6 +15,7 @@ import BlockConfirmations from "../components/BlockConfirmations";
|
||||||
import TransactionAddress from "../components/TransactionAddress";
|
import TransactionAddress from "../components/TransactionAddress";
|
||||||
import Copy from "../components/Copy";
|
import Copy from "../components/Copy";
|
||||||
import Nonce from "../components/Nonce";
|
import Nonce from "../components/Nonce";
|
||||||
|
import NavNonce from "./NavNonce";
|
||||||
import Timestamp from "../components/Timestamp";
|
import Timestamp from "../components/Timestamp";
|
||||||
import InternalTransactionOperation from "../components/InternalTransactionOperation";
|
import InternalTransactionOperation from "../components/InternalTransactionOperation";
|
||||||
import MethodName from "../components/MethodName";
|
import MethodName from "../components/MethodName";
|
||||||
|
@ -253,6 +254,7 @@ const Details: React.FC<DetailsProps> = ({
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-baseline pl-3">
|
<div className="flex items-baseline pl-3">
|
||||||
<Nonce value={txData.nonce} />
|
<Nonce value={txData.nonce} />
|
||||||
|
<NavNonce sender={txData.from} nonce={txData.nonce} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</InfoRow>
|
</InfoRow>
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
import { NavLink } from "react-router-dom";
|
||||||
|
import { ChecksummedAddress } from "../types";
|
||||||
|
import { addressByNonceURL } from "../url";
|
||||||
|
|
||||||
|
// TODO: extract common component with block/NavButton
|
||||||
|
type NavButtonProps = {
|
||||||
|
sender: ChecksummedAddress;
|
||||||
|
nonce: number;
|
||||||
|
disabled?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
const NavButton: React.FC<NavButtonProps> = ({
|
||||||
|
sender,
|
||||||
|
nonce,
|
||||||
|
disabled,
|
||||||
|
children,
|
||||||
|
}) => {
|
||||||
|
if (disabled) {
|
||||||
|
return (
|
||||||
|
<span className="bg-link-blue bg-opacity-10 text-gray-300 rounded px-2 py-1 text-xs">
|
||||||
|
{children}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<NavLink
|
||||||
|
className="bg-link-blue bg-opacity-10 text-link-blue hover:bg-opacity-100 hover:text-white rounded px-2 py-1 text-xs"
|
||||||
|
to={addressByNonceURL(sender, nonce)}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</NavLink>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default NavButton;
|
|
@ -0,0 +1,66 @@
|
||||||
|
import React, { useContext, useEffect } from "react";
|
||||||
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
|
import { faChevronLeft } from "@fortawesome/free-solid-svg-icons/faChevronLeft";
|
||||||
|
import { faChevronRight } from "@fortawesome/free-solid-svg-icons/faChevronRight";
|
||||||
|
import NavButton from "./NavButton";
|
||||||
|
import { ChecksummedAddress } from "../types";
|
||||||
|
import { RuntimeContext } from "../useRuntime";
|
||||||
|
import {
|
||||||
|
prefetchTransactionBySenderAndNonce,
|
||||||
|
useTransactionCount,
|
||||||
|
} from "../useErigonHooks";
|
||||||
|
import { useSWRConfig } from "swr";
|
||||||
|
|
||||||
|
type NavNonceProps = {
|
||||||
|
sender: ChecksummedAddress;
|
||||||
|
nonce: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
const NavNonce: React.FC<NavNonceProps> = ({ sender, nonce }) => {
|
||||||
|
const { provider } = useContext(RuntimeContext);
|
||||||
|
const count = useTransactionCount(provider, sender);
|
||||||
|
|
||||||
|
// Prefetch
|
||||||
|
const swrConfig = useSWRConfig();
|
||||||
|
useEffect(() => {
|
||||||
|
if (!provider || !sender || nonce === undefined || count === undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
prefetchTransactionBySenderAndNonce(swrConfig, provider, sender, nonce - 1);
|
||||||
|
prefetchTransactionBySenderAndNonce(swrConfig, provider, sender, nonce + 1);
|
||||||
|
if (count > 0) {
|
||||||
|
prefetchTransactionBySenderAndNonce(
|
||||||
|
swrConfig,
|
||||||
|
provider,
|
||||||
|
sender,
|
||||||
|
count - 1
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}, [swrConfig, provider, sender, nonce, count]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="pl-2 self-center flex space-x-1">
|
||||||
|
<NavButton sender={sender} nonce={nonce - 1} disabled={nonce === 0}>
|
||||||
|
<FontAwesomeIcon icon={faChevronLeft} />
|
||||||
|
</NavButton>
|
||||||
|
<NavButton
|
||||||
|
sender={sender}
|
||||||
|
nonce={nonce + 1}
|
||||||
|
disabled={count === undefined || nonce >= count - 1}
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon icon={faChevronRight} />
|
||||||
|
</NavButton>
|
||||||
|
<NavButton
|
||||||
|
sender={sender}
|
||||||
|
nonce={count !== undefined ? count - 1 : -1}
|
||||||
|
disabled={count === undefined || nonce >= count - 1}
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon icon={faChevronRight} />
|
||||||
|
<FontAwesomeIcon icon={faChevronRight} />
|
||||||
|
</NavButton>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default React.memo(NavNonce);
|
|
@ -18,6 +18,11 @@ export const blockURL = (blockNum: BlockTag) => `/block/${blockNum}`;
|
||||||
|
|
||||||
export const blockTxsURL = (blockNum: BlockTag) => `/block/${blockNum}/txs`;
|
export const blockTxsURL = (blockNum: BlockTag) => `/block/${blockNum}/txs`;
|
||||||
|
|
||||||
|
export const transactionURL = (txHash: string) => `/tx/${txHash}`;
|
||||||
|
|
||||||
|
export const addressByNonceURL = (address: ChecksummedAddress, nonce: number) =>
|
||||||
|
`/address/${address}?nonce=${nonce}`;
|
||||||
|
|
||||||
export enum SourcifySource {
|
export enum SourcifySource {
|
||||||
// Resolve trusted IPNS for root IPFS
|
// Resolve trusted IPNS for root IPFS
|
||||||
IPFS_IPNS,
|
IPFS_IPNS,
|
||||||
|
|
|
@ -6,6 +6,7 @@ import { Contract } from "@ethersproject/contracts";
|
||||||
import { defaultAbiCoder } from "@ethersproject/abi";
|
import { defaultAbiCoder } from "@ethersproject/abi";
|
||||||
import { BigNumber } from "@ethersproject/bignumber";
|
import { BigNumber } from "@ethersproject/bignumber";
|
||||||
import { arrayify, hexDataSlice, isHexString } from "@ethersproject/bytes";
|
import { arrayify, hexDataSlice, isHexString } from "@ethersproject/bytes";
|
||||||
|
import useSWR, { useSWRConfig } from "swr";
|
||||||
import { getInternalOperations } from "./nodeFunctions";
|
import { getInternalOperations } from "./nodeFunctions";
|
||||||
import {
|
import {
|
||||||
TokenMetas,
|
TokenMetas,
|
||||||
|
@ -190,6 +191,7 @@ export const useTxData = (
|
||||||
}
|
}
|
||||||
|
|
||||||
const readTxData = async () => {
|
const readTxData = async () => {
|
||||||
|
try {
|
||||||
const [_response, _receipt] = await Promise.all([
|
const [_response, _receipt] = await Promise.all([
|
||||||
provider.getTransaction(txhash),
|
provider.getTransaction(txhash),
|
||||||
provider.getTransactionReceipt(txhash),
|
provider.getTransactionReceipt(txhash),
|
||||||
|
@ -245,7 +247,10 @@ export const useTxData = (
|
||||||
};
|
};
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
tokenMetas[t.token] = null;
|
tokenMetas[t.token] = null;
|
||||||
console.warn(`Couldn't get token ${t.token} metadata; ignoring`, err);
|
console.warn(
|
||||||
|
`Couldn't get token ${t.token} metadata; ignoring`,
|
||||||
|
err
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -281,7 +286,12 @@ export const useTxData = (
|
||||||
logs: _receipt.logs,
|
logs: _receipt.logs,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
setTxData(null);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
readTxData();
|
readTxData();
|
||||||
}, [provider, txhash]);
|
}, [provider, txhash]);
|
||||||
|
|
||||||
|
@ -502,3 +512,91 @@ export const useTransactionError = (
|
||||||
|
|
||||||
return [errorMsg, data, isCustomError];
|
return [errorMsg, data, isCustomError];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const useTransactionCount = (
|
||||||
|
provider: JsonRpcProvider | undefined,
|
||||||
|
sender: ChecksummedAddress | undefined
|
||||||
|
): number | undefined => {
|
||||||
|
const { data, error } = useSWR(
|
||||||
|
provider && sender ? { provider, sender } : null,
|
||||||
|
async ({ provider, sender }): Promise<number | undefined> =>
|
||||||
|
provider.getTransactionCount(sender)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
type TransactionBySenderAndNonceKey = {
|
||||||
|
network: number;
|
||||||
|
sender: ChecksummedAddress;
|
||||||
|
nonce: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getTransactionBySenderAndNonceFetcher =
|
||||||
|
(provider: JsonRpcProvider) =>
|
||||||
|
async ({
|
||||||
|
network,
|
||||||
|
sender,
|
||||||
|
nonce,
|
||||||
|
}: TransactionBySenderAndNonceKey): Promise<string | null | undefined> => {
|
||||||
|
if (nonce < 0) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = (await provider.send("ots_getTransactionBySenderAndNonce", [
|
||||||
|
sender,
|
||||||
|
nonce,
|
||||||
|
])) as string;
|
||||||
|
|
||||||
|
// Empty or success
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const prefetchTransactionBySenderAndNonce = (
|
||||||
|
{ mutate }: ReturnType<typeof useSWRConfig>,
|
||||||
|
provider: JsonRpcProvider,
|
||||||
|
sender: ChecksummedAddress,
|
||||||
|
nonce: number
|
||||||
|
) => {
|
||||||
|
const key: TransactionBySenderAndNonceKey = {
|
||||||
|
network: provider.network.chainId,
|
||||||
|
sender,
|
||||||
|
nonce,
|
||||||
|
};
|
||||||
|
mutate(key, (curr: any) => {
|
||||||
|
if (curr) {
|
||||||
|
return curr;
|
||||||
|
}
|
||||||
|
return getTransactionBySenderAndNonceFetcher(provider)(key);
|
||||||
|
});
|
||||||
|
// }
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useTransactionBySenderAndNonce = (
|
||||||
|
provider: JsonRpcProvider | undefined,
|
||||||
|
sender: ChecksummedAddress | undefined,
|
||||||
|
nonce: number | undefined
|
||||||
|
): string | null | undefined => {
|
||||||
|
const { data, error } = useSWR<
|
||||||
|
string | null | undefined,
|
||||||
|
any,
|
||||||
|
TransactionBySenderAndNonceKey | null
|
||||||
|
>(
|
||||||
|
provider && sender && nonce !== undefined
|
||||||
|
? {
|
||||||
|
network: provider.network.chainId,
|
||||||
|
sender,
|
||||||
|
nonce,
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
getTransactionBySenderAndNonceFetcher(provider!)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
|
@ -7,7 +7,7 @@ import { SelectedResolvedName } from "./api/address-resolver/CompositeAddressRes
|
||||||
import { RuntimeContext } from "./useRuntime";
|
import { RuntimeContext } from "./useRuntime";
|
||||||
import { ChecksummedAddress } from "./types";
|
import { ChecksummedAddress } from "./types";
|
||||||
|
|
||||||
export const useAddressOrENSFromURL = (
|
export const useAddressOrENS = (
|
||||||
addressOrName: string,
|
addressOrName: string,
|
||||||
urlFixer: (address: ChecksummedAddress) => void
|
urlFixer: (address: ChecksummedAddress) => void
|
||||||
): [
|
): [
|
||||||
|
|
Loading…
Reference in New Issue