Acala and the case for a Substrate runtime without its own node
Acala - cross-chain DeFi hub and stablecoin based on Substrate for Polkadot and Kusama.
At a glance
- What is it?
- Acala is a cross-chain DeFi hub and stablecoin built as a Substrate runtime, and this repository deliberately contains no client node: the node lives in a separate repository so runtime and client releases can be decoupled. It is a Rust workspace with three runtimes, ORML modules in-tree, and a bench bot that comments on pull requests to generate weights.
- Who is it for?
- Acala suits developers who want to read or modify a production Substrate DeFi runtime, and who understand that a runtime upgrade on a live network is a governance and migration exercise rather than a deploy. It does not suit someone looking for a wallet, a node to run, or a DeFi product to use on the side.
- Can I use it commercially?
- Yes, with conditions. GPL-3.0 is a copyleft licence: if you distribute software that includes it, you must release that software's source code under the same licence. Running it internally without distributing it does not trigger that obligation.
- Is it still maintained?
- Yes. The repository last received commits 73 days ago.
- What is it written in?
- Mainly Rust, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 25, 2026, and from our analysis. They are not legal advice.
Editorial analysis
This repository is the runtime, and the node was deliberately moved out
The first thing to understand about Acala is that the repository you are looking at does not build a node. The documentation is explicit: the Acala client node has been moved to the acala-node repository, and this repository contains only the runtime code. The stated reason is that this decouples the runtime release from the client node release.
That is a real design decision rather than housekeeping. In a Substrate chain, the node is the software users run, and the runtime is the compiled state transition logic that is upgraded on chain through governance. Bundling them means every runtime change forces a node release, and every node release drags along a runtime change. Splitting them lets a chain change its logic without asking every validator and operator to upgrade their binary at the same moment, which is the whole point of having an on-chain runtime in the first place.
The project is described as a cross-chain DeFi hub and stablecoin based on Substrate for Polkadot and Kusama. The Acala Foundation initiated and facilitated it, and the foundation is described as founded by Laminar and Polkawallet, participants and contributors to the Polkadot ecosystem, with the stated aim of nurturing open finance infrastructure such as stable currency and staking liquidity.
The repository is public under GPL-3.0, and the code is Rust with the primary language being Rust. The build badge pins Substrate 2.0.0, and the default branch is `master`.
Three runtimes, one workspace, and ORML kept in the tree
The Cargo.toml defines a workspace whose members describe the shape of the project. Three runtime members are listed: `runtime/mandala`, `runtime/karura` and `runtime/acala`, alongside `runtime/common` and `runtime/integration-tests`.
Then there is the ORML block, and this is the detail that catches a newcomer. Twenty-one ORML members are workspace members: asset-registry, auction, authority, benchmarking, currencies, gradually-update, nft, oracle, parameters, payments, rewards, tokens, tokens/runtime-api, traits, unknown-tokens, utilities, vesting, xcm-support, xcm, and xtokens, plus an oracle runtime-api. The `orml` directory sits in the tree, and a `make update` target updates it. So the open interest rate and lending modules that Acala's money markets are built on are maintained in this repository rather than pulled from an external dependency.
The rest of the tree shows the rest of the system. `modules/` holds Acala's own pallets, `primitives/` the shared types, `ecosystem-modules/` includes a stable-asset library at `ecosystem-modules/stable-asset/lib/stable-asset`, and `runtime/` the three runtimes and their shared code. There is a `predeploy-contracts` directory, an `evm-bench` directory, `evm-tests` with jsontests, `chainspecs/`, `launch/`, `audit/`, `templates/`, `scripts/`, `ts-tests/` and a `devcontainer/`.
The workspace resolver is set to "2" and the workspace version is 2.37.0. The edition is 2021, and the release profile sets `panic = "unwind"` with the comment that the Acala runtime requires unwinding, which matters for anyone reading panic handling in pallet code.
Building means rustup, apt packages, and make init
The build instructions are short and they assume a Linux workstation. First Rust itself:
curl https://sh.rustup.rs -sSf | shThen the system dependencies, which the documentation flags as possibly needing more, pointing at the Substrate installation docs:
sudo apt-get install -y git clang curl make libssl-dev llvm libudev-dev protobuf-compilerThird, a one-line git setting, because the repository has submodules and the documentation says this makes life with submodules easier:
git config --global submodule.recurse trueAnd finally the toolchain bootstrap plus git hooks:
make initA pinned `rust-toolchain.toml` at the repository root decides which compiler version is used, so the rustup install is the setup step and the toolchain file is the actual selection. The `make init` target runs `./scripts/init.sh`, which is the point where the required tools and the git hooks are installed.
The `githooks/` directory at the root and the `HEADER-GPL3` file point at the same thing from the other direction. The header file is the GPL-3.0 boilerplate that source files carry, and the hooks exist to keep new files compliant and formatted. The `.rustfmt.toml` file and the `.editorconfig` file handle formatting, and `.codespell/` handles spell checking.
For the client binary itself, the documentation redirects you to the acala-node repository, so a build of this tree alone will not give you a node to sync a chain with.
make run starts a chain in a browser, not a node process
The run instructions have a detail that surprises people who expect a substrate node to appear. The documentation says you can start a development chain with `make run`, and the Makefile shows what that target actually does: it depends on `chainspec-dev` and then invokes the chopsticks runner against `chainspecs/dev.json`.
So the local development loop is not a synchronising node with a keystore. It is a JavaScript runtime emulator that executes the chain logic against a chain spec, which is why the target calls `bunx @acala-network/chopsticks` rather than launching an executable. The `bunx_or_npx` variable in the Makefile falls back to `npx` when `bunx` is not installed, so either JavaScript package runner works.
There are separate targets for the other two networks: `run-acala-dev` uses `chainspecs/acala-dev.json` in the same way. So the three runtimes each have a development chain spec, and the two dev entries that exist as Make targets are Mandala through `run` and Acala through `run-acala-dev`.
The development commands around it are the familiar substrate set. `make check-all` type checks everything, and the Makefile shows what that expands to: check-runtimes, check-benchmarks, check-tests, check-integration-tests and check-try-runtime. The runtimes are checked with the `runtime-benchmarks`, `try-runtime` and `on-chain-release-build` features, and Mandala additionally with `on-chain-release-build` and ethereum compatibility flags.
For clearing state, `make purge` removes old chain data and `make restart` purges and runs. The `make build` target skips the Wasm build for speed, `make build-full` includes it, and `make build-all` builds with `--locked`.
The bench bot turns a pull request comment into a weights file
Weight generation is handled by a bot rather than by a person running a benchmarking node and pasting results. The documentation describes a bench bot that can sync a branch with `master` and generate WeightInfos for a module or a runtime, and it pushes the generated file into your branch.
The interface is a pull request comment with a slash command. For a module, `/bench module <module_name>`, with `module_currencies` given as the example. For a runtime, `/bench runtime <runtime> <module_name>`, with `/bench runtime mandala module_currencies` as the example, and `*` accepted as the module name to cover every module in one go.
There is a third form for the EVM side. Commenting `/bench evm` benchmarks Acala EVM+ and generates precompile weights along with the GasToWeight ratio. That last output is worth noting because the conversion between EVM gas and Substrate weight is not derivable by hand, and the bot producing it means the ratio is regenerated from measurement rather than edited by hand.
The bot's existence changes how a contributor works. Rather than running the benchmarking suite locally, which needs a specific hardware profile and a long build, you open a pull request and let the bot commit the results into your branch. The review then covers both the code change and the generated weights file, which is a slightly unusual review surface.
The Cargo.toml carries a comment about the build profile for benchmarking dependencies, noting that the listed crates are suspected to be CPU intensive, unlikely to require debugging, and unlikely to be recompiled often, so they are compiled with `opt-level=3` even in dev mode, and that the majority are cryptographic libraries.
try-runtime exists because a storage change can brick a chain
If you modify storage, the documentation says you should test the data migration before upgrading the runtime. That is the `try-runtime` workflow, and it is the safety net that makes runtime upgrades on a live network defensible.
The Karura flow has three steps. The first uses a live chain, and the comment notes that `-p module_name` can specify the module:
# Use a live chain to run the migration test.
# Add `-p module_name` can specify the module.
make try-runtime-karuraThe second step creates a state snapshot, and here the flag differs, `--pallet module_name`:
# Create a state snapshot to run the migration test.
# Add `--pallet module_name` can specify the module.
cargo run --features with-karura-runtime --features try-runtime -- try-runtime --runtime existing create-snapshot --uri wss://karura.api.onfinality.io:443/public-ws karura-latest.snapThe third runs the migration test against that snapshot, pointing at the runtime Wasm and a dev chain:
# Use a state snapshot to run the migration test.
./target/release/acala try-runtime --runtime ./target/release/wbuild/karura-runtime/karura_runtime.compact.compressed.wasm --chain=karura-dev on-runtime-upgrade snap -s karura-latest.snapAcala has the parallel flow, `make try-runtime-acala`, with `--features with-acala-runtime` and the Acala endpoint, producing `acala-latest.snap` and using the acala runtime Wasm.
The inconsistency between `-p` and `--pallet` across the two steps is real, and the comments flag it, but it is a small tax. The bigger point is the shape of the workflow: a migration is executed against a snapshot of live state before it is proposed, which is the difference between a runtime upgrade and a chain freeze.
What the bug bounty covers, and what it does not
The bug bounty program is narrow, and the scope is stated in one sentence: it includes only on-chain vulnerabilities that can lead to significant economic loss or instability of the network. Details and submissions go through the listing at immunefi.com.
The word on-chain is doing the scoping work. A vulnerability in the node binary, in the RPC layer, in the wallet integration, or in the deployment tooling sits outside what the program covers, even if it is serious. For a DeFi chain, that is a defensible line to draw, since economic loss of the protocol is the failure that matters, but it means the bounty is not a full security review of the codebase.
The repository also carries an `audit/` directory, which holds the audit files for the runtime, and the `resources/` directory for other supporting files. The presence of both is worth knowing about when you are assessing how the runtime has been reviewed.
For anyone reading this as an adoption decision rather than a development decision, the point is narrower. Acala is a live protocol on Polkadot and Kusama with a released runtime version 2.37.0, and the code is public under GPL-3.0. Whether the protocol is right for a given user is a separate question from whether the runtime code is well maintained, and the repository can only speak to the second one.
Release cadence and what the version number tells you
The release history is slow and steady, which is what a runtime repository should look like. The three most recent releases are 2.35.0 on 2026-03-17, 2.36.0 on 2026-06-14, and 2.37.0 on 2026-08-13. The releases are titled jointly for Acala and Karura, which reflects that the two networks share this codebase.
The last push to the `master` branch was on 2026-08-13, the same day as the 2.37.0 release, so the branch and the release line are in step. The repository is not archived.
Two consequences follow for a user of this code. First, the version in the workspace manifest is 2.37.0, so a `cargo build` from a clean checkout produces the released runtime, and a runtime upgrade proposal is a diff against a known tag. Second, the cadence is roughly quarterly, which is slow enough that a runtime bug fix is not something you will get on demand. If you find a problem in the runtime logic, the delivery mechanism is a governance proposal, and the timeline is set by that process rather than by the maintainers' release schedule.
The practical advice for a developer is therefore to pin. The Cargo.lock is committed, the `make build-all` target uses `--locked`, and the benchmarking build targets use `--locked` as well, so the repository clearly intends reproducible builds. A fork that tracks `master` will pick up runtime changes to the money markets and the EVM, and a fork that pins a tag will not get the fixes. That choice is yours, and it should be made deliberately.
Editorial conclusion
Acala suits developers who want to read or modify a production Substrate DeFi runtime, and who understand that a runtime upgrade on a live network is a governance and migration exercise rather than a deploy. It does not suit someone looking for a wallet, a node to run, or a DeFi product to use on the side. Before you build on it, check three things: that the client node you need comes from the acala-node repository rather than this one, that your toolchain matches the pinned `rust-toolchain.toml` and the `make init` bootstrap, and that any storage change you make has a try-runtime test against a snapshot from the matching network. The GPL-3.0 licence covers the code, the bug bounty covers only on-chain vulnerabilities that can lead to significant economic loss or instability, and the last push was on 2026-08-13.
Frequently asked questions
What is Acala and what is in the AcalaNetwork/Acala repository?
Acala is a cross-chain DeFi hub and stablecoin based on Substrate for Polkadot and Kusama. This repository contains only the runtime code, because the client node was moved to the separate acala-node repository to decouple runtime and node releases.
How do I build the Acala runtime?
Install Rust with `curl https://sh.rustup.rs -sSf | sh`, install the system packages including clang, llvm, libssl-dev and protobuf-compiler, set `git config --global submodule.recurse true`, and then run `make init` to install the required tools and git hooks. The repository ships a pinned `rust-toolchain.toml`.
How do I run a local Acala development chain?
Run `make run`, which depends on `chainspec-dev` and runs the chopsticks runtime emulator against `chainspecs/dev.json`. There is also `make run-acala-dev` for the Acala dev chain spec, and `make restart` to purge old chain data and run again.
What does the Acala bench bot do?
It benchmarks modules or a runtime from a pull request comment and generates the weights file, pushing the result into your branch. The commands are `/bench module <module_name>`, `/bench runtime <runtime> <module_name>` where `*` covers all modules, and `/bench evm` for Acala EVM+ precompile weights and the GasToWeight ratio.
How do I test a runtime storage migration before upgrading?
Use the try-runtime workflow documented for each network. On Karura, run `make try-runtime-karura` against a live chain, create a snapshot with the `with-karura-runtime` and `try-runtime` features, then run the migration test against that snapshot with the runtime Wasm and `--chain=karura-dev`. The Acala flow is the same with `make try-runtime-acala` and the acala snapshot.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/acalanetwork-acala)Was this useful?