explorer: transaction hashes & links to explorers
Change-Id: Id262184f6c61c301f3d09f502b23d88c62b94e8d
This commit is contained in:
parent
84de7c2fd0
commit
aff990487d
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 24 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 {totalDays} 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 }} /> */}
|
||||
|
|
|
@ -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} />
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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 }
|
||||
|
|
|
@ -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" />:
|
||||
|
@ -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>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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]}
|
||||
{nativeExplorerUri(emitterChain, emitterAddress) ?
|
||||
{nativeExplorerContractUri(emitterChain, emitterAddress) ?
|
||||
<OutboundLink
|
||||
href={nativeExplorerUri(emitterChain, emitterAddress)}
|
||||
href={nativeExplorerContractUri(emitterChain, emitterAddress)}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
|
|
Loading…
Reference in New Issue