explorer: add Pyth instruction support (#23551)
This commit is contained in:
@@ -0,0 +1,60 @@
|
||||
import React from "react";
|
||||
import { SignatureResult, TransactionInstruction } from "@solana/web3.js";
|
||||
import { Address } from "components/common/Address";
|
||||
import { InstructionCard } from "../InstructionCard";
|
||||
import { AddMappingParams } from "./program";
|
||||
|
||||
export default function AddMappingDetailsCard({
|
||||
ix,
|
||||
index,
|
||||
result,
|
||||
info,
|
||||
innerCards,
|
||||
childIndex,
|
||||
}: {
|
||||
ix: TransactionInstruction;
|
||||
index: number;
|
||||
result: SignatureResult;
|
||||
info: AddMappingParams;
|
||||
innerCards?: JSX.Element[];
|
||||
childIndex?: number;
|
||||
}) {
|
||||
return (
|
||||
<InstructionCard
|
||||
ix={ix}
|
||||
index={index}
|
||||
result={result}
|
||||
title="Pyth: Add Mapping Account"
|
||||
innerCards={innerCards}
|
||||
childIndex={childIndex}
|
||||
>
|
||||
<tr>
|
||||
<td>Program</td>
|
||||
<td className="text-lg-end">
|
||||
<Address pubkey={ix.programId} alignRight link />
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Funding Account</td>
|
||||
<td className="text-lg-end">
|
||||
<Address pubkey={info.fundingPubkey} alignRight link />
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Mapping Account</td>
|
||||
<td className="text-lg-end">
|
||||
<Address pubkey={info.mappingPubkey} alignRight link />
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Next Mapping Account</td>
|
||||
<td className="text-lg-end">
|
||||
<Address pubkey={info.nextMappingPubkey} alignRight link />
|
||||
</td>
|
||||
</tr>
|
||||
</InstructionCard>
|
||||
);
|
||||
}
|
@@ -0,0 +1,70 @@
|
||||
import React from "react";
|
||||
import { SignatureResult, TransactionInstruction } from "@solana/web3.js";
|
||||
import { Address } from "components/common/Address";
|
||||
import { InstructionCard } from "../InstructionCard";
|
||||
import { AddPriceParams, PriceType } from "./program";
|
||||
|
||||
export default function AddPriceDetailsCard({
|
||||
ix,
|
||||
index,
|
||||
result,
|
||||
info,
|
||||
innerCards,
|
||||
childIndex,
|
||||
}: {
|
||||
ix: TransactionInstruction;
|
||||
index: number;
|
||||
result: SignatureResult;
|
||||
info: AddPriceParams;
|
||||
innerCards?: JSX.Element[];
|
||||
childIndex?: number;
|
||||
}) {
|
||||
return (
|
||||
<InstructionCard
|
||||
ix={ix}
|
||||
index={index}
|
||||
result={result}
|
||||
title="Pyth: Add Price Account"
|
||||
innerCards={innerCards}
|
||||
childIndex={childIndex}
|
||||
>
|
||||
<tr>
|
||||
<td>Program</td>
|
||||
<td className="text-lg-end">
|
||||
<Address pubkey={ix.programId} alignRight link />
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Funding Account</td>
|
||||
<td className="text-lg-end">
|
||||
<Address pubkey={info.fundingPubkey} alignRight link />
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Product Account</td>
|
||||
<td className="text-lg-end">
|
||||
<Address pubkey={info.productPubkey} alignRight link />
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Price Account</td>
|
||||
<td className="text-lg-end">
|
||||
<Address pubkey={info.pricePubkey} alignRight link />
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Exponent</td>
|
||||
<td className="text-lg-end">{info.exponent}</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Price Type</td>
|
||||
<td className="text-lg-end">{PriceType[info.priceType]}</td>
|
||||
</tr>
|
||||
</InstructionCard>
|
||||
);
|
||||
}
|
@@ -0,0 +1,60 @@
|
||||
import React from "react";
|
||||
import { SignatureResult, TransactionInstruction } from "@solana/web3.js";
|
||||
import { Address } from "components/common/Address";
|
||||
import { InstructionCard } from "../InstructionCard";
|
||||
import { AddProductParams } from "./program";
|
||||
|
||||
export default function AddProductDetailsCard({
|
||||
ix,
|
||||
index,
|
||||
result,
|
||||
info,
|
||||
innerCards,
|
||||
childIndex,
|
||||
}: {
|
||||
ix: TransactionInstruction;
|
||||
index: number;
|
||||
result: SignatureResult;
|
||||
info: AddProductParams;
|
||||
innerCards?: JSX.Element[];
|
||||
childIndex?: number;
|
||||
}) {
|
||||
return (
|
||||
<InstructionCard
|
||||
ix={ix}
|
||||
index={index}
|
||||
result={result}
|
||||
title="Pyth: Add Product"
|
||||
innerCards={innerCards}
|
||||
childIndex={childIndex}
|
||||
>
|
||||
<tr>
|
||||
<td>Program</td>
|
||||
<td className="text-lg-end">
|
||||
<Address pubkey={ix.programId} alignRight link />
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Funding Account</td>
|
||||
<td className="text-lg-end">
|
||||
<Address pubkey={info.fundingPubkey} alignRight link />
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Mapping Account</td>
|
||||
<td className="text-lg-end">
|
||||
<Address pubkey={info.mappingPubkey} alignRight link />
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Product Account</td>
|
||||
<td className="text-lg-end">
|
||||
<Address pubkey={info.productPubkey} alignRight link />
|
||||
</td>
|
||||
</tr>
|
||||
</InstructionCard>
|
||||
);
|
||||
}
|
@@ -0,0 +1,53 @@
|
||||
import React from "react";
|
||||
import { SignatureResult, TransactionInstruction } from "@solana/web3.js";
|
||||
import { Address } from "components/common/Address";
|
||||
import { InstructionCard } from "../InstructionCard";
|
||||
import { AggregatePriceParams } from "./program";
|
||||
|
||||
export default function AggregatePriceDetailsCard({
|
||||
ix,
|
||||
index,
|
||||
result,
|
||||
info,
|
||||
innerCards,
|
||||
childIndex,
|
||||
}: {
|
||||
ix: TransactionInstruction;
|
||||
index: number;
|
||||
result: SignatureResult;
|
||||
info: AggregatePriceParams;
|
||||
innerCards?: JSX.Element[];
|
||||
childIndex?: number;
|
||||
}) {
|
||||
return (
|
||||
<InstructionCard
|
||||
ix={ix}
|
||||
index={index}
|
||||
result={result}
|
||||
title="Pyth: Update Price"
|
||||
innerCards={innerCards}
|
||||
childIndex={childIndex}
|
||||
>
|
||||
<tr>
|
||||
<td>Program</td>
|
||||
<td className="text-lg-end">
|
||||
<Address pubkey={ix.programId} alignRight link />
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Funding Account</td>
|
||||
<td className="text-lg-end">
|
||||
<Address pubkey={info.fundingPubkey} alignRight link />
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Price Account</td>
|
||||
<td className="text-lg-end">
|
||||
<Address pubkey={info.pricePubkey} alignRight link />
|
||||
</td>
|
||||
</tr>
|
||||
</InstructionCard>
|
||||
);
|
||||
}
|
@@ -0,0 +1,55 @@
|
||||
import React from "react";
|
||||
import { SignatureResult, TransactionInstruction } from "@solana/web3.js";
|
||||
import { Address } from "components/common/Address";
|
||||
import { InstructionCard } from "../InstructionCard";
|
||||
import { BasePublisherOperationParams } from "./program";
|
||||
|
||||
export default function BasePublisherOperationCard({
|
||||
ix,
|
||||
index,
|
||||
result,
|
||||
operationName,
|
||||
info,
|
||||
innerCards,
|
||||
childIndex,
|
||||
}: {
|
||||
ix: TransactionInstruction;
|
||||
index: number;
|
||||
result: SignatureResult;
|
||||
operationName: string;
|
||||
info: BasePublisherOperationParams;
|
||||
innerCards?: JSX.Element[];
|
||||
childIndex?: number;
|
||||
}) {
|
||||
return (
|
||||
<InstructionCard
|
||||
ix={ix}
|
||||
index={index}
|
||||
result={result}
|
||||
title={`Pyth: ${operationName}`}
|
||||
innerCards={innerCards}
|
||||
childIndex={childIndex}
|
||||
>
|
||||
<tr>
|
||||
<td>Program</td>
|
||||
<td className="text-lg-end">
|
||||
<Address pubkey={ix.programId} alignRight link />
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Price Account</td>
|
||||
<td className="text-lg-end">
|
||||
<Address pubkey={info.pricePubkey} alignRight link />
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Publisher</td>
|
||||
<td className="text-lg-end">
|
||||
<Address pubkey={info.publisherPubkey} alignRight link />
|
||||
</td>
|
||||
</tr>
|
||||
</InstructionCard>
|
||||
);
|
||||
}
|
@@ -0,0 +1,53 @@
|
||||
import React from "react";
|
||||
import { SignatureResult, TransactionInstruction } from "@solana/web3.js";
|
||||
import { Address } from "components/common/Address";
|
||||
import { InstructionCard } from "../InstructionCard";
|
||||
import { InitMappingParams } from "./program";
|
||||
|
||||
export default function InitMappingDetailsCard({
|
||||
ix,
|
||||
index,
|
||||
result,
|
||||
info,
|
||||
innerCards,
|
||||
childIndex,
|
||||
}: {
|
||||
ix: TransactionInstruction;
|
||||
index: number;
|
||||
result: SignatureResult;
|
||||
info: InitMappingParams;
|
||||
innerCards?: JSX.Element[];
|
||||
childIndex?: number;
|
||||
}) {
|
||||
return (
|
||||
<InstructionCard
|
||||
ix={ix}
|
||||
index={index}
|
||||
result={result}
|
||||
title="Pyth: Init Mapping Account"
|
||||
innerCards={innerCards}
|
||||
childIndex={childIndex}
|
||||
>
|
||||
<tr>
|
||||
<td>Program</td>
|
||||
<td className="text-lg-end">
|
||||
<Address pubkey={ix.programId} alignRight link />
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Funding Account</td>
|
||||
<td className="text-lg-end">
|
||||
<Address pubkey={info.fundingPubkey} alignRight link />
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Mapping Account</td>
|
||||
<td className="text-lg-end">
|
||||
<Address pubkey={info.mappingPubkey} alignRight link />
|
||||
</td>
|
||||
</tr>
|
||||
</InstructionCard>
|
||||
);
|
||||
}
|
@@ -0,0 +1,63 @@
|
||||
import React from "react";
|
||||
import { SignatureResult, TransactionInstruction } from "@solana/web3.js";
|
||||
import { Address } from "components/common/Address";
|
||||
import { InstructionCard } from "../InstructionCard";
|
||||
import { InitPriceParams, PriceType } from "./program";
|
||||
|
||||
export default function InitPriceDetailsCard({
|
||||
ix,
|
||||
index,
|
||||
result,
|
||||
info,
|
||||
innerCards,
|
||||
childIndex,
|
||||
}: {
|
||||
ix: TransactionInstruction;
|
||||
index: number;
|
||||
result: SignatureResult;
|
||||
info: InitPriceParams;
|
||||
innerCards?: JSX.Element[];
|
||||
childIndex?: number;
|
||||
}) {
|
||||
return (
|
||||
<InstructionCard
|
||||
ix={ix}
|
||||
index={index}
|
||||
result={result}
|
||||
title="Pyth: Init Price Account"
|
||||
innerCards={innerCards}
|
||||
childIndex={childIndex}
|
||||
>
|
||||
<tr>
|
||||
<td>Program</td>
|
||||
<td className="text-lg-end">
|
||||
<Address pubkey={ix.programId} alignRight link />
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Funding Account</td>
|
||||
<td className="text-lg-end">
|
||||
<Address pubkey={info.fundingPubkey} alignRight link />
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Price Account</td>
|
||||
<td className="text-lg-end">
|
||||
<Address pubkey={info.pricePubkey} alignRight link />
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Exponent</td>
|
||||
<td className="text-lg-end">{info.exponent}</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Price Type</td>
|
||||
<td className="text-lg-end">{PriceType[info.priceType]}</td>
|
||||
</tr>
|
||||
</InstructionCard>
|
||||
);
|
||||
}
|
123
explorer/src/components/instruction/pyth/PythDetailsCard.tsx
Normal file
123
explorer/src/components/instruction/pyth/PythDetailsCard.tsx
Normal file
@@ -0,0 +1,123 @@
|
||||
import React from "react";
|
||||
import { SignatureResult, TransactionInstruction } from "@solana/web3.js";
|
||||
import { useCluster } from "providers/cluster";
|
||||
import { reportError } from "utils/sentry";
|
||||
import { InstructionCard } from "../InstructionCard";
|
||||
import { PythInstruction } from "./program";
|
||||
import UpdatePriceDetailsCard from "./UpdatePriceDetailsCard";
|
||||
import BasePublisherOperationCard from "./BasePublisherOperationCard";
|
||||
import AddProductDetailsCard from "./AddProductDetailsCard";
|
||||
import AddPriceDetailsCard from "./AddPriceDetailsCard";
|
||||
import UpdateProductDetailsCard from "./UpdateProductDetailsCard";
|
||||
import InitMappingDetailsCard from "./InitMappingDetailsCard";
|
||||
import AddMappingDetailsCard from "./AddMappingDetailsCard";
|
||||
import AggregatePriceDetailsCard from "./AggregatePriceDetailsCard";
|
||||
import InitPriceDetailsCard from "./InitPriceDetailsCard";
|
||||
|
||||
export function PythDetailsCard(props: {
|
||||
ix: TransactionInstruction;
|
||||
index: number;
|
||||
result: SignatureResult;
|
||||
signature: string;
|
||||
innerCards?: JSX.Element[];
|
||||
childIndex?: number;
|
||||
}) {
|
||||
const { url } = useCluster();
|
||||
const { ix, index, result, signature, innerCards, childIndex } = props;
|
||||
|
||||
try {
|
||||
let ixType = PythInstruction.decodeInstructionType(ix);
|
||||
|
||||
switch (ixType) {
|
||||
case "InitMapping":
|
||||
return (
|
||||
<InitMappingDetailsCard
|
||||
info={PythInstruction.decodeInitMapping(ix)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
case "AddMapping":
|
||||
return (
|
||||
<AddMappingDetailsCard
|
||||
info={PythInstruction.decodeAddMapping(ix)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
case "AddProduct":
|
||||
return (
|
||||
<AddProductDetailsCard
|
||||
info={PythInstruction.decodeAddProduct(ix)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
case "UpdateProduct":
|
||||
return (
|
||||
<UpdateProductDetailsCard
|
||||
info={PythInstruction.decodeUpdateProduct(ix)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
case "AddPrice":
|
||||
return (
|
||||
<AddPriceDetailsCard
|
||||
info={PythInstruction.decodeAddPrice(ix)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
case "AddPublisher":
|
||||
return (
|
||||
<BasePublisherOperationCard
|
||||
operationName="Add Publisher"
|
||||
info={PythInstruction.decodeAddPublisher(ix)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
case "DeletePublisher":
|
||||
return (
|
||||
<BasePublisherOperationCard
|
||||
operationName="Delete Publisher"
|
||||
info={PythInstruction.decodeDeletePublisher(ix)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
case "UpdatePrice":
|
||||
return (
|
||||
<UpdatePriceDetailsCard
|
||||
info={PythInstruction.decodeUpdatePrice(ix)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
case "AggregatePrice":
|
||||
return (
|
||||
<AggregatePriceDetailsCard
|
||||
info={PythInstruction.decodeAggregatePrice(ix)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
case "InitPrice":
|
||||
return (
|
||||
<InitPriceDetailsCard
|
||||
info={PythInstruction.decodeInitPrice(ix)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
reportError(error, {
|
||||
url: url,
|
||||
signature: signature,
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<InstructionCard
|
||||
ix={ix}
|
||||
index={index}
|
||||
result={result}
|
||||
title={`Pyth: Unknown`}
|
||||
innerCards={innerCards}
|
||||
childIndex={childIndex}
|
||||
defaultRaw
|
||||
/>
|
||||
);
|
||||
}
|
@@ -0,0 +1,58 @@
|
||||
import React from "react";
|
||||
import { SignatureResult, TransactionInstruction } from "@solana/web3.js";
|
||||
import { Address } from "components/common/Address";
|
||||
import { InstructionCard } from "../InstructionCard";
|
||||
import { SetMinPublishersParams } from "./program";
|
||||
|
||||
export default function SetMinPublishersDetailsCard({
|
||||
ix,
|
||||
index,
|
||||
result,
|
||||
info,
|
||||
innerCards,
|
||||
childIndex,
|
||||
}: {
|
||||
ix: TransactionInstruction;
|
||||
index: number;
|
||||
result: SignatureResult;
|
||||
info: SetMinPublishersParams;
|
||||
innerCards?: JSX.Element[];
|
||||
childIndex?: number;
|
||||
}) {
|
||||
return (
|
||||
<InstructionCard
|
||||
ix={ix}
|
||||
index={index}
|
||||
result={result}
|
||||
title="Pyth: Set Minimum Number Of Publishers"
|
||||
innerCards={innerCards}
|
||||
childIndex={childIndex}
|
||||
>
|
||||
<tr>
|
||||
<td>Program</td>
|
||||
<td className="text-lg-end">
|
||||
<Address pubkey={ix.programId} alignRight link />
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Funding Account</td>
|
||||
<td className="text-lg-end">
|
||||
<Address pubkey={info.fundingPubkey} alignRight link />
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Price Account</td>
|
||||
<td className="text-lg-end">
|
||||
<Address pubkey={info.pricePubkey} alignRight link />
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Min Publishers</td>
|
||||
<td className="text-lg-end">{info.minPublishers}</td>
|
||||
</tr>
|
||||
</InstructionCard>
|
||||
);
|
||||
}
|
@@ -0,0 +1,73 @@
|
||||
import React from "react";
|
||||
import { SignatureResult, TransactionInstruction } from "@solana/web3.js";
|
||||
import { Address } from "components/common/Address";
|
||||
import { InstructionCard } from "../InstructionCard";
|
||||
import { TradingStatus, UpdatePriceParams } from "./program";
|
||||
|
||||
export default function UpdatePriceDetailsCard({
|
||||
ix,
|
||||
index,
|
||||
result,
|
||||
info,
|
||||
innerCards,
|
||||
childIndex,
|
||||
}: {
|
||||
ix: TransactionInstruction;
|
||||
index: number;
|
||||
result: SignatureResult;
|
||||
info: UpdatePriceParams;
|
||||
innerCards?: JSX.Element[];
|
||||
childIndex?: number;
|
||||
}) {
|
||||
return (
|
||||
<InstructionCard
|
||||
ix={ix}
|
||||
index={index}
|
||||
result={result}
|
||||
title="Pyth: Update Price"
|
||||
innerCards={innerCards}
|
||||
childIndex={childIndex}
|
||||
>
|
||||
<tr>
|
||||
<td>Program</td>
|
||||
<td className="text-lg-end">
|
||||
<Address pubkey={ix.programId} alignRight link />
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Publisher</td>
|
||||
<td className="text-lg-end">
|
||||
<Address pubkey={info.publisherPubkey} alignRight link />
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Price Account</td>
|
||||
<td className="text-lg-end">
|
||||
<Address pubkey={info.pricePubkey} alignRight link />
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Status</td>
|
||||
<td className="text-lg-end">{TradingStatus[info.status]}</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Price</td>
|
||||
<td className="text-lg-end">{info.price}</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Conf</td>
|
||||
<td className="text-lg-end">{info.conf}</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Publish Slot</td>
|
||||
<td className="text-lg-end">{info.publishSlot}</td>
|
||||
</tr>
|
||||
</InstructionCard>
|
||||
);
|
||||
}
|
@@ -0,0 +1,82 @@
|
||||
import React from "react";
|
||||
import { SignatureResult, TransactionInstruction } from "@solana/web3.js";
|
||||
import { Address } from "components/common/Address";
|
||||
import { Copyable } from "components/common/Copyable";
|
||||
import { InstructionCard } from "../InstructionCard";
|
||||
import { UpdateProductParams } from "./program";
|
||||
|
||||
export default function UpdateProductDetailsCard({
|
||||
ix,
|
||||
index,
|
||||
result,
|
||||
info,
|
||||
innerCards,
|
||||
childIndex,
|
||||
}: {
|
||||
ix: TransactionInstruction;
|
||||
index: number;
|
||||
result: SignatureResult;
|
||||
info: UpdateProductParams;
|
||||
innerCards?: JSX.Element[];
|
||||
childIndex?: number;
|
||||
}) {
|
||||
const attrsJSON = JSON.stringify(
|
||||
Object.fromEntries(info.attributes),
|
||||
null,
|
||||
2
|
||||
);
|
||||
|
||||
function Content() {
|
||||
return (
|
||||
<Copyable text={attrsJSON}>
|
||||
<pre className="d-inline-block text-start mb-0">{attrsJSON}</pre>
|
||||
</Copyable>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<InstructionCard
|
||||
ix={ix}
|
||||
index={index}
|
||||
result={result}
|
||||
title="Pyth: Update Product"
|
||||
innerCards={innerCards}
|
||||
childIndex={childIndex}
|
||||
>
|
||||
<tr>
|
||||
<td>Program</td>
|
||||
<td className="text-lg-end">
|
||||
<Address pubkey={ix.programId} alignRight link />
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Funding Account</td>
|
||||
<td className="text-lg-end">
|
||||
<Address pubkey={info.fundingPubkey} alignRight link />
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Product Account</td>
|
||||
<td className="text-lg-end">
|
||||
<Address pubkey={info.productPubkey} alignRight link />
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
Attributes <span className="text-muted">(JSON)</span>
|
||||
</td>
|
||||
<td className="text-lg-end">
|
||||
<div className="d-none d-lg-flex align-items-center justify-content-end">
|
||||
<Content />
|
||||
</div>
|
||||
<div className="d-flex d-lg-none align-items-center">
|
||||
<Content />
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</InstructionCard>
|
||||
);
|
||||
}
|
482
explorer/src/components/instruction/pyth/program.ts
Normal file
482
explorer/src/components/instruction/pyth/program.ts
Normal file
@@ -0,0 +1,482 @@
|
||||
import * as BufferLayout from "@solana/buffer-layout";
|
||||
import {
|
||||
InstructionType,
|
||||
PublicKey,
|
||||
TransactionInstruction,
|
||||
} from "@solana/web3.js";
|
||||
import { Layout, uint8ArrayToBuffer } from "@solana/buffer-layout";
|
||||
|
||||
/**
|
||||
* An enumeration of valid PythInstructionTypes
|
||||
*/
|
||||
export type PythInstructionType =
|
||||
| "InitMapping"
|
||||
| "AddMapping"
|
||||
| "AddProduct"
|
||||
| "UpdateProduct"
|
||||
| "AddPrice"
|
||||
| "AddPublisher"
|
||||
| "DeletePublisher"
|
||||
| "UpdatePrice"
|
||||
| "AggregatePrice"
|
||||
| "InitPrice"
|
||||
| "InitTest"
|
||||
| "UpdateTest"
|
||||
| "SetMinPublishers";
|
||||
|
||||
export function headerLayout(property: string = "header") {
|
||||
return BufferLayout.struct(
|
||||
[BufferLayout.u32("version"), BufferLayout.u32("type")],
|
||||
property
|
||||
);
|
||||
}
|
||||
|
||||
function decodeData(type: InstructionType, buffer: Buffer): any {
|
||||
let data;
|
||||
try {
|
||||
data = type.layout.decode(buffer);
|
||||
} catch (err) {
|
||||
throw new Error("invalid instruction; " + err);
|
||||
}
|
||||
|
||||
if (data.header.type !== type.index) {
|
||||
throw new Error(
|
||||
`invalid instruction; instruction index mismatch ${data.header.type} != ${type.index}`
|
||||
);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* An uint8 length-prefixed UTF-8 string.
|
||||
*/
|
||||
class LPString extends Layout {
|
||||
getSpan(b: Uint8Array, offset?: number): number {
|
||||
return 1 + b[offset || 0];
|
||||
}
|
||||
|
||||
decode(b: Uint8Array, offset?: number): string {
|
||||
if (offset === undefined) {
|
||||
offset = 0;
|
||||
}
|
||||
return uint8ArrayToBuffer(b)
|
||||
.slice(offset + 1, offset + b[offset] + 1)
|
||||
.toString("utf-8");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A list that fills up all the available space with its elements.
|
||||
*/
|
||||
class GreedyList extends Layout {
|
||||
private element: Layout;
|
||||
|
||||
constructor(element: Layout, property?: string) {
|
||||
super(-1, property);
|
||||
this.element = element;
|
||||
}
|
||||
|
||||
getSpan(b: Uint8Array, offset?: number): number {
|
||||
return b.length - (offset || 0);
|
||||
}
|
||||
|
||||
decode(b: Uint8Array, offset?: number): string[] {
|
||||
if (offset === undefined) {
|
||||
offset = 0;
|
||||
}
|
||||
const strs = [];
|
||||
while (offset < b.length) {
|
||||
strs.push(this.element.decode(b, offset));
|
||||
offset += this.element.getSpan(b, offset);
|
||||
}
|
||||
return strs;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An enumeration of valid Pyth instruction layouts
|
||||
* @internal
|
||||
*/
|
||||
export const PYTH_INSTRUCTION_LAYOUTS: {
|
||||
[type in PythInstructionType]: InstructionType;
|
||||
} = Object.freeze({
|
||||
InitMapping: {
|
||||
index: 0,
|
||||
layout: BufferLayout.struct([headerLayout()]),
|
||||
},
|
||||
AddMapping: {
|
||||
index: 1,
|
||||
layout: BufferLayout.struct([headerLayout()]),
|
||||
},
|
||||
AddProduct: {
|
||||
index: 2,
|
||||
layout: BufferLayout.struct([headerLayout()]),
|
||||
},
|
||||
UpdateProduct: {
|
||||
index: 3,
|
||||
layout: BufferLayout.struct([
|
||||
headerLayout(),
|
||||
new GreedyList(
|
||||
BufferLayout.struct([
|
||||
new LPString(-1, "key"),
|
||||
new LPString(-1, "value"),
|
||||
]),
|
||||
"attributes"
|
||||
),
|
||||
]),
|
||||
},
|
||||
AddPrice: {
|
||||
index: 4,
|
||||
layout: BufferLayout.struct([
|
||||
headerLayout(),
|
||||
BufferLayout.s32("exponent"),
|
||||
BufferLayout.u32("priceType"),
|
||||
]),
|
||||
},
|
||||
AddPublisher: {
|
||||
index: 5,
|
||||
layout: BufferLayout.struct([
|
||||
headerLayout(),
|
||||
BufferLayout.blob(32, "publisherPubkey"),
|
||||
]),
|
||||
},
|
||||
DeletePublisher: {
|
||||
index: 6,
|
||||
layout: BufferLayout.struct([
|
||||
headerLayout(),
|
||||
BufferLayout.blob(32, "publisherPubkey"),
|
||||
]),
|
||||
},
|
||||
UpdatePrice: {
|
||||
index: 7,
|
||||
layout: BufferLayout.struct([
|
||||
headerLayout(),
|
||||
BufferLayout.u32("status"),
|
||||
BufferLayout.u32("unused1"),
|
||||
BufferLayout.ns64("price"),
|
||||
BufferLayout.nu64("conf"),
|
||||
BufferLayout.nu64("publishSlot"),
|
||||
]),
|
||||
},
|
||||
AggregatePrice: {
|
||||
index: 8,
|
||||
layout: BufferLayout.struct([headerLayout()]),
|
||||
},
|
||||
InitPrice: {
|
||||
index: 9,
|
||||
layout: BufferLayout.struct([
|
||||
headerLayout(),
|
||||
BufferLayout.s32("exponent"),
|
||||
BufferLayout.u32("priceType"),
|
||||
]),
|
||||
},
|
||||
InitTest: {
|
||||
index: 10,
|
||||
layout: BufferLayout.struct([headerLayout()]),
|
||||
},
|
||||
UpdateTest: {
|
||||
index: 11,
|
||||
layout: BufferLayout.struct([headerLayout()]),
|
||||
},
|
||||
SetMinPublishers: {
|
||||
index: 12,
|
||||
layout: BufferLayout.struct([
|
||||
headerLayout(),
|
||||
BufferLayout.u8("minPublishers"),
|
||||
BufferLayout.blob(3, "unused1"),
|
||||
]),
|
||||
},
|
||||
});
|
||||
|
||||
export enum PriceType {
|
||||
Unknown = 0,
|
||||
Price,
|
||||
}
|
||||
|
||||
export enum TradingStatus {
|
||||
Unknown = 0,
|
||||
Trading,
|
||||
Halted,
|
||||
Auction,
|
||||
}
|
||||
|
||||
export type InitMappingParams = {
|
||||
fundingPubkey: PublicKey;
|
||||
mappingPubkey: PublicKey;
|
||||
};
|
||||
|
||||
export type AddMappingParams = {
|
||||
fundingPubkey: PublicKey;
|
||||
mappingPubkey: PublicKey;
|
||||
nextMappingPubkey: PublicKey;
|
||||
};
|
||||
|
||||
export type AddProductParams = {
|
||||
fundingPubkey: PublicKey;
|
||||
mappingPubkey: PublicKey;
|
||||
productPubkey: PublicKey;
|
||||
};
|
||||
|
||||
export type UpdateProductParams = {
|
||||
fundingPubkey: PublicKey;
|
||||
productPubkey: PublicKey;
|
||||
attributes: Map<String, String>;
|
||||
};
|
||||
|
||||
export type AddPriceParams = {
|
||||
fundingPubkey: PublicKey;
|
||||
productPubkey: PublicKey;
|
||||
pricePubkey: PublicKey;
|
||||
exponent: number;
|
||||
priceType: PriceType;
|
||||
};
|
||||
|
||||
export type BasePublisherOperationParams = {
|
||||
signerPubkey: PublicKey;
|
||||
pricePubkey: PublicKey;
|
||||
publisherPubkey: PublicKey;
|
||||
};
|
||||
|
||||
export type UpdatePriceParams = {
|
||||
publisherPubkey: PublicKey;
|
||||
pricePubkey: PublicKey;
|
||||
status: TradingStatus;
|
||||
price: number;
|
||||
conf: number;
|
||||
publishSlot: number;
|
||||
};
|
||||
|
||||
export type AggregatePriceParams = {
|
||||
fundingPubkey: PublicKey;
|
||||
pricePubkey: PublicKey;
|
||||
};
|
||||
|
||||
export type InitPriceParams = {
|
||||
fundingPubkey: PublicKey;
|
||||
pricePubkey: PublicKey;
|
||||
exponent: number;
|
||||
priceType: PriceType;
|
||||
};
|
||||
|
||||
export type SetMinPublishersParams = {
|
||||
fundingPubkey: PublicKey;
|
||||
pricePubkey: PublicKey;
|
||||
minPublishers: number;
|
||||
};
|
||||
|
||||
/**
|
||||
* Pyth Instruction class
|
||||
*/
|
||||
export class PythInstruction {
|
||||
/**
|
||||
* Decode a Pyth instruction and retrieve the instruction type.
|
||||
*/
|
||||
static decodeInstructionType(
|
||||
instruction: TransactionInstruction
|
||||
): PythInstructionType {
|
||||
const header = headerLayout().decode(instruction.data);
|
||||
if (header.version !== 2) {
|
||||
throw new Error(`Unsupported Pyth version: ${header.version}`);
|
||||
}
|
||||
const typeIndex = header.type;
|
||||
|
||||
let type: PythInstructionType | undefined;
|
||||
for (const [ixType, layout] of Object.entries(PYTH_INSTRUCTION_LAYOUTS)) {
|
||||
if (layout.index === typeIndex) {
|
||||
type = ixType as PythInstructionType;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!type) {
|
||||
throw new Error("Instruction type incorrect; not a PythInstruction");
|
||||
}
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode an "init mapping" instruction and retrieve the instruction params.
|
||||
*/
|
||||
static decodeInitMapping(
|
||||
instruction: TransactionInstruction
|
||||
): InitMappingParams {
|
||||
decodeData(PYTH_INSTRUCTION_LAYOUTS.InitMapping, instruction.data);
|
||||
return {
|
||||
fundingPubkey: instruction.keys[0].pubkey,
|
||||
mappingPubkey: instruction.keys[1].pubkey,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode an "add mapping" instruction and retrieve the instruction params.
|
||||
*/
|
||||
static decodeAddMapping(
|
||||
instruction: TransactionInstruction
|
||||
): AddMappingParams {
|
||||
decodeData(PYTH_INSTRUCTION_LAYOUTS.AddMapping, instruction.data);
|
||||
return {
|
||||
fundingPubkey: instruction.keys[0].pubkey,
|
||||
mappingPubkey: instruction.keys[1].pubkey,
|
||||
nextMappingPubkey: instruction.keys[2].pubkey,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode an "add product" instruction and retrieve the instruction params.
|
||||
*/
|
||||
static decodeAddProduct(
|
||||
instruction: TransactionInstruction
|
||||
): AddProductParams {
|
||||
decodeData(PYTH_INSTRUCTION_LAYOUTS.AddProduct, instruction.data);
|
||||
return {
|
||||
fundingPubkey: instruction.keys[0].pubkey,
|
||||
mappingPubkey: instruction.keys[1].pubkey,
|
||||
productPubkey: instruction.keys[2].pubkey,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode an "add product" instruction and retrieve the instruction params.
|
||||
*/
|
||||
static decodeUpdateProduct(
|
||||
instruction: TransactionInstruction
|
||||
): UpdateProductParams {
|
||||
const { attributes } = decodeData(
|
||||
PYTH_INSTRUCTION_LAYOUTS.UpdateProduct,
|
||||
instruction.data
|
||||
);
|
||||
return {
|
||||
fundingPubkey: instruction.keys[0].pubkey,
|
||||
productPubkey: instruction.keys[1].pubkey,
|
||||
attributes: new Map(
|
||||
attributes.map((kv: { key: string; value: string }) => [
|
||||
kv.key,
|
||||
kv.value,
|
||||
])
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode an "add price" instruction and retrieve the instruction params.
|
||||
*/
|
||||
static decodeAddPrice(instruction: TransactionInstruction): AddPriceParams {
|
||||
const { exponent, priceType } = decodeData(
|
||||
PYTH_INSTRUCTION_LAYOUTS.AddPrice,
|
||||
instruction.data
|
||||
);
|
||||
return {
|
||||
fundingPubkey: instruction.keys[0].pubkey,
|
||||
productPubkey: instruction.keys[1].pubkey,
|
||||
pricePubkey: instruction.keys[2].pubkey,
|
||||
exponent,
|
||||
priceType,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode an "add publisher" instruction and retrieve the instruction params.
|
||||
*/
|
||||
static decodeAddPublisher(
|
||||
instruction: TransactionInstruction
|
||||
): BasePublisherOperationParams {
|
||||
const { publisherPubkey } = decodeData(
|
||||
PYTH_INSTRUCTION_LAYOUTS.AddPublisher,
|
||||
instruction.data
|
||||
);
|
||||
|
||||
return {
|
||||
signerPubkey: instruction.keys[0].pubkey,
|
||||
pricePubkey: instruction.keys[1].pubkey,
|
||||
publisherPubkey: PublicKey.decode(publisherPubkey),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode an "delete publisher" instruction and retrieve the instruction params.
|
||||
*/
|
||||
static decodeDeletePublisher(
|
||||
instruction: TransactionInstruction
|
||||
): BasePublisherOperationParams {
|
||||
const { publisherPubkey } = decodeData(
|
||||
PYTH_INSTRUCTION_LAYOUTS.DeletePublisher,
|
||||
instruction.data
|
||||
);
|
||||
|
||||
return {
|
||||
signerPubkey: instruction.keys[0].pubkey,
|
||||
pricePubkey: instruction.keys[1].pubkey,
|
||||
publisherPubkey: PublicKey.decode(publisherPubkey),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode an "update price" instruction and retrieve the instruction params.
|
||||
*/
|
||||
static decodeUpdatePrice(
|
||||
instruction: TransactionInstruction
|
||||
): UpdatePriceParams {
|
||||
const { status, price, conf, publishSlot } = decodeData(
|
||||
PYTH_INSTRUCTION_LAYOUTS.UpdatePrice,
|
||||
instruction.data
|
||||
);
|
||||
|
||||
return {
|
||||
publisherPubkey: instruction.keys[0].pubkey,
|
||||
pricePubkey: instruction.keys[1].pubkey,
|
||||
status,
|
||||
price,
|
||||
conf,
|
||||
publishSlot,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode an "aggregate price" instruction and retrieve the instruction params.
|
||||
*/
|
||||
static decodeAggregatePrice(
|
||||
instruction: TransactionInstruction
|
||||
): AggregatePriceParams {
|
||||
decodeData(PYTH_INSTRUCTION_LAYOUTS.AggregatePrice, instruction.data);
|
||||
|
||||
return {
|
||||
fundingPubkey: instruction.keys[0].pubkey,
|
||||
pricePubkey: instruction.keys[1].pubkey,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode an "init price" instruction and retrieve the instruction params.
|
||||
*/
|
||||
static decodeInitPrice(instruction: TransactionInstruction): InitPriceParams {
|
||||
const { exponent, priceType } = decodeData(
|
||||
PYTH_INSTRUCTION_LAYOUTS.InitPrice,
|
||||
instruction.data
|
||||
);
|
||||
return {
|
||||
fundingPubkey: instruction.keys[0].pubkey,
|
||||
pricePubkey: instruction.keys[1].pubkey,
|
||||
exponent,
|
||||
priceType,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode an "set min publishers" instruction and retrieve the instruction params.
|
||||
*/
|
||||
static decodeSetMinPublishers(
|
||||
instruction: TransactionInstruction
|
||||
): SetMinPublishersParams {
|
||||
const { minPublishers } = decodeData(
|
||||
PYTH_INSTRUCTION_LAYOUTS.SetMinPublishers,
|
||||
instruction.data
|
||||
);
|
||||
return {
|
||||
fundingPubkey: instruction.keys[0].pubkey,
|
||||
pricePubkey: instruction.keys[1].pubkey,
|
||||
minPublishers,
|
||||
};
|
||||
}
|
||||
}
|
13
explorer/src/components/instruction/pyth/types.ts
Normal file
13
explorer/src/components/instruction/pyth/types.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import { TransactionInstruction } from "@solana/web3.js";
|
||||
|
||||
export const PROGRAM_IDS: string[] = [
|
||||
"gSbePebfvPy7tRqimPoVecS2UsBvYv46ynrzWocc92s", // devnet
|
||||
"8tfDNiaEyrV6Q1U4DEXrEigs9DoDtkugzFbybENEbCDz", // testnet
|
||||
"FsJ3A3u2vn5cTVofAjvy6y5kwABJAqYWpe4975bi2epH", // mainnet
|
||||
];
|
||||
|
||||
export function isPythInstruction(
|
||||
instruction: TransactionInstruction
|
||||
): boolean {
|
||||
return PROGRAM_IDS.includes(instruction.programId.toBase58());
|
||||
}
|
@@ -41,6 +41,8 @@ import { isWormholeInstruction } from "components/instruction/wormhole/types";
|
||||
import { AssociatedTokenDetailsCard } from "components/instruction/AssociatedTokenDetailsCard";
|
||||
import { isMangoInstruction } from "components/instruction/mango/types";
|
||||
import { MangoDetailsCard } from "components/instruction/MangoDetails";
|
||||
import { isPythInstruction } from "components/instruction/pyth/types";
|
||||
import { PythDetailsCard } from "components/instruction/pyth/PythDetailsCard";
|
||||
|
||||
export type InstructionDetailsProps = {
|
||||
tx: ParsedTransaction;
|
||||
@@ -222,6 +224,8 @@ function renderInstructionCard({
|
||||
return <TokenLendingDetailsCard key={key} {...props} />;
|
||||
} else if (isWormholeInstruction(transactionIx)) {
|
||||
return <WormholeDetailsCard key={key} {...props} />;
|
||||
} else if (isPythInstruction(transactionIx)) {
|
||||
return <PythDetailsCard key={key} {...props} />;
|
||||
} else {
|
||||
return <UnknownDetailsCard key={key} {...props} />;
|
||||
}
|
||||
|
Reference in New Issue
Block a user