Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

AlgoKit Core Documentation

Welcome to the comprehensive documentation for AlgoKit Core, a collection of Rust crates that provide core functionality for Algorand blockchain development.

Overview

AlgoKit Core consists of several interconnected crates:

API Documentation

📚 Complete API Documentation

Browse the full Rust API documentation with detailed type information, function signatures, and code examples for all crates.

Architecture

This project follows a layered architecture:

  1. Core Layer (algokit_transact) - Pure Rust implementations of Algorand transaction logic
  2. FFI Layer (algokit_transact_ffi) - Language bindings and foreign function interfaces
  3. Tooling Layer (ffi_macros, uniffi_bindgen) - Development and build-time utilities

Getting Started

Each crate has its own documentation with examples and API references. Start with the algokit_transact crate to understand the core functionality, then explore the FFI bindings for your target language.

Research and Decisions

This documentation also includes our research findings and architectural decision records to help you understand the reasoning behind our design choices:

Building Documentation

To build this documentation locally:

cargo run --bin build-docs --manifest-path docs/Cargo.toml

The generated documentation will be available in the target/docs directory.

AlgoKit Transact

The core crate providing transaction building and signing functionality for the Algorand blockchain.

Overview

algokit_transact is the foundational crate that implements:

  • Transaction building and serialization

Features

  • Transaction Types: Support for all Algorand transaction types
  • Serialization: MessagePack encoding/decoding
  • Test Utilities: Optional testing helpers (feature: test_utils)

Crate Type

This crate is built as both:

  • cdylib - For dynamic linking in FFI scenarios
  • rlib - For standard Rust library usage

Key Dependencies

  • serde - Serialization framework
  • rmp-serde - MessagePack serialization
  • sha2 - SHA-256 hashing
  • ed25519-dalek - Ed25519 signatures (optional, test_utils feature)

Usage Examples

#![allow(unused)]
fn main() {
use algokit_transact::*;

// Example usage would go here
// This would typically include transaction building examples
}

API Documentation

📚 View Full API Documentation

The complete API documentation with all transaction types, signing utilities, and core functionality.

Testing

The crate includes comprehensive tests. Run them with:

cargo test --package algokit_transact

For tests that require the test utilities:

cargo test --package algokit_transact --features test_utils

AlgoKit Transact FFI

Foreign Function Interface bindings for algokit_transact, enabling usage from multiple programming languages.

Overview

algokit_transact_ffi provides language bindings for the core algokit_transact functionality, supporting:

  • UniFFI Bindings - For Python, Swift, Kotlin, and other languages
  • WebAssembly Bindings - For JavaScript/TypeScript usage
  • C-compatible ABI - For integration with C/C++ and other systems languages

Features

  • ffi_uniffi (default) - UniFFI-based bindings
  • ffi_wasm - WebAssembly/JavaScript bindings

Crate Types

Built as both:

  • cdylib - Dynamic library for FFI
  • staticlib - Static library for embedding

Architecture

The FFI layer uses several approaches:

  1. UniFFI - Generates bindings automatically from Rust definitions
  2. wasm-bindgen - Provides seamless WebAssembly integration
  3. Custom macros - Uses ffi_macros for specialized binding generation

Key Dependencies

  • algokit_transact - Core functionality
  • ffi_macros - Procedural macros for FFI code generation
  • uniffi - Multi-language binding generation
  • wasm-bindgen - WebAssembly bindings
  • tsify-next - TypeScript type generation

API Documentation

📚 View Full API Documentation

The complete API documentation with all FFI types, functions, and binding examples.

Building

UniFFI Bindings

cargo build --package algokit_transact_ffi --features ffi_uniffi

WebAssembly Bindings

cargo build --package algokit_transact_ffi --features ffi_wasm

Language Support

Python

# Example Python usage would go here

JavaScript/TypeScript

// Example JS/TS usage would go here

Swift

// Example Swift usage would go here

Testing

cargo test --package algokit_transact_ffi

For WASM-specific tests:

wasm-pack test --node

FFI Macros

Procedural macros for generating Foreign Function Interface (FFI) bindings in AlgoKit Core.

Overview

ffi_macros provides procedural macros that automatically generate the necessary attributes and code for creating FFI bindings across multiple platforms and languages. These macros streamline the process of making Rust code accessible from WebAssembly, UniFFI, and other FFI contexts.

