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
6 changes: 6 additions & 0 deletions .changeset/four-bears-shave.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@codama/nodes-from-anchor': patch
'@codama/errors': patch
---

Unwrap generic types and constants from Anchor IDLs when converting them to Codama IDLs.
2 changes: 2 additions & 0 deletions packages/errors/src/codes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ export const CODAMA_ERROR__ANCHOR__ARGUMENT_TYPE_MISSING = 2100002;
export const CODAMA_ERROR__ANCHOR__TYPE_PATH_MISSING = 2100003;
export const CODAMA_ERROR__ANCHOR__SEED_KIND_UNIMPLEMENTED = 2100004;
export const CODAMA_ERROR__ANCHOR__PROGRAM_ID_KIND_UNIMPLEMENTED = 2100005;
export const CODAMA_ERROR__ANCHOR__GENERIC_TYPE_MISSING = 2100006;

// Renderers-related errors.
// Reserve error codes in the range [2800000-2800999].
Expand All @@ -84,6 +85,7 @@ export const CODAMA_ERROR__RENDERERS__UNSUPPORTED_NODE = 2800000;
export type CodamaErrorCode =
| typeof CODAMA_ERROR__ANCHOR__ACCOUNT_TYPE_MISSING
| typeof CODAMA_ERROR__ANCHOR__ARGUMENT_TYPE_MISSING
| typeof CODAMA_ERROR__ANCHOR__GENERIC_TYPE_MISSING
| typeof CODAMA_ERROR__ANCHOR__PROGRAM_ID_KIND_UNIMPLEMENTED
| typeof CODAMA_ERROR__ANCHOR__SEED_KIND_UNIMPLEMENTED
| typeof CODAMA_ERROR__ANCHOR__TYPE_PATH_MISSING
Expand Down
4 changes: 4 additions & 0 deletions packages/errors/src/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {
import {
CODAMA_ERROR__ANCHOR__ACCOUNT_TYPE_MISSING,
CODAMA_ERROR__ANCHOR__ARGUMENT_TYPE_MISSING,
CODAMA_ERROR__ANCHOR__GENERIC_TYPE_MISSING,
CODAMA_ERROR__ANCHOR__PROGRAM_ID_KIND_UNIMPLEMENTED,
CODAMA_ERROR__ANCHOR__SEED_KIND_UNIMPLEMENTED,
CODAMA_ERROR__ANCHOR__TYPE_PATH_MISSING,
Expand Down Expand Up @@ -69,6 +70,9 @@ export type CodamaErrorContext = DefaultUnspecifiedErrorContextToUndefined<{
[CODAMA_ERROR__ANCHOR__ARGUMENT_TYPE_MISSING]: {
name: string;
};
[CODAMA_ERROR__ANCHOR__GENERIC_TYPE_MISSING]: {
name: string;
};
[CODAMA_ERROR__ANCHOR__PROGRAM_ID_KIND_UNIMPLEMENTED]: {
kind: string;
};
Expand Down
2 changes: 2 additions & 0 deletions packages/errors/src/messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import {
CODAMA_ERROR__ANCHOR__ACCOUNT_TYPE_MISSING,
CODAMA_ERROR__ANCHOR__ARGUMENT_TYPE_MISSING,
CODAMA_ERROR__ANCHOR__GENERIC_TYPE_MISSING,
CODAMA_ERROR__ANCHOR__PROGRAM_ID_KIND_UNIMPLEMENTED,
CODAMA_ERROR__ANCHOR__SEED_KIND_UNIMPLEMENTED,
CODAMA_ERROR__ANCHOR__TYPE_PATH_MISSING,
Expand Down Expand Up @@ -49,6 +50,7 @@ export const CodamaErrorMessages: Readonly<{
}> = {
[CODAMA_ERROR__ANCHOR__ACCOUNT_TYPE_MISSING]: 'Account type [$name] is missing from the IDL types.',
[CODAMA_ERROR__ANCHOR__ARGUMENT_TYPE_MISSING]: 'Argument name [$name] is missing from the instruction definition.',
[CODAMA_ERROR__ANCHOR__GENERIC_TYPE_MISSING]: 'Generic type [$name] is missing from the IDL types.',
[CODAMA_ERROR__ANCHOR__PROGRAM_ID_KIND_UNIMPLEMENTED]: 'Program ID kind [$kind] is not implemented.',
[CODAMA_ERROR__ANCHOR__SEED_KIND_UNIMPLEMENTED]: 'Seed kind [$kind] is not implemented.',
[CODAMA_ERROR__ANCHOR__TYPE_PATH_MISSING]: 'Field type is missing for path [$path] in [$idlType].',
Expand Down
2 changes: 1 addition & 1 deletion packages/nodes-from-anchor/src/discriminators.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { BytesValueNode, bytesValueNode, pascalCase, snakeCase } from '@codama/nodes';
import { sha256 } from '@noble/hashes/sha256';
import { sha256 } from '@noble/hashes/sha2';

import { hex } from './utils';

Expand Down
17 changes: 8 additions & 9 deletions packages/nodes-from-anchor/src/v01/AccountNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,23 @@ import {
} from '@codama/nodes';

import { getAnchorDiscriminatorV01 } from './../discriminators';
import { IdlV01Account, IdlV01TypeDef } from './idl';
import type { IdlV01Account, IdlV01TypeDef } from './idl';
import { typeNodeFromAnchorV01 } from './typeNodes';
import type { GenericsV01 } from './unwrapGenerics';

export function accountNodeFromAnchorV01(idl: IdlV01Account, types: IdlV01TypeDef[]): AccountNode {
export function accountNodeFromAnchorV01(
idl: IdlV01Account,
types: IdlV01TypeDef[],
generics: GenericsV01,
): AccountNode {
const name = camelCase(idl.name);
const type = types.find(({ name }) => name === idl.name);

if (!type) {
throw new CodamaError(CODAMA_ERROR__ANCHOR__ACCOUNT_TYPE_MISSING, { name: idl.name });
}

const data = typeNodeFromAnchorV01(type.type);
const data = typeNodeFromAnchorV01(type.type, generics);
assertIsNode(data, 'structTypeNode');

const discriminator = structFieldTypeNode({
Expand All @@ -39,9 +44,3 @@ export function accountNodeFromAnchorV01(idl: IdlV01Account, types: IdlV01TypeDe
name,
});
}

export function accountNodeFromAnchorV01WithTypeDefinition(types: IdlV01TypeDef[]) {
return function (idl: IdlV01Account): AccountNode {
return accountNodeFromAnchorV01(idl, types);
};
}
7 changes: 4 additions & 3 deletions packages/nodes-from-anchor/src/v01/DefinedTypeNode.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { DefinedTypeNode, definedTypeNode } from '@codama/nodes';

import { IdlV01TypeDef } from './idl';
import type { IdlV01TypeDef } from './idl';
import { typeNodeFromAnchorV01 } from './typeNodes';
import type { GenericsV01 } from './unwrapGenerics';

export function definedTypeNodeFromAnchorV01(idl: Partial<IdlV01TypeDef>): DefinedTypeNode {
export function definedTypeNodeFromAnchorV01(idl: Partial<IdlV01TypeDef>, generics: GenericsV01): DefinedTypeNode {
const name = idl.name ?? '';
const idlType = idl.type ?? { fields: [], kind: 'struct' };
const type = typeNodeFromAnchorV01(idlType);
const type = typeNodeFromAnchorV01(idlType, generics);
return definedTypeNode({ docs: idl.docs, name, type });
}
7 changes: 4 additions & 3 deletions packages/nodes-from-anchor/src/v01/InstructionArgumentNode.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { InstructionArgumentNode, instructionArgumentNode } from '@codama/nodes';

import { IdlV01Field } from './idl';
import type { IdlV01Field } from './idl';
import { typeNodeFromAnchorV01 } from './typeNodes';
import type { GenericsV01 } from './unwrapGenerics';

export function instructionArgumentNodeFromAnchorV01(idl: IdlV01Field): InstructionArgumentNode {
export function instructionArgumentNodeFromAnchorV01(idl: IdlV01Field, generics: GenericsV01): InstructionArgumentNode {
return instructionArgumentNode({
docs: idl.docs ?? [],
name: idl.name,
type: typeNodeFromAnchorV01(idl.type),
type: typeNodeFromAnchorV01(idl.type, generics),
});
}
11 changes: 8 additions & 3 deletions packages/nodes-from-anchor/src/v01/InstructionNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,18 @@ import {
} from '@codama/nodes';

import { getAnchorDiscriminatorV01 } from '../discriminators';
import { IdlV01Instruction } from './idl';
import type { IdlV01Instruction } from './idl';
import { instructionAccountNodesFromAnchorV01 } from './InstructionAccountNode';
import { instructionArgumentNodeFromAnchorV01 } from './InstructionArgumentNode';
import type { GenericsV01 } from './unwrapGenerics';

export function instructionNodeFromAnchorV01(allAccounts: AccountNode[], idl: IdlV01Instruction): InstructionNode {
export function instructionNodeFromAnchorV01(
allAccounts: AccountNode[],
idl: IdlV01Instruction,
generics: GenericsV01,
): InstructionNode {
const name = idl.name;
let dataArguments = idl.args.map(instructionArgumentNodeFromAnchorV01);
let dataArguments = idl.args.map(arg => instructionArgumentNodeFromAnchorV01(arg, generics));

const discriminatorField = instructionArgumentNode({
defaultValue: getAnchorDiscriminatorV01(idl.discriminator),
Expand Down
14 changes: 8 additions & 6 deletions packages/nodes-from-anchor/src/v01/ProgramNode.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,29 @@
import { ProgramNode, programNode, ProgramVersion } from '@codama/nodes';

import { accountNodeFromAnchorV01WithTypeDefinition } from './AccountNode';
import { accountNodeFromAnchorV01 } from './AccountNode';
import { definedTypeNodeFromAnchorV01 } from './DefinedTypeNode';
import { errorNodeFromAnchorV01 } from './ErrorNode';
import { IdlV01 } from './idl';
import { instructionNodeFromAnchorV01 } from './InstructionNode';
import { extractGenerics } from './unwrapGenerics';

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

const filteredTypes = types.filter(type => !accounts.some(account => account.name === type.name));
const definedTypes = filteredTypes.map(definedTypeNodeFromAnchorV01);
const accountNodeFromAnchorV01 = accountNodeFromAnchorV01WithTypeDefinition(types);
const accountNodes = accounts.map(accountNodeFromAnchorV01);
const definedTypes = filteredTypes.map(type => definedTypeNodeFromAnchorV01(type, generics));
const accountNodes = accounts.map(account => accountNodeFromAnchorV01(account, types, generics));

return programNode({
accounts: accountNodes,
definedTypes,
errors: errors.map(errorNodeFromAnchorV01),
instructions: instructions.map(instruction => instructionNodeFromAnchorV01(accountNodes, instruction)),
instructions: instructions.map(instruction =>
instructionNodeFromAnchorV01(accountNodes, instruction, generics),
),
name: idl.metadata.name,
origin: 'anchor',
publicKey: idl.address,
Expand Down
3 changes: 2 additions & 1 deletion packages/nodes-from-anchor/src/v01/index.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
export * from './AccountNode';
export * from './DefinedTypeNode';
export * from './ErrorNode';
export * from './idl';
export * from './InstructionAccountNode';
export * from './InstructionArgumentNode';
export * from './InstructionNode';
export * from './ProgramNode';
export * from './RootNode';
export * from './idl';
export * from './typeNodes';
export * from './unwrapGenerics';
13 changes: 8 additions & 5 deletions packages/nodes-from-anchor/src/v01/typeNodes/ArrayTypeNode.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
import { ArrayTypeNode, arrayTypeNode, fixedCountNode, numberTypeNode, prefixedCountNode } from '@codama/nodes';

import { IdlV01TypeArray, IdlV01TypeVec } from '../idl';
import type { IdlV01TypeArray, IdlV01TypeVec } from '../idl';
import type { GenericsV01 } from '../unwrapGenerics';
import { typeNodeFromAnchorV01 } from './TypeNode';

export function arrayTypeNodeFromAnchorV01(idl: IdlV01TypeArray | IdlV01TypeVec): ArrayTypeNode {
export function arrayTypeNodeFromAnchorV01(idl: IdlV01TypeArray | IdlV01TypeVec, generics: GenericsV01): ArrayTypeNode {
if ('array' in idl) {
const item = typeNodeFromAnchorV01(idl.array[0]);
return arrayTypeNode(item, fixedCountNode(idl.array[1] as number));
const item = typeNodeFromAnchorV01(idl.array[0], generics);
const size =
typeof idl.array[1] === 'number' ? idl.array[1] : parseInt(generics.constArgs[idl.array[1].generic].value);
return arrayTypeNode(item, fixedCountNode(size));
}

const item = typeNodeFromAnchorV01(idl.vec);
const item = typeNodeFromAnchorV01(idl.vec, generics);

return arrayTypeNode(item, prefixedCountNode(numberTypeNode('u32')));
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import { EnumStructVariantTypeNode, enumStructVariantTypeNode, StructTypeNode } from '@codama/nodes';

import { IdlV01DefinedFieldsNamed, IdlV01EnumVariant } from '../idl';
import type { IdlV01DefinedFieldsNamed, IdlV01EnumVariant } from '../idl';
import type { GenericsV01 } from '../unwrapGenerics';
import { structTypeNodeFromAnchorV01 } from './StructTypeNode';

export function enumStructVariantTypeNodeFromAnchorV01(
idl: IdlV01EnumVariant & { fields: IdlV01DefinedFieldsNamed },
generics: GenericsV01,
): EnumStructVariantTypeNode<StructTypeNode> {
return enumStructVariantTypeNode(
idl.name ?? '',
structTypeNodeFromAnchorV01({ fields: idl.fields, kind: 'struct' }),
structTypeNodeFromAnchorV01({ fields: idl.fields, kind: 'struct' }, generics),
);
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { EnumTupleVariantTypeNode, enumTupleVariantTypeNode, TupleTypeNode } from '@codama/nodes';

import { IdlV01DefinedFieldsTuple, IdlV01EnumVariant } from '../idl';
import type { IdlV01DefinedFieldsTuple, IdlV01EnumVariant } from '../idl';
import type { GenericsV01 } from '../unwrapGenerics';
import { tupleTypeNodeFromAnchorV01 } from './TupleTypeNode';

export function enumTupleVariantTypeNodeFromAnchorV01(
idl: IdlV01EnumVariant & { fields: IdlV01DefinedFieldsTuple },
generics: GenericsV01,
): EnumTupleVariantTypeNode<TupleTypeNode> {
return enumTupleVariantTypeNode(idl.name ?? '', tupleTypeNodeFromAnchorV01(idl.fields));
return enumTupleVariantTypeNode(idl.name ?? '', tupleTypeNodeFromAnchorV01(idl.fields, generics));
}
12 changes: 10 additions & 2 deletions packages/nodes-from-anchor/src/v01/typeNodes/EnumTypeNode.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,30 @@
import { EnumTypeNode, enumTypeNode, EnumVariantTypeNode, NumberTypeNode } from '@codama/nodes';

import { IdlV01DefinedFieldsNamed, IdlV01DefinedFieldsTuple, IdlV01EnumVariant, IdlV01TypeDefTyEnum } from '../idl';
import type {
IdlV01DefinedFieldsNamed,
IdlV01DefinedFieldsTuple,
IdlV01EnumVariant,
IdlV01TypeDefTyEnum,
} from '../idl';
import type { GenericsV01 } from '../unwrapGenerics';
import { enumEmptyVariantTypeNodeFromAnchorV01 } from './EnumEmptyVariantTypeNode';
import { enumStructVariantTypeNodeFromAnchorV01 } from './EnumStructVariantTypeNode';
import { enumTupleVariantTypeNodeFromAnchorV01 } from './EnumTupleVariantTypeNode';

export function enumTypeNodeFromAnchorV01(
idl: IdlV01TypeDefTyEnum,
generics: GenericsV01,
): EnumTypeNode<EnumVariantTypeNode[], NumberTypeNode> {
const variants = idl.variants.map((variant): EnumVariantTypeNode => {
if (!variant.fields || variant.fields.length <= 0) {
return enumEmptyVariantTypeNodeFromAnchorV01(variant);
}
if (isStructVariant(variant)) {
return enumStructVariantTypeNodeFromAnchorV01(variant);
return enumStructVariantTypeNodeFromAnchorV01(variant, generics);
}
return enumTupleVariantTypeNodeFromAnchorV01(
variant as IdlV01EnumVariant & { fields: IdlV01DefinedFieldsTuple },
generics,
);
});
return enumTypeNode(variants);
Expand Down
10 changes: 7 additions & 3 deletions packages/nodes-from-anchor/src/v01/typeNodes/OptionTypeNode.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
import { numberTypeNode, OptionTypeNode, optionTypeNode } from '@codama/nodes';

import { IdlV01TypeCOption, IdlV01TypeOption } from '../idl';
import type { IdlV01TypeCOption, IdlV01TypeOption } from '../idl';
import type { GenericsV01 } from '../unwrapGenerics';
import { typeNodeFromAnchorV01 } from './TypeNode';

export function optionTypeNodeFromAnchorV01(idl: IdlV01TypeCOption | IdlV01TypeOption): OptionTypeNode {
export function optionTypeNodeFromAnchorV01(
idl: IdlV01TypeCOption | IdlV01TypeOption,
generics: GenericsV01,
): OptionTypeNode {
const item = 'option' in idl ? idl.option : idl.coption;
const hasOptionField = 'option' in idl;

const prefix = numberTypeNode(hasOptionField ? 'u8' : 'u32');
const fixed = !hasOptionField;

return optionTypeNode(typeNodeFromAnchorV01(item), {
return optionTypeNode(typeNodeFromAnchorV01(item, generics), {
fixed,
prefix,
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
import { CODAMA_ERROR__ANCHOR__UNRECOGNIZED_IDL_TYPE, CodamaError } from '@codama/errors';
import { StructFieldTypeNode, structFieldTypeNode } from '@codama/nodes';

import { IdlV01Field, IdlV01Type } from '../idl';
import type { IdlV01Field, IdlV01Type } from '../idl';
import type { GenericsV01 } from '../unwrapGenerics';
import { typeNodeFromAnchorV01 } from './TypeNode';

export function structFieldTypeNodeFromAnchorV01(idl: IdlV01Field | IdlV01Type): StructFieldTypeNode {
export function structFieldTypeNodeFromAnchorV01(
idl: IdlV01Field | IdlV01Type,
generics: GenericsV01,
): StructFieldTypeNode {
if (!isStructField(idl)) {
throw new CodamaError(CODAMA_ERROR__ANCHOR__UNRECOGNIZED_IDL_TYPE, {
idlType: JSON.stringify(idl),
Expand All @@ -14,7 +18,7 @@ export function structFieldTypeNodeFromAnchorV01(idl: IdlV01Field | IdlV01Type):
return structFieldTypeNode({
docs: idl.docs ?? [],
name: idl.name,
type: typeNodeFromAnchorV01(idl.type),
type: typeNodeFromAnchorV01(idl.type, generics),
});
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { StructTypeNode, structTypeNode } from '@codama/nodes';

import { IdlV01DefinedFields, IdlV01TypeDefTyStruct } from '../idl';
import type { IdlV01DefinedFields, IdlV01TypeDefTyStruct } from '../idl';
import type { GenericsV01 } from '../unwrapGenerics';
import { structFieldTypeNodeFromAnchorV01 } from './StructFieldTypeNode';

export function structTypeNodeFromAnchorV01(idl: IdlV01TypeDefTyStruct): StructTypeNode {
export function structTypeNodeFromAnchorV01(idl: IdlV01TypeDefTyStruct, generics: GenericsV01): StructTypeNode {
const fields: IdlV01DefinedFields = idl.fields ?? [];

return structTypeNode(fields.map(structFieldTypeNodeFromAnchorV01));
return structTypeNode(fields.map(field => structFieldTypeNodeFromAnchorV01(field, generics)));
}
7 changes: 4 additions & 3 deletions packages/nodes-from-anchor/src/v01/typeNodes/TupleTypeNode.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { TupleTypeNode, tupleTypeNode } from '@codama/nodes';

import { IdlV01DefinedFieldsTuple } from '../idl';
import type { IdlV01DefinedFieldsTuple } from '../idl';
import type { GenericsV01 } from '../unwrapGenerics';
import { typeNodeFromAnchorV01 } from './TypeNode';

export function tupleTypeNodeFromAnchorV01(idl: IdlV01DefinedFieldsTuple): TupleTypeNode {
return tupleTypeNode(idl.map(typeNodeFromAnchorV01));
export function tupleTypeNodeFromAnchorV01(idl: IdlV01DefinedFieldsTuple, generics: GenericsV01): TupleTypeNode {
return tupleTypeNode(idl.map(type => typeNodeFromAnchorV01(type, generics)));
}
Loading