Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
597fc5c
refactor(typeManifest): Handle fixed-sized fields when extracting fie…
ERoydev Sep 10, 2025
728c27c
fix(): Handle fixed-sized optional fields in optionTypeNode Visitor
ERoydev Sep 10, 2025
ee07640
ref(sharedTemplate): Added deserializing functions and one reusable t…
ERoydev Sep 10, 2025
37a25b5
add(): Applied discriminator functions instead of array used ones
ERoydev Sep 10, 2025
94d20ab
fix(macros): Nesting depth for recursive borsh and sizes passed to bo…
ERoydev Sep 10, 2025
64bad8f
fix(templates): Use snakeCase and reuse broshMethods for serializing
ERoydev Sep 10, 2025
8ea4aeb
add(typeManifest): Regex handler added for number formats
ERoydev Sep 10, 2025
59fe25e
add(): NumberTypeNodes return int type with exact rust integer type a…
ERoydev Sep 10, 2025
e26c726
add(macros): Added formats handling in macros
ERoydev Sep 10, 2025
b15781e
add(): Add import.meta check
ERoydev Sep 10, 2025
b88ad59
fix:(): Inconsistent compilation issue fixed with ecmascript specific…
ERoydev Sep 10, 2025
f27f8fe
add(): Added tests
ERoydev Sep 15, 2025
bfa56ee
Fix: Arrays were incorrectly always treated as prefixed (with a prefi…
ERoydev Sep 15, 2025
185e6ca
fix: Discriminator borsh implementation
ERoydev Sep 15, 2025
6893303
add(): Utility regex for errors
ERoydev Sep 15, 2025
cda5120
fix: Enum still need to specify the Variant index when serialized to …
ERoydev Sep 15, 2025
4049fd9
fix: Instruction page borsh for arguments reused
ERoydev Sep 15, 2025
4d86fca
fix: Uncomment test cases
ERoydev Sep 15, 2025
3af7b5a
fix: Handle errors and baseType for optional fields
ERoydev Sep 15, 2025
abfaa02
update: Clean e2e examples
ERoydev Sep 15, 2025
9e4acd7
ref: Move util file in utils instead of fragments
ERoydev Sep 15, 2025
62100a8
comment one test case
ERoydev Sep 15, 2025
c118785
update
ERoydev Sep 16, 2025
061374d
add: Readme documentation
ERoydev Sep 16, 2025
51dbc7a
Delete src/.DS_Store
ERoydev Sep 16, 2025
bbeb518
Merge branch 'dev' into fix/array-borsh-length
ERoydev Sep 16, 2025
a72209e
fix: add example repo link
ERoydev Sep 16, 2025
2b25d2e
fix: Removed the unused dependencies
ERoydev Sep 17, 2025
d355be5
Merge branch 'fix/array-borsh-length' of https://github.com/LimeChain…
ERoydev Sep 17, 2025
29e4fd6
update: The lock file
ERoydev Sep 17, 2025
91b42e3
fix: Dependecy issues
ERoydev Sep 17, 2025
2908a81
update: Readme usage update
ERoydev Sep 18, 2025
6a55a7e
update: Add reference to Codama CLI docs in readme
ERoydev Sep 18, 2025
57170e5
add: Support for defaultValues for fields that have them from the idl
ERoydev Sep 22, 2025
dbd3be8
add: Add comments
ERoydev Sep 23, 2025
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
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ lerna-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json

# e2e test generated clients
e2e/**/lib/generated

# Runtime data
pids
*.pid
Expand Down Expand Up @@ -40,6 +43,7 @@ build/Release
# Dependency directories
node_modules/
jspm_packages/
.dart_tool

# Snowpack dependency directory (https://snowpack.dev/)
web_modules/
Expand Down
257 changes: 256 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,256 @@
# codama-dart
# Codama ➤ Renderers ➤ Dart

This package provides a Solana Dart client renderer for [Codama](https://github.com/codama-idl/codama) that generates Dart client code from Solana program IDL files.

This tool is in beta, so please report any issues or feedback.

> For a full example project using this renderer, see [codama-dart-example](https://github.com/LimeChain/codama-dart-examples).

## Installation

```sh
pnpm install codama-dart
```

## Using with Codama configuration
When you have installed Codama IDL, you can use as a renderer plugin via Codama's configuration system.
Just add the following script to your Codama configuration file.


```json
{
"scripts": {
"dart": {
"from": "@codama/renderers-dart",
"args": [
"clients/dart/src/generated",
{
"crateFolder": "clients/dart",
"formatCode": true
}
]
}
}
}
```
For more details on configuring Codama using a config file, see the official [Codama CLI documentation](https://github.com/codama-idl/codama/blob/main/packages/cli/README.md)


## Using programmatically in Node.js
You can also use this package directly in your own Node.js scripts. This approach is ideal if you want to generate Dart clients programmatically, giving you full control over the generation process and output options. Simply import the `renderVisitor` function and use it as shown below:

```ts
// create-codama-client.js
import { createFromRoot } from 'codama';
import { rootNodeFromAnchor } from '@codama/nodes-from-anchor';
import renderVisitor from 'codama-dart';

import path from 'path';
import fs from 'fs';

// Load one Anchor IDL
const idl = JSON.parse(
fs.readFileSync('target/idl/anchor_gamble.json', 'utf8')
);

// Define program name and output directory
const programName = 'anchor_gamble';
const outDir = path.join('clients', 'dart', 'generated', programName);

// Generate the Dart client
const codama = createFromRoot(rootNodeFromAnchor(idl as any));
codama.accept(renderVisitor(outDir, options));
```

## Generate file directory structure

```
.
├── accounts
│ └── array_account.dart
├── instructions
│ └── array_initialize.dart
├── types
│ ├── fixed_array_account.dart
│ ├── nested_vec_account.dart
│ └── string_account.dart
├── errors
│ └── array_errors.dart
├── accounts.dart
├── errors.dart
├── instructions.dart
├── programs.dart
├── shared.dart
├── types.dart
├── lib.dart
└── mod.dart
```

## Dependencies

```yaml
dependencies:
solana: ^0.31.2+1
```

## Examples

### Create client variable

```dart
RpcClient createSolanaClient() {
return RpcClient('http://localhost:8899');
}
```

### Instructions

```dart
import 'package:solana/solana.dart';
import 'dart:convert';
// Import the generated client `lib.dart` which exports all of the generated properties
import '../../../generated/anchor_gamble/lib.rs';

void initialize() async {
// Signer mnemonic file
final String mnemonic = File('wallet.mnemonic').readAsStringSync();

// Signer keypair
final Ed25519HDKeyPair payer = await Ed25519HDKeyPair.fromMnemonic(mnemonic);

// Create client
final RpcClient client = createSolanaClient();

// Get programId from the generated client
final gambleProgramId = gamble.AnchorGambleProgram.programId;

final systemProgram = Ed25519HDPublicKey.fromBase58(SystemProgram.programId);
final gambleCost = 1000000; // Example cost, adjust as needed

try {
final configSeeds = [utf8.encode('config')];

final config = await Ed25519HDPublicKey.findProgramAddress(
seeds: configSeeds,
programId: gambleProgramId,
);

final rewardPoolSeeds = [utf8.encode('reward_pool')];

final rewardPool = await Ed25519HDPublicKey.findProgramAddress(
seeds: rewardPoolSeeds,
programId: gambleProgramId,
);

// Use the Instruction Class method from client that is generated for you
final initializeIx = InitializeInstruction(
config: config,
reward_pool: rewardPool,
admin: payer.publicKey,
system_program: systemProgram,
gamble_cost: BigInt.from(gambleCost),
).toInstruction();

final message = Message(instructions: [initializeIx]);
final signature = await client.signAndSendTransaction(message, [payer]);

print("Transaction signature to initialize a gamble instruction: $signature");
} catch (e) {
print('Error signing and sending transaction: $e');
}
}
```

### Accounts

```dart
import 'package:solana/solana.dart';
import 'dart:convert';
// Import the generated client `lib.dart` which exports all of the generated properties
import '../../../generated/anchor_gamble/lib.dart';

void getGambleState(RpcClient client, Ed25519HDPublicKey programId) async {
try {
final configSeeds = [utf8.encode('config')];

final configAcc = await Ed25519HDPublicKey.findProgramAddress(
seeds: configSeeds,
programId: programId,
);

// Use the generated Account Class in the client `Config` and use its method to fetch from blockchain and access the deserialized property
final gamleAccount = await Config.fetch(client, configAcc);

// Access the properties from this Class
print('Admin: ${gamleAccount.admin}');
print('Gamble Cost: ${gamleAccount.gamble_cost}');
print('Config Bump: ${gamleAccount.config_bump}');
print('Reward Pool Bump: ${gamleAccount.reward_pool_bump}');

} catch (e) {
print('Error fetching Gamble State: $e');
}
}
```

### Types

```dart
// Structs

final Ed25519HDKeyPair arrayAcc = await Ed25519HDKeyPair.fromMnemonic(arrayMnemonic);
// Get the data for that account
final arrayAccount = await ArrayAccount.fetch(client, arrayAcc.publicKey);
```

```dart
// Enum (Structs following the sealed class pattern)

final Ed25519HDKeyPair enumAcc = await Ed25519HDKeyPair.fromMnemonic(enumMnemonic);

final enumAccount = await EnumAccount.fetch(client, enumAcc.publicKey);
if (enumValue is VariantA) {
print('VariantA(value0: ${enumValue.value0}, value1: ${enumValue.value1})');
} else if (enumValue is VariantB) {
print('VariantB(x: ${enumValue.x}, y: ${enumValue.y})');
} else if (enumValue is VariantC) {
print('VariantC');
....
```

### Errors

```dart
try {
...some instruction call here
} catch (e) {
// You take that from the generated `errors` folder and you find your corresponding Error Class
final dsError = DataStructuresError.fromSolanaErrorString(e);
if (dsError != null) {
print('Custom program error: $dsError');
// You can also check the type:
if (dsError is StringTooLongError) {
print('String was too long!');
}
} else {
print('Other error: $e');
}
}
```

## Program ID

The Program ID is generated based on the Program address provided in the IDL. If it is not present in the IDL, it needs to be manually filled in.

## Description

The generated code uses the Solana Dart SDK along with Borsh serialization methods.

## Not Supported Data Types

- FixedArray with u8 are not supported -> `[u8; 2]`, arrays with u8 acts as an `BytesTypeNode`, which means it is automatically set to size 8 instead of 2.

- `Vec<Option<SomeStructHere>>` -> Not supported

- Fixed Nested Arrays -> `[[u32; 3]; 2]` use `Vec<Vec<u32>>` instead and apply constraints using InitSpace macros -> `#[max_len(3, 2)]`
Loading