Purpose

This crate provides three key procedural macros:

  • #[ffi_func] - Decorates functions for FFI export
  • #[ffi_record] - Decorates structs for cross-language serialization
  • #[ffi_enum] - Decorates enums for cross-language serialization

Macros

#[ffi_func]

Automatically applies the necessary attributes for function export:

  • Converts function names to camelCase for JavaScript bindings
  • Adds wasm_bindgen attributes for WebAssembly
  • Adds uniffi::export attributes for UniFFI bindings

#[ffi_record]

Prepares structs for FFI serialization by adding:

  • Serde serialization/deserialization derives
  • WebAssembly-compatible type generation with Tsify
  • UniFFI Record derivation
  • Proper handling of Option fields
  • Automatic camelCase field renaming for JavaScript

#[ffi_enum]

Prepares enums for FFI serialization with:

  • Serde serialization/deserialization derives
  • WebAssembly-compatible type generation
  • UniFFI Enum derivation
  • Automatic camelCase variant renaming

Features

The macros automatically handle:

  • Multi-target Support - Conditional compilation for different FFI targets
  • Type Conversion - Automatic handling of Rust types to FFI-compatible types
  • Naming Conventions - Automatic conversion to target language conventions
  • Optional Fields - Special handling for Option types in different FFI contexts

Key Dependencies

  • syn - Rust code parsing and manipulation
  • quote - Code generation utilities
  • convert_case - String case conversion for naming conventions

Usage Examples

#![allow(unused)]
fn main() {
use ffi_macros::{ffi_func, ffi_record, ffi_enum};

#[ffi_func]
pub fn create_transaction() -> Transaction {
    // Function automatically exported for FFI
}

#[ffi_record]
pub struct Transaction {
    pub sender: String,
    pub receiver: String,
    pub amount: Option<u64>,
}

#[ffi_enum]
pub enum TransactionType {
    Payment,
    AssetTransfer,
    ApplicationCall,
}
}

Crate Type

This is a procedural macro crate (proc-macro = true), used at compile time to generate code.

API Documentation

📚 View Full API Documentation

The complete API documentation with all macro definitions and usage examples.

Development

When modifying the macros:

  1. Test with all FFI targets (WebAssembly, UniFFI)
  2. Verify generated code compiles correctly
  3. Update tests for new functionality
  4. Ensure backward compatibility

Testing

cargo test --package ffi_macros

Integration

This crate is primarily used by algokit_transact_ffi and should not be used directly by end users. The macros are automatically applied during the FFI binding generation process.

UniFFI Bindgen

Custom UniFFI binding generation tools and utilities.

Overview

uniffi_bindgen is a customized version of the UniFFI binding generator, tailored for AlgoKit Core's specific requirements.

Purpose

This crate provides:

  • Custom binding generation logic
  • AlgoKit-specific type mappings
  • Enhanced error handling for generated bindings
  • Optimized binding templates

Relationship to Standard UniFFI

While based on the standard UniFFI framework, this custom implementation includes:

  • Specialized handling for Algorand-specific types
  • Optimized serialization for blockchain data structures
  • Custom error types and handling patterns
  • AlgoKit-specific naming conventions

Usage

This crate is primarily used as a build tool and is not intended for direct usage by end users. It's automatically invoked during the build process of algokit_transact_ffi.

API Documentation

📚 View Full API Documentation

The complete API documentation with all binding generation tools and utilities.

Customizations

The key customizations include:

  • Type Mappings - Custom handling for Algorand addresses, hashes, and signatures
  • Error Handling - Specialized error propagation for blockchain operations
  • Performance - Optimized serialization paths for high-frequency operations

Building

cargo build --package uniffi-bindgen

Development

When modifying the binding generator:

  1. Test with all target languages
  2. Verify generated bindings compile correctly
  3. Run integration tests with algokit_transact_ffi
  4. Update documentation for any new features

Testing

cargo test --package uniffi-bindgen

API Documentation

Complete Rust API documentation for all AlgoKit Core crates.

Crates

algokit_transact

Core transaction building and signing functionality for the Algorand blockchain. Includes transaction types, serialization, and cryptographic operations.

algokit_transact_ffi

Foreign Function Interface bindings for algokit_transact, enabling usage from Python, Swift, JavaScript, and other languages.

