Loris Leiva 3 дней назад
Родитель
Сommit
07825dec4c
36 измененных файлов с 560 добавлено и 138 удалено
  1. 6 0
      .changeset/four-bears-shave.md
  2. 2 0
      packages/errors/src/codes.ts
  3. 4 0
      packages/errors/src/context.ts
  4. 2 0
      packages/errors/src/messages.ts
  5. 1 1
      packages/nodes-from-anchor/src/discriminators.ts
  6. 8 9
      packages/nodes-from-anchor/src/v01/AccountNode.ts
  7. 4 3
      packages/nodes-from-anchor/src/v01/DefinedTypeNode.ts
  8. 4 3
      packages/nodes-from-anchor/src/v01/InstructionArgumentNode.ts
  9. 8 3
      packages/nodes-from-anchor/src/v01/InstructionNode.ts
  10. 8 6
      packages/nodes-from-anchor/src/v01/ProgramNode.ts
  11. 2 1
      packages/nodes-from-anchor/src/v01/index.ts
  12. 8 5
      packages/nodes-from-anchor/src/v01/typeNodes/ArrayTypeNode.ts
  13. 4 2
      packages/nodes-from-anchor/src/v01/typeNodes/EnumStructVariantTypeNode.ts
  14. 4 2
      packages/nodes-from-anchor/src/v01/typeNodes/EnumTupleVariantTypeNode.ts
  15. 10 2
      packages/nodes-from-anchor/src/v01/typeNodes/EnumTypeNode.ts
  16. 7 3
      packages/nodes-from-anchor/src/v01/typeNodes/OptionTypeNode.ts
  17. 7 3
      packages/nodes-from-anchor/src/v01/typeNodes/StructFieldTypeNode.ts
  18. 4 3
      packages/nodes-from-anchor/src/v01/typeNodes/StructTypeNode.ts
  19. 4 3
      packages/nodes-from-anchor/src/v01/typeNodes/TupleTypeNode.ts
  20. 19 12
      packages/nodes-from-anchor/src/v01/typeNodes/TypeNode.ts
  21. 63 0
      packages/nodes-from-anchor/src/v01/unwrapGenerics.ts
  22. 4 1
      packages/nodes-from-anchor/test/v01/AccountNode.test.ts
  23. 85 8
      packages/nodes-from-anchor/test/v01/DefinedTypeNode.test.ts
  24. 10 5
      packages/nodes-from-anchor/test/v01/InstructionArgumentNode.test.ts
  25. 14 7
      packages/nodes-from-anchor/test/v01/InstructionNode.test.ts
  26. 161 0
      packages/nodes-from-anchor/test/v01/ProgramNode.test.ts
  27. 20 3
      packages/nodes-from-anchor/test/v01/typeNodes/ArrayTypeNode.test.ts
  28. 4 2
      packages/nodes-from-anchor/test/v01/typeNodes/BooleanTypeNode.test.ts
  29. 6 2
      packages/nodes-from-anchor/test/v01/typeNodes/BytesTypeNode.test.ts
  30. 14 9
      packages/nodes-from-anchor/test/v01/typeNodes/EnumTypeNode.test.ts
  31. 16 14
      packages/nodes-from-anchor/test/v01/typeNodes/NumberTypeNode.test.ts
  32. 6 4
      packages/nodes-from-anchor/test/v01/typeNodes/OptionTypeNode.test.ts
  33. 4 2
      packages/nodes-from-anchor/test/v01/typeNodes/PublicKeyTypeNode.test.ts
  34. 6 2
      packages/nodes-from-anchor/test/v01/typeNodes/StringTypeNode.test.ts
  35. 21 13
      packages/nodes-from-anchor/test/v01/typeNodes/StructTypeNode.test.ts
  36. 10 5
      packages/nodes-from-anchor/test/v01/typeNodes/TupleTypeNode.test.ts

+ 6 - 0
.changeset/four-bears-shave.md

@@ -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 - 0
packages/errors/src/codes.ts

@@ -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].
@@ -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

+ 4 - 0
packages/errors/src/context.ts

@@ -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,
@@ -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;
     };

+ 2 - 0
packages/errors/src/messages.ts

@@ -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,
@@ -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].',

+ 1 - 1
packages/nodes-from-anchor/src/discriminators.ts

@@ -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';
 

+ 8 - 9
packages/nodes-from-anchor/src/v01/AccountNode.ts

@@ -12,10 +12,15 @@ 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);
 
@@ -23,7 +28,7 @@ export function accountNodeFromAnchorV01(idl: IdlV01Account, types: IdlV01TypeDe
         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({
@@ -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);
-    };
-}

+ 4 - 3
packages/nodes-from-anchor/src/v01/DefinedTypeNode.ts

@@ -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 });
 }

+ 4 - 3
packages/nodes-from-anchor/src/v01/InstructionArgumentNode.ts

@@ -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),
     });
 }

