Skip to content

Commit 92efaa9

Browse files
authored
feat: support const program ids for pdas in anchor (#390)
1 parent 1ca91a1 commit 92efaa9

File tree

10 files changed

+93
-3
lines changed

10 files changed

+93
-3
lines changed

.changeset/swift-tomatoes-care.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
'@codama/nodes-from-anchor': patch
3+
'@codama/errors': patch
4+
---
5+
6+
Support constant program ID's for PDA instruction accounts in Anchor.

packages/errors/src/codes.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ export const CODAMA_ERROR__ANCHOR__ACCOUNT_TYPE_MISSING = 2100001;
6060
export const CODAMA_ERROR__ANCHOR__ARGUMENT_TYPE_MISSING = 2100002;
6161
export const CODAMA_ERROR__ANCHOR__TYPE_PATH_MISSING = 2100003;
6262
export const CODAMA_ERROR__ANCHOR__SEED_KIND_UNIMPLEMENTED = 2100004;
63+
export const CODAMA_ERROR__ANCHOR__PROGRAM_ID_KIND_UNIMPLEMENTED = 2100005;
6364

6465
// Renderers-related errors.
6566
// Reserve error codes in the range [2800000-2800999].
@@ -83,6 +84,7 @@ export const CODAMA_ERROR__RENDERERS__UNSUPPORTED_NODE = 2800000;
8384
export type CodamaErrorCode =
8485
| typeof CODAMA_ERROR__ANCHOR__ACCOUNT_TYPE_MISSING
8586
| typeof CODAMA_ERROR__ANCHOR__ARGUMENT_TYPE_MISSING
87+
| typeof CODAMA_ERROR__ANCHOR__PROGRAM_ID_KIND_UNIMPLEMENTED
8688
| typeof CODAMA_ERROR__ANCHOR__SEED_KIND_UNIMPLEMENTED
8789
| typeof CODAMA_ERROR__ANCHOR__TYPE_PATH_MISSING
8890
| typeof CODAMA_ERROR__ANCHOR__UNRECOGNIZED_IDL_TYPE

packages/errors/src/context.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import {
2222
import {
2323
CODAMA_ERROR__ANCHOR__ACCOUNT_TYPE_MISSING,
2424
CODAMA_ERROR__ANCHOR__ARGUMENT_TYPE_MISSING,
25+
CODAMA_ERROR__ANCHOR__PROGRAM_ID_KIND_UNIMPLEMENTED,
2526
CODAMA_ERROR__ANCHOR__SEED_KIND_UNIMPLEMENTED,
2627
CODAMA_ERROR__ANCHOR__TYPE_PATH_MISSING,
2728
CODAMA_ERROR__ANCHOR__UNRECOGNIZED_IDL_TYPE,
@@ -68,6 +69,9 @@ export type CodamaErrorContext = DefaultUnspecifiedErrorContextToUndefined<{
6869
[CODAMA_ERROR__ANCHOR__ARGUMENT_TYPE_MISSING]: {
6970
name: string;
7071
};
72+
[CODAMA_ERROR__ANCHOR__PROGRAM_ID_KIND_UNIMPLEMENTED]: {
73+
kind: string;
74+
};
7175
[CODAMA_ERROR__ANCHOR__SEED_KIND_UNIMPLEMENTED]: {
7276
kind: string;
7377
};

packages/errors/src/messages.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import {
77
CODAMA_ERROR__ANCHOR__ACCOUNT_TYPE_MISSING,
88
CODAMA_ERROR__ANCHOR__ARGUMENT_TYPE_MISSING,
9+
CODAMA_ERROR__ANCHOR__PROGRAM_ID_KIND_UNIMPLEMENTED,
910
CODAMA_ERROR__ANCHOR__SEED_KIND_UNIMPLEMENTED,
1011
CODAMA_ERROR__ANCHOR__TYPE_PATH_MISSING,
1112
CODAMA_ERROR__ANCHOR__UNRECOGNIZED_IDL_TYPE,
@@ -48,6 +49,7 @@ export const CodamaErrorMessages: Readonly<{
4849
}> = {
4950
[CODAMA_ERROR__ANCHOR__ACCOUNT_TYPE_MISSING]: 'Account type [$name] is missing from the IDL types.',
5051
[CODAMA_ERROR__ANCHOR__ARGUMENT_TYPE_MISSING]: 'Argument name [$name] is missing from the instruction definition.',
52+
[CODAMA_ERROR__ANCHOR__PROGRAM_ID_KIND_UNIMPLEMENTED]: 'Program ID kind [$kind] is not implemented.',
5153
[CODAMA_ERROR__ANCHOR__SEED_KIND_UNIMPLEMENTED]: 'Seed kind [$kind] is not implemented.',
5254
[CODAMA_ERROR__ANCHOR__TYPE_PATH_MISSING]: 'Field type is missing for path [$path] in [$idlType].',
5355
[CODAMA_ERROR__ANCHOR__UNRECOGNIZED_IDL_TYPE]: 'Unrecognized Anchor IDL type [$idlType].',

packages/nodes-from-anchor/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
"@codama/errors": "workspace:*",
5353
"@codama/nodes": "workspace:*",
5454
"@codama/visitors": "workspace:*",
55+
"@solana/codecs": "2.0.0",
5556
"@noble/hashes": "^1.7.0"
5657
},
5758
"license": "MIT",

packages/nodes-from-anchor/src/v01/InstructionAccountNode.ts

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import {
22
CODAMA_ERROR__ANCHOR__ACCOUNT_TYPE_MISSING,
33
CODAMA_ERROR__ANCHOR__ARGUMENT_TYPE_MISSING,
4+
CODAMA_ERROR__ANCHOR__PROGRAM_ID_KIND_UNIMPLEMENTED,
45
CODAMA_ERROR__ANCHOR__SEED_KIND_UNIMPLEMENTED,
56
CODAMA_ERROR__ANCHOR__TYPE_PATH_MISSING,
67
CodamaError,
@@ -26,6 +27,7 @@ import {
2627
resolveNestedTypeNode,
2728
variablePdaSeedNode,
2829
} from '@codama/nodes';
30+
import { getBase58Codec } from '@solana/codecs';
2931

3032
import { hex } from '../utils';
3133
import { IdlV01InstructionAccount, IdlV01InstructionAccountItem, IdlV01Seed } from './idl';
@@ -123,7 +125,24 @@ export function instructionAccountNodeFromAnchorV01(
123125
<[PdaSeedNode[], PdaSeedValueNode[]]>[[], []],
124126
);
125127

126-
defaultValue = pdaValueNode(pdaNode({ name, seeds }), lookups);
128+
let programId: string | undefined;
129+
if (idl.pda.program !== undefined) {
130+
const kind = idl.pda.program.kind;
131+
switch (kind) {
132+
case 'const': {
133+
programId = getBase58Codec().decode(new Uint8Array(idl.pda.program.value));
134+
break;
135+
}
136+
default: {
137+
throw new CodamaError(CODAMA_ERROR__ANCHOR__PROGRAM_ID_KIND_UNIMPLEMENTED, { kind });
138+
}
139+
}
140+
}
141+
142+
defaultValue = pdaValueNode(
143+
pdaNode({ name, seeds, ...(programId !== undefined ? { programId } : {}) }),
144+
lookups,
145+
);
127146
}
128147
}
129148

packages/nodes-from-anchor/test/v01/InstructionAccountNode.test.ts

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,57 @@ test('it ignores PDA default values if at least one seed as a path of length gre
161161
]);
162162
});
163163

164+
test('it handles PDAs with a constant program id', () => {
165+
const nodes = instructionAccountNodesFromAnchorV01(
166+
[],
167+
[],
168+
[
169+
{
170+
name: 'program_data',
171+
pda: {
172+
program: {
173+
kind: 'const',
174+
value: [
175+
2, 168, 246, 145, 78, 136, 161, 176, 226, 16, 21, 62, 247, 99, 174, 43, 0, 194, 185, 61, 22,
176+
193, 36, 210, 192, 83, 122, 16, 4, 128, 0, 0,
177+
],
178+
},
179+
seeds: [
180+
{
181+
kind: 'const',
182+
value: [
183+
166, 175, 151, 238, 166, 67, 87, 148, 114, 209, 13, 88, 186, 228, 206, 197, 182, 71,
184+
129, 195, 206, 236, 229, 223, 184, 60, 97, 249, 63, 92, 203, 27,
185+
],
186+
},
187+
],
188+
},
189+
},
190+
],
191+
);
192+
193+
expect(nodes).toEqual([
194+
instructionAccountNode({
195+
defaultValue: pdaValueNode(
196+
pdaNode({
197+
name: 'programData',
198+
programId: 'BPFLoaderUpgradeab1e11111111111111111111111',
199+
seeds: [
200+
constantPdaSeedNodeFromBytes(
201+
'base16',
202+
'a6af97eea643579472d10d58bae4cec5b64781c3ceece5dfb83c61f93f5ccb1b',
203+
),
204+
],
205+
}),
206+
[],
207+
),
208+
isSigner: false,
209+
isWritable: false,
210+
name: 'programData',
211+
}),
212+
]);
213+
});
214+
164215
test.skip('it handles account data paths of length 2', () => {
165216
const nodes = instructionAccountNodesFromAnchorV01(
166217
[

packages/renderers-js/e2e/anchor/src/generated/instructions/createGuard.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -303,7 +303,8 @@ export async function getCreateGuardInstructionAsync<
303303
}
304304
if (!accounts.mintTokenAccount.value) {
305305
accounts.mintTokenAccount.value = await getProgramDerivedAddress({
306-
programAddress,
306+
programAddress:
307+
'ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL' as Address<'ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL'>,
307308
seeds: [
308309
getAddressEncoder().encode(
309310
expectAddress(accounts.guardAuthority.value)

packages/renderers-js/e2e/anchor/src/generated/instructions/updateGuard.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,8 @@ export async function getUpdateGuardInstructionAsync<
252252
}
253253
if (!accounts.tokenAccount.value) {
254254
accounts.tokenAccount.value = await getProgramDerivedAddress({
255-
programAddress,
255+
programAddress:
256+
'ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL' as Address<'ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL'>,
256257
seeds: [
257258
getAddressEncoder().encode(
258259
expectAddress(accounts.guardAuthority.value)

pnpm-lock.yaml

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)