ffi_macros

Procedural macros for generating Foreign Function Interface code. Automates FFI binding generation and type conversions.

uniffi_bindgen

Custom UniFFI binding generation tools and utilities tailored for AlgoKit Core's specific requirements.

Research

This section contains technical research and analysis that has informed the design and implementation of AlgoKit Core.

Contents

Purpose

These research documents serve to:

  1. Document our investigation process
  2. Provide context for architectural decisions
  3. Share knowledge with the community
  4. Guide future development choices

Contributing Research

When adding new research:

  1. Focus on technical analysis relevant to AlgoKit Core
  2. Include concrete findings and recommendations
  3. Reference sources and provide reproducible examples
  4. Update this README to include the new research

Historical Context

These documents represent point-in-time analysis and may become outdated as technologies evolve. Check the last modified date and consider current alternatives when referencing this research.

glibc and musl Support

One of the difficulties of shipping binaries is deciding which C standard library versions to support. This document goes over the options and how we might want to decide which versions to support.

musl vs glibc

The main difference between musl and glibc is that glibc includes non-standard gnu-specific features whereas musl only supports POSIX standards. This results in musl being smaller but glibc being more compatible with modern Linux systems. Despite this, musl should still be supported because it is used in size-oriented distros, such as Alpine, which are commonly used in docker containers.

glibc Versions

glibc versions are backwards but not forwards compatible, meaning a system with an older version of glibc will not be able to run a binary compiled with a newer version of glibc. This means we need to deicde how far back we should go for glibc support. The latest Rust compiler requires glibc 2.17 or higher. As of Feb 3, 2025 100% of Python package consumers are on glibc 2.17+ (source: Manylinux Timeline) which makes it the ideal glibc version to support. Supporting older versions of glibc would result in compromises needing to be made in regards to which version of Rust we can use. Considering we are using bleeding edge tools, limiting ourselves in this way seems unreasonable for a very small percent (0% according to manylinux timeline) of users.

Future Support

Eventually we will need likely need to move to a newer glibc version due to toolchain requirements. In general, a good rule of thumb is to support the glibc version from two RHEL releases ago. For example, as of the time of this writing the most recent stable RHEL release is 9, which means we should support the glibc version in RHEL 7, which is 2.17. Historically this roughly aligns with the End-of-Maintenance period for RHEL.

OpenAPI Generators for Algorand APIs

Overview

The /api directory is central codebase for generating API clients for Algorand APIs across multiple languages. This document outlines the architecture, structure, and workflows implemented in this directory.

OpenAPI Generator Approach

OpenAPI generators was chosen to automate the creation of API clients in various languages (like TypeScript, Python, and Rust) from a single OpenAPI specification. This approach guarantees consistency across different language clients and significantly reduces the effort needed for maintenance. Instead of manually updating clients for each language, changes made to the API specification automatically propagate to all generated clients. The generator tool supports a wide range of languages and is a mature, actively maintained open-source project.

Structure

The /api directory is organized as follows:

  • specs/: Contains OpenAPI specifications (both v2 and v3 formats).
  • api_clients/: Holds the generated client code for different languages.
  • oas_templates/: Stores custom Mustache templates used for code generation.
  • scripts/: Includes utility scripts for generation and maintenance tasks.
  • Configuration files:
    • openapitools.json: Configures the OpenAPI generator.
    • package.json: Manages NPM dependencies and scripts.
    • tsconfig.json: Contains TypeScript configuration settings.

OpenAPI v3 vs v2

We primarily use OpenAPI v3 specifications for client generation because it offers a more generator-friendly experience.

The original algod oas spec from go algorand repository was in v2, but it also provides an auto-converted v3 specification in YAML format. We opted for JSON for consistency with the original v2 spec's format. To minimize duplication, only the original v2 spec was copied into the api/specs/ directory; the v3 JSON spec is automatically generated using the same tool leveraged in the Go Algorand repository, ensuring we stay aligned while using our preferred format.

Improvements Over Original Specification

Modifications to the original specification have mainly focused on improving documentation clarity, formatting consistency, and ensuring compatibility with the generator tools, rather than making significant changes to the API itself. Minor known issues, like problems with non-ASCII characters in JSON responses on certain endpoints, by further refining the spec as we develop test suites for each generated client language.

