diff --git a/explorer/src/components/account/TokenAccountSection.tsx b/explorer/src/components/account/TokenAccountSection.tsx
index 35f6db3041..73ab017969 100644
--- a/explorer/src/components/account/TokenAccountSection.tsx
+++ b/explorer/src/components/account/TokenAccountSection.tsx
@@ -18,6 +18,19 @@ import { useTokenRegistry } from "providers/mints/token-registry";
import { BigNumber } from "bignumber.js";
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({
account,
tokenAccount,
@@ -62,16 +75,12 @@ function MintAccountCard({
const tokenInfo = tokenRegistry.get(mintAddress);
- let bridgeContractAddress = tokenInfo?.extensions?.address;
- if (tokenInfo?.extensions?.bridgeContract && !bridgeContractAddress) {
- const extractEth = tokenInfo.extensions.bridgeContract.match(
- /0x[a-fA-F0-9]{40,64}/
- );
-
- if (extractEth) {
- bridgeContractAddress = extractEth[0];
- }
- }
+ const bridgeContractAddress = getEthAddress(
+ tokenInfo?.extensions?.bridgeContract
+ );
+ const assetContractAddress = getEthAddress(
+ tokenInfo?.extensions?.assetContract
+ );
return (
@@ -145,7 +154,7 @@ function MintAccountCard({
)}
{tokenInfo?.extensions?.bridgeContract && bridgeContractAddress && (
- Wormhole Bridge Contract |
+ Bridge Contract |
|
)}
+ {tokenInfo?.extensions?.assetContract && assetContractAddress && (
+
+ Bridged Asset Contract |
+
+
+
+ {assetContractAddress}
+
+
+ |
+
+ )}
);
diff --git a/explorer/src/components/instruction/WormholeDetailsCard.tsx b/explorer/src/components/instruction/WormholeDetailsCard.tsx
new file mode 100644
index 0000000000..a7690edc8a
--- /dev/null
+++ b/explorer/src/components/instruction/WormholeDetailsCard.tsx
@@ -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 (
+
+ );
+}
diff --git a/explorer/src/components/instruction/token-lending/types.ts b/explorer/src/components/instruction/token-lending/types.ts
index a635ed4c50..3586f2337d 100644
--- a/explorer/src/components/instruction/token-lending/types.ts
+++ b/explorer/src/components/instruction/token-lending/types.ts
@@ -28,7 +28,7 @@ export function parseTokenLendingInstructionTitle(
const code = instruction.data[0];
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];
diff --git a/explorer/src/components/instruction/wormhole/types.ts b/explorer/src/components/instruction/wormhole/types.ts
new file mode 100644
index 0000000000..15e132aa50
--- /dev/null
+++ b/explorer/src/components/instruction/wormhole/types.ts
@@ -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];
+}
diff --git a/explorer/src/components/transaction/InstructionsSection.tsx b/explorer/src/components/transaction/InstructionsSection.tsx
index 0c8385a6d2..503352b9fb 100644
--- a/explorer/src/components/transaction/InstructionsSection.tsx
+++ b/explorer/src/components/transaction/InstructionsSection.tsx
@@ -18,6 +18,7 @@ import { SystemDetailsCard } from "components/instruction/system/SystemDetailsCa
import { TokenDetailsCard } from "components/instruction/token/TokenDetailsCard";
import { TokenLendingDetailsCard } from "components/instruction/TokenLendingDetailsCard";
import { TokenSwapDetailsCard } from "components/instruction/TokenSwapDetailsCard";
+import { WormholeDetailsCard } from "components/instruction/WormholeDetailsCard";
import { UnknownDetailsCard } from "components/instruction/UnknownDetailsCard";
import {
SignatureProps,
@@ -35,6 +36,7 @@ import {
import { Cluster, useCluster } from "providers/cluster";
import { BpfUpgradeableLoaderDetailsCard } from "components/instruction/bpf-upgradeable-loader/BpfUpgradeableLoaderDetailsCard";
import { VoteDetailsCard } from "components/instruction/vote/VoteDetailsCard";
+import { isWormholeInstruction } from "components/instruction/wormhole/types";
export type InstructionDetailsProps = {
tx: ParsedTransaction;
@@ -212,6 +214,8 @@ function renderInstructionCard({
return ;
} else if (isTokenLendingInstruction(transactionIx)) {
return ;
+ } else if (isWormholeInstruction(transactionIx)) {
+ return ;
} else {
return ;
}
diff --git a/explorer/src/utils/tx.ts b/explorer/src/utils/tx.ts
index 4766687587..846c02550d 100644
--- a/explorer/src/utils/tx.ts
+++ b/explorer/src/utils/tx.ts
@@ -14,6 +14,7 @@ import {
Transaction,
PartiallyDecodedInstruction,
ParsedInstruction,
+ Secp256k1Program,
} from "@solana/web3.js";
import { Cluster } from "providers/cluster";
import { SerumMarketRegistry } from "serumMarketRegistry";
@@ -27,6 +28,7 @@ export enum PROGRAM_NAMES {
STAKE = "Stake Program",
SYSTEM = "System Program",
VOTE = "Vote Program",
+ SECP256K1 = "Secp256k1 Program",
// spl
ASSOCIATED_TOKEN = "Associated Token Program",
@@ -38,6 +40,7 @@ export enum PROGRAM_NAMES {
TOKEN = "Token Program",
// other
+ WORMHOLE = "Wormhole",
BONFIDA_POOL = "Bonfida Pool Program",
BREAK_SOLANA = "Break Solana Program",
RAYDIUM_LIQUIDITY_1 = "Raydium Liquidity Pool Program v1",
@@ -65,6 +68,7 @@ export const PROGRAM_DEPLOYMENTS = {
[PROGRAM_NAMES.STAKE]: ALL_CLUSTERS,
[PROGRAM_NAMES.SYSTEM]: ALL_CLUSTERS,
[PROGRAM_NAMES.VOTE]: ALL_CLUSTERS,
+ [PROGRAM_NAMES.SECP256K1]: ALL_CLUSTERS,
// spl
[PROGRAM_NAMES.ASSOCIATED_TOKEN]: ALL_CLUSTERS,
@@ -76,6 +80,7 @@ export const PROGRAM_DEPLOYMENTS = {
[PROGRAM_NAMES.TOKEN]: ALL_CLUSTERS,
// other
+ [PROGRAM_NAMES.WORMHOLE]: MAINNET_ONLY,
[PROGRAM_NAMES.BONFIDA_POOL]: MAINNET_ONLY,
[PROGRAM_NAMES.BREAK_SOLANA]: LIVE_CLUSTERS,
[PROGRAM_NAMES.RAYDIUM_LIQUIDITY_1]: MAINNET_ONLY,
@@ -92,6 +97,7 @@ export const PROGRAM_NAME_BY_ID = {
[StakeProgram.programId.toBase58()]: PROGRAM_NAMES.STAKE,
[SystemProgram.programId.toBase58()]: PROGRAM_NAMES.SYSTEM,
[VOTE_PROGRAM_ID.toBase58()]: PROGRAM_NAMES.VOTE,
+ [Secp256k1Program.programId.toBase58()]: PROGRAM_NAMES.SECP256K1,
// spl
ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL: PROGRAM_NAMES.ASSOCIATED_TOKEN,
@@ -103,6 +109,7 @@ export const PROGRAM_NAME_BY_ID = {
LendZqTs7gn5CTSJU1jWKhKuVpjJGom45nnwPb2AMTi: PROGRAM_NAMES.LENDING,
// other
+ WormT3McKhFJ2RkiGpdw9GKvNCrB2aB54gb2uV9MfQC: PROGRAM_NAMES.WORMHOLE,
WvmTNLpGMVbwJVYztYL4Hnsy82cJhQorxjnnXcRm3b6: PROGRAM_NAMES.BONFIDA_POOL,
BrEAK7zGZ6dM71zUDACDqJnekihmwF15noTddWTsknjC: PROGRAM_NAMES.BREAK_SOLANA,
RVKd61ztZW9GUwhRbbLoYVRE5Xf1B2tVscKqwZqXgEr:
@@ -161,7 +168,7 @@ export function tokenLabel(
if (tokenInfo.name === tokenInfo.symbol) {
return tokenInfo.name;
}
- return `${tokenInfo.name} (${tokenInfo.symbol})`;
+ return `${tokenInfo.symbol} - ${tokenInfo.name}`;
}
export function addressLabel(