|
| 1 | +# Codama ➤ Dynamic Codecs |
| 2 | + |
| 3 | +[![npm][npm-image]][npm-url] |
| 4 | +[![npm-downloads][npm-downloads-image]][npm-url] |
| 5 | + |
| 6 | +[npm-downloads-image]: https://img.shields.io/npm/dm/@codama/dynamic-codecs.svg?style=flat |
| 7 | +[npm-image]: https://img.shields.io/npm/v/@codama/dynamic-codecs.svg?style=flat&label=%40codama%2Fdynamic-codecs |
| 8 | +[npm-url]: https://www.npmjs.com/package/@codama/dynamic-codecs |
| 9 | + |
| 10 | +This package provides a set of helpers that provide `Codecs` for Codama nodes that describe data. |
| 11 | + |
| 12 | +## Installation |
| 13 | + |
| 14 | +```sh |
| 15 | +pnpm install @codama/dynamic-codecs |
| 16 | +``` |
| 17 | + |
| 18 | +> [!NOTE] |
| 19 | +> This package is **not** included in the main [`codama`](../library) package. |
| 20 | +
|
| 21 | +## Functions |
| 22 | + |
| 23 | +### `getNodeCodec(path, options?)` |
| 24 | + |
| 25 | +Given the full `NodePath` of a node inside a Codama IDL, returns a `Codec<unknown>` (as defined in `@solana/codecs`) that enables encoding and decoding data for that node. |
| 26 | + |
| 27 | +```ts |
| 28 | +const codec = getNodeCodec([root, program, definedType]); |
| 29 | +const bytes = codec.encode(someData); |
| 30 | +const decodedData = codec.decode(bytes); |
| 31 | +``` |
| 32 | + |
| 33 | +Note that it is important to provide the full `NodePath` of the node in order to properly follow link nodes inside the Codama IDL. Here is a more complex example illustrating how link nodes are resolved: |
| 34 | + |
| 35 | +```ts |
| 36 | +// Here we define a program with two types, one of which is a link to the other. |
| 37 | +const root = rootNode( |
| 38 | + programNode({ |
| 39 | + definedTypes: [ |
| 40 | + definedTypeNode({ name: 'slot', type: numberTypeNode('u64') }), |
| 41 | + definedTypeNode({ name: 'lastSlot', type: definedTypeLinkNode('slot') }), |
| 42 | + ], |
| 43 | + name: 'myProgram', |
| 44 | + publicKey: '1111', |
| 45 | + }), |
| 46 | +); |
| 47 | + |
| 48 | +// The codec for the linked `lastSlot` defined type is resolved using the `slot` defined type. |
| 49 | +const codec = getNodeCodec([root, root.program, root.program.definedTypes[1]]); |
| 50 | +expect(codec.encode(42)).toStrictEqual(hex('2a00000000000000')); |
| 51 | +expect(codec.decode(hex('2a00000000000000'))).toBe(42n); |
| 52 | +``` |
| 53 | + |
| 54 | +#### Options |
| 55 | + |
| 56 | +The `getNodeCodec` function accepts the following options. |
| 57 | + |
| 58 | +| Name | Type | Default | Description | |
| 59 | +| --------------- | --------------- | ---------- | -------------------------------------------------------- | |
| 60 | +| `bytesEncoding` | `BytesEncoding` | `"base64"` | The default encoding to use when formatting plain bytes. | |
| 61 | + |
| 62 | +#### Decoded format |
| 63 | + |
| 64 | +In the table below, we illustrate the format of each codec based on the node from which it was created. |
| 65 | + |
| 66 | +Note that we purposefully avoid types such as `Uint8Array`, `Set` or `Map` in order to keep the format JSON compatible. For instance, plain bytes are not provided as `Uint8Array` but as a tuple of type `[BytesEncoding, string]` — e.g. `["base64", "HelloWorld++"]` — where the default bytes encoding is `base64` which is configurable via the `bytesEncoding` option. |
| 67 | + |
| 68 | +| Node | Example | Notes | |
| 69 | +| --------------------------------------------------------------------------------------- | ----------------------------------------------------------- | ------------------------------------------------------------------------------------------- | |
| 70 | +| [`AccountLinkNode`](../nodes/docs/linkNodes/AccountLinkNode.md) | - | Same as `AccountNode` | |
| 71 | +| [`AccountNode`](../nodes/docs/AccountNode.md) | - | Same as `node.data` | |
| 72 | +| [`DefinedTypeLinkNode`](../nodes/docs/linkNodes/DefinedTypeLinkNode.md) | - | Same as `DefinedTypeNode` | |
| 73 | +| [`DefinedTypeNode`](../nodes/docs/DefinedTypeNode.md) | - | Same as `node.type` | |
| 74 | +| [`InstructionArgumentLinkNode`](../nodes/docs/linkNodes/InstructionArgumentLinkNode.md) | - | Same as `InstructionArgumentNode` | |
| 75 | +| [`InstructionArgumentNode`](../nodes/docs/InstructionArgumentNode.md) | - | Same as `node.type` | |
| 76 | +| [`InstructionLinkNode`](../nodes/docs/linkNodes/InstructionLinkNode.md) | - | Same as `InstructionNode` | |
| 77 | +| [`InstructionNode`](../nodes/docs/InstructionNode.md) | - | Same as a `StructTypeNode` containing all `node.arguments` | |
| 78 | +| [`AmountTypeNode`](../nodes/docs/typeNodes/AmountTypeNode.md) | `42` | Same as `NumberTypeNode` | |
| 79 | +| [`ArrayTypeNode`](../nodes/docs/typeNodes/ArrayTypeNode.md) | `[1, 2, 3]` | | |
| 80 | +| [`BooleanTypeNode`](../nodes/docs/typeNodes/BooleanTypeNode.md) | `true` or `false` | | |
| 81 | +| [`BytesTypeNode`](../nodes/docs/typeNodes/BytesTypeNode.md) | `["base16", "00ffaa"]` | Uses `bytesEncoding` option to decode | |
| 82 | +| [`DateTimeTypeNode`](../nodes/docs/typeNodes/DateTimeTypeNode.md) | `42` | Same as `NumberTypeNode` | |
| 83 | +| [`EnumTypeNode`](../nodes/docs/typeNodes/EnumTypeNode.md) | `2` or `{ __kind: "move", x: 12, y: 34 }` | Uses number indices for scalar enums. Uses discriminated unions otherwise. | |
| 84 | +| [`FixedSizeTypeNode`](../nodes/docs/typeNodes/FixedSizeTypeNode.md) | - | Same as `node.type` | |
| 85 | +| [`HiddenPrefixTypeNode`](../nodes/docs/typeNodes/HiddenPrefixTypeNode.md) | - | Same as `node.type` | |
| 86 | +| [`HiddenSuffixTypeNode`](../nodes/docs/typeNodes/HiddenSuffixTypeNode.md) | - | Same as `node.type` | |
| 87 | +| [`MapTypeNode`](../nodes/docs/typeNodes/MapTypeNode.md) | `{ key1: "value1", key2: "value2" }` | Represent `Maps` as `objects` | |
| 88 | +| [`NumberTypeNode`](../nodes/docs/typeNodes/NumberTypeNode.md) | `42` | This could be a `bigint` | |
| 89 | +| [`OptionTypeNode`](../nodes/docs/typeNodes/OptionTypeNode.md) | `{ __option: "Some", value: 42 }` or `{ __option: "None" }` | Uses value objects (instead of `T \| null`) to avoid loosing information on nested options. | |
| 90 | +| [`PostOffsetTypeNode`](../nodes/docs/typeNodes/PostOffsetTypeNode.md) | - | Same as `node.type` | |
| 91 | +| [`PreOffsetTypeNode`](../nodes/docs/typeNodes/PreOffsetTypeNode.md) | - | Same as `node.type` | |
| 92 | +| [`PublicKeyTypeNode`](../nodes/docs/typeNodes/PublicKeyTypeNode.md) | `"3QC7Pnv2KfwwdC44gPcmQWuZXmRSbUpmWMJnhenMC8CU"` | Uses base58 representations of public keys | |
| 93 | +| [`RemainderOptionTypeNode`](../nodes/docs/typeNodes/RemainderOptionTypeNode.md) | `{ __option: "Some", value: 42 }` or `{ __option: "None" }` | Same as `OptionTypeNode` | |
| 94 | +| [`SentinelTypeNode`](../nodes/docs/typeNodes/SentinelTypeNode.md) | - | Same as `node.type` | |
| 95 | +| [`SetTypeNode`](../nodes/docs/typeNodes/SetTypeNode.md) | `[1, 2, 3]` | Same as `ArrayTypeNode` | |
| 96 | +| [`SizePrefixTypeNode`](../nodes/docs/typeNodes/SizePrefixTypeNode.md) | - | Same as `node.type` | |
| 97 | +| [`SolAmountTypeNode`](../nodes/docs/typeNodes/SolAmountTypeNode.md) | `42` | Same as `NumberTypeNode` | |
| 98 | +| [`StringTypeNode`](../nodes/docs/typeNodes/StringTypeNode.md) | `"Hello World"` | Uses the encoding defined in the node — i.e. `node.encoding` | |
| 99 | +| [`StructTypeNode`](../nodes/docs/typeNodes/StructTypeNode.md) | `{ name: "John", age: 42 }` | | |
| 100 | +| [`TupleTypeNode`](../nodes/docs/typeNodes/TupleTypeNode.md) | `["John", 42]` | Uses arrays to create tuples | |
| 101 | +| [`ZeroableOptionTypeNode`](../nodes/docs/typeNodes/ZeroableOptionTypeNode.md) | `{ __option: "Some", value: 42 }` or `{ __option: "None" }` | Same as `OptionTypeNode` | |
| 102 | + |
| 103 | +### `getNodeCodecVisitor(linkables, options?)` |
| 104 | + |
| 105 | +This visitor is used by `getNodeCodec` under the hood. It returns a `Codec<unknown>` for the visited node. |
| 106 | + |
| 107 | +```ts |
| 108 | +return visit(someTypeNode, getNodeCodecVisitor(linkables)); |
| 109 | +``` |
| 110 | + |
| 111 | +### `getValueNodeVisitor(linkables, options?)` |
| 112 | + |
| 113 | +This visitor is used by the `getValueNodeVisitor` under the hood. It returns an `unknown` value for the visited `ValueNode`. |
| 114 | + |
| 115 | +```ts |
| 116 | +return visit(someValueNode, getValueNodeVisitor(linkables)); |
| 117 | +``` |
0 commit comments