Skip to content
Open
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
5 changes: 5 additions & 0 deletions .changeset/nested-pda-seeds.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@codama/nodes-from-anchor': minor
---

Resolve nested seed paths in v01 PDA extraction instead of silently skipping them
2 changes: 1 addition & 1 deletion packages/dynamic-client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@
"lint:fix": "pnpm generate-program-types && eslint --fix . && prettier --write .",
"test": "pnpm test:setup && pnpm test:types && pnpm test:treeshakability && pnpm test:unit",
"test:setup": "pnpm test:anchor:build && ( [ -n \"$CI\" ] || pnpm generate-idl-from-anchor ) && pnpm generate-program-types",
"test:anchor:build": "cd test/programs/anchor && anchor build",
"test:anchor:build": "cd test/programs/anchor && anchor build --ignore-keys",
"test:treeshakability": "for file in dist/index.*.mjs; do agadoo $file; done",
"test:types": "tsc --noEmit",
"test:unit": "vitest run",
Expand Down
10 changes: 5 additions & 5 deletions packages/dynamic-client/test/programs/anchor/tests/blog.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { loadIdl, SvmTestContext } from '../../test-utils';

const idl = loadIdl('blog-idl.json');
const programClient = createProgramClient<BlogProgramClient>(idl);
const programSoPath = path.resolve(__dirname, '..', '..', 'dumps', 'blog.so');
const programSoPath = path.resolve(__dirname, '..', 'target', 'deploy', 'blog.so');

