Parameterize erigon node url
This commit is contained in:
parent
3e58030a43
commit
da16ce8ad6
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"erigonURL": "http://localhost:8545"
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
import React, { useState, useEffect, useMemo } from "react";
|
import React, { useState, useEffect, useMemo, useContext } from "react";
|
||||||
import { useParams, useLocation, useHistory } from "react-router-dom";
|
import { useParams, useLocation, useHistory } from "react-router-dom";
|
||||||
import { ethers } from "ethers";
|
import { ethers } from "ethers";
|
||||||
import queryString from "query-string";
|
import queryString from "query-string";
|
||||||
|
@ -12,9 +12,9 @@ import ResultHeader from "./search/ResultHeader";
|
||||||
import PendingResults from "./search/PendingResults";
|
import PendingResults from "./search/PendingResults";
|
||||||
import TransactionItem from "./search/TransactionItem";
|
import TransactionItem from "./search/TransactionItem";
|
||||||
import { SearchController } from "./search/search";
|
import { SearchController } from "./search/search";
|
||||||
|
import { ProviderContext } from "./useProvider";
|
||||||
import { useENSCache } from "./useReverseCache";
|
import { useENSCache } from "./useReverseCache";
|
||||||
import { useFeeToggler } from "./search/useFeeToggler";
|
import { useFeeToggler } from "./search/useFeeToggler";
|
||||||
import { provider } from "./ethersconfig";
|
|
||||||
|
|
||||||
type BlockParams = {
|
type BlockParams = {
|
||||||
addressOrName: string;
|
addressOrName: string;
|
||||||
|
@ -26,6 +26,7 @@ type PageParams = {
|
||||||
};
|
};
|
||||||
|
|
||||||
const AddressTransactions: React.FC = () => {
|
const AddressTransactions: React.FC = () => {
|
||||||
|
const provider = useContext(ProviderContext);
|
||||||
const params = useParams<BlockParams>();
|
const params = useParams<BlockParams>();
|
||||||
const location = useLocation<PageParams>();
|
const location = useLocation<PageParams>();
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
|
@ -59,6 +60,9 @@ const AddressTransactions: React.FC = () => {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!provider) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
const resolveName = async () => {
|
const resolveName = async () => {
|
||||||
const resolvedAddress = await provider.resolveName(params.addressOrName);
|
const resolvedAddress = await provider.resolveName(params.addressOrName);
|
||||||
if (resolvedAddress !== null) {
|
if (resolvedAddress !== null) {
|
||||||
|
@ -72,20 +76,30 @@ const AddressTransactions: React.FC = () => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
resolveName();
|
resolveName();
|
||||||
}, [params.addressOrName, history, params.direction, location.search]);
|
}, [
|
||||||
|
provider,
|
||||||
|
params.addressOrName,
|
||||||
|
history,
|
||||||
|
params.direction,
|
||||||
|
location.search,
|
||||||
|
]);
|
||||||
|
|
||||||
const [controller, setController] = useState<SearchController>();
|
const [controller, setController] = useState<SearchController>();
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!checksummedAddress) {
|
if (!provider || !checksummedAddress) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const readFirstPage = async () => {
|
const readFirstPage = async () => {
|
||||||
const _controller = await SearchController.firstPage(checksummedAddress);
|
const _controller = await SearchController.firstPage(
|
||||||
|
provider,
|
||||||
|
checksummedAddress
|
||||||
|
);
|
||||||
setController(_controller);
|
setController(_controller);
|
||||||
};
|
};
|
||||||
const readMiddlePage = async (next: boolean) => {
|
const readMiddlePage = async (next: boolean) => {
|
||||||
const _controller = await SearchController.middlePage(
|
const _controller = await SearchController.middlePage(
|
||||||
|
provider,
|
||||||
checksummedAddress,
|
checksummedAddress,
|
||||||
hash!,
|
hash!,
|
||||||
next
|
next
|
||||||
|
@ -93,15 +107,18 @@ const AddressTransactions: React.FC = () => {
|
||||||
setController(_controller);
|
setController(_controller);
|
||||||
};
|
};
|
||||||
const readLastPage = async () => {
|
const readLastPage = async () => {
|
||||||
const _controller = await SearchController.lastPage(checksummedAddress);
|
const _controller = await SearchController.lastPage(
|
||||||
|
provider,
|
||||||
|
checksummedAddress
|
||||||
|
);
|
||||||
setController(_controller);
|
setController(_controller);
|
||||||
};
|
};
|
||||||
const prevPage = async () => {
|
const prevPage = async () => {
|
||||||
const _controller = await controller!.prevPage(hash!);
|
const _controller = await controller!.prevPage(provider, hash!);
|
||||||
setController(_controller);
|
setController(_controller);
|
||||||
};
|
};
|
||||||
const nextPage = async () => {
|
const nextPage = async () => {
|
||||||
const _controller = await controller!.nextPage(hash!);
|
const _controller = await controller!.nextPage(provider, hash!);
|
||||||
setController(_controller);
|
setController(_controller);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -127,10 +144,10 @@ const AddressTransactions: React.FC = () => {
|
||||||
readLastPage();
|
readLastPage();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [checksummedAddress, params.direction, hash, controller]);
|
}, [provider, checksummedAddress, params.direction, hash, controller]);
|
||||||
|
|
||||||
const page = useMemo(() => controller?.getPage(), [controller]);
|
const page = useMemo(() => controller?.getPage(), [controller]);
|
||||||
const reverseCache = useENSCache(page);
|
const reverseCache = useENSCache(provider, page);
|
||||||
|
|
||||||
document.title = `Address ${params.addressOrName} | Otterscan`;
|
document.title = `Address ${params.addressOrName} | Otterscan`;
|
||||||
|
|
||||||
|
|
65
src/App.tsx
65
src/App.tsx
|
@ -3,40 +3,47 @@ import { BrowserRouter as Router, Switch, Route } from "react-router-dom";
|
||||||
import Home from "./Home";
|
import Home from "./Home";
|
||||||
import Search from "./Search";
|
import Search from "./Search";
|
||||||
import Title from "./Title";
|
import Title from "./Title";
|
||||||
|
import { useProvider, ProviderContext } from "./useProvider";
|
||||||
|
|
||||||
const Block = React.lazy(() => import("./Block"));
|
const Block = React.lazy(() => import("./Block"));
|
||||||
const BlockTransactions = React.lazy(() => import("./BlockTransactions"));
|
const BlockTransactions = React.lazy(() => import("./BlockTransactions"));
|
||||||
const AddressTransactions = React.lazy(() => import("./AddressTransactions"));
|
const AddressTransactions = React.lazy(() => import("./AddressTransactions"));
|
||||||
const Transaction = React.lazy(() => import("./Transaction"));
|
const Transaction = React.lazy(() => import("./Transaction"));
|
||||||
|
|
||||||
const App = () => (
|
const App = () => {
|
||||||
<Suspense fallback={<>LOADING</>}>
|
const provider = useProvider();
|
||||||
<Router>
|
|
||||||
<Switch>
|
return (
|
||||||
<Route path="/" exact>
|
<Suspense fallback={<>LOADING</>}>
|
||||||
<Home />
|
<ProviderContext.Provider value={provider}>
|
||||||
</Route>
|
<Router>
|
||||||
<Route path="/search" exact>
|
<Switch>
|
||||||
<Search />
|
<Route path="/" exact>
|
||||||
</Route>
|
<Home />
|
||||||
<Route>
|
</Route>
|
||||||
<Title />
|
<Route path="/search" exact>
|
||||||
<Route path="/block/:blockNumberOrHash" exact>
|
<Search />
|
||||||
<Block />
|
</Route>
|
||||||
</Route>
|
<Route>
|
||||||
<Route path="/block/:blockNumber/txs" exact>
|
<Title />
|
||||||
<BlockTransactions />
|
<Route path="/block/:blockNumberOrHash" exact>
|
||||||
</Route>
|
<Block />
|
||||||
<Route path="/tx/:txhash">
|
</Route>
|
||||||
<Transaction />
|
<Route path="/block/:blockNumber/txs" exact>
|
||||||
</Route>
|
<BlockTransactions />
|
||||||
<Route path="/address/:addressOrName/:direction?">
|
</Route>
|
||||||
<AddressTransactions />
|
<Route path="/tx/:txhash">
|
||||||
</Route>
|
<Transaction />
|
||||||
</Route>
|
</Route>
|
||||||
</Switch>
|
<Route path="/address/:addressOrName/:direction?">
|
||||||
</Router>
|
<AddressTransactions />
|
||||||
</Suspense>
|
</Route>
|
||||||
);
|
</Route>
|
||||||
|
</Switch>
|
||||||
|
</Router>
|
||||||
|
</ProviderContext.Provider>
|
||||||
|
</Suspense>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export default React.memo(App);
|
export default React.memo(App);
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import React, { useEffect, useState, useMemo } from "react";
|
import React, { useEffect, useState, useMemo, useContext } from "react";
|
||||||
import { useParams, NavLink } from "react-router-dom";
|
import { useParams, NavLink } from "react-router-dom";
|
||||||
import { ethers, BigNumber } from "ethers";
|
import { ethers, BigNumber } from "ethers";
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
|
@ -6,7 +6,6 @@ import {
|
||||||
faChevronLeft,
|
faChevronLeft,
|
||||||
faChevronRight,
|
faChevronRight,
|
||||||
} from "@fortawesome/free-solid-svg-icons";
|
} from "@fortawesome/free-solid-svg-icons";
|
||||||
import { provider } from "./ethersconfig";
|
|
||||||
import StandardFrame from "./StandardFrame";
|
import StandardFrame from "./StandardFrame";
|
||||||
import StandardSubtitle from "./StandardSubtitle";
|
import StandardSubtitle from "./StandardSubtitle";
|
||||||
import ContentFrame from "./ContentFrame";
|
import ContentFrame from "./ContentFrame";
|
||||||
|
@ -17,6 +16,7 @@ import BlockLink from "./components/BlockLink";
|
||||||
import AddressOrENSName from "./components/AddressOrENSName";
|
import AddressOrENSName from "./components/AddressOrENSName";
|
||||||
import TransactionValue from "./components/TransactionValue";
|
import TransactionValue from "./components/TransactionValue";
|
||||||
import HexValue from "./components/HexValue";
|
import HexValue from "./components/HexValue";
|
||||||
|
import { ProviderContext } from "./useProvider";
|
||||||
import { useLatestBlockNumber } from "./useLatestBlock";
|
import { useLatestBlockNumber } from "./useLatestBlock";
|
||||||
|
|
||||||
type BlockParams = {
|
type BlockParams = {
|
||||||
|
@ -34,10 +34,15 @@ interface ExtendedBlock extends ethers.providers.Block {
|
||||||
}
|
}
|
||||||
|
|
||||||
const Block: React.FC = () => {
|
const Block: React.FC = () => {
|
||||||
|
const provider = useContext(ProviderContext);
|
||||||
const params = useParams<BlockParams>();
|
const params = useParams<BlockParams>();
|
||||||
|
|
||||||
const [block, setBlock] = useState<ExtendedBlock>();
|
const [block, setBlock] = useState<ExtendedBlock>();
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
if (!provider) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const readBlock = async () => {
|
const readBlock = async () => {
|
||||||
let blockPromise: Promise<any>;
|
let blockPromise: Promise<any>;
|
||||||
if (ethers.utils.isHexString(params.blockNumberOrHash, 32)) {
|
if (ethers.utils.isHexString(params.blockNumberOrHash, 32)) {
|
||||||
|
@ -80,7 +85,7 @@ const Block: React.FC = () => {
|
||||||
setBlock(extBlock);
|
setBlock(extBlock);
|
||||||
};
|
};
|
||||||
readBlock();
|
readBlock();
|
||||||
}, [params.blockNumberOrHash]);
|
}, [provider, params.blockNumberOrHash]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (block) {
|
if (block) {
|
||||||
|
@ -97,7 +102,7 @@ const Block: React.FC = () => {
|
||||||
}
|
}
|
||||||
}, [block]);
|
}, [block]);
|
||||||
|
|
||||||
const latestBlockNumber = useLatestBlockNumber();
|
const latestBlockNumber = useLatestBlockNumber(provider);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StandardFrame>
|
<StandardFrame>
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
import React, { useEffect, useState, useMemo } from "react";
|
import React, { useEffect, useState, useMemo, useContext } from "react";
|
||||||
import { useParams, useLocation } from "react-router";
|
import { useParams, useLocation } from "react-router";
|
||||||
import { ethers } from "ethers";
|
import { ethers } from "ethers";
|
||||||
import queryString from "query-string";
|
import queryString from "query-string";
|
||||||
import { provider } from "./ethersconfig";
|
|
||||||
import StandardFrame from "./StandardFrame";
|
import StandardFrame from "./StandardFrame";
|
||||||
import StandardSubtitle from "./StandardSubtitle";
|
import StandardSubtitle from "./StandardSubtitle";
|
||||||
import ContentFrame from "./ContentFrame";
|
import ContentFrame from "./ContentFrame";
|
||||||
|
@ -14,6 +13,7 @@ import BlockLink from "./components/BlockLink";
|
||||||
import { ProcessedTransaction } from "./types";
|
import { ProcessedTransaction } from "./types";
|
||||||
import { PAGE_SIZE } from "./params";
|
import { PAGE_SIZE } from "./params";
|
||||||
import { useFeeToggler } from "./search/useFeeToggler";
|
import { useFeeToggler } from "./search/useFeeToggler";
|
||||||
|
import { ProviderContext } from "./useProvider";
|
||||||
import { useENSCache } from "./useReverseCache";
|
import { useENSCache } from "./useReverseCache";
|
||||||
|
|
||||||
type BlockParams = {
|
type BlockParams = {
|
||||||
|
@ -25,6 +25,7 @@ type PageParams = {
|
||||||
};
|
};
|
||||||
|
|
||||||
const BlockTransactions: React.FC = () => {
|
const BlockTransactions: React.FC = () => {
|
||||||
|
const provider = useContext(ProviderContext);
|
||||||
const params = useParams<BlockParams>();
|
const params = useParams<BlockParams>();
|
||||||
const location = useLocation<PageParams>();
|
const location = useLocation<PageParams>();
|
||||||
const qs = queryString.parse(location.search);
|
const qs = queryString.parse(location.search);
|
||||||
|
@ -42,6 +43,10 @@ const BlockTransactions: React.FC = () => {
|
||||||
|
|
||||||
const [txs, setTxs] = useState<ProcessedTransaction[]>();
|
const [txs, setTxs] = useState<ProcessedTransaction[]>();
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
if (!provider) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const readBlock = async () => {
|
const readBlock = async () => {
|
||||||
const [_block, _receipts] = await Promise.all([
|
const [_block, _receipts] = await Promise.all([
|
||||||
provider.getBlockWithTransactions(blockNumber.toNumber()),
|
provider.getBlockWithTransactions(blockNumber.toNumber()),
|
||||||
|
@ -94,7 +99,7 @@ const BlockTransactions: React.FC = () => {
|
||||||
setTxs(processedResponses);
|
setTxs(processedResponses);
|
||||||
};
|
};
|
||||||
readBlock();
|
readBlock();
|
||||||
}, [blockNumber]);
|
}, [provider, blockNumber]);
|
||||||
|
|
||||||
const page = useMemo(() => {
|
const page = useMemo(() => {
|
||||||
if (!txs) {
|
if (!txs) {
|
||||||
|
@ -105,7 +110,7 @@ const BlockTransactions: React.FC = () => {
|
||||||
}, [txs, pageNumber]);
|
}, [txs, pageNumber]);
|
||||||
const total = useMemo(() => txs?.length ?? 0, [txs]);
|
const total = useMemo(() => txs?.length ?? 0, [txs]);
|
||||||
|
|
||||||
const reverseCache = useENSCache(page);
|
const reverseCache = useENSCache(provider, page);
|
||||||
|
|
||||||
document.title = `Block #${blockNumber} Txns | Otterscan`;
|
document.title = `Block #${blockNumber} Txns | Otterscan`;
|
||||||
|
|
||||||
|
|
13
src/Home.tsx
13
src/Home.tsx
|
@ -1,12 +1,13 @@
|
||||||
import React, { useState } from "react";
|
import React, { useState, useContext } from "react";
|
||||||
import { NavLink, useHistory } from "react-router-dom";
|
import { NavLink, useHistory } from "react-router-dom";
|
||||||
import { ethers } from "ethers";
|
import { ethers } from "ethers";
|
||||||
import Logo from "./Logo";
|
import Logo from "./Logo";
|
||||||
import Timestamp from "./components/Timestamp";
|
import Timestamp from "./components/Timestamp";
|
||||||
|
import { ProviderContext } from "./useProvider";
|
||||||
import { useLatestBlock } from "./useLatestBlock";
|
import { useLatestBlock } from "./useLatestBlock";
|
||||||
import { ERIGON_NODE } from "./ethersconfig";
|
|
||||||
|
|
||||||
const Home: React.FC = () => {
|
const Home: React.FC = () => {
|
||||||
|
const provider = useContext(ProviderContext);
|
||||||
const [search, setSearch] = useState<string>();
|
const [search, setSearch] = useState<string>();
|
||||||
const [canSubmit, setCanSubmit] = useState<boolean>(false);
|
const [canSubmit, setCanSubmit] = useState<boolean>(false);
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
|
@ -25,7 +26,7 @@ const Home: React.FC = () => {
|
||||||
history.push(`/search?q=${search}`);
|
history.push(`/search?q=${search}`);
|
||||||
};
|
};
|
||||||
|
|
||||||
const latestBlock = useLatestBlock();
|
const latestBlock = useLatestBlock(provider);
|
||||||
|
|
||||||
document.title = "Home | Otterscan";
|
document.title = "Home | Otterscan";
|
||||||
|
|
||||||
|
@ -65,7 +66,11 @@ const Home: React.FC = () => {
|
||||||
</NavLink>
|
</NavLink>
|
||||||
)}
|
)}
|
||||||
<span className="mx-auto mt-5 text-xs text-gray-500">
|
<span className="mx-auto mt-5 text-xs text-gray-500">
|
||||||
Using Erigon node at {ERIGON_NODE}
|
{provider ? (
|
||||||
|
<>Using Erigon node at {provider.connection.url}</>
|
||||||
|
) : (
|
||||||
|
<>Waiting for the provider...</>
|
||||||
|
)}
|
||||||
</span>
|
</span>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,4 +1,10 @@
|
||||||
import React, { useState, useEffect, useCallback, useMemo } from "react";
|
import React, {
|
||||||
|
useState,
|
||||||
|
useEffect,
|
||||||
|
useCallback,
|
||||||
|
useMemo,
|
||||||
|
useContext,
|
||||||
|
} from "react";
|
||||||
import { Route, Switch, useParams } from "react-router-dom";
|
import { Route, Switch, useParams } from "react-router-dom";
|
||||||
import { BigNumber, ethers } from "ethers";
|
import { BigNumber, ethers } from "ethers";
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
|
@ -6,7 +12,6 @@ import {
|
||||||
faCheckCircle,
|
faCheckCircle,
|
||||||
faTimesCircle,
|
faTimesCircle,
|
||||||
} from "@fortawesome/free-solid-svg-icons";
|
} from "@fortawesome/free-solid-svg-icons";
|
||||||
import { provider } from "./ethersconfig";
|
|
||||||
import StandardFrame from "./StandardFrame";
|
import StandardFrame from "./StandardFrame";
|
||||||
import StandardSubtitle from "./StandardSubtitle";
|
import StandardSubtitle from "./StandardSubtitle";
|
||||||
import Tab from "./components/Tab";
|
import Tab from "./components/Tab";
|
||||||
|
@ -22,6 +27,7 @@ import FormattedBalance from "./components/FormattedBalance";
|
||||||
import TokenTransferItem from "./TokenTransferItem";
|
import TokenTransferItem from "./TokenTransferItem";
|
||||||
import erc20 from "./erc20.json";
|
import erc20 from "./erc20.json";
|
||||||
import { TokenMetas, TokenTransfer, TransactionData, Transfer } from "./types";
|
import { TokenMetas, TokenTransfer, TransactionData, Transfer } from "./types";
|
||||||
|
import { ProviderContext } from "./useProvider";
|
||||||
|
|
||||||
const TRANSFER_TOPIC =
|
const TRANSFER_TOPIC =
|
||||||
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef";
|
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef";
|
||||||
|
@ -31,11 +37,16 @@ type TransactionParams = {
|
||||||
};
|
};
|
||||||
|
|
||||||
const Transaction: React.FC = () => {
|
const Transaction: React.FC = () => {
|
||||||
|
const provider = useContext(ProviderContext);
|
||||||
const params = useParams<TransactionParams>();
|
const params = useParams<TransactionParams>();
|
||||||
const { txhash } = params;
|
const { txhash } = params;
|
||||||
|
|
||||||
const [txData, setTxData] = useState<TransactionData>();
|
const [txData, setTxData] = useState<TransactionData>();
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
if (!provider) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const readBlock = async () => {
|
const readBlock = async () => {
|
||||||
const [_response, _receipt] = await Promise.all([
|
const [_response, _receipt] = await Promise.all([
|
||||||
provider.getTransaction(txhash),
|
provider.getTransaction(txhash),
|
||||||
|
@ -109,7 +120,7 @@ const Transaction: React.FC = () => {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
readBlock();
|
readBlock();
|
||||||
}, [txhash]);
|
}, [provider, txhash]);
|
||||||
|
|
||||||
const [transfers, setTransfers] = useState<Transfer[]>();
|
const [transfers, setTransfers] = useState<Transfer[]>();
|
||||||
const sendsEthToMiner = useMemo(() => {
|
const sendsEthToMiner = useMemo(() => {
|
||||||
|
@ -126,7 +137,7 @@ const Transaction: React.FC = () => {
|
||||||
}, [txData, transfers]);
|
}, [txData, transfers]);
|
||||||
|
|
||||||
const traceTransfersUsingOtsTrace = useCallback(async () => {
|
const traceTransfersUsingOtsTrace = useCallback(async () => {
|
||||||
if (!txData) {
|
if (!provider || !txData) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -143,7 +154,7 @@ const Transaction: React.FC = () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
setTransfers(_transfers);
|
setTransfers(_transfers);
|
||||||
}, [txData]);
|
}, [provider, txData]);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
traceTransfersUsingOtsTrace();
|
traceTransfersUsingOtsTrace();
|
||||||
}, [traceTransfersUsingOtsTrace]);
|
}, [traceTransfersUsingOtsTrace]);
|
||||||
|
|
|
@ -1,9 +0,0 @@
|
||||||
import { ethers } from "ethers";
|
|
||||||
|
|
||||||
export const ERIGON_NODE =
|
|
||||||
process.env.REACT_APP_ERIGON_URL || "http://127.0.0.1:8545";
|
|
||||||
|
|
||||||
export const provider = new ethers.providers.JsonRpcProvider(
|
|
||||||
ERIGON_NODE,
|
|
||||||
"mainnet"
|
|
||||||
);
|
|
|
@ -1,5 +1,4 @@
|
||||||
import { ethers } from "ethers";
|
import { ethers } from "ethers";
|
||||||
import { provider } from "../ethersconfig";
|
|
||||||
import { PAGE_SIZE } from "../params";
|
import { PAGE_SIZE } from "../params";
|
||||||
import { ProcessedTransaction, TransactionChunk } from "../types";
|
import { ProcessedTransaction, TransactionChunk } from "../types";
|
||||||
|
|
||||||
|
@ -27,7 +26,10 @@ export class SearchController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static rawToProcessed = (_rawRes: any) => {
|
private static rawToProcessed = (
|
||||||
|
provider: ethers.providers.JsonRpcProvider,
|
||||||
|
_rawRes: any
|
||||||
|
) => {
|
||||||
const _res: ethers.providers.TransactionResponse[] = _rawRes.txs.map(
|
const _res: ethers.providers.TransactionResponse[] = _rawRes.txs.map(
|
||||||
(t: any) => provider.formatter.transactionResponse(t)
|
(t: any) => provider.formatter.transactionResponse(t)
|
||||||
);
|
);
|
||||||
|
@ -56,6 +58,7 @@ export class SearchController {
|
||||||
};
|
};
|
||||||
|
|
||||||
private static async readBackPage(
|
private static async readBackPage(
|
||||||
|
provider: ethers.providers.JsonRpcProvider,
|
||||||
address: string,
|
address: string,
|
||||||
baseBlock: number
|
baseBlock: number
|
||||||
): Promise<TransactionChunk> {
|
): Promise<TransactionChunk> {
|
||||||
|
@ -64,10 +67,11 @@ export class SearchController {
|
||||||
baseBlock,
|
baseBlock,
|
||||||
PAGE_SIZE,
|
PAGE_SIZE,
|
||||||
]);
|
]);
|
||||||
return this.rawToProcessed(_rawRes);
|
return this.rawToProcessed(provider, _rawRes);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async readForwardPage(
|
private static async readForwardPage(
|
||||||
|
provider: ethers.providers.JsonRpcProvider,
|
||||||
address: string,
|
address: string,
|
||||||
baseBlock: number
|
baseBlock: number
|
||||||
): Promise<TransactionChunk> {
|
): Promise<TransactionChunk> {
|
||||||
|
@ -76,11 +80,14 @@ export class SearchController {
|
||||||
baseBlock,
|
baseBlock,
|
||||||
PAGE_SIZE,
|
PAGE_SIZE,
|
||||||
]);
|
]);
|
||||||
return this.rawToProcessed(_rawRes);
|
return this.rawToProcessed(provider, _rawRes);
|
||||||
}
|
}
|
||||||
|
|
||||||
static async firstPage(address: string): Promise<SearchController> {
|
static async firstPage(
|
||||||
const newTxs = await SearchController.readBackPage(address, 0);
|
provider: ethers.providers.JsonRpcProvider,
|
||||||
|
address: string
|
||||||
|
): Promise<SearchController> {
|
||||||
|
const newTxs = await SearchController.readBackPage(provider, address, 0);
|
||||||
return new SearchController(
|
return new SearchController(
|
||||||
address,
|
address,
|
||||||
newTxs.txs,
|
newTxs.txs,
|
||||||
|
@ -91,14 +98,19 @@ export class SearchController {
|
||||||
}
|
}
|
||||||
|
|
||||||
static async middlePage(
|
static async middlePage(
|
||||||
|
provider: ethers.providers.JsonRpcProvider,
|
||||||
address: string,
|
address: string,
|
||||||
hash: string,
|
hash: string,
|
||||||
next: boolean
|
next: boolean
|
||||||
): Promise<SearchController> {
|
): Promise<SearchController> {
|
||||||
const tx = await provider.getTransaction(hash);
|
const tx = await provider.getTransaction(hash);
|
||||||
const newTxs = next
|
const newTxs = next
|
||||||
? await SearchController.readBackPage(address, tx.blockNumber!)
|
? await SearchController.readBackPage(provider, address, tx.blockNumber!)
|
||||||
: await SearchController.readForwardPage(address, tx.blockNumber!);
|
: await SearchController.readForwardPage(
|
||||||
|
provider,
|
||||||
|
address,
|
||||||
|
tx.blockNumber!
|
||||||
|
);
|
||||||
return new SearchController(
|
return new SearchController(
|
||||||
address,
|
address,
|
||||||
newTxs.txs,
|
newTxs.txs,
|
||||||
|
@ -108,8 +120,11 @@ export class SearchController {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
static async lastPage(address: string): Promise<SearchController> {
|
static async lastPage(
|
||||||
const newTxs = await SearchController.readForwardPage(address, 0);
|
provider: ethers.providers.JsonRpcProvider,
|
||||||
|
address: string
|
||||||
|
): Promise<SearchController> {
|
||||||
|
const newTxs = await SearchController.readForwardPage(provider, address, 0);
|
||||||
return new SearchController(
|
return new SearchController(
|
||||||
address,
|
address,
|
||||||
newTxs.txs,
|
newTxs.txs,
|
||||||
|
@ -123,7 +138,10 @@ export class SearchController {
|
||||||
return this.txs.slice(this.pageStart, this.pageEnd);
|
return this.txs.slice(this.pageStart, this.pageEnd);
|
||||||
}
|
}
|
||||||
|
|
||||||
async prevPage(hash: string): Promise<SearchController> {
|
async prevPage(
|
||||||
|
provider: ethers.providers.JsonRpcProvider,
|
||||||
|
hash: string
|
||||||
|
): Promise<SearchController> {
|
||||||
// Already on this page
|
// Already on this page
|
||||||
if (this.txs[this.pageEnd - 1].hash === hash) {
|
if (this.txs[this.pageEnd - 1].hash === hash) {
|
||||||
return this;
|
return this;
|
||||||
|
@ -133,6 +151,7 @@ export class SearchController {
|
||||||
const overflowPage = this.txs.slice(0, this.pageStart);
|
const overflowPage = this.txs.slice(0, this.pageStart);
|
||||||
const baseBlock = this.txs[0].blockNumber;
|
const baseBlock = this.txs[0].blockNumber;
|
||||||
const prevPage = await SearchController.readForwardPage(
|
const prevPage = await SearchController.readForwardPage(
|
||||||
|
provider,
|
||||||
this.address,
|
this.address,
|
||||||
baseBlock
|
baseBlock
|
||||||
);
|
);
|
||||||
|
@ -148,7 +167,10 @@ export class SearchController {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
async nextPage(hash: string): Promise<SearchController> {
|
async nextPage(
|
||||||
|
provider: ethers.providers.JsonRpcProvider,
|
||||||
|
hash: string
|
||||||
|
): Promise<SearchController> {
|
||||||
// Already on this page
|
// Already on this page
|
||||||
if (this.txs[this.pageStart].hash === hash) {
|
if (this.txs[this.pageStart].hash === hash) {
|
||||||
return this;
|
return this;
|
||||||
|
@ -158,6 +180,7 @@ export class SearchController {
|
||||||
const overflowPage = this.txs.slice(this.pageEnd);
|
const overflowPage = this.txs.slice(this.pageEnd);
|
||||||
const baseBlock = this.txs[this.txs.length - 1].blockNumber;
|
const baseBlock = this.txs[this.txs.length - 1].blockNumber;
|
||||||
const nextPage = await SearchController.readBackPage(
|
const nextPage = await SearchController.readBackPage(
|
||||||
|
provider,
|
||||||
this.address,
|
this.address,
|
||||||
baseBlock
|
baseBlock
|
||||||
);
|
);
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
import { useState, useEffect } from "react";
|
||||||
|
|
||||||
|
export type OtterscanConfig = {
|
||||||
|
erigonURL: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useErigon = (): [boolean?, OtterscanConfig?] => {
|
||||||
|
const [configOK, setConfigOK] = useState<boolean>();
|
||||||
|
const [config, setConfig] = useState<OtterscanConfig>();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const readConfig = async () => {
|
||||||
|
const res = await fetch("/config.json");
|
||||||
|
|
||||||
|
if (res.ok) {
|
||||||
|
const _config: OtterscanConfig = await res.json();
|
||||||
|
setConfig(_config);
|
||||||
|
setConfigOK(res.ok);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
readConfig();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return [configOK, config];
|
||||||
|
};
|
|
@ -1,11 +1,14 @@
|
||||||
import { useState, useEffect } from "react";
|
import { useState, useEffect } from "react";
|
||||||
import { ethers } from "ethers";
|
import { ethers } from "ethers";
|
||||||
import { provider } from "./ethersconfig";
|
|
||||||
|
|
||||||
export const useLatestBlock = () => {
|
export const useLatestBlock = (provider?: ethers.providers.JsonRpcProvider) => {
|
||||||
const [latestBlock, setLatestBlock] = useState<ethers.providers.Block>();
|
const [latestBlock, setLatestBlock] = useState<ethers.providers.Block>();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
if (!provider) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const readLatestBlock = async () => {
|
const readLatestBlock = async () => {
|
||||||
const blockNum = await provider.getBlockNumber();
|
const blockNum = await provider.getBlockNumber();
|
||||||
const _raw = await provider.send("erigon_getHeaderByNumber", [blockNum]);
|
const _raw = await provider.send("erigon_getHeaderByNumber", [blockNum]);
|
||||||
|
@ -26,15 +29,21 @@ export const useLatestBlock = () => {
|
||||||
return () => {
|
return () => {
|
||||||
provider.removeListener("block", listener);
|
provider.removeListener("block", listener);
|
||||||
};
|
};
|
||||||
}, []);
|
}, [provider]);
|
||||||
|
|
||||||
return latestBlock;
|
return latestBlock;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useLatestBlockNumber = () => {
|
export const useLatestBlockNumber = (
|
||||||
|
provider?: ethers.providers.JsonRpcProvider
|
||||||
|
) => {
|
||||||
const [latestBlock, setLatestBlock] = useState<number>();
|
const [latestBlock, setLatestBlock] = useState<number>();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
if (!provider) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const readLatestBlock = async () => {
|
const readLatestBlock = async () => {
|
||||||
const blockNum = await provider.getBlockNumber();
|
const blockNum = await provider.getBlockNumber();
|
||||||
setLatestBlock(blockNum);
|
setLatestBlock(blockNum);
|
||||||
|
@ -49,7 +58,7 @@ export const useLatestBlockNumber = () => {
|
||||||
return () => {
|
return () => {
|
||||||
provider.removeListener("block", listener);
|
provider.removeListener("block", listener);
|
||||||
};
|
};
|
||||||
}, []);
|
}, [provider]);
|
||||||
|
|
||||||
return latestBlock;
|
return latestBlock;
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
import React from "react";
|
||||||
|
import { ethers } from "ethers";
|
||||||
|
import { useErigon } from "./useErigon";
|
||||||
|
|
||||||
|
export const DEFAULT_ERIGON_URL = "http://127.0.0.1:8545";
|
||||||
|
|
||||||
|
export const useProvider = (): ethers.providers.JsonRpcProvider | undefined => {
|
||||||
|
const [configOK, config] = useErigon();
|
||||||
|
if (!configOK) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
let erigonURL = config?.erigonURL;
|
||||||
|
if (erigonURL === "") {
|
||||||
|
console.info(`Using default erigon URL: ${DEFAULT_ERIGON_URL}`);
|
||||||
|
erigonURL = DEFAULT_ERIGON_URL;
|
||||||
|
} else {
|
||||||
|
console.log(`Using configured erigon URL: ${erigonURL}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ethers.providers.JsonRpcProvider(erigonURL, "mainnet");
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ProviderContext = React.createContext<
|
||||||
|
ethers.providers.JsonRpcProvider | undefined
|
||||||
|
>(undefined);
|
|
@ -1,12 +1,15 @@
|
||||||
import { useState, useEffect } from "react";
|
import { useState, useEffect } from "react";
|
||||||
|
import { ethers } from "ethers";
|
||||||
import { ENSReverseCache, ProcessedTransaction } from "./types";
|
import { ENSReverseCache, ProcessedTransaction } from "./types";
|
||||||
import { provider } from "./ethersconfig";
|
|
||||||
|
|
||||||
export const useENSCache = (page?: ProcessedTransaction[]) => {
|
export const useENSCache = (
|
||||||
|
provider?: ethers.providers.JsonRpcProvider,
|
||||||
|
page?: ProcessedTransaction[]
|
||||||
|
) => {
|
||||||
const [reverseCache, setReverseCache] = useState<ENSReverseCache>();
|
const [reverseCache, setReverseCache] = useState<ENSReverseCache>();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!page) {
|
if (!provider || !page) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,7 +41,7 @@ export const useENSCache = (page?: ProcessedTransaction[]) => {
|
||||||
setReverseCache(cache);
|
setReverseCache(cache);
|
||||||
};
|
};
|
||||||
reverseResolve();
|
reverseResolve();
|
||||||
}, [page]);
|
}, [provider, page]);
|
||||||
|
|
||||||
return reverseCache;
|
return reverseCache;
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue