explorer: transaction hashes & links to explorers

Change-Id: Id262184f6c61c301f3d09f502b23d88c62b94e8d
This commit is contained in:
justinschuldt 2021-10-29 15:38:41 -05:00 committed by Justin Schuldt
parent 84de7c2fd0
commit aff990487d
9 changed files with 113 additions and 43 deletions

View File

@ -87,6 +87,6 @@ GATSBY_MAINNET_BSC_CORE_BRIDGE=0x98f3c9e6E3fAce36bAAd05FE09d375Ef1464288B
GATSBY_MAINNET_BSC_TOKEN_BRIDGE=0xB6F6D86a8f9879A9c87f643768d9efc38c1Da6E7
GATSBY_MAINNET_BSC_NFT_BRIDGE=0x5a58505a96D1dbf8dF91cB21B54419FC36e93fdE
GATSBY_MAINNET_POLYGON_CORE_BRIDGE=0x98f3c9e6E3fAce36bAAd05FE09d375Ef1464288B
GATSBY_MAINNET_POLYGON_TOKEN_BRIDGE=0xB6F6D86a8f9879A9c87f643768d9efc38c1Da6E7
GATSBY_MAINNET_POLYGON_NFT_BRIDGE=0x5a58505a96D1dbf8dF91cB21B54419FC36e93fdE
GATSBY_MAINNET_POLYGON_CORE_BRIDGE=0x7A4B5a56256163F07b2C80A7cA55aBE66c4ec4d7
GATSBY_MAINNET_POLYGON_TOKEN_BRIDGE=0x5a58505a96d1dbf8df91cb21b54419fc36e93fde
GATSBY_MAINNET_POLYGON_NFT_BRIDGE=0x90bbd86a6fe93d3bc3ed6335935447e75fab7fcf

View File