describe('blog', () => {
let ctx: SvmTestContext;
Expand Down Expand Up @@ -108,8 +108,8 @@ describe('blog', () => {

const [profilePda] = await programClient.pdas.profile({ authority: payer });

// Create post (manual PDA — Codama can't express profile.post_count dependency)
const [postPda] = await programClient.pdas.post({ postId: 0, profile: profilePda });
// Create post (manual PDA — uses profile.post_count nested seed)
const [postPda] = await programClient.pdas.post({ profile: profilePda, profilePostCount: 0 });
const createPostIx = await programClient.methods
.createPost({ content: 'Original content', title: 'Original' })
.accounts({ authority: payer, post: postPda })
Expand Down Expand Up @@ -139,7 +139,7 @@ describe('blog', () => {
await ctx.sendInstruction(createProfileIx, [payer]);

const [profilePda] = await programClient.pdas.profile({ authority: payer });
const [postPda] = await programClient.pdas.post({ postId: 0, profile: profilePda });
const [postPda] = await programClient.pdas.post({ profile: profilePda, profilePostCount: 0 });

const createPostIx = await programClient.methods
.createPost({ content: 'World', title: 'Hello' })
Expand Down Expand Up @@ -175,7 +175,7 @@ describe('blog', () => {
await ctx.sendInstruction(createProfileIx, [payer]);

const [profilePda] = await programClient.pdas.profile({ authority: payer });
const [postPda] = await programClient.pdas.post({ postId: 0, profile: profilePda });
const [postPda] = await programClient.pdas.post({ profile: profilePda, profilePostCount: 0 });

const createPostIx = await programClient.methods
.createPost({ content: 'There', title: 'Hi' })
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -241,10 +241,12 @@ describe('anchor-example: commonIxs', () => {
});

describe('Circular Dependency Detection', () => {
test('SelfReferencePdaIx: should throw AccountError for A->A cycle', async () => {
await expect(
programClient.methods.selfReferencePda().accounts({ signer: payer }).instruction(),
).rejects.toThrow(/Circular dependency detected: \[recursive -> recursive\]/);
test('SelfReferencePdaIx: self-referential seed is dropped, requires manual account', async () => {
const ix = await programClient.methods
.selfReferencePda()
.accounts({ recursive: payer, signer: payer })
.instruction();
expect(ix).toBeDefined();
});

test('TwoNodeCyclePdaIx: should throw AccountError for A->B->A pattern in two-node cycle', async () => {
Expand Down
69 changes: 65 additions & 4 deletions packages/dynamic-client/test/programs/idls/blog-idl.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"kind": "rootNode",
"standard": "codama",
"version": "1.5.1",
"version": "1.6.0",
"program": {
"kind": "programNode",
"name": "blog",
Expand Down Expand Up @@ -1163,7 +1163,32 @@
"isWritable": true,
"isSigner": false,
"isOptional": false,
"docs": []
"docs": [],
"defaultValue": {
"kind": "pdaValueNode",
"pda": {
"kind": "pdaLinkNode",
"name": "post"
},
"seeds": [
{
"kind": "pdaSeedValueNode",
"name": "profile",
"value": {
"kind": "accountValueNode",
"name": "profile"
}
},
{
"kind": "pdaSeedValueNode",
"name": "profilePostCount",
"value": {
"kind": "accountValueNode",
"name": "profile"
}
}
]
}
},
{
"kind": "instructionAccountNode",
Expand Down Expand Up @@ -1607,7 +1632,7 @@
"kind": "pdaValueNode",
"pda": {
"kind": "pdaLinkNode",
"name": "post"
"name": "updatePostPost"
},
"seeds": [
{
Expand Down Expand Up @@ -1886,6 +1911,42 @@
}
]
},
{
"kind": "pdaNode",
"name": "post",
"docs": [],
"seeds": [
{
"kind": "constantPdaSeedNode",
"type": {
"kind": "bytesTypeNode"
},
"value": {
"kind": "bytesValueNode",
"data": "3sh3oZ",
"encoding": "base58"
}
},
{
"kind": "variablePdaSeedNode",
"name": "profile",
"docs": [],
"type": {
"kind": "publicKeyTypeNode"
}
},
{
"kind": "variablePdaSeedNode",
"name": "profilePostCount",
"docs": [],
"type": {
"kind": "numberTypeNode",
"format": "u64",
"endian": "le"
}
}
]
},
{
"kind": "pdaNode",
"name": "reaction",
Expand Down Expand Up @@ -1966,7 +2027,7 @@
},
{
"kind": "pdaNode",
"name": "post",
"name": "updatePostPost",
"docs": [],
"seeds": [
{
Expand Down
86 changes: 60 additions & 26 deletions packages/dynamic-client/test/programs/idls/example-idl.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"kind": "rootNode",
"standard": "codama",
"version": "1.5.1",
"version": "1.6.0",
"program": {
"kind": "programNode",
"name": "example",
Expand Down Expand Up @@ -556,7 +556,40 @@
"isWritable": true,
"isSigner": false,
"isOptional": false,
"docs": []
"docs": [],
"defaultValue": {
"kind": "pdaValueNode",
"pda": {
"kind": "pdaLinkNode",
"name": "nestedExampleAccount"
},
"seeds": [
{
"kind": "pdaSeedValueNode",
"name": "pubkey",
"value": {
"kind": "argumentValueNode",
"name": "pubkey"
}
},
{
"kind": "pdaSeedValueNode",
"name": "seedEnum",
"value": {
"kind": "argumentValueNode",
"name": "seedEnum"
}
},
{
"kind": "pdaSeedValueNode",
"name": "innerStructSeedEnum",
"value": {
"kind": "argumentValueNode",
"name": "innerStructSeedEnum"
}
}
]
}
},
{
"kind": "instructionAccountNode",
Expand Down Expand Up @@ -784,24 +817,7 @@
"isWritable": true,
"isSigner": false,
"isOptional": false,
"docs": [],
"defaultValue": {
"kind": "pdaValueNode",
"pda": {
"kind": "pdaLinkNode",
"name": "recursive"
},
"seeds": [
{
"kind": "pdaSeedValueNode",
"name": "recursive",
"value": {
"kind": "accountValueNode",
"name": "recursive"
}
}
]
}
"docs": []
},
{
"kind": "instructionAccountNode",
Expand Down Expand Up @@ -1780,7 +1796,7 @@
},
{
"kind": "pdaNode",
"name": "newAccount",
"name": "nestedExampleAccount",
"docs": [],
"seeds": [
{
Expand All @@ -1790,23 +1806,41 @@
},
"value": {
"kind": "bytesValueNode",
"data": "3x5dmD",
"data": "WxqkpQv6EzD7FjkQ2ENw5KvgMqrLQj",
"encoding": "base58"
}
},
{
"kind": "variablePdaSeedNode",
"name": "signer",
"name": "pubkey",
"docs": [],
"type": {
"kind": "publicKeyTypeNode"
}
},
{
"kind": "variablePdaSeedNode",
"name": "seedEnum",
"docs": [],
"type": {
"kind": "definedTypeLinkNode",
"name": "seedEnum"
}
},
{
"kind": "variablePdaSeedNode",
"name": "innerStructSeedEnum",
"docs": [],
"type": {
"kind": "definedTypeLinkNode",
"name": "seedEnum"
}
}
]
},
{
"kind": "pdaNode",
"name": "recursive",
"name": "newAccount",
"docs": [],
"seeds": [
{
Expand All @@ -1816,13 +1850,13 @@
},
"value": {
"kind": "bytesValueNode",
"data": "2TTMxGdnk9y5E",
"data": "3x5dmD",
"encoding": "base58"
}
},
{
"kind": "variablePdaSeedNode",
"name": "recursive",
"name": "signer",
"docs": [],
"type": {
"kind": "publicKeyTypeNode"
Expand Down
Loading