Notable changes include:

  • Adding consistent formatting and filling in missing descriptions.
  • Removing format: 'byte' from the AvmValue schema. This change prevents an overly complex type definition in the generated Python client that previously caused recursion errors when instantiating the auto-generated pydantic v2 base model.

Client Implementation

Currently, the auto-generated clients cover the transaction endpoints and leverage:

  • algokit-utils-ts and algokit-utils-py: Used within the TypeScript and Python test suites.
  • FFI binding packages: Utilized as test utilities. Transactions are constructed using the FFI bindings packages.

Our initial implementation work focuses on core endpoints critical for basic integration in the algokit-utils packages:

  • Retrieving pending transactions
  • Fetching transaction parameters
  • Posting raw transactions

This focused approach allows us to build a baseline before expanding coverage to more complex endpoints.

Testing Approach

Custom tests were written instead of modifying the auto-generated ones for several reasons:

  1. Setup Complexity: Many endpoints require intricate setup and teardown procedures that are hard to automate generically.
  2. Environment Dependencies: Tests often rely on specific environment configurations (like Sandbox).
  3. Edge Case Coverage: Custom tests provide better flexibility for covering specific edge cases and scenarios.
  4. Maintainability: Following the OpenAPI generator's recommendation, extending functionality via custom tests is more maintainable than altering the generated test code directly.

This approach aligns with best practices for using OpenAPI generators, emphasizing extension over modification.

Template System

The OpenAPI generator uses Mustache templates for code generation. We utilize custom templates stored in the oas_templates/ directory to:

  1. Extend the base templates provided by the OpenAPI generator.
  2. Tailor the generated code to better suit our project's specific needs and conventions.
  3. Inject additional utilities and helper functions where needed.
  4. Implement language-specific optimizations or adjustments.

Template Lookup Order

OpenAPI Generator looks for templates in the following order (simplified from the oas generator docs):

+--------------------------+ (api/oas_templates/{lang}/)
| 1. User Lib Path         | (custom/.../libraries/...)
+------------+-------------+
              | (Not found fetch from next level up)
              v
+--------------------------+
| 2. Base Template content
| and other embedded files | (base template files in oas generators)
+------------+-------------+

Which means that if custom template that was extended from the base does not contain files that exist in base template, the generator will look in the next level up. To explicitly prevent this from happening, we can use the .openapi-generator-ignore files are placed in generation output directory prior to invocation of the generator. This logic is handled inside scripts/generate-api-clients.ts script.

Configuration and Ignore Files

The core configuration for the OpenAPI generator resides in openapitools.json. This file defines:

  • Generator versions to use.

Actual invocation of the generator is done via scripts/generate-api-clients.ts which is a wrapper around the generator tool that is invoked via bun run.

Additionally, .openapi-generator-ignore within each template folder are used to control the regeneration process, allowing us to:

  • Protect manually customized files from being overwritten during generation runs.
  • Skip the generation of files that are not needed.
  • Maintain custom implementations alongside the automatically generated code.

Lastly, each template folder defined openapi-config.yaml file to control the generation process. This allows us to inject custom files that are not part of base templates as well as control the templatised properties offered by each language generator.

Future Work

  1. Expanded Endpoint Coverage and integration into algokit-utils: Develop custom Mustache test templates for each target language to ensure robust testing across more API endpoints. As new categories of endpoints are covered, we can perform integration into algokit-utils in parallel.
  2. CI/CD Integration & Versioning: Implement a proper CI/CD pipeline for automated client generation and introduce semantic versioning. This includes integrating the generated clients into version control and adding output stability tests to track changes easily when specifications are updated.
  3. Monorepo Management: Explore tools like Bazel or NX to improve the management of our multi-language codebase, potentially offering benefits like incremental builds and better dependency handling.
  4. Documentation Generation: Automate the generation of client-specific documentation. The default generator already creates basic Markdown files, but we aim to enhance this.

Furthe notes

Adopting a monorepo structure with tools like Bazel or NX could offer advantages such as:

  • Faster, incremental builds.
  • Coordinated versioning across packages.
  • Simplified cross-language dependency management.
  • Consistent development tooling.

FFI Garbage Collection