+ 8 - 3
packages/nodes-from-anchor/src/v01/InstructionNode.ts

@@ -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),

+ 8 - 6
packages/nodes-from-anchor/src/v01/ProgramNode.ts

@@ -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,

+ 2 - 1
packages/nodes-from-anchor/src/v01/index.ts

@@ -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';

+ 8 - 5
packages/nodes-from-anchor/src/v01/typeNodes/ArrayTypeNode.ts

@@ -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')));
 }

+ 4 - 2
packages/nodes-from-anchor/src/v01/typeNodes/EnumStructVariantTypeNode.ts

@@ -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),
     );
 }

+ 4 - 2
packages/nodes-from-anchor/src/v01/typeNodes/EnumTupleVariantTypeNode.ts

@@ -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));
 }

+ 10 - 2
packages/nodes-from-anchor/src/v01/typeNodes/EnumTypeNode.ts

@@ -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);

+ 7 - 3
packages/nodes-from-anchor/src/v01/typeNodes/OptionTypeNode.ts

@@ -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,
     });

+ 7 - 3
packages/nodes-from-anchor/src/v01/typeNodes/StructFieldTypeNode.ts

@@ -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),
@@ -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),
     });
 }
 

+ 4 - 3
packages/nodes-from-anchor/src/v01/typeNodes/StructTypeNode.ts

@@ -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)));
 }

+ 4 - 3
packages/nodes-from-anchor/src/v01/typeNodes/TupleTypeNode.ts

@@ -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)));
 }

+ 19 - 12
packages/nodes-from-anchor/src/v01/typeNodes/TypeNode.ts

@@ -10,7 +10,7 @@ import {
     TypeNode,
 } from '@codama/nodes';
 
