Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for showing self-cpi #364

Merged
merged 1 commit into from
Aug 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 26 additions & 8 deletions app/components/instruction/AnchorDetailsCard.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { Address } from '@components/common/Address';
import { BorshInstructionCoder, Idl, Instruction, Program } from '@project-serum/anchor';
import { IdlInstruction } from '@project-serum/anchor/dist/cjs/idl';
import { BorshEventCoder, BorshInstructionCoder, Idl, Instruction, Program } from '@project-serum/anchor';
import { IdlEvent, IdlInstruction } from '@project-serum/anchor/dist/cjs/idl';
import { SignatureResult, TransactionInstruction } from '@solana/web3.js';
import {
getAnchorAccountsFromInstruction,
getAnchorNameForInstruction,
getAnchorProgramName,
instructionIsSelfCPI,
mapIxArgsToRows,
} from '@utils/anchor';
import { camelToTitleCase } from '@utils/index';
Expand Down Expand Up @@ -48,12 +49,29 @@ function AnchorDetails({ ix, anchorProgram }: { ix: TransactionInstruction; anch
let decodedIxData: Instruction | null = null;
let ixDef: IdlInstruction | undefined;
if (anchorProgram) {
const coder = new BorshInstructionCoder(anchorProgram.idl);
decodedIxData = coder.decode(ix.data);
if (decodedIxData) {
ixDef = anchorProgram.idl.instructions.find(ixDef => ixDef.name === decodedIxData?.name);
if (ixDef) {
ixAccounts = getAnchorAccountsFromInstruction(decodedIxData, anchorProgram);
let coder: BorshInstructionCoder | BorshEventCoder;
if (instructionIsSelfCPI(ix.data)) {
coder = new BorshEventCoder(anchorProgram.idl);
decodedIxData = coder.decode(ix.data.slice(8).toString('base64'));
const ixEventDef = anchorProgram.idl.events?.find(
ixDef => ixDef.name === decodedIxData?.name
) as IdlEvent;

// Remap the event definition to an instruction definition
ixDef = { ...ixEventDef, accounts: [], args: ixEventDef.fields };

// Self-CPI instructions have 1 account called the eventAuthority
// https://github.com/coral-xyz/anchor/blob/04985802587c693091f836e0083e4412148c0ca6/lang/attribute/event/src/lib.rs#L165
ixAccounts = [{ isMut: false, isSigner: true, name: 'eventAuthority' }];
} else {
coder = new BorshInstructionCoder(anchorProgram.idl);
decodedIxData = coder.decode(ix.data);

if (decodedIxData) {
ixDef = anchorProgram.idl.instructions.find(ixDef => ixDef.name === decodedIxData?.name);
if (ixDef) {
ixAccounts = getAnchorAccountsFromInstruction(decodedIxData, anchorProgram);
}
}
}
}
Expand Down
11 changes: 11 additions & 0 deletions app/utils/anchor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,13 @@ import React, { Fragment, ReactNode, useState } from 'react';
import { ChevronDown, ChevronUp, CornerDownRight } from 'react-feather';
import ReactJson from 'react-json-view';

const ANCHOR_SELF_CPI_TAG = Buffer.from('1d9acb512ea545e4', 'hex').reverse();
const ANCHOR_SELF_CPI_NAME = 'Anchor Self Invocation';

export function instructionIsSelfCPI(ixData: Buffer): boolean {
return ixData.slice(0, 8).equals(ANCHOR_SELF_CPI_TAG);
}

export function getAnchorProgramName(program: Program | null): string | undefined {
return program && 'name' in program.idl ? snakeToTitleCase(program.idl.name) : undefined;
}
Expand Down Expand Up @@ -38,6 +45,10 @@ export function ProgramName({ programId, cluster, url }: { programId: PublicKey;
}

export function getAnchorNameForInstruction(ix: TransactionInstruction, program: Program): string | null {
if (instructionIsSelfCPI(ix.data)) {
return ANCHOR_SELF_CPI_NAME;
}

const coder = new BorshInstructionCoder(program.idl);
const decodedIx = coder.decode(ix.data);

Expand Down
Loading