One of the challenges with using FFIs in some languages is that they do not offer a way to integrate into the garbage collector. If consumers of the FFI interfaces are not aware of this, they could potentially expose themselves to memory leaks. This document is an overview of the state of garbage collection integration across various languages and if/how our chosen tools use them. If a language or tool is not listed, it should be assumed that the state of garbage collection integration is unknown.

Because GC-integration varies significantly between languages, it seems like the best approach for a safe and consistent FFI across multiple languages is to limit the amount of Rust-owned objects that shared over the FFI boundary.

Summary

This table indicates whether the specific language can safely use Rust-owned objects through the bindings via GC-integration

LanguageStatus
JavaScript (Web/Node)
Python
Swift
React Native
Kotlin
Go

Languages

JavaScript (Web/Node)

The WeakRefs TC39 proposal enables objects to integrate with the JavaScript engine's garbage collection via WeakRefFinalizationRegistry. This is currently supported by 95% of browsers.

The following note, however, should be taken into consideration if we were to rely on this behavior (source)

Garbage collectors are complicated. If an application or library depends on GC cleaning up a WeakRef or calling a finalizer in a timely, predictable manner, it's likely to be disappointed: the cleanup may happen much later than expected, or not at all. Sources of variability include:

  • One object might be garbage-collected much sooner than another object, even if they become unreachable at the same time, e.g., due to generational collection.
  • Garbage collection work can be split up over time using incremental and concurrent techniques.
  • Various runtime heuristics can be used to balance memory usage, responsiveness.
  • The JavaScript engine may hold references to things which look like they are unreachable (e.g., in closures, or inline caches).
  • Different JavaScript engines may do these things differently, or the same engine may change its algorithms across versions.
  • Complex factors may lead to objects being held alive for unexpected amounts of time, such as use with certain APIs.

wasm-bindgen makes use of WeakRefFinalizationRegistry by default Source:

React Native

The React Native UniFFI bindings do not currently make use of FinalizationRegistry due to lack of support in Hermes (source)

Python

__del__ is the destructor for a given object that is called upon garbage collection. UniFFI bindings make use of __del__ for objects with Rust-owned memory.

Kotlin

Kotlin provides various ways to handle object lifecycles, but it is not possible to integrate directly into the GC. Various strategies for using foreign objects generated by UniFFI bindings can be found here.

Go

Go has runtime.SetFinalizer but it is not a perfect solution, especially for objects in memory. From the documentation:

The finalizer is scheduled to run at some arbitrary time after the program can no longer reach the object to which obj points. There is no guarantee that finalizers will run before a program exits, so typically they are useful only for releasing non-memory resources associated with an object during a long-running program.

This means we should likely not rely on SetFinalizer for garbage collecting Rust-owned objects. Or, at the very least, more extensive testing will be required if we were to use SetFinalizer

Swift

Swift is not a GC language, but uses reference-counted pointers. All objects going over the FFI via UniFFI are wrapped in a Rust ARC (even if not explicit, it is done automatically by the binding generator). This presumably means that the reference count is shared between Swift and Rust

Architecture Decision Records (ADRs)

This section contains Architecture Decision Records documenting important decisions made during the development of AlgoKit Core.

Contents

ADR Format

Each ADR follows a standard format:

  • Status - Proposed, Accepted, Deprecated, or Superseded
  • Context - The situation or problem
  • Decision - What we decided to do
  • Consequences - The results of the decision

Numbering

ADRs are numbered sequentially starting from 0000. When adding a new ADR:

  1. Use the next available number
  2. Include a descriptive title
  3. Follow the standard format
  4. Update this README

Purpose

ADRs help us:

  • Remember why we made certain decisions
  • Avoid relitigating settled questions
  • Onboard new team members quickly
  • Learn from past decisions

Living Documents

While decisions should be stable, ADRs can be updated to:

  • Fix typos or improve clarity
  • Add new consequences discovered over time
  • Mark decisions as superseded by newer ADRs

status: proposed date: 2024-12-17 decision-makers: Bruno Martins, Joe Polny, David Rojas consulted: Algorand Foundation CTO office, MakerX engineering team

Native Implementations vs FFI for Core Algorand Functionality

Context and Problem Statement

