Skip to content

Commit

Permalink
[wip]: Add flexible PDA to IDL and link to both accounts
Browse files Browse the repository at this point in the history
  • Loading branch information
lorisleiva committed Jan 21, 2025
1 parent 4d2d12d commit 0df9ca0
Show file tree
Hide file tree
Showing 7 changed files with 137 additions and 23 deletions.
19 changes: 7 additions & 12 deletions clients/js/src/fetchMetadataWithContent.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,18 @@
import { Account, Address, GetAccountInfoApi, Rpc } from '@solana/web3.js';
import {
fetchMetadata,
findCanonicalPda,
findNonCanonicalPda,
Metadata,
SeedArgs,
} from './generated';
import { fetchMetadataFromSeeds, Metadata, SeedArgs } from './generated';
import { unpackAndFetchData } from './packData';

export async function fetchMetadataWithContent(
rpc: Rpc<GetAccountInfoApi>,
program: Address,
seed: SeedArgs,
authority?: Address
authority: Address | null = null
): Promise<Account<Metadata> & { content: string }> {
const [metadata] = authority
? await findNonCanonicalPda({ program, authority, seed })
: await findCanonicalPda({ program, seed });
const account = await fetchMetadata(rpc, metadata);
const account = await fetchMetadataFromSeeds(rpc, {
program,
authority,
seed,
});
const content = await unpackAndFetchData({ rpc, ...account.data });
return Object.freeze({ ...account, content });
}
21 changes: 21 additions & 0 deletions clients/js/src/generated/accounts/buffer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import {
type OptionOrNullable,
type ReadonlyUint8Array,
} from '@solana/web3.js';
import { MetadataSeeds, findMetadataPda } from '../pdas';
import {
AccountDiscriminator,
getAccountDiscriminatorDecoder,
Expand Down Expand Up @@ -172,3 +173,23 @@ export async function fetchAllMaybeBuffer(
const maybeAccounts = await fetchEncodedAccounts(rpc, addresses, config);
return maybeAccounts.map((maybeAccount) => decodeBuffer(maybeAccount));
}

export async function fetchBufferFromSeeds(
rpc: Parameters<typeof fetchEncodedAccount>[0],
seeds: MetadataSeeds,
config: FetchAccountConfig & { programAddress?: Address } = {}
): Promise<Account<Buffer>> {
const maybeAccount = await fetchMaybeBufferFromSeeds(rpc, seeds, config);
assertAccountExists(maybeAccount);
return maybeAccount;
}

export async function fetchMaybeBufferFromSeeds(
rpc: Parameters<typeof fetchEncodedAccount>[0],
seeds: MetadataSeeds,
config: FetchAccountConfig & { programAddress?: Address } = {}
): Promise<MaybeAccount<Buffer>> {
const { programAddress, ...fetchConfig } = config;
const [address] = await findMetadataPda(seeds, { programAddress });
return await fetchMaybeBuffer(rpc, address, fetchConfig);
}
21 changes: 21 additions & 0 deletions clients/js/src/generated/accounts/metadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import {
type OptionOrNullable,
type ReadonlyUint8Array,
} from '@solana/web3.js';
import { MetadataSeeds, findMetadataPda } from '../pdas';
import {
AccountDiscriminator,
getAccountDiscriminatorDecoder,
Expand Down Expand Up @@ -202,3 +203,23 @@ export async function fetchAllMaybeMetadata(
const maybeAccounts = await fetchEncodedAccounts(rpc, addresses, config);
return maybeAccounts.map((maybeAccount) => decodeMetadata(maybeAccount));
}

export async function fetchMetadataFromSeeds(
rpc: Parameters<typeof fetchEncodedAccount>[0],
seeds: MetadataSeeds,
config: FetchAccountConfig & { programAddress?: Address } = {}
): Promise<Account<Metadata>> {
const maybeAccount = await fetchMaybeMetadataFromSeeds(rpc, seeds, config);
assertAccountExists(maybeAccount);
return maybeAccount;
}

export async function fetchMaybeMetadataFromSeeds(
rpc: Parameters<typeof fetchEncodedAccount>[0],
seeds: MetadataSeeds,
config: FetchAccountConfig & { programAddress?: Address } = {}
): Promise<MaybeAccount<Metadata>> {
const { programAddress, ...fetchConfig } = config;
const [address] = await findMetadataPda(seeds, { programAddress });
return await fetchMaybeMetadata(rpc, address, fetchConfig);
}
1 change: 1 addition & 0 deletions clients/js/src/generated/pdas/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@
*/

export * from './canonical';
export * from './metadata';
export * from './nonCanonical';
45 changes: 45 additions & 0 deletions clients/js/src/generated/pdas/metadata.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/**
* This code was AUTOGENERATED using the codama library.
* Please DO NOT EDIT THIS FILE, instead use visitors
* to add features, then rerun codama to update it.
*
* @see https://github.com/codama-idl/codama
*/

import {
getAddressEncoder,
getOptionEncoder,
getProgramDerivedAddress,
type Address,
type OptionOrNullable,
type ProgramDerivedAddress,
} from '@solana/web3.js';
import { getSeedEncoder, type SeedArgs } from '../types';

export type MetadataSeeds = {
/** The program to which the metadata belongs. */
program: Address;
/** The third-party authority managing this metadata account, if non-canonical. */
authority: OptionOrNullable<Address>;
/** The seed deriving the metadata account. */
seed: SeedArgs;
};

export async function findMetadataPda(
seeds: MetadataSeeds,
config: { programAddress?: Address | undefined } = {}
): Promise<ProgramDerivedAddress> {
const {
programAddress = '4FX3oHhpAkJcb2tFFrq9JBY8gc4RhCRM5g75VG9QHnj1' as Address<'4FX3oHhpAkJcb2tFFrq9JBY8gc4RhCRM5g75VG9QHnj1'>,
} = config;
return await getProgramDerivedAddress({
programAddress,
seeds: [
getAddressEncoder().encode(seeds.program),
getOptionEncoder(getAddressEncoder(), { prefix: null }).encode(
seeds.authority
),
getSeedEncoder().encode(seeds.seed),
],
});
}
15 changes: 6 additions & 9 deletions clients/js/src/internals.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import {
TransactionMessageWithBlockhashLifetime,
TransactionSigner,
} from '@solana/web3.js';
import { findCanonicalPda, findNonCanonicalPda, SeedArgs } from './generated';
import { findMetadataPda, SeedArgs } from './generated';
import { getProgramAuthority } from './utils';

export type PdaDetails = {
Expand All @@ -46,14 +46,11 @@ export async function getPdaDetails(input: {
input.program
);
const isCanonical = !!authority && authority === authorityAddress;
const [metadata] = isCanonical
? await findCanonicalPda({ program: input.program, seed: input.seed })
: await findNonCanonicalPda({
program: input.program,
authority: authorityAddress,
seed: input.seed,
});

const [metadata] = await findMetadataPda({
program: input.program,
authority: isCanonical ? null : authorityAddress,
seed: input.seed,
});
return { metadata, isCanonical, programData };
}

Expand Down
38 changes: 36 additions & 2 deletions program/idl.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,38 @@
"type": { "kind": "definedTypeLinkNode", "name": "seed" }
}
]
},
{
"kind": "pdaNode",
"name": "metadata",
"docs": [
"The derivation for metadata accounts, canonical or not, depending if an authority is provided."
],
"seeds": [
{
"kind": "variablePdaSeedNode",
"name": "program",
"docs": ["The program to which the metadata belongs."],
"type": { "kind": "publicKeyTypeNode" }
},
{
"kind": "variablePdaSeedNode",
"name": "authority",
"docs": [
"The third-party authority managing this metadata account, if non-canonical."
],
"type": {
"kind": "remainderOptionTypeNode",
"item": { "kind": "publicKeyTypeNode" }
}
},
{
"kind": "variablePdaSeedNode",
"name": "seed",
"docs": ["The seed deriving the metadata account."],
"type": { "kind": "definedTypeLinkNode", "name": "seed" }
}
]
}
],
"accounts": [
Expand Down Expand Up @@ -134,7 +166,8 @@
"type": { "kind": "bytesTypeNode" }
}
]
}
},
"pda": { "kind": "pdaLinkNode", "name": "metadata" }
},
{
"kind": "accountNode",
Expand Down Expand Up @@ -254,7 +287,8 @@
"type": { "kind": "bytesTypeNode" }
}
]
}
},
"pda": { "kind": "pdaLinkNode", "name": "metadata" }
}
],
"instructions": [
Expand Down

0 comments on commit 0df9ca0

Please sign in to comment.