Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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/strong-planes-hide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@codama/nodes-from-anchor': minor
---

Add support for default shank ix discriminators (using 1 byte at the start of the ix data) in nodes-from-anchor
6 changes: 4 additions & 2 deletions packages/nodes-from-anchor/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,10 @@ This package converts Anchor IDLs from various versions into Codama IDLs.
pnpm install @codama/nodes-from-anchor
```

> [!NOTE]
> This package is **not** included in the main [`codama`](../library) package.
> ⚠️ NOTES ⚠️
>
> - This package is **not** included in the main [`codama`](../library) package.
> - A fair word of advice is that in case the `metadata.origin` key of the idl is not set, it is assumed to be `"anchor"`. So if you are trying to parse a Shank IDL, be sure that origin is set to `"shank"`.

## Functions

Expand Down
16 changes: 15 additions & 1 deletion packages/nodes-from-anchor/src/v00/InstructionNode.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {
bytesTypeNode,
bytesValueNode,
camelCase,
DiscriminatorNode,
fieldDiscriminatorNode,
Expand All @@ -16,7 +17,11 @@ import { instructionAccountNodesFromAnchorV00 } from './InstructionAccountNode';
import { instructionArgumentNodeFromAnchorV00 } from './InstructionArgumentNode';
import { typeNodeFromAnchorV00 } from './typeNodes';

export function instructionNodeFromAnchorV00(idl: IdlV00Instruction, origin?: 'anchor' | 'shank'): InstructionNode {
export function instructionNodeFromAnchorV00(
idl: IdlV00Instruction,
ixIndex: number,
origin?: 'anchor' | 'shank',
): InstructionNode {
const idlName = idl.name ?? '';
const name = camelCase(idlName);
let dataArguments = (idl.args ?? []).map(instructionArgumentNodeFromAnchorV00);
Expand All @@ -41,6 +46,15 @@ export function instructionNodeFromAnchorV00(idl: IdlV00Instruction, origin?: 'a
});
dataArguments = [discriminatorField, ...dataArguments];
discriminators = [fieldDiscriminatorNode('discriminator')];
} else if (origin === 'shank') {
const discriminatorField = instructionArgumentNode({
defaultValue: bytesValueNode('base16', ixIndex.toString(16)),
defaultValueStrategy: 'omitted',
name: 'discriminator',
type: fixedSizeTypeNode(bytesTypeNode(), 1),
});
dataArguments = [discriminatorField, ...dataArguments];
discriminators = [fieldDiscriminatorNode('discriminator')];
}

return instructionNode({
Expand Down
4 changes: 3 additions & 1 deletion packages/nodes-from-anchor/src/v00/ProgramNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ export function programNodeFromAnchorV00(idl: IdlV00): ProgramNode {
const origin = (idl?.metadata as { origin?: 'anchor' | 'shank' })?.origin ?? 'anchor';
const pdas = (idl.accounts ?? []).filter(account => (account.seeds ?? []).length > 0).map(pdaNodeFromAnchorV00);
const accounts = (idl.accounts ?? []).map(a => accountNodeFromAnchorV00(a, origin));
const instructions = (idl.instructions ?? []).map(i => instructionNodeFromAnchorV00(i, origin));
const instructions = (idl.instructions ?? []).map((instruction, index) =>
instructionNodeFromAnchorV00(instruction, index, origin),
);
return programNode({
accounts,
definedTypes: (idl?.types ?? []).map(definedTypeNodeFromAnchorV00),
Expand Down
14 changes: 9 additions & 5 deletions packages/nodes-from-anchor/test/v00/InstructionNode.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,14 @@ import { expect, test } from 'vitest';
import { instructionNodeFromAnchorV00 } from '../../src';

test('it creates instruction nodes', () => {
const node = instructionNodeFromAnchorV00({
accounts: [{ isMut: true, isSigner: false, name: 'mint' }],
args: [{ name: 'amount', type: 'u8' }],
name: 'mintTokens',
});
const node = instructionNodeFromAnchorV00(
{
accounts: [{ isMut: true, isSigner: false, name: 'mint' }],
args: [{ name: 'amount', type: 'u8' }],
name: 'mintTokens',
},
0,
);

expect(node).toEqual(
instructionNode({
Expand All @@ -35,6 +38,7 @@ test('it creates instruction nodes with anchor discriminators', () => {
args: [],
name: 'myInstruction',
},
0,
'anchor',
);

Expand Down
20 changes: 19 additions & 1 deletion packages/nodes-from-anchor/test/v00/ProgramNode.test.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
import {
accountNode,
bytesTypeNode,
bytesValueNode,
constantPdaSeedNodeFromProgramId,
definedTypeNode,
errorNode,
fieldDiscriminatorNode,
fixedSizeTypeNode,
instructionArgumentNode,
instructionNode,
pdaLinkNode,
pdaNode,
Expand Down Expand Up @@ -36,7 +41,20 @@ test('it creates program nodes', () => {
name: 'myError',
}),
],
instructions: [instructionNode({ name: 'myInstruction' })],
instructions: [
instructionNode({
arguments: [
instructionArgumentNode({
defaultValue: bytesValueNode('base16', (0).toString(16)),
defaultValueStrategy: 'omitted',
name: 'discriminator',
type: fixedSizeTypeNode(bytesTypeNode(), 1),
}),
],
discriminators: [fieldDiscriminatorNode('discriminator')],
name: 'myInstruction',
}),
],
name: 'myProgram',
origin: 'shank',
pdas: [pdaNode({ name: 'myAccount', seeds: [constantPdaSeedNodeFromProgramId()] })],
Expand Down