It is desirable for the Algorand Foundation to create and maintain libraries for multiple languages that provide core Algorand functionality. These libraries should be able to help developers use Algorand but also not reinvent the wheel by trying to handle functionality that is already available in external libraries (i.e. keypair generation, signing, etc.). There should be consistency across languages so that the API is relatively similar and there is not one language that gets ahead or falls behind the others in terms of features, as we've seen with the existing SDKs and AlgoKit Utils libraries. The exact languages we want to target are TBD, but ideally we can get the most popular languages for popular platforms (Python, JS for web and node, Kotlin for Android, Swift for iOS).

Decision Drivers

  • The chosen approach should be easy to implement and maintain
  • The chosen approach should enable cross-language consistency
  • The chosen approach should enable a sane API for core Algorand functionalities (exact list to be determined later)

Considered Options

  • Native implementations in each language
  • Core Rust implementation with handwritten FFI bindings
  • Core Rust implementation with generated FFI-bindings using UniFFI and wasm-pack
  • Core Rust implementation with generated FFI-bindings using language-specific binding generators

Decision Outcome

TBD. Currently leaning towards "Core Rust implementation with generated FFI-bindings using UniFFI and wasm-pack" with the initial spike of algokit_transact.

Confirmation

Research will be done on the FFI/WASM toolchains and an initial spike will be done to determine the feasibility of the approach.

The spike of the solution(s) should explore the following:

  • Implementation/maintenance burden
  • Performance
  • Toolchain maturity
  • Developer experience
  • Cross-platform support
  • Packaging

Pros and Cons of the Options

Native implementations in each language

  • Good, because we can fully leverage the language's capabilities and features
  • Neutral, because we can use some existing Algorand libraries for some functionality
  • Bad, because it will be a significant implementation and maintenance burden

Core Rust implementation with handwritten FFI bindings

One rust implementation with one FFI interface that is used by all languages.

  • Good, because we have one core implementation to maintain
  • Good, because it will be easier to maintain consistency across languages
  • Bad, because Rust is new to the Algorand Foundation engineering team
  • Bad, because there is a cost to crossing the FFI boundary, particularly for light compute functions
  • Bad, because writing C bindings can get quite difficult with complex types

Core Rust implementation with generated FFI-bindings using UniFFI and WASM

One rust implementation with one FFI interface that is used by all languages.

  • Good, because we have one core implementation to maintain
  • Good, because it will be easier to maintain consistency across languages
  • Good, because we can leverage the UniFFI toolchain to generate bindings for multiple languages (Python, Kotlin, Swift... potentially more)
  • Good, because we can expect the UniFFI toolchain to mature and get more bindings over time (third-party bindings for other languages)
  • Good, because if we want to target other languages, we can use the UniFFI framework to write binding generators
  • Good, because we can leverage the wasm-pack toolchain to generate bindings for JS (node and browser)
  • Neutral, these toolchains are fairly new (both v0)
  • Neutral, all languages have to have the exact same API and type definitions, even if it means not taking advantage of the language's capabilities
  • Bad, because Rust is new to the Algorand Foundation engineering team
  • Bad, because there is a cost to crossing the FFI boundary, particularly for light compute functions
  • Bad, because we have to accept some of the performance tradeoffs (made in favor of safety) of UniFFI
  • Bad, because we will need to ship binaries with the library for each language

Core Rust implementation with generated FFI-bindings using language-specific binding generators

One rust implementation with one FFI interface that is used by all languages.

  • Good, because we have one core implementation to maintain
  • Good, because it will be easier to maintain consistency across languages
  • Good, because we can leverage the wasm-pack toolchain to generate bindings for JS (node and browser)
  • Good, because we can leverage language-specific binding generators (i.e. PyO3 for Python) to generate performant bindings that take advantage of the language's capabilities
  • Bad, because Rust is new to the Algorand Foundation engineering team
  • Bad, because there is a cost to crossing the FFI boundary, particularly for light compute functions
  • Bad, because there will be a cost to maintaining the interface and bindings for each language
  • Bad, because it might be harder to maintain consistency across languages
  • Bad, because we will need to ship binaries with the library for each language

More Information

It should be noted that there are other toolchains similar to UniFFI for generating multi-language FFI bindings. UniFFI seemed like the clear winner based on Mozilla's ADR that lead to the creation of UniFFI.

Contributing to AlgoKit Core

Welcome to the contributing section! This contains guides and resources for developers who want to contribute to AlgoKit Core.

Contents