-import {
+import type {
     IdlV01DefinedFields,
     IdlV01DefinedFieldsNamed,
     IdlV01DefinedFieldsTuple,
@@ -18,6 +18,7 @@ import {
     IdlV01Type,
     IdlV01TypeDefTy,
 } from '../idl';
+import { type GenericsV01, unwrapGenericTypeFromAnchorV01 } from '../unwrapGenerics';
 import { arrayTypeNodeFromAnchorV01 } from './ArrayTypeNode';
 import { enumTypeNodeFromAnchorV01 } from './EnumTypeNode';
 import { optionTypeNodeFromAnchorV01 } from './OptionTypeNode';
@@ -44,7 +45,7 @@ const IDL_V01_TYPE_LEAVES = [
     'shortU16',
 ] as const;
 
-export const typeNodeFromAnchorV01 = (idlType: IdlV01Type | IdlV01TypeDefTy): TypeNode => {
+export const typeNodeFromAnchorV01 = (idlType: IdlV01Type | IdlV01TypeDefTy, generics: GenericsV01): TypeNode => {
     // Leaf.
     if (typeof idlType === 'string' && IDL_V01_TYPE_LEAVES.includes(idlType)) {
         if (idlType === 'bool') return booleanTypeNode();
@@ -63,47 +64,53 @@ export const typeNodeFromAnchorV01 = (idlType: IdlV01Type | IdlV01TypeDefTy): Ty
 
     // Array.
     if ('array' in idlType && isArrayOfSize(idlType.array, 2)) {
-        return arrayTypeNodeFromAnchorV01(idlType);
+        return arrayTypeNodeFromAnchorV01(idlType, generics);
     }
 
     // Vec.
     if ('vec' in idlType) {
-        return arrayTypeNodeFromAnchorV01(idlType);
+        return arrayTypeNodeFromAnchorV01(idlType, generics);
     }
 
     // Defined link.
-    // TODO: Support generics.
     if ('defined' in idlType && typeof idlType.defined === 'object') {
-        return definedTypeLinkNode(idlType.defined.name);
+        return 'generics' in idlType.defined
+            ? unwrapGenericTypeFromAnchorV01(idlType, generics)
+            : definedTypeLinkNode(idlType.defined.name);
+    }
+
+    // Generic reference.
+    if ('generic' in idlType) {
+        return typeNodeFromAnchorV01(generics.typeArgs[idlType.generic].type, generics);
     }
 
     // Enum.
     if ('kind' in idlType && idlType.kind === 'enum' && 'variants' in idlType) {
-        return enumTypeNodeFromAnchorV01(idlType);
+        return enumTypeNodeFromAnchorV01(idlType, generics);
     }
 
     // Alias.
     if ('kind' in idlType && idlType.kind === 'alias' && 'value' in idlType) {
-        return typeNodeFromAnchorV01(idlType.value);
+        return typeNodeFromAnchorV01(idlType.value, generics);
     }
 
     // Option.
     if ('option' in idlType) {
-        return optionTypeNodeFromAnchorV01(idlType);
+        return optionTypeNodeFromAnchorV01(idlType, generics);
     }
 
     if ('coption' in idlType) {
-        return optionTypeNodeFromAnchorV01(idlType);
+        return optionTypeNodeFromAnchorV01(idlType, generics);
     }
 
     // Struct and Tuple.
     if ('kind' in idlType && idlType.kind === 'struct') {
         const fields = idlType.fields ?? [];
         if (isStructFieldArray(fields)) {
-            return structTypeNodeFromAnchorV01(idlType);
+            return structTypeNodeFromAnchorV01(idlType, generics);
         }
         if (isTupleFieldArray(fields)) {
-            return tupleTypeNodeFromAnchorV01(fields);
+            return tupleTypeNodeFromAnchorV01(fields, generics);
         }
     }
 

+ 63 - 0
packages/nodes-from-anchor/src/v01/unwrapGenerics.ts

@@ -0,0 +1,63 @@
+import { CODAMA_ERROR__ANCHOR__GENERIC_TYPE_MISSING, CodamaError } from '@codama/errors';
+import { TypeNode } from '@codama/nodes';
+
+import {
+    IdlV01GenericArgConst,
+    IdlV01GenericArgType,
+    IdlV01TypeDef,
+    IdlV01TypeDefGenericConst,
+    IdlV01TypeDefGenericType,
+    IdlV01TypeDefined,
+} from './idl';
+import { typeNodeFromAnchorV01 } from './typeNodes';
+
+export type GenericsV01 = {
+    constArgs: Record<string, IdlV01GenericArgConst & IdlV01TypeDefGenericConst>;
+    typeArgs: Record<string, IdlV01GenericArgType & IdlV01TypeDefGenericType>;
+    types: Record<string, IdlV01TypeDef & Required<Pick<IdlV01TypeDef, 'generics'>>>;
+};
+
+export function extractGenerics(types: IdlV01TypeDef[]): [IdlV01TypeDef[], GenericsV01] {
+    const [nonGenericTypes, genericTypes] = types.reduce(
+        (acc, type) => {
+            acc['generics' in type ? 1 : 0].push(type);
+            return acc;
+        },
+        [[], []] as [IdlV01TypeDef[], IdlV01TypeDef[]],
+    );
+
+    const generics = {
+        constArgs: {},
+        typeArgs: {},
+        types: Object.fromEntries(genericTypes.map(type => [type.name, type])),
+    } as GenericsV01;
+
+    return [nonGenericTypes, generics];
+}
+
+export function unwrapGenericTypeFromAnchorV01(type: IdlV01TypeDefined, generics: GenericsV01): TypeNode {
+    const genericType = generics.types[type.defined.name];
+    if (!genericType) {
+        throw new CodamaError(CODAMA_ERROR__ANCHOR__GENERIC_TYPE_MISSING, { name: type.defined.name });
+    }
+
+    const constArgs: GenericsV01['constArgs'] = {};
+    const typeArgs: GenericsV01['typeArgs'] = {};
+
+    const genericDefinitions = genericType.generics ?? [];
+    const genericArgs = type.defined.generics ?? [];
+    genericDefinitions.forEach((genericDefinition, index) => {
+        const genericArg = genericArgs[index];
+        if (genericDefinition.kind === 'const') {
+            constArgs[genericDefinition.name] = genericArg as IdlV01GenericArgConst & IdlV01TypeDefGenericConst;
+        } else {
+            typeArgs[genericDefinition.name] = genericArg as IdlV01GenericArgType & IdlV01TypeDefGenericType;
+        }
+    });
+
+    return typeNodeFromAnchorV01(genericType.type, {
+        ...generics,
+        constArgs: { ...generics.constArgs, ...constArgs },
+        typeArgs: { ...generics.typeArgs, ...typeArgs },
+    });
+}

+ 4 - 1
packages/nodes-from-anchor/test/v01/AccountNode.test.ts

@@ -9,7 +9,9 @@ import {
 } from '@codama/nodes';
 import { expect, test } from 'vitest';
 
-import { accountNodeFromAnchorV01, getAnchorDiscriminatorV01 } from '../../src';
+import { accountNodeFromAnchorV01, GenericsV01, getAnchorDiscriminatorV01 } from '../../src';
+
+const generics = {} as GenericsV01;
 
 test('it creates account nodes with anchor discriminators', () => {
     const node = accountNodeFromAnchorV01(
@@ -32,6 +34,7 @@ test('it creates account nodes with anchor discriminators', () => {
                 },
             },
         ],
+        generics,
     );
 
     expect(node).toEqual(

+ 85 - 8
packages/nodes-from-anchor/test/v01/DefinedTypeNode.test.ts

@@ -1,16 +1,20 @@
-import { definedTypeNode, numberTypeNode, structFieldTypeNode, structTypeNode } from '@codama/nodes';
+/* eslint-disable sort-keys-fix/sort-keys-fix */
+import { definedTypeNode, numberTypeNode, structFieldTypeNode, structTypeNode, tupleTypeNode } from '@codama/nodes';
 import { expect, test } from 'vitest';
 
-import { definedTypeNodeFromAnchorV01 } from '../../src';
+import { definedTypeNodeFromAnchorV01, GenericsV01 } from '../../src';
 
 test('it creates defined type nodes', () => {
-    const node = definedTypeNodeFromAnchorV01({
-        name: 'MyType',
-        type: {
-            fields: [{ name: 'my_field', type: 'u64' }],
-            kind: 'struct',
+    const node = definedTypeNodeFromAnchorV01(
+        {
+            name: 'MyType',
+            type: {
+                fields: [{ name: 'my_field', type: 'u64' }],
+                kind: 'struct',
+            },
         },
-    });
+        {} as GenericsV01,
+    );
 
     expect(node).toEqual(
         definedTypeNode({
@@ -24,3 +28,76 @@ test('it creates defined type nodes', () => {
         }),
     );
 });
+
+test('it unwraps generic arguments', () => {
+    const node = definedTypeNodeFromAnchorV01(
+        {
+            name: 'Buffer',
+            type: {
+                fields: [{ name: 'data', type: { generic: 'T' } }],
+                kind: 'struct',
+            },
+        },
+        {
+            constArgs: {},
+            typeArgs: { T: { name: 'T', kind: 'type', type: 'u64' } },
+            types: {},
+        },
+    );
+
+    expect(node).toEqual(
+        definedTypeNode({
+            name: 'buffer',
+            type: structTypeNode([
+                structFieldTypeNode({
+                    name: 'data',
+                    type: numberTypeNode('u64'),
+                }),
+            ]),
+        }),
+    );
+});
+
+test('it unwraps nested generic types', () => {
+    const node = definedTypeNodeFromAnchorV01(
+        {
+            name: 'Buffer',
+            type: {
+                fields: [{ name: 'data', type: { generic: 'T' } }],
+                kind: 'struct',
+            },
+        },
+        {
+            constArgs: {},
+            typeArgs: {
+                T: {
+                    name: 'T',
+                    kind: 'type',
+                    type: { defined: { name: 'PrefixedData', generics: [{ kind: 'type', type: 'u8' }] } },
+                },
+            },
+            types: {
+                PrefixedData: {
+                    name: 'PrefixedData',
+                    generics: [{ name: 'T', kind: 'type' }],
+                    type: {
+                        fields: ['u64', { generic: 'T' }],
+                        kind: 'struct',
+                    },
+                },
+            },
+        },
+    );
+
+    expect(node).toEqual(
+        definedTypeNode({
+            name: 'buffer',
+            type: structTypeNode([
+                structFieldTypeNode({
+                    name: 'data',
+                    type: tupleTypeNode([numberTypeNode('u64'), numberTypeNode('u8')]),
+                }),
+            ]),
+        }),
+    );
+});

+ 10 - 5
packages/nodes-from-anchor/test/v01/InstructionArgumentNode.test.ts

@@ -1,13 +1,18 @@
 import { instructionArgumentNode, numberTypeNode } from '@codama/nodes';
 import { expect, test } from 'vitest';
 
-import { instructionArgumentNodeFromAnchorV01 } from '../../src';
+import { GenericsV01, instructionArgumentNodeFromAnchorV01 } from '../../src';
+
+const generics = {} as GenericsV01;
 
 test('it creates instruction argument nodes', () => {
-    const node = instructionArgumentNodeFromAnchorV01({
-        name: 'my_instruction_argument',
-        type: 'u8',
-    });
+    const node = instructionArgumentNodeFromAnchorV01(
+        {
+            name: 'my_instruction_argument',
+            type: 'u8',
+        },
+        generics,
+    );
 
     expect(node).toEqual(
         instructionArgumentNode({

+ 14 - 7
packages/nodes-from-anchor/test/v01/InstructionNode.test.ts

@@ -13,7 +13,9 @@ import {
 } from '@codama/nodes';
 import { expect, test } from 'vitest';
 
-import { getAnchorDiscriminatorV01, instructionNodeFromAnchorV01 } from '../../src';
+import { GenericsV01, getAnchorDiscriminatorV01, instructionNodeFromAnchorV01 } from '../../src';
+
+const generics = {} as GenericsV01;
 
 test('it creates instruction nodes', () => {
     const node = instructionNodeFromAnchorV01(
@@ -50,6 +52,7 @@ test('it creates instruction nodes', () => {
             discriminator: [246, 28, 6, 87, 251, 45, 50, 42],
             name: 'mintTokens',
         },
+        generics,
     );
 
     expect(node).toEqual(
@@ -88,12 +91,16 @@ test('it creates instruction nodes', () => {
 });
 
 test('it creates instruction nodes with anchor discriminators', () => {
-    const node = instructionNodeFromAnchorV01([], {
-        accounts: [],
-        args: [],
-        discriminator: [246, 28, 6, 87, 251, 45, 50, 42],
-        name: 'myInstruction',
-    });
+    const node = instructionNodeFromAnchorV01(
+        [],
+        {
+            accounts: [],
+            args: [],
+            discriminator: [246, 28, 6, 87, 251, 45, 50, 42],
+            name: 'myInstruction',
+        },
+        generics,
+    );
 
     expect(node).toEqual(
         instructionNode({

+ 161 - 0
packages/nodes-from-anchor/test/v01/ProgramNode.test.ts

@@ -2,10 +2,17 @@ import {
     accountNode,
     accountValueNode,
     argumentValueNode,
+    arrayTypeNode,
     bytesTypeNode,
     constantPdaSeedNodeFromBytes,
+    definedTypeLinkNode,
+    definedTypeNode,
+    enumEmptyVariantTypeNode,
+    enumTupleVariantTypeNode,
+    enumTypeNode,
     errorNode,
     fieldDiscriminatorNode,
+    fixedCountNode,
     fixedSizeTypeNode,
     instructionAccountNode,
     instructionArgumentNode,
@@ -18,6 +25,7 @@ import {
     publicKeyTypeNode,
     structFieldTypeNode,
     structTypeNode,
+    tupleTypeNode,
     variablePdaSeedNode,
 } from '@codama/nodes';
 import { expect, test } from 'vitest';
@@ -146,3 +154,156 @@ test('it creates program nodes', () => {
         }),
     );
 });
+
+test('it unwraps and removes generic types', () => {
+    const node = programNodeFromAnchorV01({
+        address: '1111',
+        instructions: [],
+        metadata: { name: 'my_program', spec: '0.1.0', version: '1.2.3' },
+        types: [
+            {
+                generics: [
+                    { kind: 'const', name: 'N', type: 'usize' },
+                    { kind: 'type', name: 'T' },
+                ],
+                name: 'SimpleAllocator',
+                type: {
+                    fields: [
+                        {
+                            name: 'state',
+                            type: { array: [{ defined: { name: 'ItemState' } }, { generic: 'N' }] },
+                        },
+                        {
+                            name: 'data',
+                            type: { array: [{ generic: 'T' }, { generic: 'N' }] },
+                        },
+                    ],
+                    kind: 'struct',
+                },
+            },
+            {
+                name: 'AccountData',
+                type: {
+                    kind: 'enum',
+                    variants: [
+                        { name: 'Unknown' },
+                        {
+                            fields: [
+                                {
+                                    defined: {
+                                        generics: [
+                                            { kind: 'const', value: '1000' },
+                                            { kind: 'type', type: { defined: { name: 'VirtualTimelockAccount' } } },
+                                        ],
+                                        name: 'SimpleAllocator',
+                                    },
+                                },
+                            ],
+                            name: 'Timelock',
+                        },
+                        {
+                            fields: [
+                                {
+                                    defined: {
+                                        generics: [
+                                            { kind: 'const', value: '500' },
+                                            { kind: 'type', type: { defined: { name: 'VirtualDurableNonce' } } },
+                                        ],
+                                        name: 'SimpleAllocator',
+                                    },
+                                },
+                            ],
+                            name: 'Nonce',
+                        },
+                        {
+                            fields: [
+                                {
+                                    defined: {
+                                        generics: [
+                                            { kind: 'const', value: '250' },
+                                            { kind: 'type', type: { defined: { name: 'VirtualRelayAccount' } } },
+                                        ],
+                                        name: 'SimpleAllocator',
+                                    },
+                                },
+                            ],
+                            name: 'Relay',
+                        },
+                    ],
+                },
+            },
+        ],
+    });
+
+    expect(node).toEqual(
+        programNode({
+            definedTypes: [
+                definedTypeNode({
+                    name: 'AccountData',
+                    type: enumTypeNode([
+                        enumEmptyVariantTypeNode('unknown'),
+                        enumTupleVariantTypeNode(
+                            'timelock',
+                            tupleTypeNode([
+                                structTypeNode([
+                                    structFieldTypeNode({
+                                        name: 'state',
+                                        type: arrayTypeNode(definedTypeLinkNode('itemState'), fixedCountNode(1000)),
+                                    }),
+                                    structFieldTypeNode({
+                                        name: 'data',
+                                        type: arrayTypeNode(
+                                            definedTypeLinkNode('virtualTimelockAccount'),
+                                            fixedCountNode(1000),
+                                        ),
+                                    }),
+                                ]),
+                            ]),
+                        ),
+                        enumTupleVariantTypeNode(
+                            'nonce',
+                            tupleTypeNode([
+                                structTypeNode([
+                                    structFieldTypeNode({
+                                        name: 'state',
+                                        type: arrayTypeNode(definedTypeLinkNode('itemState'), fixedCountNode(500)),
+                                    }),
+                                    structFieldTypeNode({
+                                        name: 'data',
+                                        type: arrayTypeNode(
+                                            definedTypeLinkNode('virtualDurableNonce'),
+                                            fixedCountNode(500),
+                                        ),
+                                    }),
+                                ]),
+                            ]),
+                        ),
+                        enumTupleVariantTypeNode(
+                            'relay',
+                            tupleTypeNode([
+                                structTypeNode([
+                                    structFieldTypeNode({
+                                        name: 'state',
+                                        type: arrayTypeNode(definedTypeLinkNode('itemState'), fixedCountNode(250)),
+                                    }),
+                                    structFieldTypeNode({
+                                        name: 'data',
+                                        type: arrayTypeNode(
+                                            definedTypeLinkNode('virtualRelayAccount'),
+                                            fixedCountNode(250),
+                                        ),
+                                    }),
+                                ]),
+                            ]),
+                        ),
+                    ]),
+                }),
+            ],
+            name: 'myProgram',
+            origin: 'anchor',
+            pdas: [],
+            publicKey: '1111',
+            version: '1.2.3',
+        }),
+    );
+});

+ 20 - 3
packages/nodes-from-anchor/test/v01/typeNodes/ArrayTypeNode.test.ts

@@ -1,11 +1,28 @@
 import { arrayTypeNode, fixedCountNode, numberTypeNode, prefixedCountNode } from '@codama/nodes';
 import { expect, test } from 'vitest';
 
-import { typeNodeFromAnchorV01 } from '../../../src';
+import { GenericsV01, typeNodeFromAnchorV01 } from '../../../src';
+
+const generics = {} as GenericsV01;
 
 test('it creates array type nodes', () => {
-    expect(typeNodeFromAnchorV01({ array: ['u8', 2] })).toEqual(arrayTypeNode(numberTypeNode('u8'), fixedCountNode(2)));
-    expect(typeNodeFromAnchorV01({ vec: 'u8' })).toEqual(
+    expect(typeNodeFromAnchorV01({ array: ['u8', 2] }, generics)).toEqual(
+        arrayTypeNode(numberTypeNode('u8'), fixedCountNode(2)),
+    );
+    expect(typeNodeFromAnchorV01({ vec: 'u8' }, generics)).toEqual(
         arrayTypeNode(numberTypeNode('u8'), prefixedCountNode(numberTypeNode('u32'))),
     );
 });
+
+test('it unwraps array nodes with generic sizes', () => {
+    expect(
+        typeNodeFromAnchorV01(
+            { array: ['u8', { generic: 'N' }] },
+            {
+                constArgs: { N: { kind: 'const', name: 'N', type: 'usize', value: '100' } },
+                typeArgs: {},
+                types: {},
+            },
+        ),
+    ).toEqual(arrayTypeNode(numberTypeNode('u8'), fixedCountNode(100)));
+});

+ 4 - 2
packages/nodes-from-anchor/test/v01/typeNodes/BooleanTypeNode.test.ts

@@ -1,8 +1,10 @@
 import { booleanTypeNode } from '@codama/nodes';
 import { expect, test } from 'vitest';
 
-import { typeNodeFromAnchorV01 } from '../../../src';
+import { GenericsV01, typeNodeFromAnchorV01 } from '../../../src';
+
+const generics = {} as GenericsV01;
 
 test('it creates boolean type nodes', () => {
-    expect(typeNodeFromAnchorV01('bool')).toEqual(booleanTypeNode());
+    expect(typeNodeFromAnchorV01('bool', generics)).toEqual(booleanTypeNode());
 });

+ 6 - 2
packages/nodes-from-anchor/test/v01/typeNodes/BytesTypeNode.test.ts

@@ -1,8 +1,12 @@
 import { bytesTypeNode, numberTypeNode, sizePrefixTypeNode } from '@codama/nodes';
 import { expect, test } from 'vitest';
 
-import { typeNodeFromAnchorV01 } from '../../../src';
+import { GenericsV01, typeNodeFromAnchorV01 } from '../../../src';
+
+const generics = {} as GenericsV01;
 
 test('it creates bytes type nodes', () => {
-    expect(typeNodeFromAnchorV01('bytes')).toEqual(sizePrefixTypeNode(bytesTypeNode(), numberTypeNode('u32')));
+    expect(typeNodeFromAnchorV01('bytes', generics)).toEqual(
+        sizePrefixTypeNode(bytesTypeNode(), numberTypeNode('u32')),
+    );
 });

+ 14 - 9
packages/nodes-from-anchor/test/v01/typeNodes/EnumTypeNode.test.ts

@@ -11,17 +11,22 @@ import {
 } from '@codama/nodes';
 import { expect, test } from 'vitest';
 
-import { typeNodeFromAnchorV01 } from '../../../src';
+import { GenericsV01, typeNodeFromAnchorV01 } from '../../../src';
+
+const generics = {} as GenericsV01;
 
 test('it creates enum type nodes', () => {
-    const node = typeNodeFromAnchorV01({
-        kind: 'enum',
-        variants: [
-            { name: 'variantA' }, // Empty variant.
-            { fields: ['u16', 'bool'], name: 'variantB' }, // Tuple variant.
-            { fields: [{ name: 'age', type: 'u8' }], name: 'variantC' }, // Struct variant.
-        ],
-    });
+    const node = typeNodeFromAnchorV01(
+        {
+            kind: 'enum',
+            variants: [
+                { name: 'variantA' }, // Empty variant.
+                { fields: ['u16', 'bool'], name: 'variantB' }, // Tuple variant.
+                { fields: [{ name: 'age', type: 'u8' }], name: 'variantC' }, // Struct variant.
+            ],
+        },
+        generics,
+    );
 
     expect(node).toEqual(
         enumTypeNode([

+ 16 - 14
packages/nodes-from-anchor/test/v01/typeNodes/NumberTypeNode.test.ts

@@ -1,20 +1,22 @@
 import { numberTypeNode } from '@codama/nodes';
 import { expect, test } from 'vitest';
 
-import { typeNodeFromAnchorV01 } from '../../../src';
+import { GenericsV01, typeNodeFromAnchorV01 } from '../../../src';
+
+const generics = {} as GenericsV01;
 
 test('it creates number type nodes', () => {
-    expect(typeNodeFromAnchorV01('f32')).toEqual(numberTypeNode('f32'));
-    expect(typeNodeFromAnchorV01('f64')).toEqual(numberTypeNode('f64'));
-    expect(typeNodeFromAnchorV01('i8')).toEqual(numberTypeNode('i8'));
-    expect(typeNodeFromAnchorV01('i16')).toEqual(numberTypeNode('i16'));
-    expect(typeNodeFromAnchorV01('i32')).toEqual(numberTypeNode('i32'));
-    expect(typeNodeFromAnchorV01('i64')).toEqual(numberTypeNode('i64'));
-    expect(typeNodeFromAnchorV01('i128')).toEqual(numberTypeNode('i128'));
-    expect(typeNodeFromAnchorV01('shortU16')).toEqual(numberTypeNode('shortU16'));
-    expect(typeNodeFromAnchorV01('u8')).toEqual(numberTypeNode('u8'));
-    expect(typeNodeFromAnchorV01('u16')).toEqual(numberTypeNode('u16'));
-    expect(typeNodeFromAnchorV01('u32')).toEqual(numberTypeNode('u32'));
-    expect(typeNodeFromAnchorV01('u64')).toEqual(numberTypeNode('u64'));
-    expect(typeNodeFromAnchorV01('u128')).toEqual(numberTypeNode('u128'));
+    expect(typeNodeFromAnchorV01('f32', generics)).toEqual(numberTypeNode('f32'));
+    expect(typeNodeFromAnchorV01('f64', generics)).toEqual(numberTypeNode('f64'));
+    expect(typeNodeFromAnchorV01('i8', generics)).toEqual(numberTypeNode('i8'));
+    expect(typeNodeFromAnchorV01('i16', generics)).toEqual(numberTypeNode('i16'));
+    expect(typeNodeFromAnchorV01('i32', generics)).toEqual(numberTypeNode('i32'));
+    expect(typeNodeFromAnchorV01('i64', generics)).toEqual(numberTypeNode('i64'));
+    expect(typeNodeFromAnchorV01('i128', generics)).toEqual(numberTypeNode('i128'));
+    expect(typeNodeFromAnchorV01('shortU16', generics)).toEqual(numberTypeNode('shortU16'));
+    expect(typeNodeFromAnchorV01('u8', generics)).toEqual(numberTypeNode('u8'));
+    expect(typeNodeFromAnchorV01('u16', generics)).toEqual(numberTypeNode('u16'));
+    expect(typeNodeFromAnchorV01('u32', generics)).toEqual(numberTypeNode('u32'));
+    expect(typeNodeFromAnchorV01('u64', generics)).toEqual(numberTypeNode('u64'));
+    expect(typeNodeFromAnchorV01('u128', generics)).toEqual(numberTypeNode('u128'));
 });

+ 6 - 4
packages/nodes-from-anchor/test/v01/typeNodes/OptionTypeNode.test.ts

@@ -1,17 +1,19 @@
 import { numberTypeNode, optionTypeNode } from '@codama/nodes';
 import { expect, test } from 'vitest';
 
-import { typeNodeFromAnchorV01 } from '../../../src';
+import { GenericsV01, typeNodeFromAnchorV01 } from '../../../src';
+
+const generics = {} as GenericsV01;
 
 test('it creates option type nodes', () => {
-    expect(typeNodeFromAnchorV01({ option: 'u8' })).toEqual(optionTypeNode(numberTypeNode('u8')));
+    expect(typeNodeFromAnchorV01({ option: 'u8' }, generics)).toEqual(optionTypeNode(numberTypeNode('u8')));
 });
 
 test('it creates option type nodes with fixed size', () => {
-    expect(typeNodeFromAnchorV01({ coption: 'u8' })).toEqual(
+    expect(typeNodeFromAnchorV01({ coption: 'u8' }, generics)).toEqual(
         optionTypeNode(numberTypeNode('u8'), { fixed: true, prefix: numberTypeNode('u32') }),
     );
-    expect(typeNodeFromAnchorV01({ coption: 'u8' })).toEqual(
+    expect(typeNodeFromAnchorV01({ coption: 'u8' }, generics)).toEqual(
         optionTypeNode(numberTypeNode('u8'), { fixed: true, prefix: numberTypeNode('u32') }),
     );
 });

+ 4 - 2
packages/nodes-from-anchor/test/v01/typeNodes/PublicKeyTypeNode.test.ts

@@ -1,8 +1,10 @@
 import { publicKeyTypeNode } from '@codama/nodes';
 import { expect, test } from 'vitest';
 
-import { typeNodeFromAnchorV01 } from '../../../src';
+import { GenericsV01, typeNodeFromAnchorV01 } from '../../../src';
+
+const generics = {} as GenericsV01;
 
 test('it creates public key type nodes', () => {
-    expect(typeNodeFromAnchorV01('pubkey')).toEqual(publicKeyTypeNode());
+    expect(typeNodeFromAnchorV01('pubkey', generics)).toEqual(publicKeyTypeNode());
 });

+ 6 - 2
packages/nodes-from-anchor/test/v01/typeNodes/StringTypeNode.test.ts

@@ -1,8 +1,12 @@
 import { numberTypeNode, sizePrefixTypeNode, stringTypeNode } from '@codama/nodes';
 import { expect, test } from 'vitest';
 
-import { typeNodeFromAnchorV01 } from '../../../src';
+import { GenericsV01, typeNodeFromAnchorV01 } from '../../../src';
+
+const generics = {} as GenericsV01;
 
 test('it creates string type nodes', () => {
-    expect(typeNodeFromAnchorV01('string')).toEqual(sizePrefixTypeNode(stringTypeNode('utf8'), numberTypeNode('u32')));
+    expect(typeNodeFromAnchorV01('string', generics)).toEqual(
+        sizePrefixTypeNode(stringTypeNode('utf8'), numberTypeNode('u32')),
+    );
 });

+ 21 - 13
packages/nodes-from-anchor/test/v01/typeNodes/StructTypeNode.test.ts

@@ -12,17 +12,22 @@ import {
 } from '@codama/nodes';
 import { expect, test } from 'vitest';
 
-import { typeNodeFromAnchorV01 } from '../../../src';
+import { GenericsV01, typeNodeFromAnchorV01 } from '../../../src';
+
+const generics = {} as GenericsV01;
 
 test('it creates struct type nodes', () => {
-    const node = typeNodeFromAnchorV01({
-        fields: [
-            { name: 'name', type: 'string' },
-            { name: 'age', type: 'u8' },
-            { name: 'created_at', type: 'u8' },
-        ],
-        kind: 'struct',
-    });
+    const node = typeNodeFromAnchorV01(
+        {
+            fields: [
+                { name: 'name', type: 'string' },
+                { name: 'age', type: 'u8' },
+                { name: 'created_at', type: 'u8' },
+            ],
+            kind: 'struct',
+        },
+        generics,
+    );
 
     expect(node).toEqual(
         structTypeNode([
@@ -37,10 +42,13 @@ test('it creates struct type nodes', () => {
 });
 
 test('it creates tuple type nodes when unnamed fields are provided', () => {
-    const node = typeNodeFromAnchorV01({
-        fields: ['u8', { vec: 'pubkey' }],
-        kind: 'struct',
-    });
+    const node = typeNodeFromAnchorV01(
+        {
+            fields: ['u8', { vec: 'pubkey' }],
+            kind: 'struct',
+        },
+        generics,
+    );
 
     expect(node).toEqual(
         tupleTypeNode([

+ 10 - 5
packages/nodes-from-anchor/test/v01/typeNodes/TupleTypeNode.test.ts

@@ -1,13 +1,18 @@
 import { numberTypeNode, publicKeyTypeNode, tupleTypeNode } from '@codama/nodes';
 import { expect, test } from 'vitest';
 
-import { typeNodeFromAnchorV01 } from '../../../src';
+import { GenericsV01, typeNodeFromAnchorV01 } from '../../../src';
+
+const generics = {} as GenericsV01;
 
 test('it creates tuple type nodes', () => {
-    const node = typeNodeFromAnchorV01({
-        fields: ['u8', 'pubkey'],
-        kind: 'struct',
-    });
+    const node = typeNodeFromAnchorV01(
+        {
+            fields: ['u8', 'pubkey'],
+            kind: 'struct',
+        },
+        generics,
+    );
 
     expect(node).toEqual(tupleTypeNode([numberTypeNode('u8'), publicKeyTypeNode()]));
 });