@ -3,13 +3,14 @@ import { Spin, Typography } from 'antd'
const { Title } = Typography
import { FormattedMessage } from 'gatsby-plugin-intl'
import { arrayify, isHexString, zeroPad } from "ethers/lib/utils";
import { Bech32, toHex } from "@cosmjs/encoding"
import { arrayify, isHexString, zeroPad, hexlify } from "ethers/lib/utils";
import { Bech32, toHex, fromHex } from "@cosmjs/encoding"
import { ExplorerSummary } from '~/components/ExplorerSummary';
import { titleStyles } from '~/styles';
import { NetworkContext } from '~/components/NetworkSelect';
import { getEmitterAddressSolana } from "@certusone/wormhole-sdk";
import { chainIDs } from '~/utils/misc/constants';
import { ChainIDs, chainIDs } from '~/utils/misc/constants';
import { PublicKey } from '@solana/web3.js';
export interface VAA {
Version: number | string,
@ -25,10 +26,10 @@ export interface VAA {
}
export interface BigTableMessage {
InitiatingTxID?: string
SignedVAABytes: string // base64 encoded byte array
SignedVAA: VAA
QuorumTime: string // "2021-08-11 00:16:11.757 +0000 UTC"
EmitterChain: "solana" | "ethereum" | "terra" | "bsc"
SignedVAABytes?: string // base64 encoded byte array
SignedVAA?: VAA
QuorumTime?: string // "2021-08-11 00:16:11.757 +0000 UTC"
EmitterChain: keyof ChainIDs
EmitterAddress: string
Sequence: string
}
@ -98,14 +99,31 @@ const ExplorerQuery = (props: ExplorerQuery) => {
if (sequence.length <= 15) {
paddedSequence = sequence.padStart(16, "0")
} else if (sequence.length >= 17) {
paddedSequence = sequence.slice(-16)
} else {
paddedSequence = sequence
}
url = `${base}/readrow?emitterChain=${emitterChain}&emitterAddress=${paddedAddress}&sequence=${paddedSequence}`
} else if (txId) {
url = `${base}/transaction?id=${txId}`
let transformedTxId = txId
if (isHexString(txId)) {
// valid hexString, no transformation needed.
} else {
try {
let pubKey = new PublicKey(txId).toBytes()
let solHex = hexlify(pubKey)
transformedTxId = solHex
} catch (_) {
// not solana, try terra
try {
let arr = fromHex(txId)
let terraHex = hexlify(arr)
transformedTxId = terraHex
} catch (_) {
// do nothing
}
}
}
url = `${base}/transaction?id=${transformedTxId}`
}
fetch(url)

View File

@ -65,8 +65,8 @@ const ChainOverviewCard: React.FC<ChainOverviewCardProps> = ({ Icon, iconStyle,
loading ? "loading" : intl.formatMessage({ id: "explorer.comingSoon" })}>
<Card
style={{
width: 200,
paddingTop: 10
width: 190,
paddingTop: 10,
}}
className="hover-z-index"
cover={<Icon style={{ height: 140, ...iconStyle }} />}
@ -86,11 +86,11 @@ const ChainOverviewCard: React.FC<ChainOverviewCardProps> = ({ Icon, iconStyle,
{!!totalCount ? (
<>
<div style={{ display: 'flex', justifyContent: "space-between", alignItems: 'center', gap: 12 }}>
<div><Text type="secondary" style={{ fontSize: 14 }}>last 24 hours</Text></div>
<div><Text type="secondary" style={{ fontSize: 14 }}>last&nbsp;24&nbsp;hours</Text></div>
<div><Text className={animate ? "highlight-new-val" : ""} style={{ fontSize: 26 }}>{lastDayCount}</Text></div>
</div>
<div style={{ display: 'flex', justifyContent: "center", alignItems: 'center', gap: 12 }}>
<div><Text type="secondary" style={{ fontSize: 14 }}>last {totalDays} days</Text></div>
<div><Text type="secondary" style={{ fontSize: 14 }}>last&nbsp;{totalDays}&nbsp;days</Text></div>
<div><Text className={animate ? "highlight-new-val" : ""} style={{ fontSize: 26 }}>{totalCount}</Text></div>
</div>
{/* <Statistic title={<span>last 24 hours</span>} value={totals?.LastDayCount[dataKey]} style={{ display: 'flex', justifyContent: "space-between", alignItems: 'center', gap: 12 }} valueStyle={{ fontSize: 26 }} /> */}

View File

@ -177,7 +177,7 @@ const Stats: React.FC<StatsProps> = ({ emitterChain, emitterAddress }) => {
return (
<>
{!emitterChain && !emitterAddress &&
<div style={{ display: 'flex', justifyContent: 'space-around', alignItems: 'flex-end', flexWrap: 'wrap', marginBottom: 40 }}>
<div style={{ display: 'flex', justifyContent: 'center', alignItems: 'flex-end', flexWrap: 'wrap', marginBottom: 40, gap: 20 }}>
<ChainOverviewCard totalDays={daysSinceDataStart} totals={totals} dataKey="1" title={ChainID[1]} Icon={SolanaIcon} iconStyle={{ height: 120, margin: '10px 0' }} />
<ChainOverviewCard totalDays={daysSinceDataStart} totals={totals} dataKey="2" title={ChainID[2]} Icon={EthereumIcon} />
<ChainOverviewCard totalDays={daysSinceDataStart} totals={totals} dataKey="3" title={ChainID[3]} Icon={TerraIcon} />

View File

@ -55,13 +55,13 @@ const RecentMessages = (props: RecentMessagesProps) => {
{
title: "message",
key: "payload",
render: (item: BigTableMessage) => <DecodePayload
render: (item: BigTableMessage) => item.SignedVAABytes ? <DecodePayload
base64VAA={item.SignedVAABytes}
emitterChainName={item.EmitterChain}
emitterAddress={item.EmitterAddress}
showType={true}
showSummary={true}
/>
/> : null
},
{
title: "sequence",

View File

@ -30,7 +30,7 @@ const makeGroupName = (groupKey: string, activeNetwork: ActiveNetwork, emitterCh
const getNativeAddress = (chainId: number, emitterAddress: string, activeNetwork?: ActiveNetwork): string => {
let nativeAddress = ""
if (chainId === chainIDs["ethereum"] || chainId === chainIDs["bsc"]) {
if (chainId === chainIDs["ethereum"] || chainId === chainIDs["bsc"] || chainId === chainIDs["polygon"]) {
// remove zero-padding
let unpadded = emitterAddress.slice(-40)
nativeAddress = `0x${unpadded}`.toLowerCase()
@ -81,7 +81,7 @@ const contractNameFormatter = (address: string, chainId: number, activeNetwork?:
}
const nativeExplorerUri = (chainId: number, address: string, activeNetwork?: ActiveNetwork): string => {
const nativeExplorerContractUri = (chainId: number, address: string, activeNetwork?: ActiveNetwork): string => {
if (!activeNetwork) {
activeNetwork = useContext(NetworkContext).activeNetwork
}
@ -100,17 +100,40 @@ const nativeExplorerUri = (chainId: number, address: string, activeNetwork?: Act
} else if (chainId === chainIDs["bsc"]) {
let base = "https://bscscan.com/address/"
return `${base}${nativeAddress}`
} else if (chainId === chainIDs["polygon"]) {
let base = "https://polygonscan.com/address/"
return `${base}${nativeAddress}`
}
}
return ""
}
const nativeExplorerTxUri = (chainId: number, transactionId: string): string => {
if (chainId === chainIDs["solana"]) {
let base = "https://explorer.solana.com/address/"
return `${base}${transactionId}`
} else if (chainId === chainIDs["ethereum"]) {
let base = "https://etherscan.io/tx/"
return `${base}${transactionId}`
} else if (chainId === chainIDs["terra"]) {
let base = "https://finder.terra.money/columbus-5/tx/"
return `${base}${transactionId}`
} else if (chainId === chainIDs["bsc"]) {
let base = "https://bscscan.com/tx/"
return `${base}${transactionId}`
} else if (chainId === chainIDs["polygon"]) {
let base = "https://polygonscan.com/tx/"
return `${base}${transactionId}`
}
return ""
}
const chainColors: { [chain: string]: string } = {
"*": "hsl(183, 100%, 61%)",
"1": "hsl(297, 100%, 61%)",
"2": "hsl(235, 5%, 43%)",
"3": "hsl(235, 100%, 61%)",
"4": "hsl(54, 100%, 61%)"
"4": "hsl(54, 100%, 61%)",
"5": "hsl(271, 100%, 61%)",
}
export { makeDate, makeGroupName, chainColors, truncateAddress, contractNameFormatter, nativeExplorerUri }
export { makeDate, makeGroupName, chainColors, truncateAddress, contractNameFormatter, nativeExplorerContractUri, nativeExplorerTxUri }

View File

@ -1,4 +1,4 @@
import React, { useEffect } from 'react';
import React from 'react';
import { Button, Spin, Typography } from 'antd'
const { Title } = Typography
import { useIntl, FormattedMessage } from 'gatsby-plugin-intl'
@ -8,8 +8,10 @@ import ReactTimeAgo from 'react-time-ago'
import { titleStyles } from '~/styles';
import { CloseOutlined, ReloadOutlined } from '@ant-design/icons';
import { Link } from 'gatsby';
import { contractNameFormatter, nativeExplorerUri } from '../ExplorerStats/utils';
import { contractNameFormatter, nativeExplorerContractUri, nativeExplorerTxUri } from '../ExplorerStats/utils';
import { OutboundLink } from 'gatsby-plugin-google-gtag';
import { chainIDs } from '~/utils/misc/constants';
import { hexToNativeString } from '@certusone/wormhole-sdk';
interface SummaryProps {
emitterChain?: number,
@ -27,6 +29,24 @@ const Summary = (props: SummaryProps) => {
const intl = useIntl()
const { SignedVAA, ...message } = props.message
const { EmitterChain, EmitterAddress, InitiatingTxID } = message
// get chainId from chain name
let chainId = chainIDs[EmitterChain]
let transactionId: string | undefined
if (InitiatingTxID) {
if (chainId === chainIDs["ethereum"] || chainId === chainIDs["bsc"] || chainId === chainIDs["polygon"]) {
transactionId = InitiatingTxID
} else {
if (chainId === chainIDs["solana"]) {
const txId = InitiatingTxID.slice(2) // remove the leading "0x"
transactionId = hexToNativeString(txId, chainId)
} else if (chainId === chainIDs["terra"]) {
transactionId = InitiatingTxID.slice(2) // remove the leading "0x"
}
}
}
return (
<>
<div style={{ display: 'flex', justifyContent: 'space-between', gap: 8, alignItems: 'baseline' }}>
@ -63,18 +83,7 @@ const Summary = (props: SummaryProps) => {
style={{ fontSize: 12, marginBottom: 20 }}
>{JSON.stringify(SignedVAA, undefined, 2)}</pre>
</div>
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
{props.emitterChain && props.emitterAddress && nativeExplorerUri(props.emitterChain, props.emitterAddress) ?
<OutboundLink
href={nativeExplorerUri(props.emitterChain, props.emitterAddress)}
target="_blank"
rel="noopener noreferrer"
style={{ fontSize: 16 }}
>
{'View "'}{contractNameFormatter(props.emitterAddress, props.emitterChain)}{'" emitter contract on native explorer'}
</OutboundLink> : <div />}
<div style={{ display: 'flex', justifyContent: "flex-end" }}>
{props.lastFetched ? (
<span>
<FormattedMessage id="explorer.lastUpdated" />:&nbsp;
@ -83,6 +92,26 @@ const Summary = (props: SummaryProps) => {
) : null}
</div>
<div style={{ display: 'flex', flexDirection: 'column', }}>
{EmitterChain && EmitterAddress && nativeExplorerContractUri(chainId, EmitterAddress) ?
<OutboundLink
href={nativeExplorerContractUri(chainId, EmitterAddress)}
target="_blank"
rel="noopener noreferrer"
style={{ fontSize: 16, marginBottom: '6px 0' }}
>
{'View "'}{contractNameFormatter(EmitterAddress, chainId)}{'" contract on native explorer'}
</OutboundLink> : <div />}
{transactionId && EmitterChain ?
<OutboundLink
href={nativeExplorerTxUri(chainId, transactionId)}
target="_blank"
rel="noopener noreferrer"
style={{ fontSize: 16, margin: '6px 0' }}
>
{'View transaction "'}{transactionId}{'" on native explorer'}
</OutboundLink> : <div />}
</div>
</>
)
}

View File

@ -149,7 +149,7 @@ const parseAssetMetaPayload = (arr: Buffer): AssetMetaPayload => {
}
}
function useBase64ToBuffer(base64VAA: string) {
function useBase64ToBuffer(base64VAA: string = "") {
const [buf, setBuf] = useState<Buffer>()
function convertbase64ToBinary(base64: string) {
@ -177,7 +177,7 @@ function useBase64ToBuffer(base64VAA: string) {
return buf
}
interface DecodePayloadProps {
base64VAA: string
base64VAA?: string
emitterChainName: keyof ChainIDs
emitterAddress: string
showType?: boolean

View File

@ -16,7 +16,7 @@ import { WithNetwork, NetworkSelect } from '~/components/NetworkSelect'
import { ExplorerSearchForm, ExplorerTxForm } from '~/components/App/ExplorerSearch';
import { ChainID } from '~/utils/misc/constants';
import { OutboundLink } from 'gatsby-plugin-google-gtag';
import { nativeExplorerUri } from '~/components/ExplorerStats/utils';
import { nativeExplorerContractUri } from '~/components/ExplorerStats/utils';
import { CloseOutlined } from '@ant-design/icons';
@ -127,9 +127,9 @@ const Explorer: React.FC<ExplorerProps> = ({ location, navigate }) => {
// show heading with the context of the address
<Title level={3} style={{ ...titleStyles }}>
Recent messages from {ChainID[emitterChain]}&nbsp;
{nativeExplorerUri(emitterChain, emitterAddress) ?
{nativeExplorerContractUri(emitterChain, emitterAddress) ?
<OutboundLink
href={nativeExplorerUri(emitterChain, emitterAddress)}
href={nativeExplorerContractUri(emitterChain, emitterAddress)}
target="_blank"
rel="noopener noreferrer"
>