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
9 changes: 9 additions & 0 deletions .changeset/wicked-kangaroos-agree.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
"@codama/errors": patch
"@codama/node-types": minor
"@codama/nodes-from-anchor": patch
"@codama/nodes": minor
"@codama/visitors-core": patch
---

Add new `EventNode` to `ProgramNode` and update the Anchor adapter accordingly.
18 changes: 9 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,15 +114,15 @@ Feel free to PR your own visitor here for others to discover. Note that they are

### Generates program clients

| Visitor | Description | Maintainer |
| ----------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- | -------------------------------------------- |
| `@codama/renderers-go` ([docs](https://github.com/codama-idl/renderers-go)) | Generates a Go client compatible with [the Solana Go SDK](https://github.com/gagliardetto/solana-go). | [Sonic](hhttps://github.com/sonicfromnewyoke)|
| `@codama/renderers-js` ([docs](https://github.com/codama-idl/renderers-js)) | Generates a JavaScript client compatible with [Solana Kit](https://www.solanakit.com/). | [Anza](https://www.anza.xyz/) |
| `@codama/renderers-js-umi` ([docs](https://github.com/codama-idl/renderers-js-umi)) | Generates a JavaScript client compatible with [the Umi framework](https://developers.metaplex.com/umi). | [Metaplex](https://www.metaplex.com/) |
| `@codama/renderers-rust` ([docs](https://github.com/codama-idl/renderers-rust)) | Generates a Rust client compatible with [the Solana SDK](https://github.com/anza-xyz/solana-sdk). | [Anza](https://www.anza.xyz/) |
| `@codama/renderers-vixen-parser` ([docs](https://github.com/codama-idl/renderers-vixen-parser)) | Generates [Yellowstone](https://github.com/rpcpool/yellowstone-grpc) account and instruction parsers. | [Triton One](https://triton.one/) |
| `@limechain/codama-dart` ([docs](https://github.com/limechain/codama-dart)) | Generates a Dart client. | [LimeChain](https://github.com/limechain/) |
| `codama-py` ([docs](https://github.com/Solana-ZH/codama-py)) | Generates a Python client. | [Solar](https://github.com/Solana-ZH) |
| Visitor | Description | Maintainer |
| ----------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- | --------------------------------------------- |
| `@codama/renderers-go` ([docs](https://github.com/codama-idl/renderers-go)) | Generates a Go client compatible with [the Solana Go SDK](https://github.com/gagliardetto/solana-go). | [Sonic](hhttps://github.com/sonicfromnewyoke) |
| `@codama/renderers-js` ([docs](https://github.com/codama-idl/renderers-js)) | Generates a JavaScript client compatible with [Solana Kit](https://www.solanakit.com/). | [Anza](https://www.anza.xyz/) |
| `@codama/renderers-js-umi` ([docs](https://github.com/codama-idl/renderers-js-umi)) | Generates a JavaScript client compatible with [the Umi framework](https://developers.metaplex.com/umi). | [Metaplex](https://www.metaplex.com/) |
| `@codama/renderers-rust` ([docs](https://github.com/codama-idl/renderers-rust)) | Generates a Rust client compatible with [the Solana SDK](https://github.com/anza-xyz/solana-sdk). | [Anza](https://www.anza.xyz/) |
| `@codama/renderers-vixen-parser` ([docs](https://github.com/codama-idl/renderers-vixen-parser)) | Generates [Yellowstone](https://github.com/rpcpool/yellowstone-grpc) account and instruction parsers. | [Triton One](https://triton.one/) |
| `@limechain/codama-dart` ([docs](https://github.com/limechain/codama-dart)) | Generates a Dart client. | [LimeChain](https://github.com/limechain/) |
| `codama-py` ([docs](https://github.com/Solana-ZH/codama-py)) | Generates a Python client. | [Solar](https://github.com/Solana-ZH) |

### Provides utility

Expand Down
2 changes: 2 additions & 0 deletions packages/errors/src/codes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ 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;
export const CODAMA_ERROR__ANCHOR__EVENT_TYPE_MISSING = 2100007;

// Renderers-related errors.
// Reserve error codes in the range [2800000-2800999].
Expand All @@ -86,6 +87,7 @@ export const CODAMA_ERROR__RENDERERS__MISSING_DEPENDENCY_VERSIONS = 2800001;
export type CodamaErrorCode =
| typeof CODAMA_ERROR__ANCHOR__ACCOUNT_TYPE_MISSING
| typeof CODAMA_ERROR__ANCHOR__ARGUMENT_TYPE_MISSING
| typeof CODAMA_ERROR__ANCHOR__EVENT_TYPE_MISSING
| typeof CODAMA_ERROR__ANCHOR__GENERIC_TYPE_MISSING
| typeof CODAMA_ERROR__ANCHOR__PROGRAM_ID_KIND_UNIMPLEMENTED
| typeof CODAMA_ERROR__ANCHOR__SEED_KIND_UNIMPLEMENTED
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__EVENT_TYPE_MISSING,
CODAMA_ERROR__ANCHOR__GENERIC_TYPE_MISSING,
CODAMA_ERROR__ANCHOR__PROGRAM_ID_KIND_UNIMPLEMENTED,
CODAMA_ERROR__ANCHOR__SEED_KIND_UNIMPLEMENTED,
Expand Down Expand Up @@ -71,6 +72,9 @@ export type CodamaErrorContext = DefaultUnspecifiedErrorContextToUndefined<{
[CODAMA_ERROR__ANCHOR__ARGUMENT_TYPE_MISSING]: {
name: string;
};
[CODAMA_ERROR__ANCHOR__EVENT_TYPE_MISSING]: {
name: string;
};
[CODAMA_ERROR__ANCHOR__GENERIC_TYPE_MISSING]: {
name: 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__EVENT_TYPE_MISSING,
CODAMA_ERROR__ANCHOR__GENERIC_TYPE_MISSING,
CODAMA_ERROR__ANCHOR__PROGRAM_ID_KIND_UNIMPLEMENTED,
CODAMA_ERROR__ANCHOR__SEED_KIND_UNIMPLEMENTED,
Expand Down Expand Up @@ -51,6 +52,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__EVENT_TYPE_MISSING]: 'Event type [$name] is missing from the IDL types.',
[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.',
Expand Down
18 changes: 18 additions & 0 deletions packages/node-types/src/EventNode.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import type { DiscriminatorNode } from './discriminatorNodes';
import type { CamelCaseString, Docs } from './shared';
import type { TypeNode } from './typeNodes';

export interface EventNode<
TData extends TypeNode = TypeNode,
TDiscriminators extends DiscriminatorNode[] | undefined = DiscriminatorNode[] | undefined,
> {
readonly kind: 'eventNode';

// Data.
readonly name: CamelCaseString;
readonly docs?: Docs;

// Children.
readonly data: TData;
readonly discriminators?: TDiscriminators;
}
2 changes: 2 additions & 0 deletions packages/node-types/src/Node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import type { RegisteredCountNode } from './countNodes/CountNode';
import type { DefinedTypeNode } from './DefinedTypeNode';
import type { RegisteredDiscriminatorNode } from './discriminatorNodes/DiscriminatorNode';
import type { ErrorNode } from './ErrorNode';
import type { EventNode } from './EventNode';
import type { InstructionAccountNode } from './InstructionAccountNode';
import type { InstructionArgumentNode } from './InstructionArgumentNode';
import type { InstructionByteDeltaNode } from './InstructionByteDeltaNode';
Expand All @@ -24,6 +25,7 @@ export type Node =
| AccountNode
| DefinedTypeNode
| ErrorNode
| EventNode
| InstructionAccountNode
| InstructionArgumentNode
| InstructionByteDeltaNode
Expand Down
3 changes: 3 additions & 0 deletions packages/node-types/src/ProgramNode.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { AccountNode } from './AccountNode';
import type { DefinedTypeNode } from './DefinedTypeNode';
import type { ErrorNode } from './ErrorNode';
import type { EventNode } from './EventNode';
import type { InstructionNode } from './InstructionNode';
import type { PdaNode } from './PdaNode';
import type { CamelCaseString, Docs, ProgramVersion } from './shared';
Expand All @@ -11,6 +12,7 @@ export interface ProgramNode<
TInstructions extends InstructionNode[] = InstructionNode[],
TDefinedTypes extends DefinedTypeNode[] = DefinedTypeNode[],
TErrors extends ErrorNode[] = ErrorNode[],
TEvents extends EventNode[] = EventNode[],
> {
readonly kind: 'programNode';

Expand All @@ -26,5 +28,6 @@ export interface ProgramNode<
readonly instructions: TInstructions;
readonly definedTypes: TDefinedTypes;
readonly pdas: TPdas;
readonly events: TEvents;
readonly errors: TErrors;
}
1 change: 1 addition & 0 deletions packages/node-types/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export * from './AccountNode';
export * from './DefinedTypeNode';
export * from './ErrorNode';
export * from './EventNode';
export * from './InstructionAccountNode';
export * from './InstructionArgumentNode';
export * from './InstructionByteDeltaNode';
Expand Down
38 changes: 38 additions & 0 deletions packages/nodes-from-anchor/src/v01/EventNode.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { CODAMA_ERROR__ANCHOR__EVENT_TYPE_MISSING, CodamaError } from '@codama/errors';
import {
bytesTypeNode,
camelCase,
constantDiscriminatorNode,
constantValueNode,
EventNode,
eventNode,
fixedSizeTypeNode,
hiddenPrefixTypeNode,
} from '@codama/nodes';

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

export function eventNodeFromAnchorV01(idl: IdlV01Event, types: IdlV01TypeDef[], generics: GenericsV01): EventNode {
const name = camelCase(idl.name);
const type = types.find(candidate => candidate.name === idl.name);

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

const data = typeNodeFromAnchorV01(type.type, generics);
const discriminator = getAnchorDiscriminatorV01(idl.discriminator);
const discriminatorConstant = constantValueNode(
fixedSizeTypeNode(bytesTypeNode(), idl.discriminator.length),
discriminator,
);

return eventNode({
data: hiddenPrefixTypeNode(data, [discriminatorConstant]),
discriminators: [constantDiscriminatorNode(discriminatorConstant)],
name,
});
}
8 changes: 7 additions & 1 deletion packages/nodes-from-anchor/src/v01/ProgramNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,30 @@ import { ProgramNode, programNode, ProgramVersion } from '@codama/nodes';
import { accountNodeFromAnchorV01 } from './AccountNode';
import { definedTypeNodeFromAnchorV01 } from './DefinedTypeNode';
import { errorNodeFromAnchorV01 } from './ErrorNode';
import { eventNodeFromAnchorV01 } from './EventNode';
import { IdlV01 } from './idl';
import { instructionNodeFromAnchorV01 } from './InstructionNode';
import { extractGenerics } from './unwrapGenerics';

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

const filteredTypes = types.filter(type => !accounts.some(account => account.name === type.name));
const filteredTypes = types.filter(
type =>
!accounts.some(account => account.name === type.name) && !events.some(event => event.name === type.name),
);
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),
events: events.map(event => eventNodeFromAnchorV01(event, types, generics)),
instructions: instructions.map(instruction => instructionNodeFromAnchorV01(instruction, generics)),
name: idl.metadata.name,
origin: 'anchor',
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
@@ -1,5 +1,6 @@
export * from './AccountNode';
export * from './DefinedTypeNode';
export * from './EventNode';
export * from './ErrorNode';
export * from './idl';
export * from './InstructionAccountNode';
Expand Down
118 changes: 118 additions & 0 deletions packages/nodes-from-anchor/test/v01/EventNode.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import { CODAMA_ERROR__ANCHOR__EVENT_TYPE_MISSING, CodamaError } from '@codama/errors';
import { constantDiscriminatorNode, constantValueNode } from '@codama/nodes';
import {
bytesTypeNode,
eventNode,
fixedSizeTypeNode,
hiddenPrefixTypeNode,
numberTypeNode,
structFieldTypeNode,
structTypeNode,
tupleTypeNode,
} from '@codama/nodes';
import { expect, test } from 'vitest';

import { eventNodeFromAnchorV01, GenericsV01, getAnchorDiscriminatorV01 } from '../../src';

const generics = {} as GenericsV01;

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

expect(node).toEqual(
eventNode({
data: hiddenPrefixTypeNode(
structTypeNode([structFieldTypeNode({ name: 'amount', type: numberTypeNode('u32') })]),
[
constantValueNode(
fixedSizeTypeNode(bytesTypeNode(), 8),
getAnchorDiscriminatorV01([246, 28, 6, 87, 251, 45, 50, 42]),
),
],
),
discriminators: [
constantDiscriminatorNode(
constantValueNode(
fixedSizeTypeNode(bytesTypeNode(), 8),
getAnchorDiscriminatorV01([246, 28, 6, 87, 251, 45, 50, 42]),
),
),
],
name: 'myEvent',
}),
);
});

test('it creates tuple event nodes with anchor discriminators', () => {
const node = eventNodeFromAnchorV01(
{
discriminator: [246, 28, 6, 87, 251, 45, 50, 42],
name: 'TupleEvent',
},
[
{
docs: [],
name: 'TupleEvent',
type: {
fields: ['u32', 'u64'],
kind: 'struct',
},
},
],
generics,
);

expect(node).toEqual(
eventNode({
data: hiddenPrefixTypeNode(tupleTypeNode([numberTypeNode('u32'), numberTypeNode('u64')]), [
constantValueNode(
fixedSizeTypeNode(bytesTypeNode(), 8),
getAnchorDiscriminatorV01([246, 28, 6, 87, 251, 45, 50, 42]),
),
]),
discriminators: [
constantDiscriminatorNode(
constantValueNode(
fixedSizeTypeNode(bytesTypeNode(), 8),
getAnchorDiscriminatorV01([246, 28, 6, 87, 251, 45, 50, 42]),
),
),
],
name: 'tupleEvent',
}),
);
});

test('it throws when the backing event type is missing', () => {
expect(() =>
eventNodeFromAnchorV01(
{
discriminator: [246, 28, 6, 87, 251, 45, 50, 42],
name: 'MissingEvent',
},
[],
generics,
),
).toThrow(new CodamaError(CODAMA_ERROR__ANCHOR__EVENT_TYPE_MISSING, { name: 'MissingEvent' }));
});
Loading
Loading