Skip to content

Commit

Permalink
[experimental] Use account name directly for data types and codecs (#212
Browse files Browse the repository at this point in the history
)
  • Loading branch information
lorisleiva authored Apr 15, 2024
1 parent 665ed3b commit 8cb70c8
Show file tree
Hide file tree
Showing 23 changed files with 280 additions and 421 deletions.
5 changes: 5 additions & 0 deletions .changeset/witty-dryers-beg.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@metaplex-foundation/kinobi": minor
---

Use account name directly for data types and codecs
14 changes: 7 additions & 7 deletions src/renderers/js-experimental/fragments/accountFetchHelpers.njk
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
export function {{ decodeFunction }}<TAddress extends string = string>(encodedAccount: EncodedAccount<TAddress>): {{ accountType }}<TAddress>;
export function {{ decodeFunction }}<TAddress extends string = string>(encodedAccount: MaybeEncodedAccount<TAddress>): {{ accountMaybeType }}<TAddress>;
export function {{ decodeFunction }}<TAddress extends string = string>(encodedAccount: EncodedAccount<TAddress> | MaybeEncodedAccount<TAddress>): {{ accountType }}<TAddress> | {{ accountMaybeType }}<TAddress> {
export function {{ decodeFunction }}<TAddress extends string = string>(encodedAccount: EncodedAccount<TAddress>): Account<{{ accountType }}, TAddress>;
export function {{ decodeFunction }}<TAddress extends string = string>(encodedAccount: MaybeEncodedAccount<TAddress>): MaybeAccount<{{ accountType }}, TAddress>;
export function {{ decodeFunction }}<TAddress extends string = string>(encodedAccount: EncodedAccount<TAddress> | MaybeEncodedAccount<TAddress>): Account<{{ accountType }}, TAddress> | MaybeAccount<{{ accountType }}, TAddress> {
return decodeAccount(encodedAccount as MaybeEncodedAccount<TAddress>, {{ decoderFunction }});
}

export async function {{ fetchFunction }}<TAddress extends string = string>(
rpc: Parameters<typeof fetchEncodedAccount>[0],
address: Address<TAddress>,
config?: FetchAccountConfig,
): Promise<{{ accountType }}<TAddress>> {
): Promise<Account<{{ accountType }}, TAddress>> {
const maybeAccount = await {{ fetchMaybeFunction }}(rpc, address, config);
assertAccountExists(maybeAccount);
return maybeAccount;
Expand All @@ -18,7 +18,7 @@ export async function {{ fetchMaybeFunction }}<TAddress extends string = string>
rpc: Parameters<typeof fetchEncodedAccount>[0],
address: Address<TAddress>,
config?: FetchAccountConfig,
): Promise<{{ accountMaybeType }}<TAddress>> {
): Promise<MaybeAccount<{{ accountType }}, TAddress>> {
const maybeAccount = await fetchEncodedAccount(rpc, address, config);
return {{ decodeFunction }}(maybeAccount);
}
Expand All @@ -27,7 +27,7 @@ export async function {{ fetchAllFunction }}(
rpc: Parameters<typeof fetchEncodedAccounts>[0],
addresses: Array<Address>,
config?: FetchAccountsConfig,
): Promise<{{ accountType }}[]> {
): Promise<Account<{{ accountType }}>[]> {
const maybeAccounts = await {{ fetchAllMaybeFunction }}(rpc, addresses, config);
assertAccountsExist(maybeAccounts);
return maybeAccounts;
Expand All @@ -37,7 +37,7 @@ export async function {{ fetchAllMaybeFunction }}(
rpc: Parameters<typeof fetchEncodedAccounts>[0],
addresses: Array<Address>,
config?: FetchAccountsConfig,
): Promise<{{ accountMaybeType }}[]> {
): Promise<MaybeAccount<{{ accountType }}>[]> {
const maybeAccounts = await fetchEncodedAccounts(rpc, addresses, config);
return maybeAccounts.map((maybeAccount) => {{ decodeFunction }}(maybeAccount));
}
16 changes: 10 additions & 6 deletions src/renderers/js-experimental/fragments/accountFetchHelpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,17 @@ export function getAccountFetchHelpersFragment(
}
): Fragment {
const { accountNode, typeManifest, nameApi, customAccountData } = scope;
const accountDataName = nameApi.accountDataType(accountNode.name);
const decoderFunctionFragment = customAccountData.has(accountNode.name)
const hasCustomData = customAccountData.has(accountNode.name);
const accountTypeFragment = hasCustomData
? typeManifest.strictType.clone()
: fragment(nameApi.dataType(accountNode.name));
const decoderFunctionFragment = hasCustomData
? typeManifest.decoder.clone()
: fragment(`${nameApi.decoderFunction(accountDataName)}()`);
: fragment(`${nameApi.decoderFunction(accountNode.name)}()`);

return fragmentFromTemplate('accountFetchHelpers.njk', {
decoderFunction: decoderFunctionFragment.render,
accountType: nameApi.accountType(accountNode.name),
accountMaybeType: nameApi.accountMaybeType(accountNode.name),
accountType: accountTypeFragment.render,
decodeFunction: nameApi.accountDecodeFunction(accountNode.name),
fetchFunction: nameApi.accountFetchFunction(accountNode.name),
fetchMaybeFunction: nameApi.accountFetchMaybeFunction(accountNode.name),
Expand All @@ -27,9 +29,10 @@ export function getAccountFetchHelpersFragment(
accountNode.name
),
})
.mergeImportsWith(decoderFunctionFragment)
.mergeImportsWith(accountTypeFragment, decoderFunctionFragment)
.addImports('solanaAddresses', ['Address'])
.addImports('solanaAccounts', [
'Account',
'assertAccountExists',
'assertAccountsExist',
'decodeAccount',
Expand All @@ -38,6 +41,7 @@ export function getAccountFetchHelpersFragment(
'fetchEncodedAccounts',
'FetchAccountConfig',
'FetchAccountsConfig',
'MaybeAccount',
'MaybeEncodedAccount',
]);
}
4 changes: 2 additions & 2 deletions src/renderers/js-experimental/fragments/accountPdaHelpers.njk
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export async function {{ fetchFromSeedsFunction }}(
seeds: {{ pdaSeedsType }},
{% endif %}
config: FetchAccountConfig & { programAddress?: Address } = {},
): Promise<{{ accountType }}> {
): Promise<Account<{{ accountType }}>> {
const maybeAccount = await {{ fetchMaybeFromSeedsFunction }}(rpc, {% if hasVariableSeeds %}seeds, {% endif %}config);
assertAccountExists(maybeAccount);
return maybeAccount;
Expand All @@ -18,7 +18,7 @@ export async function {{ fetchMaybeFromSeedsFunction }}(
seeds: {{ pdaSeedsType }},
{% endif %}
config: FetchAccountConfig & { programAddress?: Address } = {},
): Promise<{{ accountMaybeType }}> {
): Promise<MaybeAccount<{{ accountType }}>> {
const { programAddress, ...fetchConfig } = config;
const [address] = await {{ findPdaFunction }}({% if hasVariableSeeds %}seeds, {% endif %}{ programAddress });
return await {{ fetchMaybeFunction }}(rpc, address, fetchConfig);
Expand Down
26 changes: 22 additions & 4 deletions src/renderers/js-experimental/fragments/accountPdaHelpers.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,43 @@
import { AccountNode, ProgramNode, isNodeFilter } from '../../../nodes';
import type { TypeManifest } from '../TypeManifest';
import type { GlobalFragmentScope } from '../getRenderMapVisitor';
import { Fragment, fragment, fragmentFromTemplate } from './common';

export function getAccountPdaHelpersFragment(
scope: Pick<GlobalFragmentScope, 'nameApi' | 'linkables'> & {
scope: Pick<
GlobalFragmentScope,
'nameApi' | 'linkables' | 'customAccountData'
> & {
accountNode: AccountNode;
programNode: ProgramNode;
typeManifest: TypeManifest;
}
): Fragment {
const { accountNode, programNode, nameApi, linkables } = scope;
const {
accountNode,
programNode,
nameApi,
linkables,
customAccountData,
typeManifest,
} = scope;
const pdaNode = accountNode.pda ? linkables.get(accountNode.pda) : undefined;
if (!pdaNode) {
return fragment('');
}

const accountTypeFragment = customAccountData.has(accountNode.name)
? typeManifest.strictType.clone()
: fragment(nameApi.dataType(accountNode.name));

const importFrom = 'generatedPdas';
const pdaSeedsType = nameApi.pdaSeedsType(pdaNode.name);
const findPdaFunction = nameApi.pdaFindFunction(pdaNode.name);
const hasVariableSeeds =
pdaNode.seeds.filter(isNodeFilter('variablePdaSeedNode')).length > 0;

return fragmentFromTemplate('accountPdaHelpers.njk', {
accountType: nameApi.accountType(accountNode.name),
accountMaybeType: nameApi.accountMaybeType(accountNode.name),
accountType: accountTypeFragment.render,
pdaSeedsType,
findPdaFunction,
fetchFunction: nameApi.accountFetchFunction(accountNode.name),
Expand All @@ -36,13 +51,16 @@ export function getAccountPdaHelpersFragment(
program: programNode,
hasVariableSeeds,
})
.mergeImportsWith(accountTypeFragment)
.addImports(
importFrom,
hasVariableSeeds ? [pdaSeedsType, findPdaFunction] : [findPdaFunction]
)
.addImports('solanaAddresses', ['Address'])
.addImports('solanaAccounts', [
'Account',
'assertAccountExists',
'FetchAccountConfig',
'MaybeAccount',
]);
}
8 changes: 0 additions & 8 deletions src/renderers/js-experimental/fragments/accountType.njk

This file was deleted.

30 changes: 9 additions & 21 deletions src/renderers/js-experimental/fragments/accountType.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { AccountNode } from '../../../nodes';
import { TypeManifest } from '../TypeManifest';
import type { GlobalFragmentScope } from '../getRenderMapVisitor';
import { Fragment, fragment, fragmentFromTemplate } from './common';
import { Fragment, fragment } from './common';
import { getTypeWithCodecFragment } from './typeWithCodec';

export function getAccountTypeFragment(
Expand All @@ -11,26 +11,14 @@ export function getAccountTypeFragment(
}
): Fragment {
const { accountNode, typeManifest, nameApi, customAccountData } = scope;
const customData = customAccountData.get(accountNode.name);
const accountDataName = nameApi.accountDataType(accountNode.name);
const typeWithCodecFragment = customData
? fragment('')
: getTypeWithCodecFragment({
name: accountDataName,
manifest: typeManifest,
nameApi,
});

const dataNameFragment = customData
? typeManifest.strictType.clone()
: fragment(nameApi.dataType(accountDataName));
if (customAccountData.has(accountNode.name)) {
return fragment('');
}

return fragmentFromTemplate('accountType.njk', {
accountType: nameApi.accountType(accountNode.name),
accountMaybeType: nameApi.accountMaybeType(accountNode.name),
dataName: dataNameFragment.render,
typeWithCodec: typeWithCodecFragment,
})
.mergeImportsWith(dataNameFragment, typeWithCodecFragment)
.addImports('solanaAccounts', ['Account', 'MaybeAccount']);
return getTypeWithCodecFragment({
name: accountNode.name,
manifest: typeManifest,
nameApi,
});
}
1 change: 0 additions & 1 deletion src/renderers/js-experimental/fragments/pdaFunction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ export function getPdaFunctionFragment(
pdaNode.seeds.filter(isNodeFilter('variablePdaSeedNode')).length > 0;

return fragmentFromTemplate('pdaFunction.njk', {
accountType: nameApi.accountType(pdaNode.name),
pdaSeedsType: nameApi.pdaSeedsType(pdaNode.name),
findPdaFunction: nameApi.pdaFindFunction(pdaNode.name),
program: programNode,
Expand Down
5 changes: 2 additions & 3 deletions src/renderers/js-experimental/getTypeManifestVisitor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,9 @@ export function getTypeManifestVisitor(input: {
(visitor) =>
extendVisitor(visitor, {
visitAccount(account, { self }) {
const accountDataName = nameApi.accountDataType(account.name);
parentName = {
strict: nameApi.dataType(accountDataName),
loose: nameApi.dataArgsType(accountDataName),
strict: nameApi.dataType(account.name),
loose: nameApi.dataArgsType(account.name),
};
const link = customAccountData.get(account.name)?.linkNode;
const manifest = link ? visit(link, self) : visit(account.data, self);
Expand Down
6 changes: 0 additions & 6 deletions src/renderers/js-experimental/nameTransformers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,6 @@ export type NameTransformerKey =
| 'codecFunction'
| 'pdaSeedsType'
| 'pdaFindFunction'
| 'accountType'
| 'accountMaybeType'
| 'accountDataType'
| 'accountDecodeFunction'
| 'accountFetchFunction'
| 'accountFetchAllFunction'
Expand Down Expand Up @@ -99,9 +96,6 @@ export const DEFAULT_NAME_TRANSFORMERS: NameTransformers = {
codecFunction: (name) => `get${pascalCase(name)}Codec`,
pdaSeedsType: (name) => `${pascalCase(name)}Seeds`,
pdaFindFunction: (name) => `find${pascalCase(name)}Pda`,
accountType: (name) => `${pascalCase(name)}`,
accountMaybeType: (name) => `Maybe${pascalCase(name)}`,
accountDataType: (name) => `${pascalCase(name)}AccountData`,
accountDecodeFunction: (name) => `decode${pascalCase(name)}`,
accountFetchFunction: (name) => `fetch${pascalCase(name)}`,
accountFetchAllFunction: (name) => `fetchAll${pascalCase(name)}`,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,17 +46,7 @@ import {
getCandyMachineDataEncoder,
} from '../types';

export type CandyMachine<TAddress extends string = string> = Account<
CandyMachineAccountData,
TAddress
>;

export type MaybeCandyMachine<TAddress extends string = string> = MaybeAccount<
CandyMachineAccountData,
TAddress
>;

export type CandyMachineAccountData = {
export type CandyMachine = {
discriminator: Array<number>;
/** Features versioning flags. */
features: bigint;
Expand All @@ -72,7 +62,7 @@ export type CandyMachineAccountData = {
data: CandyMachineData;
};

export type CandyMachineAccountDataArgs = {
export type CandyMachineArgs = {
/** Features versioning flags. */
features: number | bigint;
/** Authority address. */
Expand All @@ -87,7 +77,7 @@ export type CandyMachineAccountDataArgs = {
data: CandyMachineDataArgs;
};

export function getCandyMachineAccountDataEncoder(): Encoder<CandyMachineAccountDataArgs> {
export function getCandyMachineEncoder(): Encoder<CandyMachineArgs> {
return transformEncoder(
getStructEncoder([
['discriminator', getArrayEncoder(getU8Encoder(), { size: 8 })],
Expand All @@ -105,7 +95,7 @@ export function getCandyMachineAccountDataEncoder(): Encoder<CandyMachineAccount
);
}

export function getCandyMachineAccountDataDecoder(): Decoder<CandyMachineAccountData> {
export function getCandyMachineDecoder(): Decoder<CandyMachine> {
return getStructDecoder([
['discriminator', getArrayDecoder(getU8Decoder(), { size: 8 })],
['features', getU64Decoder()],
Expand All @@ -117,36 +107,30 @@ export function getCandyMachineAccountDataDecoder(): Decoder<CandyMachineAccount
]);
}

export function getCandyMachineAccountDataCodec(): Codec<
CandyMachineAccountDataArgs,
CandyMachineAccountData
> {
return combineCodec(
getCandyMachineAccountDataEncoder(),
getCandyMachineAccountDataDecoder()
);
export function getCandyMachineCodec(): Codec<CandyMachineArgs, CandyMachine> {
return combineCodec(getCandyMachineEncoder(), getCandyMachineDecoder());
}

export function decodeCandyMachine<TAddress extends string = string>(
encodedAccount: EncodedAccount<TAddress>
): CandyMachine<TAddress>;
): Account<CandyMachine, TAddress>;
export function decodeCandyMachine<TAddress extends string = string>(
encodedAccount: MaybeEncodedAccount<TAddress>
): MaybeCandyMachine<TAddress>;
): MaybeAccount<CandyMachine, TAddress>;
export function decodeCandyMachine<TAddress extends string = string>(
encodedAccount: EncodedAccount<TAddress> | MaybeEncodedAccount<TAddress>
): CandyMachine<TAddress> | MaybeCandyMachine<TAddress> {
): Account<CandyMachine, TAddress> | MaybeAccount<CandyMachine, TAddress> {
return decodeAccount(
encodedAccount as MaybeEncodedAccount<TAddress>,
getCandyMachineAccountDataDecoder()
getCandyMachineDecoder()
);
}

export async function fetchCandyMachine<TAddress extends string = string>(
rpc: Parameters<typeof fetchEncodedAccount>[0],
address: Address<TAddress>,
config?: FetchAccountConfig
): Promise<CandyMachine<TAddress>> {
): Promise<Account<CandyMachine, TAddress>> {
const maybeAccount = await fetchMaybeCandyMachine(rpc, address, config);
assertAccountExists(maybeAccount);
return maybeAccount;
Expand All @@ -156,7 +140,7 @@ export async function fetchMaybeCandyMachine<TAddress extends string = string>(
rpc: Parameters<typeof fetchEncodedAccount>[0],
address: Address<TAddress>,
config?: FetchAccountConfig
): Promise<MaybeCandyMachine<TAddress>> {
): Promise<MaybeAccount<CandyMachine, TAddress>> {
const maybeAccount = await fetchEncodedAccount(rpc, address, config);
return decodeCandyMachine(maybeAccount);
}
Expand All @@ -165,7 +149,7 @@ export async function fetchAllCandyMachine(
rpc: Parameters<typeof fetchEncodedAccounts>[0],
addresses: Array<Address>,
config?: FetchAccountsConfig
): Promise<CandyMachine[]> {
): Promise<Account<CandyMachine>[]> {
const maybeAccounts = await fetchAllMaybeCandyMachine(rpc, addresses, config);
assertAccountsExist(maybeAccounts);
return maybeAccounts;
Expand All @@ -175,7 +159,7 @@ export async function fetchAllMaybeCandyMachine(
rpc: Parameters<typeof fetchEncodedAccounts>[0],
addresses: Array<Address>,
config?: FetchAccountsConfig
): Promise<MaybeCandyMachine[]> {
): Promise<MaybeAccount<CandyMachine>[]> {
const maybeAccounts = await fetchEncodedAccounts(rpc, addresses, config);
return maybeAccounts.map((maybeAccount) => decodeCandyMachine(maybeAccount));
}
Loading

0 comments on commit 8cb70c8

Please sign in to comment.