Getting Started

  1. Read the Contributing Guide for the contribution process
  2. Review the Learning Resources to understand the technical foundation
  3. Check the main repository for current issues and discussion

Code of Conduct

All contributors are expected to follow our code of conduct, which emphasizes:

  • Respectful and inclusive communication
  • Constructive feedback and collaboration
  • Focus on technical merit and project goals

Types of Contributions

We welcome various types of contributions:

  • Bug fixes - Help us identify and resolve issues
  • Feature development - Implement new functionality
  • Documentation - Improve guides, examples, and API docs
  • Testing - Add test coverage and improve test quality
  • Performance - Optimize critical paths and reduce resource usage

Community

Join our community discussions:

  • GitHub Issues - For bug reports and feature requests
  • GitHub Discussions - For questions and general discussion
  • Pull Requests - For code contributions and reviews

Thank you for your interest in contributing to AlgoKit Core!

Contributing Guide

Principles

See the core principles in the repository's README

Rust crates vs FFI libraries

The implementation of the rust crate should be completely seperate from the foreign interfaces. For example, algokit_transact does not depend on UniFFI or wasm-bindgen. Instead, there's a seperate crate algokit_transact_ffi that provides the foreign interfaces.

Debugging Rust Code is VS Code

Prerequisites

Install the CodeLLDB extension for Visual Studio Code to debug Rust code.

Debug Configurations

The project includes pre-configured debug configurations in .vscode/launch.json:

  • Debug unit tests in algokit_transact: Debug tests for the core transaction functionality
  • Debug unit tests in algokit_transact_ffi: Debug tests for the FFI bindings

How to Debug

  1. Set breakpoints by clicking in the gutter next to line numbers
  2. Go to the Debug view (Ctrl+Shift+D or Cmd+Shift+D) and select a configuration for the crate you want to debug
  3. Press F5 to start debugging
  4. Use the debug toolbar to step through code (F10 for step over, F11 for step into)

This document contains a non-exhaustive list of learning resources that may be helpful to new contributors. If you used a different resource to help contribute to this repository please PR it!

Rust

If you are new to Rust, the learning curve can be a bit steep. This is largely due to the fact that Rust is not a garbage-collected language and has strict memory borrow checking rules baked into the compiler. At first, this might slow development velocity compared to less-strict GC languages, but this is what enables developers to write safe and performant low-level code with Rust.

Rust Book

The official Rust book is a fantastic way to get started with Rust. It covers the most concepts of the language in a detailed and concise manner. If you want to learn hands-on, check out [Rustlings][#Rustlings] which is an interactive walkthrough of the book!

Rustlings

Rustlings is a set of interactive exercises and quizzes that can be used in any terminal. This is a great way to get hands on with Rust as a complete beginner.

Comprehensive Rust

If you prefer a structured approach to learning a new language, Google's Comprehensive Rust is a Rust curriculum broken into individual days with morning and afternoon sessions. This guide is targeted towards Android development, but the Rust sections are general-purpose.

Learning Rust With Entirely Too Many Linked Lists

One of the most challenging aspects of learning Rust, particularly for developers used to garbage-collected languages, is getting a handle on all the different types of pointers. Once you have tackled the main concepts in the official Rust Book, you might still need some hands-on practice to feel comfortable with all these ways to reference memory. Learning Rust With Entirely Too Many Linked Lists is an unofficial guide on using all of these pointer types by implementing various data structures that require their usage. The implementations in this repository likely won't have too many complex data structures, but it's a good way to get more comfortable with one of the more complex parts of the language.

Little Book of Rust Books

The Little Book of Rust Books is a collection of all the official and unofficial Rust books. There is A LOT covered across all of these books, but if you come across any in particular that were useful for contributing to this repo or simply learn rust please PR them here!

UniFFI

User Guide

The UniFFI user guide is a good place to get started on how UniFFI works and how to develop with it.

Matrix Channel

The UniFFI matrix channel is a great place to get in touch with the maintainers and other developers using UniFFI.

Docs

Of course, the documentation of the UniFFI codebase is also a great place to learn more about how to use UniFFI and how it works.

Source Code

If you want to gain a deeper understanding of the internals of UniFFI, the source is available on GitHub

ADRs

The UniFFI ADRs provide a lot of insight into why UniFFI was created and the various decisions that were made along the way of implementation