explorer/wormhole (#15948)
* feat: add lending instruction names * chore: capitalize words * feat: add wormhole card * feat: add asset contract * chore: format
This commit is contained in:
parent
40997d0aef
commit
9b26c45be6
|
@ -18,6 +18,19 @@ import { useTokenRegistry } from "providers/mints/token-registry";
|
||||||
import { BigNumber } from "bignumber.js";
|
import { BigNumber } from "bignumber.js";
|
||||||
import { Copyable } from "components/common/Copyable";
|
import { Copyable } from "components/common/Copyable";
|
||||||
|
|
||||||
|
const getEthAddress = (link?: string) => {
|
||||||
|
let address = "";
|
||||||
|
if (link) {
|
||||||
|
const extractEth = link.match(/0x[a-fA-F0-9]{40,64}/);
|
||||||
|
|
||||||
|
if (extractEth) {
|
||||||
|
address = extractEth[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return address;
|
||||||
|
};
|
||||||
|
|
||||||
export function TokenAccountSection({
|
export function TokenAccountSection({
|
||||||
account,
|
account,
|
||||||
tokenAccount,
|
tokenAccount,
|
||||||
|
@ -62,16 +75,12 @@ function MintAccountCard({
|
||||||
|
|
||||||
const tokenInfo = tokenRegistry.get(mintAddress);
|
const tokenInfo = tokenRegistry.get(mintAddress);
|
||||||
|
|
||||||
let bridgeContractAddress = tokenInfo?.extensions?.address;
|
const bridgeContractAddress = getEthAddress(
|
||||||
if (tokenInfo?.extensions?.bridgeContract && !bridgeContractAddress) {
|
tokenInfo?.extensions?.bridgeContract
|
||||||
const extractEth = tokenInfo.extensions.bridgeContract.match(
|
);
|
||||||
/0x[a-fA-F0-9]{40,64}/
|
const assetContractAddress = getEthAddress(
|
||||||
);
|
tokenInfo?.extensions?.assetContract
|
||||||
|
);
|
||||||
if (extractEth) {
|
|
||||||
bridgeContractAddress = extractEth[0];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="card">
|
<div className="card">
|
||||||
|
@ -145,7 +154,7 @@ function MintAccountCard({
|
||||||
)}
|
)}
|
||||||
{tokenInfo?.extensions?.bridgeContract && bridgeContractAddress && (
|
{tokenInfo?.extensions?.bridgeContract && bridgeContractAddress && (
|
||||||
<tr>
|
<tr>
|
||||||
<td>Wormhole Bridge Contract</td>
|
<td>Bridge Contract</td>
|
||||||
<td className="text-lg-right">
|
<td className="text-lg-right">
|
||||||
<Copyable text={bridgeContractAddress}>
|
<Copyable text={bridgeContractAddress}>
|
||||||
<a
|
<a
|
||||||
|
@ -159,6 +168,22 @@ function MintAccountCard({
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
)}
|
)}
|
||||||
|
{tokenInfo?.extensions?.assetContract && assetContractAddress && (
|
||||||
|
<tr>
|
||||||
|
<td>Bridged Asset Contract</td>
|
||||||
|
<td className="text-lg-right">
|
||||||
|
<Copyable text={assetContractAddress}>
|
||||||
|
<a
|
||||||
|
href={tokenInfo.extensions.bridgeContract}
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
>
|
||||||
|
{assetContractAddress}
|
||||||
|
</a>
|
||||||
|
</Copyable>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
)}
|
||||||
</TableCardBody>
|
</TableCardBody>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
import React from "react";
|
||||||
|
import { TransactionInstruction, SignatureResult } from "@solana/web3.js";
|
||||||
|
import { InstructionCard } from "./InstructionCard";
|
||||||
|
import { useCluster } from "providers/cluster";
|
||||||
|
import { reportError } from "utils/sentry";
|
||||||
|
import { parsWormholeInstructionTitle } from "./wormhole/types";
|
||||||
|
|
||||||
|
export function WormholeDetailsCard({
|
||||||
|
ix,
|
||||||
|
index,
|
||||||
|
result,
|
||||||
|
signature,
|
||||||
|
innerCards,
|
||||||
|
childIndex,
|
||||||
|
}: {
|
||||||
|
ix: TransactionInstruction;
|
||||||
|
index: number;
|
||||||
|
result: SignatureResult;
|
||||||
|
signature: string;
|
||||||
|
innerCards?: JSX.Element[];
|
||||||
|
childIndex?: number;
|
||||||
|
}) {
|
||||||
|
const { url } = useCluster();
|
||||||
|
|
||||||
|
let title;
|
||||||
|
try {
|
||||||
|
title = parsWormholeInstructionTitle(ix);
|
||||||
|
} catch (error) {
|
||||||
|
reportError(error, {
|
||||||
|
url: url,
|
||||||
|
signature: signature,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<InstructionCard
|
||||||
|
ix={ix}
|
||||||
|
index={index}
|
||||||
|
result={result}
|
||||||
|
title={`Wormhole: ${title || "Unknown"}`}
|
||||||
|
innerCards={innerCards}
|
||||||
|
childIndex={childIndex}
|
||||||
|
defaultRaw
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
|
@ -28,7 +28,7 @@ export function parseTokenLendingInstructionTitle(
|
||||||
const code = instruction.data[0];
|
const code = instruction.data[0];
|
||||||
|
|
||||||
if (!(code in INSTRUCTION_LOOKUP)) {
|
if (!(code in INSTRUCTION_LOOKUP)) {
|
||||||
throw new Error(`Unrecognized Token Swap instruction code: ${code}`);
|
throw new Error(`Unrecognized Token Lending instruction code: ${code}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
return INSTRUCTION_LOOKUP[code];
|
return INSTRUCTION_LOOKUP[code];
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
import { TransactionInstruction } from "@solana/web3.js";
|
||||||
|
|
||||||
|
export const PROGRAM_IDS: string[] = [
|
||||||
|
"WormT3McKhFJ2RkiGpdw9GKvNCrB2aB54gb2uV9MfQC", // mainnet / testnet / devnet
|
||||||
|
];
|
||||||
|
|
||||||
|
const INSTRUCTION_LOOKUP: { [key: number]: string } = {
|
||||||
|
0: "Initialize Bridge",
|
||||||
|
1: "Transfer Assets Out",
|
||||||
|
2: "Post VAA",
|
||||||
|
3: "Evict Transfer Proposal",
|
||||||
|
4: "Evict Claimed VAA",
|
||||||
|
5: "Poke Proposal",
|
||||||
|
6: "Verify Signatures",
|
||||||
|
7: "Create Wrapped Asset",
|
||||||
|
};
|
||||||
|
|
||||||
|
export function isWormholeInstruction(
|
||||||
|
instruction: TransactionInstruction
|
||||||
|
): boolean {
|
||||||
|
return PROGRAM_IDS.includes(instruction.programId.toBase58());
|
||||||
|
}
|
||||||
|
|
||||||
|
export function parsWormholeInstructionTitle(
|
||||||
|
instruction: TransactionInstruction
|
||||||
|
): string {
|
||||||
|
const code = instruction.data[0];
|
||||||
|
|
||||||
|
if (!(code in INSTRUCTION_LOOKUP)) {
|
||||||
|
throw new Error(`Unrecognized Wormhole instruction code: ${code}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return INSTRUCTION_LOOKUP[code];
|
||||||
|
}
|
|
@ -18,6 +18,7 @@ import { SystemDetailsCard } from "components/instruction/system/SystemDetailsCa
|
||||||
import { TokenDetailsCard } from "components/instruction/token/TokenDetailsCard";
|
import { TokenDetailsCard } from "components/instruction/token/TokenDetailsCard";
|
||||||
import { TokenLendingDetailsCard } from "components/instruction/TokenLendingDetailsCard";
|
import { TokenLendingDetailsCard } from "components/instruction/TokenLendingDetailsCard";
|
||||||
import { TokenSwapDetailsCard } from "components/instruction/TokenSwapDetailsCard";
|
import { TokenSwapDetailsCard } from "components/instruction/TokenSwapDetailsCard";
|
||||||
|
import { WormholeDetailsCard } from "components/instruction/WormholeDetailsCard";
|
||||||
import { UnknownDetailsCard } from "components/instruction/UnknownDetailsCard";
|
import { UnknownDetailsCard } from "components/instruction/UnknownDetailsCard";
|
||||||
import {
|
import {
|
||||||
SignatureProps,
|
SignatureProps,
|
||||||
|
@ -35,6 +36,7 @@ import {
|
||||||
import { Cluster, useCluster } from "providers/cluster";
|
import { Cluster, useCluster } from "providers/cluster";
|
||||||
import { BpfUpgradeableLoaderDetailsCard } from "components/instruction/bpf-upgradeable-loader/BpfUpgradeableLoaderDetailsCard";
|
import { BpfUpgradeableLoaderDetailsCard } from "components/instruction/bpf-upgradeable-loader/BpfUpgradeableLoaderDetailsCard";
|
||||||
import { VoteDetailsCard } from "components/instruction/vote/VoteDetailsCard";
|
import { VoteDetailsCard } from "components/instruction/vote/VoteDetailsCard";
|
||||||
|
import { isWormholeInstruction } from "components/instruction/wormhole/types";
|
||||||
|
|
||||||
export type InstructionDetailsProps = {
|
export type InstructionDetailsProps = {
|
||||||
tx: ParsedTransaction;
|
tx: ParsedTransaction;
|
||||||
|
@ -212,6 +214,8 @@ function renderInstructionCard({
|
||||||
return <TokenSwapDetailsCard key={key} {...props} />;
|
return <TokenSwapDetailsCard key={key} {...props} />;
|
||||||
} else if (isTokenLendingInstruction(transactionIx)) {
|
} else if (isTokenLendingInstruction(transactionIx)) {
|
||||||
return <TokenLendingDetailsCard key={key} {...props} />;
|
return <TokenLendingDetailsCard key={key} {...props} />;
|
||||||
|
} else if (isWormholeInstruction(transactionIx)) {
|
||||||
|
return <WormholeDetailsCard key={key} {...props} />;
|
||||||
} else {
|
} else {
|
||||||
return <UnknownDetailsCard key={key} {...props} />;
|
return <UnknownDetailsCard key={key} {...props} />;
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,7 @@ import {
|
||||||
Transaction,
|
Transaction,
|
||||||
PartiallyDecodedInstruction,
|
PartiallyDecodedInstruction,
|
||||||
ParsedInstruction,
|
ParsedInstruction,
|
||||||
|
Secp256k1Program,
|
||||||
} from "@solana/web3.js";
|
} from "@solana/web3.js";
|
||||||
import { Cluster } from "providers/cluster";
|
import { Cluster } from "providers/cluster";
|
||||||
import { SerumMarketRegistry } from "serumMarketRegistry";
|
import { SerumMarketRegistry } from "serumMarketRegistry";
|
||||||
|
@ -27,6 +28,7 @@ export enum PROGRAM_NAMES {
|
||||||
STAKE = "Stake Program",
|
STAKE = "Stake Program",
|
||||||
SYSTEM = "System Program",
|
SYSTEM = "System Program",
|
||||||
VOTE = "Vote Program",
|
VOTE = "Vote Program",
|
||||||
|
SECP256K1 = "Secp256k1 Program",
|
||||||
|
|
||||||
// spl
|
// spl
|
||||||
ASSOCIATED_TOKEN = "Associated Token Program",
|
ASSOCIATED_TOKEN = "Associated Token Program",
|
||||||
|
@ -38,6 +40,7 @@ export enum PROGRAM_NAMES {
|
||||||
TOKEN = "Token Program",
|
TOKEN = "Token Program",
|
||||||
|
|
||||||
// other
|
// other
|
||||||
|
WORMHOLE = "Wormhole",
|
||||||
BONFIDA_POOL = "Bonfida Pool Program",
|
BONFIDA_POOL = "Bonfida Pool Program",
|
||||||
BREAK_SOLANA = "Break Solana Program",
|
BREAK_SOLANA = "Break Solana Program",
|
||||||
RAYDIUM_LIQUIDITY_1 = "Raydium Liquidity Pool Program v1",
|
RAYDIUM_LIQUIDITY_1 = "Raydium Liquidity Pool Program v1",
|
||||||
|
@ -65,6 +68,7 @@ export const PROGRAM_DEPLOYMENTS = {
|
||||||
[PROGRAM_NAMES.STAKE]: ALL_CLUSTERS,
|
[PROGRAM_NAMES.STAKE]: ALL_CLUSTERS,
|
||||||
[PROGRAM_NAMES.SYSTEM]: ALL_CLUSTERS,
|
[PROGRAM_NAMES.SYSTEM]: ALL_CLUSTERS,
|
||||||
[PROGRAM_NAMES.VOTE]: ALL_CLUSTERS,
|
[PROGRAM_NAMES.VOTE]: ALL_CLUSTERS,
|
||||||
|
[PROGRAM_NAMES.SECP256K1]: ALL_CLUSTERS,
|
||||||
|
|
||||||
// spl
|
// spl
|
||||||
[PROGRAM_NAMES.ASSOCIATED_TOKEN]: ALL_CLUSTERS,
|
[PROGRAM_NAMES.ASSOCIATED_TOKEN]: ALL_CLUSTERS,
|
||||||
|
@ -76,6 +80,7 @@ export const PROGRAM_DEPLOYMENTS = {
|
||||||
[PROGRAM_NAMES.TOKEN]: ALL_CLUSTERS,
|
[PROGRAM_NAMES.TOKEN]: ALL_CLUSTERS,
|
||||||
|
|
||||||
// other
|
// other
|
||||||
|
[PROGRAM_NAMES.WORMHOLE]: MAINNET_ONLY,
|
||||||
[PROGRAM_NAMES.BONFIDA_POOL]: MAINNET_ONLY,
|
[PROGRAM_NAMES.BONFIDA_POOL]: MAINNET_ONLY,
|
||||||
[PROGRAM_NAMES.BREAK_SOLANA]: LIVE_CLUSTERS,
|
[PROGRAM_NAMES.BREAK_SOLANA]: LIVE_CLUSTERS,
|
||||||
[PROGRAM_NAMES.RAYDIUM_LIQUIDITY_1]: MAINNET_ONLY,
|
[PROGRAM_NAMES.RAYDIUM_LIQUIDITY_1]: MAINNET_ONLY,
|
||||||
|
@ -92,6 +97,7 @@ export const PROGRAM_NAME_BY_ID = {
|
||||||
[StakeProgram.programId.toBase58()]: PROGRAM_NAMES.STAKE,
|
[StakeProgram.programId.toBase58()]: PROGRAM_NAMES.STAKE,
|
||||||
[SystemProgram.programId.toBase58()]: PROGRAM_NAMES.SYSTEM,
|
[SystemProgram.programId.toBase58()]: PROGRAM_NAMES.SYSTEM,
|
||||||
[VOTE_PROGRAM_ID.toBase58()]: PROGRAM_NAMES.VOTE,
|
[VOTE_PROGRAM_ID.toBase58()]: PROGRAM_NAMES.VOTE,
|
||||||
|
[Secp256k1Program.programId.toBase58()]: PROGRAM_NAMES.SECP256K1,
|
||||||
|
|
||||||
// spl
|
// spl
|
||||||
ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL: PROGRAM_NAMES.ASSOCIATED_TOKEN,
|
ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL: PROGRAM_NAMES.ASSOCIATED_TOKEN,
|
||||||
|
@ -103,6 +109,7 @@ export const PROGRAM_NAME_BY_ID = {
|
||||||
LendZqTs7gn5CTSJU1jWKhKuVpjJGom45nnwPb2AMTi: PROGRAM_NAMES.LENDING,
|
LendZqTs7gn5CTSJU1jWKhKuVpjJGom45nnwPb2AMTi: PROGRAM_NAMES.LENDING,
|
||||||
|
|
||||||
// other
|
// other
|
||||||
|
WormT3McKhFJ2RkiGpdw9GKvNCrB2aB54gb2uV9MfQC: PROGRAM_NAMES.WORMHOLE,
|
||||||
WvmTNLpGMVbwJVYztYL4Hnsy82cJhQorxjnnXcRm3b6: PROGRAM_NAMES.BONFIDA_POOL,
|
WvmTNLpGMVbwJVYztYL4Hnsy82cJhQorxjnnXcRm3b6: PROGRAM_NAMES.BONFIDA_POOL,
|
||||||
BrEAK7zGZ6dM71zUDACDqJnekihmwF15noTddWTsknjC: PROGRAM_NAMES.BREAK_SOLANA,
|
BrEAK7zGZ6dM71zUDACDqJnekihmwF15noTddWTsknjC: PROGRAM_NAMES.BREAK_SOLANA,
|
||||||
RVKd61ztZW9GUwhRbbLoYVRE5Xf1B2tVscKqwZqXgEr:
|
RVKd61ztZW9GUwhRbbLoYVRE5Xf1B2tVscKqwZqXgEr:
|
||||||
|
@ -161,7 +168,7 @@ export function tokenLabel(
|
||||||
if (tokenInfo.name === tokenInfo.symbol) {
|
if (tokenInfo.name === tokenInfo.symbol) {
|
||||||
return tokenInfo.name;
|
return tokenInfo.name;
|
||||||
}
|
}
|
||||||
return `${tokenInfo.name} (${tokenInfo.symbol})`;
|
return `${tokenInfo.symbol} - ${tokenInfo.name}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function addressLabel(
|
export function addressLabel(
|
||||||
|
|
Loading…
Reference in New Issue