diff --git a/.changeset/long-stamps-bow.md b/.changeset/long-stamps-bow.md new file mode 100644 index 000000000..fb2f758c5 --- /dev/null +++ b/.changeset/long-stamps-bow.md @@ -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. diff --git a/packages/nodes-from-anchor/src/v00/PdaNode.ts b/packages/nodes-from-anchor/src/v00/PdaNode.ts index cec5be7f3..03a05bd61 100644 --- a/packages/nodes-from-anchor/src/v00/PdaNode.ts +++ b/packages/nodes-from-anchor/src/v00/PdaNode.ts @@ -10,32 +10,28 @@ 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] : [], ); } @@ -43,3 +39,11 @@ export function pdaNodeFromAnchorV00(idl: IdlV00PdaDef): PdaNode { }); 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); +} diff --git a/packages/nodes-from-anchor/test/v00/PdaNode.test.ts b/packages/nodes-from-anchor/test/v00/PdaNode.test.ts index d3c6b48a5..be85a409c 100644 --- a/packages/nodes-from-anchor/test/v00/PdaNode.test.ts +++ b/packages/nodes-from-anchor/test/v00/PdaNode.test.ts @@ -1,9 +1,11 @@ import { + bytesTypeNode, constantPdaSeedNode, constantPdaSeedNodeFromProgramId, numberTypeNode, numberValueNode, pdaNode, + stringTypeNode, variablePdaSeedNode, } from '@codama/nodes'; import { expect, test } from 'vitest'; @@ -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())] })); +});