Skip to content

Commit

Permalink
fix: finding and typing pdas. account referencing types.
Browse files Browse the repository at this point in the history
  • Loading branch information
kespinola committed May 6, 2024
1 parent 781f619 commit ffc6a39
Show file tree
Hide file tree
Showing 8 changed files with 166 additions and 18 deletions.
29 changes: 25 additions & 4 deletions packages/nodes-from-anchor/src/v01/AccountNode.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { KINOBI_ERROR__VISITORS__ACCOUNT_FIELD_NOT_FOUND, KinobiError } from '@kinobi-so/errors';
import {
AccountNode,
accountNode,
Expand All @@ -9,23 +10,43 @@ import {
} from '@kinobi-so/nodes';

import { getAnchorDiscriminatorV01 } from './../discriminators';
import { IdlV01Account } from './idl';
import { IdlV01Account, IdlV01TypeDef, IdlV01TypeDefTyStruct } from './idl';
import { structTypeNodeFromAnchorV01 } from './typeNodes';

export function accountNodeFromAnchorV01(idl: IdlV01Account): AccountNode {
export function accountNodeFromAnchorV01(idl: IdlV01Account, types: IdlV01TypeDef[]): AccountNode {
const idlName = idl.name;
const name = camelCase(idlName);

const type = types.find(t => t.name === idl.name);

if (!type) {
throw new KinobiError(KINOBI_ERROR__VISITORS__ACCOUNT_FIELD_NOT_FOUND, {});
}

if (type.type.kind !== 'struct') {
throw new KinobiError(KINOBI_ERROR__VISITORS__ACCOUNT_FIELD_NOT_FOUND, {});
}

const idlTypeStruct = type.type as IdlV01TypeDefTyStruct;

const data = structTypeNodeFromAnchorV01(idlTypeStruct);

const discriminator = structFieldTypeNode({
defaultValue: getAnchorDiscriminatorV01(idl.discriminator),
defaultValueStrategy: 'omitted',
name: 'discriminator',
type: bytesTypeNode(),
});

// TODO: How to set defined type link to the field definition in types list?
return accountNode({
data: structTypeNode([discriminator]),
data: structTypeNode([discriminator, ...data.fields]),
discriminators: [fieldDiscriminatorNode('discriminator')],
name,
});
}

export function accountNodeFromAnchorV01WithTypeDefinition(types: IdlV01TypeDef[]) {
return function (idl: IdlV01Account): AccountNode {
return accountNodeFromAnchorV01(idl, types);
};
}
35 changes: 35 additions & 0 deletions packages/nodes-from-anchor/src/v01/PdaNode.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { KINOBI_ERROR__UNRECOGNIZED_ANCHOR_IDL_TYPE, KinobiError } from '@kinobi-so/errors';
import {
bytesTypeNode,
camelCase,
constantPdaSeedNode,
PdaNode,
pdaNode,
PdaSeedNode,
publicKeyTypeNode,
variablePdaSeedNode,
} from '@kinobi-so/nodes';

import { getAnchorDiscriminatorV01 } from './../discriminators';
import { IdlV01InstructionAccount } from './idl';

export function pdaNodeFromAnchorV01(idl: IdlV01InstructionAccount): PdaNode {
const seeds = idl.pda?.seeds.map((seed): PdaSeedNode => {
switch (seed.kind) {
case 'const':
return constantPdaSeedNode(bytesTypeNode(), getAnchorDiscriminatorV01(seed.value));
case 'account':
return variablePdaSeedNode(seed.path, publicKeyTypeNode());
case 'arg':
throw new KinobiError(KINOBI_ERROR__UNRECOGNIZED_ANCHOR_IDL_TYPE, { idlType: seed });
}
});

if (!seeds) {
throw new KinobiError(KINOBI_ERROR__UNRECOGNIZED_ANCHOR_IDL_TYPE, { idlType: idl });
}

const name = camelCase(idl.name);

return pdaNode({ name, seeds });
}
16 changes: 12 additions & 4 deletions packages/nodes-from-anchor/src/v01/ProgramNode.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,33 @@
import { ProgramNode, programNode, ProgramVersion } from '@kinobi-so/nodes';

import { accountNodeFromAnchorV01 } from './AccountNode';
import { accountNodeFromAnchorV01WithTypeDefinition } from './AccountNode';
import { definedTypeNodeFromAnchorV01 } from './DefinedTypeNode';
import { errorNodeFromAnchorV01 } from './ErrorNode';
import { IdlV01 } from './idl';
import { IdlV01, IdlV01InstructionAccount } from './idl';
import { instructionNodeFromAnchorV01 } from './InstructionNode';
import { pdaNodeFromAnchorV01 } from './PdaNode';

export function programNodeFromAnchorV01(idl: IdlV01): ProgramNode {
const types = idl.types ?? [];
const accounts = idl.accounts ?? [];
const instructions = idl.instructions ?? [];
const errors = idl.errors ?? [];

const definedTypes = types.map(definedTypeNodeFromAnchorV01);
const accountNodeFromAnchorV01 = accountNodeFromAnchorV01WithTypeDefinition(types);
const pdas = instructions
.flatMap<IdlV01InstructionAccount>(instruction => instruction.accounts)
.filter(account => !account.pda?.program)
.map(pdaNodeFromAnchorV01);

return programNode({
accounts: accounts.map(accountNodeFromAnchorV01),
definedTypes: types.map(definedTypeNodeFromAnchorV01),
definedTypes,
errors: errors.map(errorNodeFromAnchorV01),
instructions: instructions.map(instructionNodeFromAnchorV01),
name: idl.metadata.name,
origin: 'anchor',
pdas: [],
pdas,
publicKey: idl.address,
version: idl.metadata.version as ProgramVersion,
});
Expand Down
1 change: 1 addition & 0 deletions packages/nodes-from-anchor/src/v01/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export * from './ErrorNode';
export * from './InstructionAccountNode';
export * from './InstructionArgumentNode';
export * from './InstructionNode';
export * from './PdaNode';
export * from './ProgramNode';
export * from './RootNode';
export * from './idl';
Expand Down
3 changes: 1 addition & 2 deletions packages/nodes-from-anchor/src/v01/typeNodes/TypeNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,7 @@ export const typeNodeFromAnchorV01 = (idlType: IdlV01Type | IdlV01TypeDefTy): Ty

// Struct.
if ('kind' in idlType && 'fields' in idlType && idlType.kind === 'struct') {
// TODO: bug here where struct type field is getting handled as a tuple if it has 2 fields
if (isArrayOfSize(idlType.fields, 2)) {
if (isArrayOfSize(idlType.fields, 2) && idlType.fields?.every(field => typeof field === 'string')) {
return tupleTypeNodeFromAnchorV01(idlType.fields as IdlV01DefinedFieldsTuple);
}

Expand Down
30 changes: 26 additions & 4 deletions packages/nodes-from-anchor/test/v01/AccountNode.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {
accountNode,
bytesTypeNode,
fieldDiscriminatorNode,
numberTypeNode,
structFieldTypeNode,
structTypeNode,
} from '@kinobi-so/nodes';
Expand All @@ -10,10 +11,27 @@ import test from 'ava';
import { accountNodeFromAnchorV01, getAnchorDiscriminatorV01 } from '../../src/index.js';

test('it creates account nodes with anchor discriminators', t => {
const node = accountNodeFromAnchorV01({
discriminator: [246, 28, 6, 87, 251, 45, 50, 42],
name: 'MyAccount',
});
const node = accountNodeFromAnchorV01(
{
discriminator: [246, 28, 6, 87, 251, 45, 50, 42],
name: 'MyAccount',
},
[
{
docs: [],
name: 'MyAccount',
type: {
fields: [
{
name: 'name',
type: 'u32',
},
],
kind: 'struct',
},
},
],
);

t.deepEqual(
node,
Expand All @@ -25,6 +43,10 @@ test('it creates account nodes with anchor discriminators', t => {
name: 'discriminator',
type: bytesTypeNode(),
}),
structFieldTypeNode({
name: 'name',
type: numberTypeNode('u32'),
}),
]),
discriminators: [fieldDiscriminatorNode('discriminator')],
name: 'myAccount',
Expand Down
27 changes: 27 additions & 0 deletions packages/nodes-from-anchor/test/v01/PdaNode.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { bytesTypeNode, constantPdaSeedNode, pdaNode, publicKeyTypeNode, variablePdaSeedNode } from '@kinobi-so/nodes';
import test from 'ava';

import { getAnchorDiscriminatorV01, pdaNodeFromAnchorV01 } from '../../src/index.js';

test('it creates PDA nodes', t => {
const node = pdaNodeFromAnchorV01({
name: 'myPda',
pda: {
seeds: [
{ kind: 'const', value: [42, 31, 29] },
{ kind: 'account', path: 'authority' },
],
},
});

t.deepEqual(
node,
pdaNode({
name: 'myPda',
seeds: [
constantPdaSeedNode(bytesTypeNode(), getAnchorDiscriminatorV01([42, 31, 29])),
variablePdaSeedNode('authority', publicKeyTypeNode()),
],
}),
);
});
43 changes: 39 additions & 4 deletions packages/nodes-from-anchor/test/v01/ProgramNode.test.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
import {
accountNode,
bytesTypeNode,
constantPdaSeedNode,
definedTypeNode,
errorNode,
fieldDiscriminatorNode,
instructionAccountNode,
instructionArgumentNode,
instructionNode,
pdaNode,
programNode,
publicKeyTypeNode,
structFieldTypeNode,
structTypeNode,
variablePdaSeedNode,
} from '@kinobi-so/nodes';
import test from 'ava';

Expand All @@ -20,10 +25,25 @@ test('it creates program nodes', t => {
address: '1111',
errors: [{ code: 42, msg: 'my error message', name: 'myError' }],
instructions: [
{ accounts: [], args: [], discriminator: [246, 28, 6, 87, 251, 45, 50, 42], name: 'myInstruction' },
{
accounts: [
{
name: 'authority',
pda: {
seeds: [
{ kind: 'const', value: [42, 31, 29] },
{ kind: 'account', path: 'owner' },
],
},
},
],
args: [],
discriminator: [246, 28, 6, 87, 251, 45, 50, 42],
name: 'myInstruction',
},
],
metadata: { name: 'myProgram', spec: '0.1.0', version: '1.2.3' },
types: [{ name: 'myType', type: { fields: [], kind: 'struct' } }],
types: [{ name: 'MyAccount', type: { fields: [], kind: 'struct' } }],
});

t.deepEqual(
Expand All @@ -43,7 +63,7 @@ test('it creates program nodes', t => {
name: 'myAccount',
}),
],
definedTypes: [definedTypeNode({ name: 'myType', type: structTypeNode([]) })],
definedTypes: [definedTypeNode({ name: 'myAccount', type: structTypeNode([]) })],
errors: [
errorNode({
code: 42,
Expand All @@ -54,6 +74,13 @@ test('it creates program nodes', t => {
],
instructions: [
instructionNode({
accounts: [
instructionAccountNode({
isSigner: false,
isWritable: false,
name: 'authority',
}),
],
arguments: [
instructionArgumentNode({
defaultValue: getAnchorDiscriminatorV01([246, 28, 6, 87, 251, 45, 50, 42]),
Expand All @@ -68,7 +95,15 @@ test('it creates program nodes', t => {
],
name: 'myProgram',
origin: 'anchor',
pdas: [],
pdas: [
pdaNode({
name: 'authority',
seeds: [
constantPdaSeedNode(bytesTypeNode(), getAnchorDiscriminatorV01([42, 31, 29])),
variablePdaSeedNode('owner', publicKeyTypeNode()),
],
}),
],
publicKey: '1111',
version: '1.2.3',
}),
Expand Down

0 comments on commit ffc6a39

Please sign in to comment.