Skip to content
Merged
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/long-stamps-bow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@codama/nodes-from-anchor': patch
---

Fix variable `string` and `bytes` PDA seeds from legacy Anchor IDLs to derive from their raw bytes instead of their Borsh size-prefixed encoding, matching the existing behaviour of constant seeds.
20 changes: 12 additions & 8 deletions packages/nodes-from-anchor/src/v00/PdaNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,36 +10,40 @@ import {
PdaSeedNode,
stringTypeNode,
stringValueNode,
TypeNode,
variablePdaSeedNode,
} from '@codama/nodes';

import { IdlV00PdaDef } from './idl';
import { IdlV00PdaDef, IdlV00Type } from './idl';
import { typeNodeFromAnchorV00 } from './typeNodes';

export function pdaNodeFromAnchorV00(idl: IdlV00PdaDef): PdaNode {
const name = camelCase(idl.name ?? '');
const seeds = (idl.seeds ?? []).map((seed): PdaSeedNode => {
if (seed.kind === 'constant') {
const type = (() => {
if (seed.type === 'string') return stringTypeNode('utf8');
if (seed.type === 'bytes') return bytesTypeNode();
return typeNodeFromAnchorV00(seed.type);
})();
const value = (() => {
if (typeof seed.value === 'string') return stringValueNode(seed.value);
if (typeof seed.value === 'number') return numberValueNode(seed.value);
return booleanValueNode(seed.value);
})();
return constantPdaSeedNode(type, value);
return constantPdaSeedNode(pdaSeedTypeNodeFromAnchorV00(seed.type), value);
}
if (seed.kind === 'variable') {
return variablePdaSeedNode(
seed.name,
typeNodeFromAnchorV00(seed.type),
pdaSeedTypeNodeFromAnchorV00(seed.type),
seed.description ? [seed.description] : [],
);
}
return constantPdaSeedNodeFromProgramId();
});
return pdaNode({ name, seeds });
}

function pdaSeedTypeNodeFromAnchorV00(type: IdlV00Type): TypeNode {
// Anchor derives PDA seeds from raw bytes, so strings and byte
// arrays lose their Borsh size prefix when used as seeds.
if (type === 'string') return stringTypeNode('utf8');
if (type === 'bytes') return bytesTypeNode();
return typeNodeFromAnchorV00(type);
}
20 changes: 20 additions & 0 deletions packages/nodes-from-anchor/test/v00/PdaNode.test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import {
bytesTypeNode,
constantPdaSeedNode,
constantPdaSeedNodeFromProgramId,
numberTypeNode,
numberValueNode,
pdaNode,
stringTypeNode,
variablePdaSeedNode,
} from '@codama/nodes';
import { expect, test } from 'vitest';
Expand Down Expand Up @@ -31,3 +33,21 @@ test('it creates PDA nodes', () => {
}),
);
});

test('it removes the string prefix from variable seeds', () => {
const node = pdaNodeFromAnchorV00({
name: 'myPda',
seeds: [{ description: '', kind: 'variable', name: 'label', type: 'string' }],
});

expect(node).toEqual(pdaNode({ name: 'myPda', seeds: [variablePdaSeedNode('label', stringTypeNode('utf8'))] }));
});

test('it removes the bytes prefix from variable seeds', () => {
const node = pdaNodeFromAnchorV00({
name: 'myPda',
seeds: [{ description: '', kind: 'variable', name: 'seedData', type: 'bytes' }],
});

expect(node).toEqual(pdaNode({ name: 'myPda', seeds: [variablePdaSeedNode('seedData', bytesTypeNode())] }));
});