Merge branch 'feature/self-destruct-trace' into develop

This commit is contained in:
Willian Mitsuda 2021-07-19 20:50:19 -03:00
commit efaf613c16
20 changed files with 378 additions and 121 deletions

View File

@ -13,7 +13,7 @@ import NavButton from "./components/NavButton";
import Timestamp from "./components/Timestamp"; import Timestamp from "./components/Timestamp";
import GasValue from "./components/GasValue"; import GasValue from "./components/GasValue";
import BlockLink from "./components/BlockLink"; import BlockLink from "./components/BlockLink";
import AddressOrENSName from "./components/AddressOrENSName"; import DecoratedAddressLink from "./components/DecoratedAddressLink";
import TransactionValue from "./components/TransactionValue"; import TransactionValue from "./components/TransactionValue";
import HexValue from "./components/HexValue"; import HexValue from "./components/HexValue";
import { RuntimeContext } from "./useRuntime"; import { RuntimeContext } from "./useRuntime";
@ -159,10 +159,7 @@ const Block: React.FC = () => {
in this block in this block
</InfoRow> </InfoRow>
<InfoRow title="Mined by"> <InfoRow title="Mined by">
<AddressOrENSName <DecoratedAddressLink address={block.miner} miner />
address={block.miner}
minerAddress={block.miner}
/>
</InfoRow> </InfoRow>
<InfoRow title="Block Reward"> <InfoRow title="Block Reward">
<TransactionValue value={block.blockReward.add(block.feeReward)} /> <TransactionValue value={block.blockReward.add(block.feeReward)} />

View File

@ -2,22 +2,27 @@ import React from "react";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faCaretRight } from "@fortawesome/free-solid-svg-icons"; import { faCaretRight } from "@fortawesome/free-solid-svg-icons";
import AddressHighlighter from "./components/AddressHighlighter"; import AddressHighlighter from "./components/AddressHighlighter";
import AddressOrENSName from "./components/AddressOrENSName"; import DecoratedAddressLink from "./components/DecoratedAddressLink";
import AddressLink from "./components/AddressLink";
import TokenLogo from "./components/TokenLogo";
import FormattedBalance from "./components/FormattedBalance"; import FormattedBalance from "./components/FormattedBalance";
import { TokenMetas, TokenTransfer } from "./types"; import {
AddressContext,
TokenMetas,
TokenTransfer,
TransactionData,
} from "./types";
type TokenTransferItemProps = { type TokenTransferItemProps = {
t: TokenTransfer; t: TokenTransfer;
txData: TransactionData;
tokenMetas: TokenMetas; tokenMetas: TokenMetas;
}; };
const TokenTransferItem: React.FC<TokenTransferItemProps> = ({ const TokenTransferItem: React.FC<TokenTransferItemProps> = ({
t, t,
txData,
tokenMetas, tokenMetas,
}) => ( }) => (
<div className="flex items-baseline space-x-2 truncate"> <div className="flex items-baseline space-x-2 px-2 py-1 truncate hover:bg-gray-100">
<span className="text-gray-500"> <span className="text-gray-500">
<FontAwesomeIcon icon={faCaretRight} size="1x" /> <FontAwesomeIcon icon={faCaretRight} size="1x" />
</span> </span>
@ -25,13 +30,23 @@ const TokenTransferItem: React.FC<TokenTransferItemProps> = ({
<div className="flex space-x-1"> <div className="flex space-x-1">
<span className="font-bold">From</span> <span className="font-bold">From</span>
<AddressHighlighter address={t.from}> <AddressHighlighter address={t.from}>
<AddressOrENSName address={t.from} /> <DecoratedAddressLink
address={t.from}
addressCtx={AddressContext.FROM}
txFrom={t.from === txData.from}
txTo={t.from === txData.to}
/>
</AddressHighlighter> </AddressHighlighter>
</div> </div>
<div className="flex space-x-1"> <div className="flex space-x-1">
<span className="font-bold">To</span> <span className="font-bold">To</span>
<AddressHighlighter address={t.to}> <AddressHighlighter address={t.to}>
<AddressOrENSName address={t.to} /> <DecoratedAddressLink
address={t.to}
addressCtx={AddressContext.TO}
txFrom={t.to === txData.from}
txTo={t.to === txData.to}
/>
</AddressHighlighter> </AddressHighlighter>
</div> </div>
<div className="col-span-3 flex space-x-1"> <div className="col-span-3 flex space-x-1">
@ -42,23 +57,17 @@ const TokenTransferItem: React.FC<TokenTransferItemProps> = ({
decimals={tokenMetas[t.token].decimals} decimals={tokenMetas[t.token].decimals}
/> />
</span> </span>
<span className="flex space-x-1 items-baseline truncate"> <AddressHighlighter address={t.token}>
{tokenMetas[t.token] ? ( <DecoratedAddressLink
<> address={t.token}
<div className="self-center"> text={
<TokenLogo address={t.token} name={tokenMetas[t.token].name} /> tokenMetas[t.token]
</div> ? `${tokenMetas[t.token].name} (${tokenMetas[t.token].symbol})`
<AddressLink : ""
address={t.token} }
text={`${tokenMetas[t.token].name} (${ tokenMeta={tokenMetas[t.token]}
tokenMetas[t.token].symbol />
})`} </AddressHighlighter>
/>
</>
) : (
<AddressOrENSName address={t.token} />
)}
</span>
</div> </div>
</div> </div>
</div> </div>

View File

@ -1,10 +1,4 @@
import React, { import React, { useState, useEffect, useMemo, useContext } from "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 StandardFrame from "./StandardFrame"; import StandardFrame from "./StandardFrame";
@ -13,9 +7,10 @@ import Tab from "./components/Tab";
import Details from "./transaction/Details"; import Details from "./transaction/Details";
import Logs from "./transaction/Logs"; import Logs from "./transaction/Logs";
import erc20 from "./erc20.json"; import erc20 from "./erc20.json";
import { TokenMetas, TokenTransfer, TransactionData, Transfer } from "./types"; import { TokenMetas, TokenTransfer, TransactionData } from "./types";
import { RuntimeContext } from "./useRuntime"; import { RuntimeContext } from "./useRuntime";
import { SelectionContext, useSelection } from "./useSelection"; import { SelectionContext, useSelection } from "./useSelection";
import { useInternalTransfers } from "./useErigonHooks";
const TRANSFER_TOPIC = const TRANSFER_TOPIC =
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"; "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef";
@ -110,42 +105,19 @@ const Transaction: React.FC = () => {
readBlock(); readBlock();
}, [provider, txhash]); }, [provider, txhash]);
const [transfers, setTransfers] = useState<Transfer[]>(); const intTransfers = useInternalTransfers(provider, txData);
const sendsEthToMiner = useMemo(() => { const sendsEthToMiner = useMemo(() => {
if (!txData || !transfers) { if (!txData || !intTransfers) {
return false; return false;
} }
for (const t of transfers) { for (const t of intTransfers) {
if (t.to === txData.miner) { if (t.to === txData.miner) {
return true; return true;
} }
} }
return false; return false;
}, [txData, transfers]); }, [txData, intTransfers]);
const traceTransfers = useCallback(async () => {
if (!provider || !txData) {
return;
}
const r = await provider.send("ots_getTransactionTransfers", [
txData.transactionHash,
]);
const _transfers: Transfer[] = [];
for (const t of r) {
_transfers.push({
from: ethers.utils.getAddress(t.from),
to: ethers.utils.getAddress(t.to),
value: t.value,
});
}
setTransfers(_transfers);
}, [provider, txData]);
useEffect(() => {
traceTransfers();
}, [traceTransfers]);
const selectionCtx = useSelection(); const selectionCtx = useSelection();
@ -164,7 +136,7 @@ const Transaction: React.FC = () => {
<Route path="/tx/:txhash/" exact> <Route path="/tx/:txhash/" exact>
<Details <Details
txData={txData} txData={txData}
transfers={transfers} internalTransfers={intTransfers}
sendsEthToMiner={sendsEthToMiner} sendsEthToMiner={sendsEthToMiner}
/> />
</Route> </Route>

View File

@ -6,7 +6,7 @@ type AddressProps = {
const Address: React.FC<AddressProps> = ({ address }) => ( const Address: React.FC<AddressProps> = ({ address }) => (
<span className="font-address text-gray-400 truncate" title={address}> <span className="font-address text-gray-400 truncate" title={address}>
<p className="truncate">{address}</p> <span className="truncate">{address}</span>
</span> </span>
); );

View File

@ -4,16 +4,23 @@ import { NavLink } from "react-router-dom";
type AddressLinkProps = { type AddressLinkProps = {
address: string; address: string;
text?: string; text?: string;
dontOverrideColors?: boolean;
}; };
const AddressLink: React.FC<AddressLinkProps> = ({ address, text }) => ( const AddressLink: React.FC<AddressLinkProps> = ({
address,
text,
dontOverrideColors,
}) => (
<NavLink <NavLink
className="text-link-blue hover:text-link-blue-hover font-address truncate" className={`${
dontOverrideColors ? "" : "text-link-blue hover:text-link-blue-hover"
} font-address truncate`}
to={`/address/${address}`} to={`/address/${address}`}
> >
<p className="truncate" title={text ?? address}> <span className="truncate" title={text ?? address}>
{text ?? address} {text ?? address}
</p> </span>
</NavLink> </NavLink>
); );

View File

@ -1,6 +1,4 @@
import React from "react"; import React from "react";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faCoins } from "@fortawesome/free-solid-svg-icons";
import Address from "./Address"; import Address from "./Address";
import AddressLink from "./AddressLink"; import AddressLink from "./AddressLink";
import ENSName from "./ENSName"; import ENSName from "./ENSName";
@ -10,21 +8,18 @@ type AddressOrENSNameProps = {
address: string; address: string;
ensName?: string; ensName?: string;
selectedAddress?: string; selectedAddress?: string;
minerAddress?: string; text?: string;
dontOverrideColors?: boolean;
}; };
const AddressOrENSName: React.FC<AddressOrENSNameProps> = ({ const AddressOrENSName: React.FC<AddressOrENSNameProps> = ({
address, address,
ensName, ensName,
selectedAddress, selectedAddress,
minerAddress, text,
dontOverrideColors,
}) => ( }) => (
<div className="flex items-baseline space-x-1 truncate"> <>
{minerAddress !== undefined && minerAddress === address && (
<span className="text-yellow-400" title="Miner address">
<FontAwesomeIcon icon={faCoins} size="1x" />
</span>
)}
{address === selectedAddress ? ( {address === selectedAddress ? (
<> <>
{ensName ? ( {ensName ? (
@ -36,13 +31,21 @@ const AddressOrENSName: React.FC<AddressOrENSNameProps> = ({
) : ( ) : (
<> <>
{ensName ? ( {ensName ? (
<ENSNameLink name={ensName} address={address} /> <ENSNameLink
name={ensName}
address={address}
dontOverrideColors={dontOverrideColors}
/>
) : ( ) : (
<AddressLink address={address} /> <AddressLink
address={address}
text={text}
dontOverrideColors={dontOverrideColors}
/>
)} )}
</> </>
)} )}
</div> </>
); );
export default React.memo(AddressOrENSName); export default React.memo(AddressOrENSName);

View File

@ -0,0 +1,79 @@
import React from "react";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import {
faMoneyBillAlt,
faBurn,
faCoins,
} from "@fortawesome/free-solid-svg-icons";
import TokenLogo from "./TokenLogo";
import AddressOrENSName from "./AddressOrENSName";
import { AddressContext, TokenMeta, ZERO_ADDRESS } from "../types";
type DecoratedAddressLinkProps = {
address: string;
ensName?: string;
selectedAddress?: string;
text?: string;
addressCtx?: AddressContext;
miner?: boolean;
selfDestruct?: boolean;
txFrom?: boolean;
txTo?: boolean;
tokenMeta?: TokenMeta;
};
const DecoratedAddresssLink: React.FC<DecoratedAddressLinkProps> = ({
address,
ensName,
selectedAddress,
text,
addressCtx,
miner,
selfDestruct,
txFrom,
txTo,
tokenMeta,
}) => {
const mint = addressCtx === AddressContext.FROM && address === ZERO_ADDRESS;
const burn = addressCtx === AddressContext.TO && address === ZERO_ADDRESS;
return (
<div
className={`flex items-baseline space-x-1 ${txFrom ? "bg-red-50" : ""} ${
txTo ? "bg-green-50" : ""
} ${mint ? "italic text-green-500 hover:text-green-700" : ""} ${
burn ? "line-through text-orange-500 hover:text-orange-700" : ""
} ${selfDestruct ? "line-through opacity-70 hover:opacity-100" : ""}`}
>
{mint && (
<span className="text-green-500" title="Mint address">
<FontAwesomeIcon icon={faMoneyBillAlt} size="1x" />
</span>
)}
{burn && (
<span className="text-orange-500" title="Burn address">
<FontAwesomeIcon icon={faBurn} size="1x" />
</span>
)}
{miner && (
<span className="text-yellow-400" title="Miner address">
<FontAwesomeIcon icon={faCoins} size="1x" />
</span>
)}
{tokenMeta && (
<div className="self-center">
<TokenLogo address={address} name={tokenMeta.name} />
</div>
)}
<AddressOrENSName
address={address}
ensName={ensName}
selectedAddress={selectedAddress}
text={text}
dontOverrideColors={mint || burn}
/>
</div>
);
};
export default React.memo(DecoratedAddresssLink);

View File

@ -18,7 +18,7 @@ const ENSName: React.FC<ENSNameProps> = ({ name, address }) => (
width={12} width={12}
height={12} height={12}
/> />
<p className="truncate">{name}</p> <span className="truncate">{name}</span>
</div> </div>
); );

View File

@ -5,11 +5,18 @@ import ENSLogo from "./ensLogo.svg";
type ENSNameLinkProps = { type ENSNameLinkProps = {
name: string; name: string;
address: string; address: string;
dontOverrideColors?: boolean;
}; };
const ENSNameLink: React.FC<ENSNameLinkProps> = ({ name, address }) => ( const ENSNameLink: React.FC<ENSNameLinkProps> = ({
name,
address,
dontOverrideColors,
}) => (
<NavLink <NavLink
className="flex items-baseline space-x-1 font-sans text-link-blue hover:text-link-blue-hover truncate" className={`flex items-baseline space-x-1 font-sans ${
dontOverrideColors ? "" : "text-link-blue hover:text-link-blue-hover"
} truncate`}
to={`/address/${name}`} to={`/address/${name}`}
title={`${name}: ${address}`} title={`${name}: ${address}`}
> >
@ -20,7 +27,7 @@ const ENSNameLink: React.FC<ENSNameLinkProps> = ({ name, address }) => (
width={12} width={12}
height={12} height={12}
/> />
<p className="truncate">{name}</p> <span className="truncate">{name}</span>
</NavLink> </NavLink>
); );

View File

@ -0,0 +1,25 @@
import React from "react";
import InternalTransfer from "./InternalTransfer";
import InternalSelfDestruct from "./InternalSelfDestruct";
import { TransactionData, Transfer, TransferType } from "../types";
type InternalOperationProps = {
txData: TransactionData;
transfer: Transfer;
};
const InternalOperation: React.FC<InternalOperationProps> = ({
txData,
transfer,
}) => (
<>
{transfer.type === TransferType.TRANSFER && (
<InternalTransfer txData={txData} transfer={transfer} />
)}
{transfer.type === TransferType.SELF_DESTRUCT && (
<InternalSelfDestruct txData={txData} transfer={transfer} />
)}
</>
);
export default React.memo(InternalOperation);

View File

@ -0,0 +1,73 @@
import React, { useContext } from "react";
import { ethers } from "ethers";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faAngleRight, faBomb } from "@fortawesome/free-solid-svg-icons";
import AddressHighlighter from "./AddressHighlighter";
import DecoratedAddressLink from "./DecoratedAddressLink";
import { RuntimeContext } from "../useRuntime";
import { TransactionData, Transfer } from "../types";
const CHI_ADDRESS = "0x0000000000004946c0e9F43F4Dee607b0eF1fA1c";
const GST2_ADDRESS = "0x0000000000b3F879cb30FE243b4Dfee438691c04";
type InternalSelfDestructProps = {
txData: TransactionData;
transfer: Transfer;
};
const InternalSelfDestruct: React.FC<InternalSelfDestructProps> = ({
txData,
transfer,
}) => {
const { provider } = useContext(RuntimeContext);
const network = provider?.network;
const toMiner = txData.miner !== undefined && transfer.to === txData.miner;
return (
<>
<div className="flex items-baseline space-x-1 text-xs">
<span className="text-gray-500">
<span className="text-red-900">
<FontAwesomeIcon icon={faBomb} size="1x" />
</span>{" "}
SELF DESTRUCT
</span>
<span>Contract</span>
<div className="flex items-baseline">
<AddressHighlighter address={transfer.from}>
<DecoratedAddressLink address={transfer.from} selfDestruct />
</AddressHighlighter>
</div>
{network?.chainId === 1 && transfer.to === CHI_ADDRESS && (
<span className="text-gray-400">(Chi Gastoken)</span>
)}
{network?.chainId === 1 && transfer.to === GST2_ADDRESS && (
<span className="text-gray-400">(GST2 Gastoken)</span>
)}
</div>
{!transfer.value.isZero() && (
<div className="ml-5 flex items-baseline space-x-1 text-xs">
<span className="text-gray-500">
<FontAwesomeIcon icon={faAngleRight} size="1x" /> TRANSFER
</span>
<span>{ethers.utils.formatEther(transfer.value)} Ether</span>
<div className="flex items-baseline">
<span className="text-gray-500">To</span>
<AddressHighlighter address={transfer.to}>
<div
className={`flex items-baseline space-x-1 ${
toMiner ? "rounded px-2 py-1 bg-yellow-100" : ""
}`}
>
<DecoratedAddressLink address={transfer.to} miner={toMiner} />
</div>
</AddressHighlighter>
</div>
</div>
)}
</>
);
};
export default React.memo(InternalSelfDestruct);

View File

@ -1,9 +1,9 @@
import React from "react"; import React from "react";
import { ethers } from "ethers"; import { ethers } from "ethers";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faAngleRight, faCoins } from "@fortawesome/free-solid-svg-icons"; import { faAngleRight } from "@fortawesome/free-solid-svg-icons";
import AddressHighlighter from "./AddressHighlighter"; import AddressHighlighter from "./AddressHighlighter";
import AddressLink from "./AddressLink"; import DecoratedAddressLink from "./DecoratedAddressLink";
import { TransactionData, Transfer } from "../types"; import { TransactionData, Transfer } from "../types";
type InternalTransferProps = { type InternalTransferProps = {
@ -33,12 +33,12 @@ const InternalTransfer: React.FC<InternalTransferProps> = ({
fromMiner ? "rounded px-2 py-1 bg-yellow-100" : "" fromMiner ? "rounded px-2 py-1 bg-yellow-100" : ""
}`} }`}
> >
{fromMiner && ( <DecoratedAddressLink
<span className="text-yellow-400" title="Miner address"> address={transfer.from}
<FontAwesomeIcon icon={faCoins} size="1x" /> miner={fromMiner}
</span> txFrom={transfer.from === txData.from}
)} txTo={transfer.from === txData.to}
<AddressLink address={transfer.from} /> />
</div> </div>
</AddressHighlighter> </AddressHighlighter>
</div> </div>
@ -50,12 +50,12 @@ const InternalTransfer: React.FC<InternalTransferProps> = ({
toMiner ? "rounded px-2 py-1 bg-yellow-100" : "" toMiner ? "rounded px-2 py-1 bg-yellow-100" : ""
}`} }`}
> >
{toMiner && ( <DecoratedAddressLink
<span className="text-yellow-400" title="Miner address"> address={transfer.to}
<FontAwesomeIcon icon={faCoins} size="1x" /> miner={toMiner}
</span> txFrom={transfer.to === txData.from}
)} txTo={transfer.to === txData.to}
<AddressLink address={transfer.to} /> />
</div> </div>
</AddressHighlighter> </AddressHighlighter>
</div> </div>

22
src/nodeFunctions.ts Normal file
View File

@ -0,0 +1,22 @@
import { ethers } from "ethers";
import { TransactionData, Transfer } from "./types";
export const getTransactionTransfers = async (
provider: ethers.providers.JsonRpcProvider,
txData: TransactionData
) => {
const rawTransfers = await provider.send("ots_getTransactionTransfers", [
txData.transactionHash,
]);
const _transfers: Transfer[] = [];
for (const t of rawTransfers) {
_transfers.push({
type: t.type,
from: ethers.utils.getAddress(t.from),
to: ethers.utils.getAddress(t.to),
value: t.value,
});
}
return _transfers;
};

View File

@ -1,3 +1,3 @@
export const MIN_API_LEVEL = 1; export const MIN_API_LEVEL = 2;
export const PAGE_SIZE = 25; export const PAGE_SIZE = 25;

View File

@ -4,7 +4,7 @@ import { faExclamationCircle } from "@fortawesome/free-solid-svg-icons";
import MethodName from "../components/MethodName"; import MethodName from "../components/MethodName";
import BlockLink from "../components/BlockLink"; import BlockLink from "../components/BlockLink";
import TransactionLink from "../components/TransactionLink"; import TransactionLink from "../components/TransactionLink";
import AddressOrENSName from "../components/AddressOrENSName"; import DecoratedAddressLink from "../components/DecoratedAddressLink";
import TimestampAge from "../components/TimestampAge"; import TimestampAge from "../components/TimestampAge";
import AddressHighlighter from "../components/AddressHighlighter"; import AddressHighlighter from "../components/AddressHighlighter";
import TransactionDirection, { import TransactionDirection, {
@ -71,11 +71,11 @@ const TransactionItem: React.FC<TransactionItemProps> = ({
<span className="truncate"> <span className="truncate">
{tx.from && ( {tx.from && (
<AddressHighlighter address={tx.from}> <AddressHighlighter address={tx.from}>
<AddressOrENSName <DecoratedAddressLink
address={tx.from} address={tx.from}
ensName={ensFrom} ensName={ensFrom}
selectedAddress={selectedAddress} selectedAddress={selectedAddress}
minerAddress={tx.miner} miner={tx.miner === tx.from}
/> />
</AddressHighlighter> </AddressHighlighter>
)} )}
@ -91,11 +91,11 @@ const TransactionItem: React.FC<TransactionItemProps> = ({
<span className="truncate"> <span className="truncate">
{tx.to && ( {tx.to && (
<AddressHighlighter address={tx.to}> <AddressHighlighter address={tx.to}>
<AddressOrENSName <DecoratedAddressLink
address={tx.to} address={tx.to}
ensName={ensTo} ensName={ensTo}
selectedAddress={selectedAddress} selectedAddress={selectedAddress}
minerAddress={tx.miner} miner={tx.miner === tx.to}
/> />
</AddressHighlighter> </AddressHighlighter>
)} )}

View File

@ -9,10 +9,10 @@ import ContentFrame from "../ContentFrame";
import InfoRow from "../components/InfoRow"; import InfoRow from "../components/InfoRow";
import BlockLink from "../components/BlockLink"; import BlockLink from "../components/BlockLink";
import AddressHighlighter from "../components/AddressHighlighter"; import AddressHighlighter from "../components/AddressHighlighter";
import AddressOrENSName from "../components/AddressOrENSName"; import DecoratedAddressLink from "../components/DecoratedAddressLink";
import Copy from "../components/Copy"; import Copy from "../components/Copy";
import Timestamp from "../components/Timestamp"; import Timestamp from "../components/Timestamp";
import InternalTransfer from "../components/InternalTransfer"; import InternalOperation from "../components/InternalOperation";
import MethodName from "../components/MethodName"; import MethodName from "../components/MethodName";
import GasValue from "../components/GasValue"; import GasValue from "../components/GasValue";
import FormattedBalance from "../components/FormattedBalance"; import FormattedBalance from "../components/FormattedBalance";
@ -21,13 +21,13 @@ import { TransactionData, Transfer } from "../types";
type DetailsProps = { type DetailsProps = {
txData: TransactionData; txData: TransactionData;
transfers?: Transfer[]; internalTransfers?: Transfer[];
sendsEthToMiner: boolean; sendsEthToMiner: boolean;
}; };
const Details: React.FC<DetailsProps> = ({ const Details: React.FC<DetailsProps> = ({
txData, txData,
transfers, internalTransfers,
sendsEthToMiner, sendsEthToMiner,
}) => ( }) => (
<ContentFrame tabs> <ContentFrame tabs>
@ -64,7 +64,11 @@ const Details: React.FC<DetailsProps> = ({
<InfoRow title="From"> <InfoRow title="From">
<div className="flex items-baseline space-x-2 -ml-1"> <div className="flex items-baseline space-x-2 -ml-1">
<AddressHighlighter address={txData.from}> <AddressHighlighter address={txData.from}>
<AddressOrENSName address={txData.from} minerAddress={txData.miner} /> <DecoratedAddressLink
address={txData.from}
miner={txData.from === txData.miner}
txFrom
/>
</AddressHighlighter> </AddressHighlighter>
<Copy value={txData.from} /> <Copy value={txData.from} />
</div> </div>
@ -72,14 +76,18 @@ const Details: React.FC<DetailsProps> = ({
<InfoRow title="Interacted With (To)"> <InfoRow title="Interacted With (To)">
<div className="flex items-baseline space-x-2 -ml-1"> <div className="flex items-baseline space-x-2 -ml-1">
<AddressHighlighter address={txData.to}> <AddressHighlighter address={txData.to}>
<AddressOrENSName address={txData.to} minerAddress={txData.miner} /> <DecoratedAddressLink
address={txData.to}
miner={txData.to === txData.miner}
txTo
/>
</AddressHighlighter> </AddressHighlighter>
<Copy value={txData.to} /> <Copy value={txData.to} />
</div> </div>
{transfers && ( {internalTransfers && (
<div className="mt-2 space-y-1"> <div className="mt-2 space-y-1">
{transfers.map((t, i) => ( {internalTransfers.map((t, i) => (
<InternalTransfer key={i} txData={txData} transfer={t} /> <InternalOperation key={i} txData={txData} transfer={t} />
))} ))}
</div> </div>
)} )}
@ -89,9 +97,14 @@ const Details: React.FC<DetailsProps> = ({
</InfoRow> </InfoRow>
{txData.tokenTransfers.length > 0 && ( {txData.tokenTransfers.length > 0 && (
<InfoRow title={`Tokens Transferred (${txData.tokenTransfers.length})`}> <InfoRow title={`Tokens Transferred (${txData.tokenTransfers.length})`}>
<div className="space-y-2"> <div>
{txData.tokenTransfers.map((t, i) => ( {txData.tokenTransfers.map((t, i) => (
<TokenTransferItem key={i} t={t} tokenMetas={txData.tokenMetas} /> <TokenTransferItem
key={i}
t={t}
txData={txData}
tokenMetas={txData.tokenMetas}
/>
))} ))}
</div> </div>
</InfoRow> </InfoRow>

View File

@ -1,6 +1,6 @@
import React from "react"; import React from "react";
import ContentFrame from "../ContentFrame"; import ContentFrame from "../ContentFrame";
import AddressLink from "../components/AddressLink"; import DecoratedAddressLink from "../components/DecoratedAddressLink";
import { TransactionData } from "../types"; import { TransactionData } from "../types";
type LogsProps = { type LogsProps = {
@ -22,7 +22,12 @@ const Logs: React.FC<LogsProps> = ({ txData }) => (
<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="font-bold text-right">Address</div> <div className="font-bold text-right">Address</div>
<div className="col-span-11"> <div className="col-span-11">
<AddressLink address={l.address} /> <DecoratedAddressLink
address={l.address}
miner={l.address === txData.miner}
txFrom={l.address === txData.from}
txTo={l.address === txData.to}
/>
</div> </div>
</div> </div>
{l.topics.map((t, i) => ( {l.topics.map((t, i) => (
@ -45,6 +50,7 @@ const Logs: React.FC<LogsProps> = ({ txData }) => (
<textarea <textarea
className="w-full h-20 bg-gray-50 font-mono focus:outline-none border rounded p-2" className="w-full h-20 bg-gray-50 font-mono focus:outline-none border rounded p-2"
value={l.data} value={l.data}
readOnly
/> />
</div> </div>
</div> </div>

View File

@ -57,12 +57,26 @@ export type TransactionData = {
logs: ethers.providers.Log[]; logs: ethers.providers.Log[];
}; };
// The VOID...
export const ZERO_ADDRESS = "0x0000000000000000000000000000000000000000";
export enum AddressContext {
FROM,
TO,
}
export type From = { export type From = {
current: string; current: string;
depth: number; depth: number;
}; };
export enum TransferType {
TRANSFER = 0,
SELF_DESTRUCT = 1,
}
export type Transfer = { export type Transfer = {
type: TransferType;
from: string; from: string;
to: string; to: string;
value: BigNumber; value: BigNumber;

30
src/useErigonHooks.ts Normal file
View File

@ -0,0 +1,30 @@
import { ethers } from "ethers";
import { useState, useEffect } from "react";
import { getTransactionTransfers } from "./nodeFunctions";
import { TransactionData, Transfer } from "./types";
export const useInternalTransfers = (
provider: ethers.providers.JsonRpcProvider | undefined,
txData: TransactionData | undefined
): Transfer[] | undefined => {
const [intTransfers, setIntTransfers] = useState<Transfer[]>();
useEffect(() => {
const traceTransfers = async () => {
if (!provider || !txData) {
return;
}
const _transfers = await getTransactionTransfers(provider, txData);
for (const t of _transfers) {
t.from = provider.formatter.address(t.from);
t.to = provider.formatter.address(t.to);
t.value = provider.formatter.bigNumber(t.value);
}
setIntTransfers(_transfers);
};
traceTransfers();
}, [provider, txData]);
return intTransfers;
};

View File

@ -1,7 +1,7 @@
import React, { useState, useContext } from "react"; import React, { useState, useContext } from "react";
export type Selection = { export type Selection = {
type: string; type: "address";
content: string; content: string;
}; };