Web: fix vaa parsing (#31)

* web: fix broken response objects & update parseVaa method

* Removed _parseVAAAlgorand

* onchain-data: replaced serum rpc and added more try/catch in case of failure

* update axios

* web: set proxy and fixed unique key in TokenDetails.tsx

Co-authored-by: Evan Gray <battledingo@gmail.com>
This commit is contained in:
ckeun 2022-11-17 17:30:11 -06:00 committed by GitHub
parent ff4f7f4ee7
commit e7c81723ca
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 649 additions and 11134 deletions

View File

@ -242,7 +242,12 @@ export async function getAlgoCustody(chainInfo, useAllowList = true) {
export async function grabAlgoCustodyData(chain, useAllowList) {
const chainInfo = CHAIN_INFO_MAP[chain];
const balances = await getAlgoCustody(chainInfo, useAllowList);
var balances = [];
try {
balances = await getAlgoCustody(chainInfo, useAllowList);
} catch (e) {
console.log(`could not grab ${chainInfo.name} data`);
}
// await updateTable(chainInfo, balances);
const chainInfo_ = {
...chainInfo,

View File

@ -206,7 +206,12 @@ export async function getAptosCustody(chainInfo, useAllowList = true) {
export async function grabAptosCustodyData(chain, useAllowList) {
const chainInfo = CHAIN_INFO_MAP[chain];
const balances = await getAptosCustody(chainInfo, useAllowList);
var balances = [];
try {
balances = await getAptosCustody(chainInfo, useAllowList);
} catch (e) {
console.log(`could not grab ${chainInfo.name} data`);
}
// await updateTable(chainInfo, balances);
const chainInfo_ = {
...chainInfo,

View File

@ -348,7 +348,12 @@ export async function getEvmCustody(chainInfo, useAllowList = true) {
export async function grabEvmCustodyData(chain, useAllowList) {
const chainInfo = CHAIN_INFO_MAP[chain];
const balances = await getEvmCustody(chainInfo, useAllowList);
var balances = [];
try {
balances = await getEvmCustody(chainInfo, useAllowList);
} catch (e) {
console.log(`could not grab ${chainInfo.name} data`);
}
// await updateTable(chainInfo, balances);
const chainInfo_ = {
...chainInfo,

View File

@ -214,7 +214,12 @@ export async function getNearCustody(chainInfo, useAllowList = true) {
export async function grabNearCustodyData(chain, useAllowList) {
const chainInfo = CHAIN_INFO_MAP[chain];
const balances = await getNearCustody(chainInfo, useAllowList);
var balances = [];
try {
balances = await getNearCustody(chainInfo, useAllowList);
} catch (e) {
console.log("could not grab Near data");
}
// await updateTable(chainInfo, balances);
const chainInfo_ = {
...chainInfo,

View File

@ -310,7 +310,12 @@ export async function getSolanaCustody(chainInfo, useAllowList = true) {
export async function grabSolanaCustodyData(chain, useAllowList) {
const chainInfo = CHAIN_INFO_MAP[chain];
const balances = await getSolanaCustody(chainInfo, useAllowList);
var balances = [];
try {
balances = await getSolanaCustody(chainInfo, useAllowList);
} catch (e) {
console.log("could not grab Solana data");
}
if (balances.length === 0) {
console.log(`could not get ${chainInfo.name} custody data`);
}

View File

@ -312,7 +312,12 @@ export async function getTerraCustody(chainInfo, useAllowList) {
export async function grabTerraCustodyData(chain, useAllowList) {
const chainInfo = CHAIN_INFO_MAP[chain];
const balances = await getTerraCustody(chainInfo, useAllowList);
var balances = [];
try {
balances = await getTerraCustody(chainInfo, useAllowList);
} catch (e) {
console.log("could not grab Solana data");
}
if (balances === undefined) {
console.log("could not pull terra balances");
return { balances: [] };

View File

@ -177,8 +177,7 @@ export var CHAIN_INFO_MAP = {
name: "solana",
evm: false,
chain_id: CHAIN_ID_SOLANA,
endpoint_url:
process.env.SOLANA_RPC || "https://solana-api.projectserum.com",
endpoint_url: process.env.SOLANA_RPC || "https://rpc.ankr.com/solana",
core_bridge: "worm2ZoG2kUd4vFXhvjh93UUH596ayRfgQ2MgjNMTth",
token_bridge_address: "wormDTUJ6AWPNvk59vGQbDvGJmqbDTdgWgAqcLBCgUb",
custody_address: "GugU1tP7doLeTw9hQP51xRJyS8Da1fWxuiy2rVrnMD2m",

11562
web/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -5,7 +5,7 @@
"license": "Apache-2.0",
"proxy": "http://server:4000",
"dependencies": {
"@certusone/wormhole-sdk": "^0.7.6",
"@certusone/wormhole-sdk": "^0.9.2",
"@certusone/wormhole-sdk-proto-web": "^0.0.4",
"@emotion/react": "^11.10.0",
"@emotion/styled": "^11.10.0",
@ -23,7 +23,7 @@
"@types/react-dom": "^18.0.6",
"@types/react-router-dom": "^5.3.3",
"assert": "^2.0.0",
"axios": "^0.27.2",
"axios": "^1.1.3",
"binary-parser": "^2.2.1",
"coingecko-api": "^1.0.10",
"dotenv": "^16.0.2",

View File

@ -12,26 +12,7 @@ import useEnqueuedVaaDetails, {
} from "../hooks/useEnqueuedVaaDetails";
import Table from "./Table";
import numeral from "numeral";
// async function VaaExists(row: EnqueuedVaaDetailsResponse) {
// try {
// const chainId: ChainId = findChainId(row.chainId.toString());
// const vaa = await getSignedVAAWithRetry(
// WORMHOLE_RPC_HOSTS,
// chainId,
// row.emitterAddress,
// row.sequence.toString(),
// { transport: NodeHttpTransport() },
// 1000,
// 4
// );
// if (vaa != undefined) {
// return true;
// }
// } catch (e) {
// return false;
// }
// }
import EnqueuedVaaExists from "./EnqueuedVaaExists";
const columnHelper = createColumnHelper<EnqueuedVaaDetailsResponse>();
@ -73,14 +54,11 @@ const columns = [
? new Date(info.getValue() * 1000).toLocaleString()
: null,
}),
// columnHelper.display({
// id: "hasQuorum",
// header: () => "Has Quorum?",
// cell: (info) => {
// const value = VaaExists(info.row.original);
// return value;
// },
// }),
columnHelper.display({
id: "hasQuorum",
header: () => "Has Quorum?",
cell: (info) => EnqueuedVaaExists(info.row.original),
}),
columnHelper.accessor("txHash", {
header: () => "Transaction",
cell: (info) => (
@ -100,7 +78,8 @@ function EnqueuedVaaDetails(id: string) {
state: {
sorting,
},
getRowId: (chain) => chain.chainId,
getRowId: (vaa) =>
`${vaa.chainId}/${vaa.emitterAddress.slice(2)}/${vaa.sequence}`,
getCoreRowModel: getCoreRowModel(),
getSortedRowModel: getSortedRowModel(),
onSortingChange: setSorting,

View File

@ -0,0 +1,46 @@
import axios from "axios";
import { useEffect, useState } from "react";
import { EnqueuedVaaDetailsResponse } from "../hooks/useEnqueuedVaaDetails";
const VAA_CHECK_TIMEOUT = 60000;
function EnqueuedVaaExists(row: EnqueuedVaaDetailsResponse) {
const [vaaHasQuorum, setVaaHasQuorum] = useState<boolean | null>(null);
useEffect(() => {
let cancelled = false;
(async () => {
while (!cancelled) {
setVaaHasQuorum(null);
let result = false;
try {
const response = await axios.get(
`/api/vaas/${row.chainId}/${row.emitterAddress.slice(2)}/${
row.sequence
}`
);
if (response.data) result = true;
} catch (e) {}
if (!cancelled) {
setVaaHasQuorum(result);
if (result) {
cancelled = true;
return;
}
await new Promise((resolve) =>
setTimeout(resolve, VAA_CHECK_TIMEOUT)
);
}
}
})();
return () => {
cancelled = true;
};
}, [row]);
return (
<span role="img">
{vaaHasQuorum === null ? "⏳" : vaaHasQuorum ? "✅" : "❌"}
</span>
);
}
export default EnqueuedVaaExists;

View File

@ -46,34 +46,34 @@ const columns = [
header: () => "Sequence",
cell: (info) => info.getValue().split("/")[2],
}),
columnHelper.accessor("vaa", {
columnHelper.accessor("vaas", {
id: "type",
header: () => "Type",
cell: (info) =>
vaa.parse(Buffer.from(info.getValue(), "base64")).payload.type,
}),
columnHelper.accessor("vaa", {
columnHelper.accessor("vaas", {
id: "chain",
header: () => "Chain",
cell: (info) =>
(vaa.parse(Buffer.from(info.getValue(), "base64")).payload as any)
.chain || "",
}),
columnHelper.accessor("vaa", {
columnHelper.accessor("vaas", {
id: "address",
header: () => "Address",
cell: (info) =>
(vaa.parse(Buffer.from(info.getValue(), "base64")).payload as any)
.address || "",
}),
columnHelper.accessor("vaa", {
columnHelper.accessor("vaas", {
id: "module",
header: () => "Module",
cell: (info) =>
(vaa.parse(Buffer.from(info.getValue(), "base64")).payload as any)
.module || "",
}),
columnHelper.accessor("createdAt", {
columnHelper.accessor("updatedAt", {
header: () => "Observed At",
cell: (info) => new Date(info.getValue()).toLocaleString(),
}),
@ -95,7 +95,7 @@ const obsColumns = [
function VAADetails({ row }: { row: Row<VAAsResponse> }): ReactElement {
return (
<Typography variant="body2" sx={{ wordBreak: "break-all" }}>
{Buffer.from(row.original.vaa, "base64").toString("hex")}
{Buffer.from(row.original.vaas, "base64").toString("hex")}
</Typography>
);
}
@ -118,10 +118,10 @@ function Governance() {
// NOTE: this ignores differing digests
Object.entries(
obs.reduce((obvsById, o) => {
if (!obvsById[o.messageid]) {
obvsById[o.messageid] = [];
if (!obvsById[o.messageId]) {
obvsById[o.messageId] = [];
}
obvsById[o.messageid].push(o);
obvsById[o.messageId].push(o);
return obvsById;
}, {} as any)
).map(([key, val]: [string, any]) => ({

View File

@ -1,5 +1,6 @@
import { ChainId, tryHexToNativeString } from "@certusone/wormhole-sdk";
import { _parseVAAAlgorand } from "@certusone/wormhole-sdk/lib/esm/algorand/Algorand";
import { parseVaa } from "@certusone/wormhole-sdk/lib/cjs/vaa";
import { parseTransferPayload } from "@certusone/wormhole-sdk/lib/cjs/utils";
import { ChevronRight } from "@mui/icons-material";
import { Card, IconButton, Typography } from "@mui/material";
import { Box } from "@mui/system";
@ -61,16 +62,18 @@ const columns = [
];
function VAADetails({ row }: { row: Row<VAAsResponse> }): ReactElement {
const parsedVaa = _parseVAAAlgorand(
new Uint8Array(Buffer.from(row.original.vaa, "base64"))
const parsedVaa = parseVaa(
new Uint8Array(Buffer.from(row.original.vaas, "base64"))
);
let token = parsedVaa.Contract;
const payload = parsedVaa.payload;
const parsedPayload = parseTransferPayload(payload);
let token = parsedPayload.originAddress;
// FromChain is a misnomer - actually OriginChain
if (parsedVaa.Contract && parsedVaa.FromChain)
if (parsedPayload.originAddress && parsedPayload.originChain)
try {
token = tryHexToNativeString(
parsedVaa.Contract,
parsedVaa.FromChain as ChainId
parsedPayload.originAddress,
parsedPayload.originChain as ChainId
);
} catch (e) {}
return (
@ -79,15 +82,15 @@ function VAADetails({ row }: { row: Row<VAAsResponse> }): ReactElement {
<br />
Timestamp: {new Date(parsedVaa.timestamp * 1000).toLocaleString()}
<br />
Consistency: {parsedVaa.consistency}
Consistency: {parsedVaa.consistencyLevel}
<br />
Nonce: {parsedVaa.nonce}
<br />
Origin: {parsedVaa.FromChain}
Origin: {parsedPayload.originChain}
<br />
Token: {token}
<br />
Amount: {BigNumber.from(parsedVaa.Amount).toString()}
Amount: {BigNumber.from(parsedPayload.amount).toString()}
<br />
</>
);

View File

@ -67,7 +67,7 @@ function TokenDetails(id: string) {
state: {
sorting,
},
getRowId: (token) => token.name,
getRowId: (token) => token.tokenAddress,
getCoreRowModel: getCoreRowModel(),
getSortedRowModel: getSortedRowModel(),
onSortingChange: setSorting,

View File

@ -1,5 +1,9 @@
import { ChainId, tryHexToNativeString } from "@certusone/wormhole-sdk";
import { _parseVAAAlgorand } from "@certusone/wormhole-sdk/lib/esm/algorand/Algorand";
import {
ChainId,
parseTransferPayload,
parseVaa,
tryHexToNativeString,
} from "@certusone/wormhole-sdk";
import { ChevronRight } from "@mui/icons-material";
import { Card, IconButton, Typography } from "@mui/material";
import { Box } from "@mui/system";
@ -63,16 +67,18 @@ const columns = [
];
function VAADetails({ row }: { row: Row<VAAsResponse> }): ReactElement {
const parsedVaa = _parseVAAAlgorand(
new Uint8Array(Buffer.from(row.original.vaa, "base64"))
const parsedVaa = parseVaa(
new Uint8Array(Buffer.from(row.original.vaas, "base64"))
);
let token = parsedVaa.Contract;
const payload = parsedVaa.payload;
const parsedPayload = parseTransferPayload(payload);
let token = parsedPayload.originAddress;
// FromChain is a misnomer - actually OriginChain
if (parsedVaa.Contract && parsedVaa.FromChain)
if (parsedPayload.originAddress && parsedPayload.originChain)
try {
token = tryHexToNativeString(
parsedVaa.Contract,
parsedVaa.FromChain as ChainId
parsedPayload.originAddress,
parsedPayload.originChain as ChainId
);
} catch (e) {}
return (
@ -81,15 +87,15 @@ function VAADetails({ row }: { row: Row<VAAsResponse> }): ReactElement {
<br />
Timestamp: {new Date(parsedVaa.timestamp * 1000).toLocaleString()}
<br />
Consistency: {parsedVaa.consistency}
Consistency: {parsedVaa.consistencyLevel}
<br />
Nonce: {parsedVaa.nonce}
<br />
Origin: {parsedVaa.FromChain}
Origin: {parsedPayload.originChain}
<br />
Token: {token}
<br />
Amount: {BigNumber.from(parsedVaa.Amount).toString()}
Amount: {BigNumber.from(parsedPayload.amount).toString()}
<br />
</>
);

View File

@ -5,7 +5,7 @@ import { POLL_TIME } from "../utils/consts";
export type EnqueuedVaa = {
chainId: number;
emitterAddres: string;
emitterAddress: string;
sequence: number;
notionalValue: number;
txHash: string;

View File

@ -14,7 +14,7 @@ export type HeartbeatNetwork = {
export type HeartbeatResponse = {
boottimestamp: NumberLong;
counter: number;
createdAt: string;
indexedAt: string;
features: string[] | null;
guardianaddr: string;
networks: HeartbeatNetwork[];

View File

@ -4,11 +4,11 @@ import { useNetworkContext } from "../contexts/NetworkContext";
import { POLL_TIME } from "../utils/consts";
export type ObservationsResponse = {
createdAt: string;
indexedAt: string;
updatedAt: string;
addr: string;
hash: string;
messageid: string;
messageId: string;
signature: string;
txhash: string;
_id: string;

View File

@ -4,9 +4,15 @@ import { useNetworkContext } from "../contexts/NetworkContext";
import { POLL_TIME } from "../utils/consts";
export type VAAsResponse = {
createdAt: string;
indexedAt: string;
updatedAt: string;
vaa: string;
emitterAddr: string;
emitterChain: number;
guardianSetIndex: number;
sequence: number;
timestamp: string;
version: number;
vaas: string;
_id: string;
};