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:
- algokit_transact - Core transaction building and signing functionality
- algokit_transact_ffi - Foreign Function Interface bindings for multiple languages
- ffi_macros - Procedural macros for FFI code generation
- uniffi_bindgen - Custom UniFFI binding generation tools
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:
- Core Layer (
algokit_transact
) - Pure Rust implementations of Algorand transaction logic - FFI Layer (
algokit_transact_ffi
) - Language bindings and foreign function interfaces - 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:
- Research - Technical research and analysis
- Architecture Decisions - Documented decisions and their rationale
- Contributing - Guidelines for contributors
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 scenariosrlib
- For standard Rust library usage
Key Dependencies
serde
- Serialization frameworkrmp-serde
- MessagePack serializationsha2
- SHA-256 hashinged25519-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
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 bindingsffi_wasm
- WebAssembly/JavaScript bindings
Crate Types
Built as both:
cdylib
- Dynamic library for FFIstaticlib
- Static library for embedding
Architecture
The FFI layer uses several approaches:
- UniFFI - Generates bindings automatically from Rust definitions
- wasm-bindgen - Provides seamless WebAssembly integration
- Custom macros - Uses
ffi_macros
for specialized binding generation
Key Dependencies
algokit_transact
- Core functionalityffi_macros
- Procedural macros for FFI code generationuniffi
- Multi-language binding generationwasm-bindgen
- WebAssembly bindingstsify-next
- TypeScript type generation
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 manipulationquote
- Code generation utilitiesconvert_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
The complete API documentation with all macro definitions and usage examples.
Development
When modifying the macros:
- Test with all FFI targets (WebAssembly, UniFFI)
- Verify generated code compiles correctly
- Update tests for new functionality
- 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
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:
- Test with all target languages
- Verify generated bindings compile correctly
- Run integration tests with
algokit_transact_ffi
- 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
- glibc and musl - Analysis of C library compatibility for FFI bindings
- OpenAPI Generators - Research on API documentation and client generation tools
- FFI Garbage Collection - Memory management strategies for foreign function interfaces
Purpose
These research documents serve to:
- Document our investigation process
- Provide context for architectural decisions
- Share knowledge with the community
- Guide future development choices
Contributing Research
When adding new research:
- Focus on technical analysis relevant to AlgoKit Core
- Include concrete findings and recommendations
- Reference sources and provide reproducible examples
- 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 theAvmValue
schema. This change prevents an overly complex type definition in the generated Python client that previously caused recursion errors when instantiating the auto-generatedpydantic
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:
- Setup Complexity: Many endpoints require intricate setup and teardown procedures that are hard to automate generically.
- Environment Dependencies: Tests often rely on specific environment configurations (like Sandbox).
- Edge Case Coverage: Custom tests provide better flexibility for covering specific edge cases and scenarios.
- 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:
- Extend the base templates provided by the OpenAPI generator.
- Tailor the generated code to better suit our project's specific needs and conventions.
- Inject additional utilities and helper functions where needed.
- 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
- 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.
- 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.
- 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.
- 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
Language | Status |
---|---|
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
- 0000: Native Implementations vs FFI - Decision on implementation strategy for multi-language support
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:
- Use the next available number
- Include a descriptive title
- Follow the standard format
- 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
- Contributing Guide - Step-by-step guide for making contributions
- Learning Resources - Helpful resources for understanding the codebase and related technologies
Getting Started
- Read the Contributing Guide for the contribution process
- Review the Learning Resources to understand the technical foundation
- 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
- Set breakpoints by clicking in the gutter next to line numbers
- Go to the Debug view (
Ctrl+Shift+D
orCmd+Shift+D
) and select a configuration for the crate you want to debug - Press
F5
to start debugging - 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