Mango v3 integration (#19437)
* mango v3 integration Signed-off-by: microwavedcola1 <microwavedcola@gmail.com> * more instructions InitMangoAccount, Deposit, Withdraw, InitSpotOpenOrders, ConsumeEvents Signed-off-by: microwavedcola1 <microwavedcola@gmail.com> * add support for addPerpMarket Signed-off-by: microwavedcola1 <microwavedcola@gmail.com> * code review Signed-off-by: microwavedcola1 <microwavedcola@gmail.com> * make group config handling generic Signed-off-by: microwavedcola1 <microwavedcola@gmail.com> * code review Signed-off-by: microwavedcola1 <microwavedcola@gmail.com> * fix issue where rpc calls would be done infinitely - basically react useeffect (when to change) was not narrow enough + added some caching Signed-off-by: microwavedcola1 <microwavedcola@gmail.com>
This commit is contained in:
@@ -51,6 +51,7 @@ import { useQuery } from "utils/url";
|
||||
import { TokenInfoMap } from "@solana/spl-token-registry";
|
||||
import { useTokenRegistry } from "providers/mints/token-registry";
|
||||
import { getTokenProgramInstructionName } from "utils/instruction";
|
||||
import { isMangoInstruction, parseMangoInstructionTitle } from "components/instruction/mango/types";
|
||||
|
||||
const TRUNCATE_TOKEN_LENGTH = 10;
|
||||
const ALL_TOKENS = "";
|
||||
@@ -502,6 +503,16 @@ const TokenTransactionRow = React.memo(
|
||||
reportError(error, { signature: tx.signature });
|
||||
return undefined;
|
||||
}
|
||||
} else if (
|
||||
transactionInstruction &&
|
||||
isMangoInstruction(transactionInstruction)
|
||||
) {
|
||||
try {
|
||||
name = parseMangoInstructionTitle(transactionInstruction);
|
||||
} catch (error) {
|
||||
reportError(error, { signature: tx.signature });
|
||||
return undefined;
|
||||
}
|
||||
} else {
|
||||
if (
|
||||
ix.accounts.findIndex((account) =>
|
||||
|
161
explorer/src/components/instruction/MangoDetails.tsx
Normal file
161
explorer/src/components/instruction/MangoDetails.tsx
Normal file
@@ -0,0 +1,161 @@
|
||||
import { SignatureResult, TransactionInstruction } from "@solana/web3.js";
|
||||
import { useCluster } from "providers/cluster";
|
||||
import { reportError } from "utils/sentry";
|
||||
import { InstructionCard } from "./InstructionCard";
|
||||
import { AddOracleDetailsCard } from "./mango/AddOracleDetailsCard";
|
||||
import { AddPerpMarketDetailsCard } from "./mango/AddPerpMarketDetailsCard";
|
||||
import { AddSpotMarketDetailsCard } from "./mango/AddSpotMarketDetailsCard";
|
||||
import { CancelPerpOrderDetailsCard } from "./mango/CancelPerpOrderDetailsCard";
|
||||
import { CancelSpotOrderDetailsCard } from "./mango/CancelSpotOrderDetailsCard";
|
||||
import { ChangePerpMarketParamsDetailsCard } from "./mango/ChangePerpMarketParamsDetailsCard";
|
||||
import { ConsumeEventsDetailsCard } from "./mango/ConsumeEventsDetailsCard";
|
||||
import { GenericMngoAccountDetailsCard } from "./mango/GenericMngoAccountDetailsCard";
|
||||
import { GenericPerpMngoDetailsCard } from "./mango/GenericPerpMngoDetailsCard";
|
||||
import { GenericSpotMngoDetailsCard } from "./mango/GenericSpotMngoDetailsCard";
|
||||
import { PlacePerpOrderDetailsCard } from "./mango/PlacePerpOrderDetailsCard";
|
||||
import { PlaceSpotOrderDetailsCard } from "./mango/PlaceSpotOrderDetailsCard";
|
||||
import {
|
||||
decodeAddPerpMarket,
|
||||
decodeAddSpotMarket,
|
||||
decodeCancelPerpOrder,
|
||||
decodeCancelSpotOrder,
|
||||
decodeChangePerpMarketParams,
|
||||
decodePlacePerpOrder,
|
||||
decodePlaceSpotOrder,
|
||||
parseMangoInstructionTitle,
|
||||
} from "./mango/types";
|
||||
|
||||
export function MangoDetailsCard(props: {
|
||||
ix: TransactionInstruction;
|
||||
index: number;
|
||||
result: SignatureResult;
|
||||
signature: string;
|
||||
innerCards?: JSX.Element[];
|
||||
childIndex?: number;
|
||||
}) {
|
||||
const { ix, index, result, signature, innerCards, childIndex } = props;
|
||||
|
||||
const { url } = useCluster();
|
||||
|
||||
let title;
|
||||
try {
|
||||
title = parseMangoInstructionTitle(ix);
|
||||
|
||||
switch (title) {
|
||||
case "InitMangoAccount":
|
||||
return (
|
||||
<GenericMngoAccountDetailsCard
|
||||
mangoAccountKeyLocation={1}
|
||||
title={title}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
case "Deposit":
|
||||
return (
|
||||
<GenericMngoAccountDetailsCard
|
||||
mangoAccountKeyLocation={1}
|
||||
title={title}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
case "Withdraw":
|
||||
return (
|
||||
<GenericMngoAccountDetailsCard
|
||||
mangoAccountKeyLocation={1}
|
||||
title={title}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
case "InitSpotOpenOrders":
|
||||
return (
|
||||
<GenericMngoAccountDetailsCard
|
||||
mangoAccountKeyLocation={1}
|
||||
title={title}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
case "PlaceSpotOrder":
|
||||
return (
|
||||
<PlaceSpotOrderDetailsCard
|
||||
info={decodePlaceSpotOrder(ix)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
case "CancelSpotOrder":
|
||||
return (
|
||||
<CancelSpotOrderDetailsCard
|
||||
info={decodeCancelSpotOrder(ix)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
case "AddPerpMarket":
|
||||
return (
|
||||
<AddPerpMarketDetailsCard info={decodeAddPerpMarket(ix)} {...props} />
|
||||
);
|
||||
case "PlacePerpOrder":
|
||||
return (
|
||||
<PlacePerpOrderDetailsCard
|
||||
info={decodePlacePerpOrder(ix)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
case "ConsumeEvents":
|
||||
return <ConsumeEventsDetailsCard {...props} />;
|
||||
case "CancelPerpOrder":
|
||||
return (
|
||||
<CancelPerpOrderDetailsCard
|
||||
info={decodeCancelPerpOrder(ix)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
case "SettleFunds":
|
||||
return (
|
||||
<GenericSpotMngoDetailsCard
|
||||
accountKeyLocation={2}
|
||||
spotMarketkeyLocation={5}
|
||||
title={title}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
case "RedeemMngo":
|
||||
return (
|
||||
<GenericPerpMngoDetailsCard
|
||||
mangoAccountKeyLocation={3}
|
||||
perpMarketKeyLocation={4}
|
||||
title={title}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
case "ChangePerpMarketParams":
|
||||
return (
|
||||
<ChangePerpMarketParamsDetailsCard
|
||||
info={decodeChangePerpMarketParams(ix)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
case "AddOracle":
|
||||
return <AddOracleDetailsCard {...props} />;
|
||||
case "AddSpotMarket":
|
||||
return (
|
||||
<AddSpotMarketDetailsCard info={decodeAddSpotMarket(ix)} {...props} />
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
reportError(error, {
|
||||
url: url,
|
||||
signature: signature,
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<InstructionCard
|
||||
ix={ix}
|
||||
index={index}
|
||||
result={result}
|
||||
title={`Mango: ${title || "Unknown"}`}
|
||||
innerCards={innerCards}
|
||||
childIndex={childIndex}
|
||||
defaultRaw
|
||||
/>
|
||||
);
|
||||
}
|
@@ -0,0 +1,23 @@
|
||||
import { SignatureResult, TransactionInstruction } from "@solana/web3.js";
|
||||
import { InstructionCard } from "../InstructionCard";
|
||||
|
||||
export function AddOracleDetailsCard(props: {
|
||||
ix: TransactionInstruction;
|
||||
index: number;
|
||||
result: SignatureResult;
|
||||
innerCards?: JSX.Element[];
|
||||
childIndex?: number;
|
||||
}) {
|
||||
const { ix, index, result, innerCards, childIndex } = props;
|
||||
|
||||
return (
|
||||
<InstructionCard
|
||||
ix={ix}
|
||||
index={index}
|
||||
result={result}
|
||||
title="Mango: AddOracle"
|
||||
innerCards={innerCards}
|
||||
childIndex={childIndex}
|
||||
></InstructionCard>
|
||||
);
|
||||
}
|
@@ -0,0 +1,76 @@
|
||||
import { SignatureResult, TransactionInstruction } from "@solana/web3.js";
|
||||
import moment from "moment";
|
||||
import { InstructionCard } from "../InstructionCard";
|
||||
import { AddPerpMarket } from "./types";
|
||||
|
||||
export function AddPerpMarketDetailsCard(props: {
|
||||
ix: TransactionInstruction;
|
||||
index: number;
|
||||
result: SignatureResult;
|
||||
info: AddPerpMarket;
|
||||
innerCards?: JSX.Element[];
|
||||
childIndex?: number;
|
||||
}) {
|
||||
const { ix, index, result, info, innerCards, childIndex } = props;
|
||||
|
||||
return (
|
||||
<InstructionCard
|
||||
ix={ix}
|
||||
index={index}
|
||||
result={result}
|
||||
title="Mango: AddPerpMarket"
|
||||
innerCards={innerCards}
|
||||
childIndex={childIndex}
|
||||
>
|
||||
<tr>
|
||||
<td>Market index</td>
|
||||
<td className="text-lg-right">{info.marketIndex}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Maintenance leverage</td>
|
||||
<td className="text-lg-right">{info.maintLeverage}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Initial leverage</td>
|
||||
<td className="text-lg-right">{info.initLeverage}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Liquidation fee</td>
|
||||
<td className="text-lg-right">{info.liquidationFee}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Maker fee</td>
|
||||
<td className="text-lg-right">{info.makerFee}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Taker fee</td>
|
||||
<td className="text-lg-right">{info.takerFee}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Base lot size</td>
|
||||
<td className="text-lg-right">{info.baseLotSize}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Quote lot size</td>
|
||||
<td className="text-lg-right">{info.quoteLotSize}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Rate</td>
|
||||
<td className="text-lg-right">{info.rate}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Max depth bps</td>
|
||||
<td className="text-lg-right">{info.maxDepthBps}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
MNGO per{" "}
|
||||
{moment.duration(info.targetPeriodLength, "seconds").humanize()}
|
||||
</td>
|
||||
<td className="text-lg-right">
|
||||
{info.mngoPerPeriod} {}
|
||||
</td>
|
||||
</tr>
|
||||
</InstructionCard>
|
||||
);
|
||||
}
|
@@ -0,0 +1,62 @@
|
||||
import { SignatureResult, TransactionInstruction } from "@solana/web3.js";
|
||||
import { InstructionCard } from "../InstructionCard";
|
||||
import { AddSpotMarket, spotMarketFromIndex } from "./types";
|
||||
|
||||
export function AddSpotMarketDetailsCard(props: {
|
||||
ix: TransactionInstruction;
|
||||
index: number;
|
||||
result: SignatureResult;
|
||||
info: AddSpotMarket;
|
||||
innerCards?: JSX.Element[];
|
||||
childIndex?: number;
|
||||
}) {
|
||||
const { ix, index, result, info, innerCards, childIndex } = props;
|
||||
|
||||
return (
|
||||
<InstructionCard
|
||||
ix={ix}
|
||||
index={index}
|
||||
result={result}
|
||||
title="Mango: AddSpotMarket"
|
||||
innerCards={innerCards}
|
||||
childIndex={childIndex}
|
||||
>
|
||||
{spotMarketFromIndex(ix, info.marketIndex) !== "UNKNOWN" && (
|
||||
<tr>
|
||||
<td>Market</td>
|
||||
<td className="text-lg-right">
|
||||
{spotMarketFromIndex(ix, info.marketIndex)}
|
||||
</td>
|
||||
</tr>
|
||||
)}
|
||||
<tr>
|
||||
<td>Market index</td>
|
||||
<td className="text-lg-right">{info.marketIndex}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Maint leverage</td>
|
||||
<td className="text-lg-right">{info.maintLeverage}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Init leverage</td>
|
||||
<td className="text-lg-right">{info.initLeverage}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Liquidation fee</td>
|
||||
<td className="text-lg-right">{info.liquidationFee}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Optimal util</td>
|
||||
<td className="text-lg-right">{info.optimalUtil}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Optimal rate</td>
|
||||
<td className="text-lg-right">{info.optimalRate}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Max rate</td>
|
||||
<td className="text-lg-right">{info.maxRate}</td>
|
||||
</tr>
|
||||
</InstructionCard>
|
||||
);
|
||||
}
|
@@ -0,0 +1,58 @@
|
||||
import { SignatureResult, TransactionInstruction } from "@solana/web3.js";
|
||||
import { Address } from "components/common/Address";
|
||||
import { InstructionCard } from "../InstructionCard";
|
||||
import { CancelPerpOrder, getPerpMarketFromInstruction } from "./types";
|
||||
|
||||
export function CancelPerpOrderDetailsCard(props: {
|
||||
ix: TransactionInstruction;
|
||||
index: number;
|
||||
result: SignatureResult;
|
||||
info: CancelPerpOrder;
|
||||
innerCards?: JSX.Element[];
|
||||
childIndex?: number;
|
||||
}) {
|
||||
const { ix, index, result, info, innerCards, childIndex } = props;
|
||||
const mangoAccount = ix.keys[1];
|
||||
const perpMarketAccountMeta = ix.keys[3];
|
||||
const mangoPerpMarketConfig = getPerpMarketFromInstruction(
|
||||
ix,
|
||||
perpMarketAccountMeta
|
||||
);
|
||||
|
||||
return (
|
||||
<InstructionCard
|
||||
ix={ix}
|
||||
index={index}
|
||||
result={result}
|
||||
title="Mango: CancelPerpOrder"
|
||||
innerCards={innerCards}
|
||||
childIndex={childIndex}
|
||||
>
|
||||
<tr>
|
||||
<td>Mango account</td>
|
||||
<td>
|
||||
<Address pubkey={mangoAccount.pubkey} alignRight link />
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
{mangoPerpMarketConfig !== undefined && (
|
||||
<tr>
|
||||
<td>Perp market</td>
|
||||
<td className="text-lg-right">{mangoPerpMarketConfig.name}</td>
|
||||
</tr>
|
||||
)}
|
||||
|
||||
<tr>
|
||||
<td>Perp market address</td>
|
||||
<td>
|
||||
<Address pubkey={perpMarketAccountMeta.pubkey} alignRight link />
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Order Id</td>
|
||||
<td className="text-lg-right">{info.orderId}</td>
|
||||
</tr>
|
||||
</InstructionCard>
|
||||
);
|
||||
}
|
@@ -0,0 +1,58 @@
|
||||
import { SignatureResult, TransactionInstruction } from "@solana/web3.js";
|
||||
import { Address } from "components/common/Address";
|
||||
import { InstructionCard } from "../InstructionCard";
|
||||
import { CancelSpotOrder, getSpotMarketFromInstruction } from "./types";
|
||||
|
||||
export function CancelSpotOrderDetailsCard(props: {
|
||||
ix: TransactionInstruction;
|
||||
index: number;
|
||||
result: SignatureResult;
|
||||
info: CancelSpotOrder;
|
||||
innerCards?: JSX.Element[];
|
||||
childIndex?: number;
|
||||
}) {
|
||||
const { ix, index, result, info, innerCards, childIndex } = props;
|
||||
const mangoAccount = ix.keys[2];
|
||||
const spotMarketAccountMeta = ix.keys[4];
|
||||
const mangoSpotMarketConfig = getSpotMarketFromInstruction(
|
||||
ix,
|
||||
spotMarketAccountMeta
|
||||
);
|
||||
|
||||
return (
|
||||
<InstructionCard
|
||||
ix={ix}
|
||||
index={index}
|
||||
result={result}
|
||||
title="Mango: CancelSpotOrder"
|
||||
innerCards={innerCards}
|
||||
childIndex={childIndex}
|
||||
>
|
||||
<tr>
|
||||
<td>Mango account</td>
|
||||
<td>
|
||||
<Address pubkey={mangoAccount.pubkey} alignRight link />
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
{mangoSpotMarketConfig !== undefined && (
|
||||
<tr>
|
||||
<td>Spot market</td>
|
||||
<td className="text-lg-right">{mangoSpotMarketConfig.name}</td>
|
||||
</tr>
|
||||
)}
|
||||
|
||||
<tr>
|
||||
<td>Spot market address</td>
|
||||
<td>
|
||||
<Address pubkey={spotMarketAccountMeta.pubkey} alignRight link />
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Order Id</td>
|
||||
<td className="text-lg-right">{info.orderId}</td>
|
||||
</tr>
|
||||
</InstructionCard>
|
||||
);
|
||||
}
|
@@ -0,0 +1,122 @@
|
||||
import { SignatureResult, TransactionInstruction } from "@solana/web3.js";
|
||||
import moment from "moment";
|
||||
import { useCluster } from "providers/cluster";
|
||||
import { useEffect, useState } from "react";
|
||||
import { InstructionCard } from "../InstructionCard";
|
||||
import {
|
||||
ChangePerpMarketParams,
|
||||
getPerpMarketFromInstruction,
|
||||
getPerpMarketFromPerpMarketConfig,
|
||||
} from "./types";
|
||||
|
||||
export function ChangePerpMarketParamsDetailsCard(props: {
|
||||
ix: TransactionInstruction;
|
||||
index: number;
|
||||
result: SignatureResult;
|
||||
info: ChangePerpMarketParams;
|
||||
innerCards?: JSX.Element[];
|
||||
childIndex?: number;
|
||||
}) {
|
||||
const { ix, index, result, info, innerCards, childIndex } = props;
|
||||
|
||||
const perpMarketAccountMeta = ix.keys[1];
|
||||
const mangoPerpMarketConfig = getPerpMarketFromInstruction(
|
||||
ix,
|
||||
perpMarketAccountMeta
|
||||
);
|
||||
|
||||
const cluster = useCluster();
|
||||
const [targetPeriodLength, setTargetPeriodLength] = useState<number | null>(
|
||||
null
|
||||
);
|
||||
useEffect(() => {
|
||||
async function getTargetPeriodLength() {
|
||||
if (mangoPerpMarketConfig === undefined) {
|
||||
return;
|
||||
}
|
||||
const mangoPerpMarket = await getPerpMarketFromPerpMarketConfig(
|
||||
cluster.url,
|
||||
mangoPerpMarketConfig
|
||||
);
|
||||
|
||||
setTargetPeriodLength(
|
||||
mangoPerpMarket.liquidityMiningInfo.targetPeriodLength.toNumber()
|
||||
);
|
||||
}
|
||||
|
||||
getTargetPeriodLength();
|
||||
}, [cluster.url, mangoPerpMarketConfig]);
|
||||
|
||||
return (
|
||||
<InstructionCard
|
||||
ix={ix}
|
||||
index={index}
|
||||
result={result}
|
||||
title="Mango: ChangePerpMarketParams"
|
||||
innerCards={innerCards}
|
||||
childIndex={childIndex}
|
||||
>
|
||||
{info.initLeverageOption && (
|
||||
<tr>
|
||||
<td>Init leverage</td>
|
||||
<td className="text-lg-right">{info.initLeverage}</td>
|
||||
</tr>
|
||||
)}
|
||||
{info.liquidationFeeOption && (
|
||||
<tr>
|
||||
<td>Liquidation fee</td>
|
||||
<td className="text-lg-right">{info.liquidationFee}</td>
|
||||
</tr>
|
||||
)}
|
||||
{info.maintLeverageOption && (
|
||||
<tr>
|
||||
<td>Maint leverage</td>
|
||||
<td className="text-lg-right">{info.maintLeverage}</td>
|
||||
</tr>
|
||||
)}
|
||||
{info.makerFeeOption && (
|
||||
<tr>
|
||||
<td>Maker fee</td>
|
||||
<td className="text-lg-right">{info.makerFee}</td>
|
||||
</tr>
|
||||
)}
|
||||
{info.mngoPerPeriodOption && (
|
||||
<tr>
|
||||
<td>
|
||||
MNGO per{" "}
|
||||
{targetPeriodLength !== null &&
|
||||
moment.duration(targetPeriodLength, "seconds").humanize()}
|
||||
</td>
|
||||
<td className="text-lg-right">
|
||||
{info.mngoPerPeriod} {}
|
||||
</td>
|
||||
</tr>
|
||||
)}
|
||||
|
||||
{info.maxDepthBpsOption && (
|
||||
<tr>
|
||||
<td>Max depth bps</td>
|
||||
<td className="text-lg-right">{info.maxDepthBps}</td>
|
||||
</tr>
|
||||
)}
|
||||
{info.rateOption && (
|
||||
<tr>
|
||||
<td>Rate</td>
|
||||
<td className="text-lg-right">{info.rate}</td>
|
||||
</tr>
|
||||
)}
|
||||
{info.takerFeeOption && (
|
||||
<tr>
|
||||
<td>Taker fee</td>
|
||||
<td className="text-lg-right">{info.takerFee}</td>
|
||||
</tr>
|
||||
)}
|
||||
{info.targetPeriodLengthOption && (
|
||||
<tr>
|
||||
<td>Target period length</td>
|
||||
<td className="text-lg-right">{info.targetPeriodLength}</td>
|
||||
</tr>
|
||||
)}
|
||||
</InstructionCard>
|
||||
);
|
||||
}
|
@@ -0,0 +1,45 @@
|
||||
import { SignatureResult, TransactionInstruction } from "@solana/web3.js";
|
||||
import { Address } from "components/common/Address";
|
||||
import { InstructionCard } from "../InstructionCard";
|
||||
import { getPerpMarketFromInstruction } from "./types";
|
||||
|
||||
export function ConsumeEventsDetailsCard(props: {
|
||||
ix: TransactionInstruction;
|
||||
index: number;
|
||||
result: SignatureResult;
|
||||
innerCards?: JSX.Element[];
|
||||
childIndex?: number;
|
||||
}) {
|
||||
const { ix, index, result, innerCards, childIndex } = props;
|
||||
|
||||
const perpMarketAccountMeta = ix.keys[2];
|
||||
const mangoPerpMarketConfig = getPerpMarketFromInstruction(
|
||||
ix,
|
||||
perpMarketAccountMeta
|
||||
);
|
||||
|
||||
return (
|
||||
<InstructionCard
|
||||
ix={ix}
|
||||
index={index}
|
||||
result={result}
|
||||
title={"Mango: ConsumeEvents"}
|
||||
innerCards={innerCards}
|
||||
childIndex={childIndex}
|
||||
>
|
||||
{mangoPerpMarketConfig !== undefined && (
|
||||
<tr>
|
||||
<td>Perp market</td>
|
||||
<td className="text-lg-right">{mangoPerpMarketConfig.name}</td>
|
||||
</tr>
|
||||
)}
|
||||
|
||||
<tr>
|
||||
<td>Perp market address</td>
|
||||
<td>
|
||||
<Address pubkey={perpMarketAccountMeta.pubkey} alignRight link />
|
||||
</td>
|
||||
</tr>
|
||||
</InstructionCard>
|
||||
);
|
||||
}
|
@@ -0,0 +1,42 @@
|
||||
import { SignatureResult, TransactionInstruction } from "@solana/web3.js";
|
||||
import { Address } from "components/common/Address";
|
||||
import { InstructionCard } from "../InstructionCard";
|
||||
|
||||
export function GenericMngoAccountDetailsCard(props: {
|
||||
ix: TransactionInstruction;
|
||||
index: number;
|
||||
result: SignatureResult;
|
||||
mangoAccountKeyLocation: number;
|
||||
title: String;
|
||||
innerCards?: JSX.Element[];
|
||||
childIndex?: number;
|
||||
}) {
|
||||
const {
|
||||
ix,
|
||||
index,
|
||||
result,
|
||||
mangoAccountKeyLocation,
|
||||
title,
|
||||
innerCards,
|
||||
childIndex,
|
||||
} = props;
|
||||
const mangoAccount = ix.keys[mangoAccountKeyLocation];
|
||||
|
||||
return (
|
||||
<InstructionCard
|
||||
ix={ix}
|
||||
index={index}
|
||||
result={result}
|
||||
title={"Mango: " + title}
|
||||
innerCards={innerCards}
|
||||
childIndex={childIndex}
|
||||
>
|
||||
<tr>
|
||||
<td>Mango account</td>
|
||||
<td>
|
||||
<Address pubkey={mangoAccount.pubkey} alignRight link />
|
||||
</td>
|
||||
</tr>
|
||||
</InstructionCard>
|
||||
);
|
||||
}
|
@@ -0,0 +1,64 @@
|
||||
import { SignatureResult, TransactionInstruction } from "@solana/web3.js";
|
||||
import { Address } from "components/common/Address";
|
||||
import { InstructionCard } from "../InstructionCard";
|
||||
import { getPerpMarketFromInstruction } from "./types";
|
||||
|
||||
export function GenericPerpMngoDetailsCard(props: {
|
||||
ix: TransactionInstruction;
|
||||
index: number;
|
||||
result: SignatureResult;
|
||||
mangoAccountKeyLocation: number;
|
||||
perpMarketKeyLocation: number;
|
||||
title: String;
|
||||
innerCards?: JSX.Element[];
|
||||
childIndex?: number;
|
||||
}) {
|
||||
const {
|
||||
ix,
|
||||
index,
|
||||
result,
|
||||
mangoAccountKeyLocation,
|
||||
perpMarketKeyLocation,
|
||||
title,
|
||||
innerCards,
|
||||
childIndex,
|
||||
} = props;
|
||||
const mangoAccount = ix.keys[mangoAccountKeyLocation];
|
||||
const perpMarketAccountMeta = ix.keys[perpMarketKeyLocation];
|
||||
const mangoPerpMarketConfig = getPerpMarketFromInstruction(
|
||||
ix,
|
||||
perpMarketAccountMeta
|
||||
);
|
||||
|
||||
return (
|
||||
<InstructionCard
|
||||
ix={ix}
|
||||
index={index}
|
||||
result={result}
|
||||
title={"Mango: " + title}
|
||||
innerCards={innerCards}
|
||||
childIndex={childIndex}
|
||||
>
|
||||
<tr>
|
||||
<td>Mango account</td>
|
||||
<td>
|
||||
<Address pubkey={mangoAccount.pubkey} alignRight link />
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
{mangoPerpMarketConfig !== undefined && (
|
||||
<tr>
|
||||
<td>Perp market</td>
|
||||
<td className="text-lg-right">{mangoPerpMarketConfig.name}</td>
|
||||
</tr>
|
||||
)}
|
||||
|
||||
<tr>
|
||||
<td>Perp market address</td>
|
||||
<td>
|
||||
<Address pubkey={perpMarketAccountMeta.pubkey} alignRight link />
|
||||
</td>
|
||||
</tr>
|
||||
</InstructionCard>
|
||||
);
|
||||
}
|
@@ -0,0 +1,64 @@
|
||||
import { SignatureResult, TransactionInstruction } from "@solana/web3.js";
|
||||
import { Address } from "components/common/Address";
|
||||
import { InstructionCard } from "../InstructionCard";
|
||||
import { getSpotMarketFromInstruction } from "./types";
|
||||
|
||||
export function GenericSpotMngoDetailsCard(props: {
|
||||
ix: TransactionInstruction;
|
||||
index: number;
|
||||
result: SignatureResult;
|
||||
accountKeyLocation: number;
|
||||
spotMarketkeyLocation: number;
|
||||
title: String;
|
||||
innerCards?: JSX.Element[];
|
||||
childIndex?: number;
|
||||
}) {
|
||||
const {
|
||||
ix,
|
||||
index,
|
||||
result,
|
||||
accountKeyLocation,
|
||||
spotMarketkeyLocation,
|
||||
title,
|
||||
innerCards,
|
||||
childIndex,
|
||||
} = props;
|
||||
const mangoAccount = ix.keys[accountKeyLocation];
|
||||
const spotMarketAccountMeta = ix.keys[spotMarketkeyLocation];
|
||||
const mangoSpotMarketConfig = getSpotMarketFromInstruction(
|
||||
ix,
|
||||
spotMarketAccountMeta
|
||||
);
|
||||
|
||||
return (
|
||||
<InstructionCard
|
||||
ix={ix}
|
||||
index={index}
|
||||
result={result}
|
||||
title={"Mango: " + title}
|
||||
innerCards={innerCards}
|
||||
childIndex={childIndex}
|
||||
>
|
||||
<tr>
|
||||
<td>Mango account</td>
|
||||
<td>
|
||||
<Address pubkey={mangoAccount.pubkey} alignRight link />
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
{mangoSpotMarketConfig !== undefined && (
|
||||
<tr>
|
||||
<td>Spot market</td>
|
||||
<td className="text-lg-right">{mangoSpotMarketConfig.name}</td>
|
||||
</tr>
|
||||
)}
|
||||
|
||||
<tr>
|
||||
<td>Spot market address</td>
|
||||
<td>
|
||||
<Address pubkey={spotMarketAccountMeta.pubkey} alignRight link />
|
||||
</td>
|
||||
</tr>
|
||||
</InstructionCard>
|
||||
);
|
||||
}
|
@@ -0,0 +1,118 @@
|
||||
import { SignatureResult, TransactionInstruction } from "@solana/web3.js";
|
||||
import BN from "bn.js";
|
||||
import { Address } from "components/common/Address";
|
||||
import { useCluster } from "providers/cluster";
|
||||
import { useEffect, useState } from "react";
|
||||
import { InstructionCard } from "../InstructionCard";
|
||||
import {
|
||||
getPerpMarketFromInstruction,
|
||||
getPerpMarketFromPerpMarketConfig,
|
||||
OrderLotDetails,
|
||||
PlacePerpOrder,
|
||||
} from "./types";
|
||||
|
||||
export function PlacePerpOrderDetailsCard(props: {
|
||||
ix: TransactionInstruction;
|
||||
index: number;
|
||||
result: SignatureResult;
|
||||
info: PlacePerpOrder;
|
||||
innerCards?: JSX.Element[];
|
||||
childIndex?: number;
|
||||
}) {
|
||||
const { ix, index, result, info, innerCards, childIndex } = props;
|
||||
const mangoAccount = ix.keys[1];
|
||||
const perpMarketAccountMeta = ix.keys[4];
|
||||
const mangoPerpMarketConfig = getPerpMarketFromInstruction(
|
||||
ix,
|
||||
perpMarketAccountMeta
|
||||
);
|
||||
|
||||
const cluster = useCluster();
|
||||
const [orderLotDetails, setOrderLotDetails] =
|
||||
useState<OrderLotDetails | null>(null);
|
||||
useEffect(() => {
|
||||
async function getOrderLotDetails() {
|
||||
if (mangoPerpMarketConfig === undefined) {
|
||||
return;
|
||||
}
|
||||
const mangoPerpMarket = await getPerpMarketFromPerpMarketConfig(
|
||||
cluster.url,
|
||||
mangoPerpMarketConfig
|
||||
);
|
||||
const maxBaseQuantity = mangoPerpMarket.baseLotsToNumber(
|
||||
new BN(info.quantity.toString())
|
||||
);
|
||||
const limitPrice = mangoPerpMarket.priceLotsToNumber(
|
||||
new BN(info.price.toString())
|
||||
);
|
||||
setOrderLotDetails({
|
||||
price: limitPrice,
|
||||
size: maxBaseQuantity,
|
||||
} as OrderLotDetails);
|
||||
}
|
||||
getOrderLotDetails();
|
||||
}, [cluster.url, info.quantity, info.price, mangoPerpMarketConfig]);
|
||||
|
||||
return (
|
||||
<InstructionCard
|
||||
ix={ix}
|
||||
index={index}
|
||||
result={result}
|
||||
title="Mango: PlacePerpOrder"
|
||||
innerCards={innerCards}
|
||||
childIndex={childIndex}
|
||||
>
|
||||
<tr>
|
||||
<td>Mango account</td>
|
||||
<td>
|
||||
{" "}
|
||||
<Address pubkey={mangoAccount.pubkey} alignRight link />
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
{mangoPerpMarketConfig !== undefined && (
|
||||
<tr>
|
||||
<td>Perp market</td>
|
||||
<td className="text-lg-right">{mangoPerpMarketConfig.name}</td>
|
||||
</tr>
|
||||
)}
|
||||
|
||||
<tr>
|
||||
<td>Perp market address</td>
|
||||
<td>
|
||||
<Address pubkey={perpMarketAccountMeta.pubkey} alignRight link />
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
{info.clientOrderId !== "0" && (
|
||||
<tr>
|
||||
<td>Client order Id</td>
|
||||
<td className="text-lg-right">{info.clientOrderId}</td>
|
||||
</tr>
|
||||
)}
|
||||
|
||||
<tr>
|
||||
<td>Order type</td>
|
||||
<td className="text-lg-right">{info.orderType}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>side</td>
|
||||
<td className="text-lg-right">{info.side}</td>
|
||||
</tr>
|
||||
|
||||
{orderLotDetails !== null && (
|
||||
<tr>
|
||||
<td>price</td>
|
||||
<td className="text-lg-right">{orderLotDetails?.price} USDC</td>
|
||||
</tr>
|
||||
)}
|
||||
|
||||
{orderLotDetails !== null && (
|
||||
<tr>
|
||||
<td>quantity</td>
|
||||
<td className="text-lg-right">{orderLotDetails?.size}</td>
|
||||
</tr>
|
||||
)}
|
||||
</InstructionCard>
|
||||
);
|
||||
}
|
@@ -0,0 +1,130 @@
|
||||
import { SignatureResult, TransactionInstruction } from "@solana/web3.js";
|
||||
import BN from "bn.js";
|
||||
import { Address } from "components/common/Address";
|
||||
import { useCluster } from "providers/cluster";
|
||||
import { useEffect, useState } from "react";
|
||||
import { InstructionCard } from "../InstructionCard";
|
||||
import {
|
||||
getSpotMarketFromInstruction,
|
||||
getSpotMarketFromSpotMarketConfig,
|
||||
OrderLotDetails,
|
||||
PlaceSpotOrder,
|
||||
} from "./types";
|
||||
|
||||
export function PlaceSpotOrderDetailsCard(props: {
|
||||
ix: TransactionInstruction;
|
||||
index: number;
|
||||
result: SignatureResult;
|
||||
info: PlaceSpotOrder;
|
||||
innerCards?: JSX.Element[];
|
||||
childIndex?: number;
|
||||
}) {
|
||||
const { ix, index, result, info, innerCards, childIndex } = props;
|
||||
const mangoAccount = ix.keys[1];
|
||||
const spotMarketAccountMeta = ix.keys[5];
|
||||
const mangoSpotMarketConfig = getSpotMarketFromInstruction(
|
||||
ix,
|
||||
spotMarketAccountMeta
|
||||
);
|
||||
|
||||
const cluster = useCluster();
|
||||
const [orderLotDetails, setOrderLotDetails] =
|
||||
useState<OrderLotDetails | null>(null);
|
||||
useEffect(() => {
|
||||
async function getOrderLotDetails() {
|
||||
if (mangoSpotMarketConfig === undefined) {
|
||||
return;
|
||||
}
|
||||
const mangoSpotMarket = await getSpotMarketFromSpotMarketConfig(
|
||||
ix.programId,
|
||||
cluster.url,
|
||||
mangoSpotMarketConfig
|
||||
);
|
||||
if (mangoSpotMarket === undefined) {
|
||||
return;
|
||||
}
|
||||
const maxBaseQuantity = mangoSpotMarket.baseSizeLotsToNumber(
|
||||
new BN(info.maxBaseQuantity.toString())
|
||||
);
|
||||
const limitPrice = mangoSpotMarket.priceLotsToNumber(
|
||||
new BN(info.limitPrice.toString())
|
||||
);
|
||||
setOrderLotDetails({
|
||||
price: limitPrice,
|
||||
size: maxBaseQuantity,
|
||||
} as OrderLotDetails);
|
||||
}
|
||||
getOrderLotDetails();
|
||||
}, [
|
||||
cluster.url,
|
||||
info.maxBaseQuantity,
|
||||
info.limitPrice,
|
||||
ix.programId,
|
||||
mangoSpotMarketConfig,
|
||||
]);
|
||||
|
||||
return (
|
||||
<InstructionCard
|
||||
ix={ix}
|
||||
index={index}
|
||||
result={result}
|
||||
title="Mango: PlaceSpotOrder"
|
||||
innerCards={innerCards}
|
||||
childIndex={childIndex}
|
||||
>
|
||||
<tr>
|
||||
<td>Mango account</td>
|
||||
<td>
|
||||
{" "}
|
||||
<Address pubkey={mangoAccount.pubkey} alignRight link />
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
{mangoSpotMarketConfig !== undefined && (
|
||||
<tr>
|
||||
<td>Spot market</td>
|
||||
<td className="text-lg-right">{mangoSpotMarketConfig.name}</td>
|
||||
</tr>
|
||||
)}
|
||||
|
||||
<tr>
|
||||
<td>Spot market address</td>
|
||||
<td>
|
||||
<Address pubkey={spotMarketAccountMeta.pubkey} alignRight link />
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Order type</td>
|
||||
<td className="text-lg-right">{info.orderType}</td>
|
||||
</tr>
|
||||
|
||||
{info.clientId !== "0" && (
|
||||
<tr>
|
||||
<td>Client Id</td>
|
||||
<td className="text-lg-right">{info.clientId}</td>
|
||||
</tr>
|
||||
)}
|
||||
|
||||
<tr>
|
||||
<td>Side</td>
|
||||
<td className="text-lg-right">{info.side}</td>
|
||||
</tr>
|
||||
|
||||
{orderLotDetails !== null && (
|
||||
<tr>
|
||||
<td>Limit price</td>
|
||||
{/* todo fix price */}
|
||||
<td className="text-lg-right">{orderLotDetails?.price} USDC</td>
|
||||
</tr>
|
||||
)}
|
||||
|
||||
{orderLotDetails !== null && (
|
||||
<tr>
|
||||
<td>Size</td>
|
||||
<td className="text-lg-right">{orderLotDetails?.size}</td>
|
||||
</tr>
|
||||
)}
|
||||
</InstructionCard>
|
||||
);
|
||||
}
|
430
explorer/src/components/instruction/mango/types.ts
Normal file
430
explorer/src/components/instruction/mango/types.ts
Normal file
@@ -0,0 +1,430 @@
|
||||
import {
|
||||
Config,
|
||||
GroupConfig,
|
||||
MangoInstructionLayout,
|
||||
PerpMarket,
|
||||
PerpMarketConfig,
|
||||
PerpMarketLayout,
|
||||
SpotMarketConfig,
|
||||
} from "@blockworks-foundation/mango-client";
|
||||
import { Market } from "@project-serum/serum";
|
||||
import {
|
||||
AccountInfo,
|
||||
AccountMeta,
|
||||
Connection,
|
||||
PublicKey,
|
||||
TransactionInstruction,
|
||||
} from "@solana/web3.js";
|
||||
|
||||
// note: mainnet.1 suffices since its a superset of mainnet.0
|
||||
const mangoGroups = Config.ids().groups.filter(
|
||||
(group) => group.name !== "mainnet.0"
|
||||
);
|
||||
|
||||
// caching of account info's by public keys
|
||||
let accountInfoCache: Record<string, Promise<AccountInfo<Buffer> | null>> = {};
|
||||
function getAccountInfo(
|
||||
clusterUrl: string,
|
||||
publicKey: PublicKey
|
||||
): Promise<AccountInfo<Buffer> | null> {
|
||||
if (publicKey.toBase58() in accountInfoCache) {
|
||||
return accountInfoCache[publicKey.toBase58()];
|
||||
}
|
||||
const connection = new Connection(clusterUrl);
|
||||
const accountInfoPromise = connection.getAccountInfo(publicKey);
|
||||
accountInfoCache[publicKey.toBase58()] = accountInfoPromise;
|
||||
return accountInfoPromise;
|
||||
}
|
||||
|
||||
function findGroupConfig(programId: PublicKey): GroupConfig | undefined {
|
||||
const filtered = mangoGroups.filter((group) =>
|
||||
group.mangoProgramId.equals(programId)
|
||||
);
|
||||
if (filtered.length) {
|
||||
return filtered[0];
|
||||
}
|
||||
}
|
||||
|
||||
export const isMangoInstruction = (instruction: TransactionInstruction) => {
|
||||
return mangoGroups
|
||||
.map((group) => group.mangoProgramId.toBase58())
|
||||
.includes(instruction.programId.toBase58());
|
||||
};
|
||||
|
||||
export const INSTRUCTION_LOOKUP: { [key: number]: string } = {
|
||||
0: "InitMangoGroup",
|
||||
1: "InitMangoAccount",
|
||||
2: "Deposit",
|
||||
3: "Withdraw",
|
||||
4: "AddSpotMarket",
|
||||
5: "AddToBasket",
|
||||
6: "Borrow",
|
||||
7: "CachePrices",
|
||||
8: "CacheRootBanks",
|
||||
9: "PlaceSpotOrder",
|
||||
10: "AddOracle",
|
||||
11: "AddPerpMarket",
|
||||
12: "PlacePerpOrder",
|
||||
13: "CancelPerpOrderByClientId",
|
||||
14: "CancelPerpOrder",
|
||||
15: "ConsumeEvents",
|
||||
16: "CachePerpMarkets",
|
||||
17: "UpdateFunding",
|
||||
18: "SetOracle",
|
||||
19: "SettleFunds",
|
||||
20: "CancelSpotOrder",
|
||||
21: "UpdateRootBank",
|
||||
22: "SettlePnl",
|
||||
23: "SettleBorrow",
|
||||
24: "ForceCancelSpotOrders",
|
||||
25: "ForceCancelPerpOrders",
|
||||
26: "LiquidateTokenAndToken",
|
||||
27: "LiquidateTokenAndPerp",
|
||||
28: "LiquidatePerpMarket",
|
||||
29: "SettleFees",
|
||||
30: "ResolvePerpBankruptcy",
|
||||
31: "ResolveTokenBankruptcy",
|
||||
32: "InitSpotOpenOrders",
|
||||
33: "RedeemMngo",
|
||||
34: "AddMangoAccountInfo",
|
||||
35: "DepositMsrm",
|
||||
36: "WithdrawMsrm",
|
||||
37: "ChangePerpMarketParams",
|
||||
};
|
||||
|
||||
export const parseMangoInstructionTitle = (
|
||||
instruction: TransactionInstruction
|
||||
): string => {
|
||||
const code = instruction.data[0];
|
||||
|
||||
if (!(code in INSTRUCTION_LOOKUP)) {
|
||||
throw new Error(`Unrecognized Mango instruction code: ${code}`);
|
||||
}
|
||||
return INSTRUCTION_LOOKUP[code];
|
||||
};
|
||||
|
||||
export type Deposit = {
|
||||
quantity: number;
|
||||
};
|
||||
|
||||
export const decodeDeposit = (ix: TransactionInstruction): Deposit => {
|
||||
const decoded = MangoInstructionLayout.decode(ix.data);
|
||||
const deposit: Deposit = {
|
||||
quantity: decoded.Deposit.quantity.toNumber(),
|
||||
};
|
||||
return deposit;
|
||||
};
|
||||
|
||||
export type AddToBasket = {
|
||||
marketIndex: number;
|
||||
};
|
||||
|
||||
export const decodeAddToBasket = (ix: TransactionInstruction): AddToBasket => {
|
||||
const decoded = MangoInstructionLayout.decode(ix.data);
|
||||
const addToBasket: AddToBasket = {
|
||||
marketIndex: decoded.AddToBasket.marketIndex.toNumber(),
|
||||
};
|
||||
return addToBasket;
|
||||
};
|
||||
|
||||
export type Withdraw = {
|
||||
quantity: number;
|
||||
allowBorrow: String;
|
||||
};
|
||||
|
||||
export const decodeWithdraw = (ix: TransactionInstruction): Withdraw => {
|
||||
const decoded = MangoInstructionLayout.decode(ix.data);
|
||||
const withdraw: Withdraw = {
|
||||
quantity: decoded.Withdraw.quantity.toNumber(),
|
||||
allowBorrow: decoded.Withdraw.allowBorrow.toString(),
|
||||
};
|
||||
return withdraw;
|
||||
};
|
||||
|
||||
export type PlaceSpotOrder = {
|
||||
side: String;
|
||||
limitPrice: number;
|
||||
maxBaseQuantity: number;
|
||||
maxQuoteQuantity: number;
|
||||
selfTradeBehavior: String;
|
||||
orderType: String;
|
||||
clientId: String;
|
||||
limit: String;
|
||||
};
|
||||
|
||||
export const decodePlaceSpotOrder = (
|
||||
ix: TransactionInstruction
|
||||
): PlaceSpotOrder => {
|
||||
const decoded = MangoInstructionLayout.decode(ix.data);
|
||||
const placeSpotOrder: PlaceSpotOrder = {
|
||||
side: decoded.PlaceSpotOrder.side.toString(),
|
||||
limitPrice: decoded.PlaceSpotOrder.limitPrice.toNumber(),
|
||||
maxBaseQuantity: decoded.PlaceSpotOrder.maxBaseQuantity.toNumber(),
|
||||
maxQuoteQuantity: decoded.PlaceSpotOrder.maxQuoteQuantity.toNumber(),
|
||||
selfTradeBehavior: decoded.PlaceSpotOrder.selfTradeBehavior,
|
||||
orderType: decoded.PlaceSpotOrder.orderType.toString(),
|
||||
clientId: decoded.PlaceSpotOrder.clientId.toString(),
|
||||
limit: decoded.PlaceSpotOrder.limit.toString(),
|
||||
};
|
||||
|
||||
return placeSpotOrder;
|
||||
};
|
||||
|
||||
export type CancelSpotOrder = {
|
||||
orderId: String;
|
||||
side: String;
|
||||
};
|
||||
|
||||
export const decodeCancelSpotOrder = (
|
||||
ix: TransactionInstruction
|
||||
): CancelSpotOrder => {
|
||||
const decoded = MangoInstructionLayout.decode(ix.data);
|
||||
const cancelSpotOrder: CancelSpotOrder = {
|
||||
orderId: decoded.CancelSpotOrder.orderId.toString(),
|
||||
side: decoded.CancelSpotOrder.side.toString(),
|
||||
};
|
||||
return cancelSpotOrder;
|
||||
};
|
||||
|
||||
export type PlacePerpOrder = {
|
||||
price: number;
|
||||
quantity: number;
|
||||
clientOrderId: String;
|
||||
side: String;
|
||||
orderType: String;
|
||||
};
|
||||
export const decodePlacePerpOrder = (
|
||||
ix: TransactionInstruction
|
||||
): PlacePerpOrder => {
|
||||
const decoded = MangoInstructionLayout.decode(ix.data);
|
||||
const placePerpOrder: PlacePerpOrder = {
|
||||
price: decoded.PlacePerpOrder.price.toNumber(),
|
||||
quantity: decoded.PlacePerpOrder.quantity.toNumber(),
|
||||
clientOrderId: decoded.PlacePerpOrder.clientOrderId.toString(),
|
||||
side: decoded.PlacePerpOrder.side.toString(),
|
||||
orderType: decoded.PlacePerpOrder.orderType.toString(),
|
||||
};
|
||||
|
||||
return placePerpOrder;
|
||||
};
|
||||
|
||||
export type CancelPerpOrder = {
|
||||
orderId: String;
|
||||
invalidIdOk: String;
|
||||
};
|
||||
|
||||
export const decodeCancelPerpOrder = (
|
||||
ix: TransactionInstruction
|
||||
): CancelPerpOrder => {
|
||||
const decoded = MangoInstructionLayout.decode(ix.data);
|
||||
const cancelPerpOrder: CancelPerpOrder = {
|
||||
orderId: decoded.CancelPerpOrder.orderId.toString(),
|
||||
invalidIdOk: decoded.CancelPerpOrder.invalidIdOk.toString(),
|
||||
};
|
||||
return cancelPerpOrder;
|
||||
};
|
||||
|
||||
export type ChangePerpMarketParams = {
|
||||
maintLeverageOption: Boolean;
|
||||
maintLeverage: number;
|
||||
initLeverageOption: Boolean;
|
||||
initLeverage: number;
|
||||
liquidationFeeOption: Boolean;
|
||||
liquidationFee: number;
|
||||
makerFeeOption: Boolean;
|
||||
makerFee: number;
|
||||
takerFeeOption: Boolean;
|
||||
takerFee: number;
|
||||
rateOption: Boolean;
|
||||
rate: number;
|
||||
maxDepthBpsOption: Boolean;
|
||||
maxDepthBps: number;
|
||||
targetPeriodLengthOption: Boolean;
|
||||
targetPeriodLength: number;
|
||||
mngoPerPeriodOption: Boolean;
|
||||
mngoPerPeriod: number;
|
||||
};
|
||||
|
||||
export const decodeChangePerpMarketParams = (
|
||||
ix: TransactionInstruction
|
||||
): ChangePerpMarketParams => {
|
||||
const decoded = MangoInstructionLayout.decode(ix.data);
|
||||
const changePerpMarketParams: ChangePerpMarketParams = {
|
||||
maintLeverageOption: decoded.ChangePerpMarketParams.maintLeverageOption,
|
||||
maintLeverage: decoded.ChangePerpMarketParams.maintLeverage.toString(),
|
||||
initLeverageOption: decoded.ChangePerpMarketParams.initLeverageOption,
|
||||
initLeverage: decoded.ChangePerpMarketParams.initLeverage.toString(),
|
||||
liquidationFeeOption: decoded.ChangePerpMarketParams.liquidationFeeOption,
|
||||
liquidationFee: decoded.ChangePerpMarketParams.liquidationFee.toString(),
|
||||
makerFeeOption: decoded.ChangePerpMarketParams.makerFeeOption,
|
||||
makerFee: decoded.ChangePerpMarketParams.makerFee.toString(),
|
||||
takerFeeOption: decoded.ChangePerpMarketParams.takerFeeOption,
|
||||
takerFee: decoded.ChangePerpMarketParams.takerFee.toString(),
|
||||
rateOption: decoded.ChangePerpMarketParams.rateOption,
|
||||
rate: decoded.ChangePerpMarketParams.rate.toString(),
|
||||
maxDepthBpsOption: decoded.ChangePerpMarketParams.maxDepthBpsOption,
|
||||
maxDepthBps: decoded.ChangePerpMarketParams.maxDepthBps.toString(),
|
||||
targetPeriodLengthOption:
|
||||
decoded.ChangePerpMarketParams.targetPeriodLengthOption,
|
||||
targetPeriodLength:
|
||||
decoded.ChangePerpMarketParams.targetPeriodLength.toString(),
|
||||
mngoPerPeriodOption: decoded.ChangePerpMarketParams.mngoPerPeriodOption,
|
||||
mngoPerPeriod: decoded.ChangePerpMarketParams.mngoPerPeriod.toString(),
|
||||
};
|
||||
return changePerpMarketParams;
|
||||
};
|
||||
|
||||
export type AddSpotMarket = {
|
||||
marketIndex: number;
|
||||
maintLeverage: number;
|
||||
initLeverage: number;
|
||||
liquidationFee: number;
|
||||
optimalUtil: number;
|
||||
optimalRate: number;
|
||||
maxRate: number;
|
||||
};
|
||||
|
||||
export const decodeAddSpotMarket = (
|
||||
ix: TransactionInstruction
|
||||
): AddSpotMarket => {
|
||||
const decoded = MangoInstructionLayout.decode(ix.data);
|
||||
const addSpotMarket: AddSpotMarket = {
|
||||
marketIndex: decoded.AddSpotMarket.marketIndex.toNumber(),
|
||||
maintLeverage: decoded.AddSpotMarket.maintLeverage.toNumber(),
|
||||
initLeverage: decoded.AddSpotMarket.initLeverage.toNumber(),
|
||||
liquidationFee: decoded.AddSpotMarket.liquidationFee.toNumber(),
|
||||
optimalUtil: decoded.AddSpotMarket.optimalUtil.toNumber(),
|
||||
optimalRate: decoded.AddSpotMarket.optimalRate.toNumber(),
|
||||
maxRate: decoded.AddSpotMarket.maxRate.toNumber(),
|
||||
};
|
||||
return addSpotMarket;
|
||||
};
|
||||
|
||||
export type AddPerpMarket = {
|
||||
marketIndex: number;
|
||||
maintLeverage: number;
|
||||
initLeverage: number;
|
||||
liquidationFee: number;
|
||||
makerFee: number;
|
||||
takerFee: number;
|
||||
baseLotSize: number;
|
||||
quoteLotSize: number;
|
||||
rate: number;
|
||||
maxDepthBps: number;
|
||||
targetPeriodLength: number;
|
||||
mngoPerPeriod: number;
|
||||
};
|
||||
|
||||
export const decodeAddPerpMarket = (
|
||||
ix: TransactionInstruction
|
||||
): AddPerpMarket => {
|
||||
const decoded = MangoInstructionLayout.decode(ix.data);
|
||||
const addPerpMarket: AddPerpMarket = {
|
||||
marketIndex: decoded.AddPerpMarket.marketIndex.toNumber(),
|
||||
maintLeverage: decoded.AddPerpMarket.maintLeverage.toNumber(),
|
||||
initLeverage: decoded.AddPerpMarket.initLeverage.toNumber(),
|
||||
liquidationFee: decoded.AddPerpMarket.liquidationFee.toNumber(),
|
||||
makerFee: decoded.AddPerpMarket.makerFee.toNumber(),
|
||||
takerFee: decoded.AddPerpMarket.takerFee.toNumber(),
|
||||
baseLotSize: decoded.AddPerpMarket.baseLotSize.toNumber(),
|
||||
quoteLotSize: decoded.AddPerpMarket.quoteLotSize.toNumber(),
|
||||
rate: decoded.AddPerpMarket.rate.toNumber(),
|
||||
maxDepthBps: decoded.AddPerpMarket.maxDepthBps.toNumber(),
|
||||
targetPeriodLength: decoded.AddPerpMarket.targetPeriodLength.toNumber(),
|
||||
mngoPerPeriod: decoded.AddPerpMarket.mngoPerPeriod.toNumber(),
|
||||
};
|
||||
return addPerpMarket;
|
||||
};
|
||||
|
||||
export type OrderLotDetails = {
|
||||
price: number;
|
||||
size: number;
|
||||
};
|
||||
|
||||
////
|
||||
|
||||
export function logAllKeys(keys: AccountMeta[]) {
|
||||
keys.map((key) => console.log(key.pubkey.toBase58()));
|
||||
}
|
||||
|
||||
export function getSpotMarketFromInstruction(
|
||||
ix: TransactionInstruction,
|
||||
spotMarket: AccountMeta
|
||||
): SpotMarketConfig | undefined {
|
||||
const groupConfig = findGroupConfig(ix.programId);
|
||||
if (groupConfig === undefined) {
|
||||
return;
|
||||
}
|
||||
const spotMarketConfigs = groupConfig.spotMarkets.filter((mangoSpotMarket) =>
|
||||
spotMarket.pubkey.equals(mangoSpotMarket.publicKey)
|
||||
);
|
||||
if (spotMarketConfigs.length) {
|
||||
return spotMarketConfigs[0];
|
||||
}
|
||||
}
|
||||
|
||||
export async function getSpotMarketFromSpotMarketConfig(
|
||||
programId: PublicKey,
|
||||
clusterUrl: string,
|
||||
mangoSpotMarketConfig: SpotMarketConfig
|
||||
): Promise<Market | undefined> {
|
||||
const connection = new Connection(clusterUrl);
|
||||
const groupConfig = findGroupConfig(programId);
|
||||
if (groupConfig === undefined) {
|
||||
return;
|
||||
}
|
||||
return await Market.load(
|
||||
connection,
|
||||
mangoSpotMarketConfig.publicKey,
|
||||
undefined,
|
||||
groupConfig.serumProgramId
|
||||
);
|
||||
}
|
||||
|
||||
export function getPerpMarketFromInstruction(
|
||||
ix: TransactionInstruction,
|
||||
perpMarket: AccountMeta
|
||||
): PerpMarketConfig | undefined {
|
||||
const groupConfig = findGroupConfig(ix.programId);
|
||||
if (groupConfig === undefined) {
|
||||
return;
|
||||
}
|
||||
const perpMarketConfigs = groupConfig.perpMarkets.filter((mangoPerpMarket) =>
|
||||
perpMarket.pubkey.equals(mangoPerpMarket.publicKey)
|
||||
);
|
||||
if (perpMarketConfigs.length) {
|
||||
return perpMarketConfigs[0];
|
||||
}
|
||||
}
|
||||
|
||||
export async function getPerpMarketFromPerpMarketConfig(
|
||||
clusterUrl: string,
|
||||
mangoPerpMarketConfig: PerpMarketConfig
|
||||
): Promise<PerpMarket> {
|
||||
const acc = await getAccountInfo(clusterUrl, mangoPerpMarketConfig.publicKey);
|
||||
const decoded = PerpMarketLayout.decode(acc?.data);
|
||||
|
||||
return new PerpMarket(
|
||||
mangoPerpMarketConfig.publicKey,
|
||||
mangoPerpMarketConfig.baseDecimals,
|
||||
mangoPerpMarketConfig.quoteDecimals,
|
||||
decoded
|
||||
);
|
||||
}
|
||||
|
||||
export function spotMarketFromIndex(
|
||||
ix: TransactionInstruction,
|
||||
marketIndex: number
|
||||
): String | undefined {
|
||||
const groupConfig = findGroupConfig(ix.programId);
|
||||
if (groupConfig === undefined) {
|
||||
return;
|
||||
}
|
||||
const spotMarketConfigs = groupConfig.spotMarkets.filter(
|
||||
(spotMarketConfig) => spotMarketConfig.marketIndex === marketIndex
|
||||
);
|
||||
if (!spotMarketConfigs.length) {
|
||||
return "UNKNOWN";
|
||||
}
|
||||
return spotMarketConfigs[0].name;
|
||||
}
|
@@ -39,6 +39,8 @@ import { BpfUpgradeableLoaderDetailsCard } from "components/instruction/bpf-upgr
|
||||
import { VoteDetailsCard } from "components/instruction/vote/VoteDetailsCard";
|
||||
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";
|
||||
|
||||
export type InstructionDetailsProps = {
|
||||
tx: ParsedTransaction;
|
||||
@@ -208,6 +210,8 @@ function renderInstructionCard({
|
||||
|
||||
if (isBonfidaBotInstruction(transactionIx)) {
|
||||
return <BonfidaBotDetailsCard key={key} {...props} />;
|
||||
} else if (isMangoInstruction(transactionIx)) {
|
||||
return <MangoDetailsCard key={key} {...props} />;
|
||||
} else if (isSerumInstruction(transactionIx)) {
|
||||
return <SerumDetailsCard key={key} {...props} />;
|
||||
} else if (isTokenSwapInstruction(transactionIx)) {
|
||||
|
@@ -50,6 +50,7 @@ export enum PROGRAM_NAMES {
|
||||
SERUM_1 = "Serum Program v1",
|
||||
SERUM_2 = "Serum Program v2",
|
||||
SERUM_3 = "Serum Program v3",
|
||||
MANGO_3 = "Mango Program v3",
|
||||
}
|
||||
|
||||
const ALL_CLUSTERS = [
|
||||
@@ -90,6 +91,7 @@ export const PROGRAM_DEPLOYMENTS = {
|
||||
[PROGRAM_NAMES.SERUM_1]: MAINNET_ONLY,
|
||||
[PROGRAM_NAMES.SERUM_2]: MAINNET_ONLY,
|
||||
[PROGRAM_NAMES.SERUM_3]: MAINNET_ONLY,
|
||||
[PROGRAM_NAMES.MANGO_3]: MAINNET_ONLY,
|
||||
} as const;
|
||||
|
||||
export const PROGRAM_NAME_BY_ID = {
|
||||
@@ -121,6 +123,7 @@ export const PROGRAM_NAME_BY_ID = {
|
||||
BJ3jrUzddfuSrZHXSCxMUUQsjKEyLmuuyZebkcaFp2fg: PROGRAM_NAMES.SERUM_1,
|
||||
EUqojwWA2rd19FZrzeBncJsm38Jm1hEhE3zsmX3bRc2o: PROGRAM_NAMES.SERUM_2,
|
||||
"9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin": PROGRAM_NAMES.SERUM_3,
|
||||
mv3ekLzLbnVPNxjSKvqBpU3ZeZXPQdEC3bp5MDEBG68: PROGRAM_NAMES.MANGO_3,
|
||||
} as const;
|
||||
|
||||
export type LoaderName = typeof LOADER_IDS[keyof typeof LOADER_IDS];
|
||||
|
Reference in New Issue
Block a user