Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor anchor program IDL page to load, even if it cannot be validated
Browse files Browse the repository at this point in the history
ngundotra committed Jun 17, 2024
1 parent c3f5bbd commit 48f3a9b
Showing 6 changed files with 38 additions and 27 deletions.
10 changes: 5 additions & 5 deletions app/address/[address]/layout.tsx
Original file line number Diff line number Diff line change
@@ -630,7 +630,7 @@ function getAnchorTabs(pubkey: PublicKey, account: Account) {
tabComponents.push({
component: (
<React.Suspense key={anchorProgramTab.slug} fallback={<></>}>
<AnchorProgramLink tab={anchorProgramTab} address={pubkey.toString()} pubkey={pubkey} />
<AnchorProgramIdlLink tab={anchorProgramTab} address={pubkey.toString()} pubkey={pubkey} />
</React.Suspense>
),
tab: anchorProgramTab,
@@ -653,13 +653,13 @@ function getAnchorTabs(pubkey: PublicKey, account: Account) {
return tabComponents;
}

function AnchorProgramLink({ tab, address, pubkey }: { tab: Tab; address: string; pubkey: PublicKey }) {
function AnchorProgramIdlLink({ tab, address, pubkey }: { tab: Tab; address: string; pubkey: PublicKey }) {
const { url } = useCluster();
const anchorProgram = useAnchorProgram(pubkey.toString(), url);
const { idl } = useAnchorProgram(pubkey.toString(), url);
const anchorProgramPath = useClusterPath({ pathname: `/address/${address}/${tab.path}` });
const selectedLayoutSegment = useSelectedLayoutSegment();
const isActive = selectedLayoutSegment === tab.path;
if (!anchorProgram) {
if (!idl) {
return null;
}

@@ -674,7 +674,7 @@ function AnchorProgramLink({ tab, address, pubkey }: { tab: Tab; address: string

function AccountDataLink({ address, tab, programId }: { address: string; tab: Tab; programId: PublicKey }) {
const { url } = useCluster();
const accountAnchorProgram = useAnchorProgram(programId.toString(), url);
const { program: accountAnchorProgram } = useAnchorProgram(programId.toString(), url);
const accountDataPath = useClusterPath({ pathname: `/address/${address}/${tab.path}` });
const selectedLayoutSegment = useSelectedLayoutSegment();
const isActive = selectedLayoutSegment === tab.path;
2 changes: 1 addition & 1 deletion app/components/account/AnchorAccountCard.tsx
Original file line number Diff line number Diff line change
@@ -10,7 +10,7 @@ import React, { useMemo } from 'react';
export function AnchorAccountCard({ account }: { account: Account }) {
const { lamports } = account;
const { url } = useCluster();
const anchorProgram = useAnchorProgram(account.owner.toString(), url);
const { program: anchorProgram } = useAnchorProgram(account.owner.toString(), url);
const rawData = account.data.raw;
const programName = getAnchorProgramName(anchorProgram) || 'Unknown Program';

6 changes: 3 additions & 3 deletions app/components/account/AnchorProgramCard.tsx
Original file line number Diff line number Diff line change
@@ -6,9 +6,9 @@ import ReactJson from 'react-json-view';

export function AnchorProgramCard({ programId }: { programId: string }) {
const { url } = useCluster();
const program = useAnchorProgram(programId, url);
const { idl } = useAnchorProgram(programId, url);

if (!program) {
if (!idl) {
return null;
}

@@ -24,7 +24,7 @@ export function AnchorProgramCard({ programId }: { programId: string }) {
</div>

<div className="card metadata-json-viewer m-4">
<ReactJson src={program.idl} theme={'solarized'} style={{ padding: 25 }} collapsed={1} />
<ReactJson src={idl} theme={'solarized'} style={{ padding: 25 }} collapsed={1} name={null} />
</div>
</div>
</>
2 changes: 1 addition & 1 deletion app/components/transaction/InstructionsSection.tsx
Original file line number Diff line number Diff line change
@@ -162,7 +162,7 @@ function InstructionCard({
url: string;
}) {
const key = `${index}-${childIndex}`;
const anchorProgram = useAnchorProgram(ix.programId.toString(), url);
const { program: anchorProgram } = useAnchorProgram(ix.programId.toString(), url);

if ('parsed' in ix) {
const props = {
43 changes: 27 additions & 16 deletions app/providers/anchor.tsx
Original file line number Diff line number Diff line change
@@ -6,15 +6,13 @@ import pako from 'pako';
import { useEffect, useMemo } from 'react';

import { useAccountInfo, useFetchAccountInfo } from './accounts';
import { useCluster } from './cluster';

const cachedAnchorProgramPromises: Record<
string,
void | { __type: 'promise'; promise: Promise<void> } | { __type: 'result'; result: Program<Idl> | null }
void | { __type: 'promise'; promise: Promise<void> } | { __type: 'result'; result: Idl | null }
> = {};

function useProgramElf(programAddress: string) {
const { url } = useCluster();
function useIdlFromSolanaProgramBinary(programAddress: string): Idl | null {
const fetchAccountInfo = useFetchAccountInfo();
const programInfo = useAccountInfo(programAddress);
const programDataAddress: string | undefined = programInfo?.data?.data.parsed?.parsed.info['programData'];
@@ -39,14 +37,13 @@ function useProgramElf(programAddress: string) {
const raw = Buffer.from(programDataInfo.data.data.raw.slice(offset));

try {
const idl = parseIdlFromElf(raw);
return new Program(idl, programAddress, getProvider(url));
} catch (_e) {
return parseIdlFromElf(raw);
} catch (e) {
return null;
}
}
return null;
}, [programDataInfo, programInfo, programAddress, url]);
}, [programDataInfo, programInfo]);
return param;
}

@@ -82,16 +79,21 @@ function getProvider(url: string) {
return new Provider(new Connection(url), new NodeWallet(Keypair.generate()), {});
}

function useAnchorIdlAccount(programAddress: string, url: string): Program | null {
function useIdlFromAnchorProgramSeed(programAddress: string, url: string): Idl | null {
const key = `${programAddress}-${url}`;
const cacheEntry = cachedAnchorProgramPromises[key];

if (cacheEntry === undefined) {
const promise = Program.at(programAddress, getProvider(url))
.then(program => {
const programId = new PublicKey(programAddress);
const promise = Program.fetchIdl<Idl>(programId, getProvider(url))
.then(idl => {
if (!idl) {
throw new Error(`IDL not found for program: ${programAddress.toString()}`);
}

cachedAnchorProgramPromises[key] = {
__type: 'result',
result: program,
result: idl,
};
})
.catch(_ => {
@@ -108,10 +110,19 @@ function useAnchorIdlAccount(programAddress: string, url: string): Program | nul
return cacheEntry.result;
}

export function useAnchorProgram(programAddress: string, url: string): Program | null {
const idlFromBinary = useProgramElf(programAddress);
const idlFromAccount = useAnchorIdlAccount(programAddress, url);
return idlFromBinary ?? idlFromAccount;
export function useAnchorProgram(programAddress: string, url: string): { program: Program | null; idl: Idl | null } {
const idlFromBinary = useIdlFromSolanaProgramBinary(programAddress);
const idlFromAnchorProgram = useIdlFromAnchorProgramSeed(programAddress, url);
const idl = idlFromBinary ?? idlFromAnchorProgram;
const program: Program<Idl> | null = useMemo(() => {
if (!idl) return null;
try {
return new Program(idl, new PublicKey(programAddress), getProvider(url));
} catch (e) {
return null;
}
}, [idl, programAddress, url]);
return { idl, program };
}

export type AnchorAccount = {
2 changes: 1 addition & 1 deletion app/utils/anchor.tsx
Original file line number Diff line number Diff line change
@@ -23,7 +23,7 @@ export function AnchorProgramName({
url: string;
defaultName?: string;
}) {
const program = useAnchorProgram(programId.toString(), url);
const { program } = useAnchorProgram(programId.toString(), url);
const programName = getAnchorProgramName(program) || defaultName;
return <>{programName}</>;
}

0 comments on commit 48f3a9b

Please sign in to comment.