The ALKANES specification is hosted at 👉🏻👉🏼👉🏽👉🏾👉🏿 https://github.com/kungfuflex/alkanes-rs/wiki
This repository hosts Rust sources for the ALKANES metaprotocol. The indexer for ALKANES can be built as the top level crate in the monorepo, with builds targeting wasm32-unknown-unknown, usable within the METASHREW indexer stack.
ALKANES is a metaprotocol designed to support an incarnation of DeFi as we have traditionally seen it, but designed specifically for the Bitcoin consensus model and supporting structures. The ALKANES genesis block is 880000. Builders can end-to-end test their own alkanes smart contracts against the exact mainnet indexer code path, no real funds and no live regtest required, using this repository's test harness (see Testing alkanes end-to-end below).
Public SUBFROST RPC endpoints: mainnet https://mainnet.subfrost.io/v4/jsonrpc, signet https://signet.subfrost.io/v4/jsonrpc.
Protocol fees are accepted in terms of Bitcoin and compute is metered with the wasmi fuel implementation, for protection against DoS.
This repository is a pure Rust implementation, built entirely for a WASM target and even tested within the WASM test runner wasm-bindgen-test-runner.
The top level crate in the monorepo contains sources for the ALKANES indexer, built for the METASHREW environment.
ALKANES is designed and implemented as a subprotocol of runes, one which is protorunes compatible. In order to encapsulate the behavior of protorunes for a Rust build system, a Rust implementation of protorunes generics is contained in the monorepo in crates/protorune.
For information on protorunes, refer to the specification hosted at:
https://github.com/kungfuflex/protorune/wiki
The indexer stack used to synchronize the state of the metaprotocol and offer an RPC to consume its data and features is METASHREW. METASHREW is started with a WASM binary of the indexer program, produced with a normal build of this repository as alkanes.wasm.
Bindings to the METASHREW environment are consumed from the pinned kungfuflex/metashrew dependency (the authoritative METASHREW repository); the environment-agnostic pieces live in crates/metashrew-support.
Sources needed to build both metashrew and protorunes meant to be shared with builds of individual alkanes or the generic alkanes-runtime bindings are factored out into crates/metashrew-support and crates/protorune-support such that they can be imported into an alkane build without the metashrew import definitions leaking in and generating import statements for the METASHREW environment.
In this way, all crates with a -support suffix can be imported into any Rust project since they do not depend on a specific environment or wasm-bindgen.
This design is permissive enough for this monorepo to host alkanes-runtime, which is a complete set of bindings for building alkane smart contracts to a WASM format, suitable for deployment within the witness envelope of a Bitcoin transaction.
Boilerplate for various alkanes are included and prefixed with alkanes-std- and placed in the crates/ directory. The build system is designed such that the WASM builds of each crate with this prefix is made available to the test suite as a Rust source file.
Which branch are you on? The build differs between the two long-lived branches, so pull the right one and follow the matching instructions:
main(this branch), the release line; tagged release candidates (e.g.v2.2.1-rc.4) are cut from here, and it is what mainnet runs. It is a mixed workspace (the default cargo target is native), so the indexer wasm must be built with an explicit target and package:cargo build --release --target wasm32-unknown-unknown --features mainnet -p alkanes --locked(see below). Standard alkanes live incrates/alkanes-std-*and are built by the test harness.develop, active development, with a different layout: the top-level crate targets wasm by default, socargo build --releasealone producesalkanes.wasm; the standard alkanes live inalkanes/with prebuilt WASM committed to the repo (rebuilt via./scripts/build-std.sh). If you are ondevelop, follow that branch's README, not this one.
The production ALKANES indexer wasm is built with the command:
cargo build --release --target wasm32-unknown-unknown --features mainnet -p alkanes --lockedThis is the exact, reproducible build used for the shipped alkanes.wasm (see scripts/Dockerfile.wasm / scripts/build.sh, which pin the toolchain and set SOURCE_DATE_EPOCH). Replace mainnet with your network of choice, constants are defined for luckycoin, regtest, mainnet, dogecoin, bellscoin, and fractal; for other networks or test networks, use the regtest feature.
The alkanes.wasm file is produced at target/wasm32-unknown-unknown/release/alkanes.wasm, and a WASM for every crate prefixed with alkanes-std- is made available to the test suite.
Note: this repository is a mixed workspace, the top-level
alkanesindexer crate targetswasm32-unknown-unknown, while the CLI, SDKs, and tooling crates build natively. The default cargo target is native, so pass--target wasm32-unknown-unknown -p alkanesexplicitly when building the indexer.
Refer to the METASHREW documentation for descriptions of the indexer stack used for ALKANES. The authoritative METASHREW repository is:
https://github.com/kungfuflex/metashrew
To index ALKANES on mainnet you must run matching, current versions of both components:
- alkanes-rs
v2.2.1-rc.4(latest), built toalkanes.wasmand loaded via--indexer. Seev2.2.1-rc.4. - metashrew
v9.0.5-rc.14, thekungfuflex/metashrewindexer stack (rockshrew-mono).
Running mismatched versions can produce divergent state. Live mainnet system health, indexer height, sync status, and RPC availability, can be checked at https://mainnet.subfrost.io.
A sample command may look like:
~/metashrew/target/release/rockshrew-mono --daemon-rpc-url http://localhost:8332 --auth bitcoinrpc:bitcoinrpc --db-path ~/.metashrew --indexer ~/alkanes-rs/target/wasm32-unknown-unknown/release/alkanes.wasm --start-block 880000 --host 0.0.0.0 --port 8080 --cors '*'The most useful thing this repository gives contract authors is a test harness that runs your alkane through the exact same indexer code path that runs on mainnet, the real wasmi execution, the real protorune/alkanes state transitions, the real view functions, all in-memory. You build a Bitcoin block, drop in a transaction whose protostone deploys and calls your contract, index it, then call view functions to assert on the result. No real funds and no live regtest node.
An alkanes test project compiles to and runs on wasm32-unknown-unknown under wasm-bindgen-test-runner (the same WASM runner the indexer itself is tested with). One-time setup:
rustup target add wasm32-unknown-unknown
# The runner's version MUST match the pinned wasm-bindgen (0.2.100), or you get a
# "schema version" mismatch:
cargo install -f wasm-bindgen-cli --version 0.2.100with a .cargo/config.toml that points the runner at wasm:
[build]
target = "wasm32-unknown-unknown"
[target.wasm32-unknown-unknown]
runner = "wasm-bindgen-test-runner"An alkane exposes its interface as opcodes, numeric selectors, directly analogous to method selectors on an EVM contract. You declare them with a MessageDispatch enum: #[opcode(N)] is the selector, and #[returns(T)] marks a view method (one that only reads and returns data, the analogue of a view/pure function you'd call with eth_call):
use alkanes_runtime::{declare_alkane, message::MessageDispatch, runtime::AlkaneResponder};
use alkanes_support::response::CallResponse;
use anyhow::Result;
#[derive(Default)]
struct MyToken(());
#[derive(MessageDispatch)]
enum MyTokenMessage {
#[opcode(0)]
Initialize { token_units: u128 }, // state-changing: mints the initial supply
#[opcode(99)]
#[returns(String)]
GetName, // VIEW: read-only, returns the name
}
impl AlkaneResponder for MyToken {}
impl MyToken {
fn initialize(&self, token_units: u128) -> Result<CallResponse> {
let context = self.context()?;
let mut response = CallResponse::forward(&context.incoming_alkanes);
// ... persist name, mint `token_units` into response.alkanes ...
Ok(response)
}
fn get_name(&self) -> Result<CallResponse> {
let context = self.context()?;
let mut response = CallResponse::forward(&context.incoming_alkanes);
response.data = b"MyToken".to_vec(); // read-only: just fill response.data
Ok(response)
}
}
declare_alkane! {
impl AlkaneResponder for MyToken { type Message = MyTokenMessage; }
}declare_alkane! emits the wasm entrypoints (__execute, __meta); the crate is crate-type = ["cdylib", "rlib"]. A build.rs compiles your contract to wasm and generates a get_bytes() your tests can deploy, copy the pattern from free-mint's build.rs. (Stock contracts are also available prebuilt via alkanes::precompiled::*::get_bytes(), used below.)
Add the harness as dev-dependencies. The test-utils feature is required, it's what makes alkanes::tests::helpers public, and your metashrew source must match the one alkanes-rs uses (kungfuflex/metashrew, tag v9.0.5-rc.8) so the workspace resolves to a single metashrew:
[dev-dependencies]
alkanes = { git = "https://github.com/kungfuflex/alkanes-rs", features = ["test-utils"] }
protorune = { git = "https://github.com/kungfuflex/alkanes-rs", features = ["test-utils"] }
alkanes-support = { git = "https://github.com/kungfuflex/alkanes-rs" }
metashrew-core = { git = "https://github.com/kungfuflex/metashrew", tag = "v9.0.5-rc.8", features = ["test-utils"] }
wasm-bindgen-test = "0.3"
anyhow = "1"The test below deploys the stock owned-token contract in a block, indexes it, and reads its GetName view opcode with call_view, the eth_call analogue: it executes a view opcode against current state and returns its bytes, without persisting any state change. (This exact test passes against main.)
use alkanes::indexer::index_block;
use alkanes::precompiled::{alkanes_std_auth_token_build, alkanes_std_owned_token_build};
use alkanes::tests::helpers::{self as alkane_helpers, clear};
use alkanes::view;
use alkanes_support::cellpack::Cellpack;
use alkanes_support::constants::AUTH_TOKEN_FACTORY_ID;
use alkanes_support::id::AlkaneId;
use alkanes_support::utils::string_to_u128_list;
use wasm_bindgen_test::wasm_bindgen_test;
#[wasm_bindgen_test]
fn deploy_and_read_view() -> anyhow::Result<()> {
clear(); // reset the in-memory indexer state
// owned-token opcode 1 = InitializeWithNameSymbol(auth_units, token_units, name, symbol).
// inputs[0] is the opcode; Strings are packed into u128s via string_to_u128_list.
let mut init = vec![1u128, 1, 1000];
init.extend(string_to_u128_list("MyToken".to_string()));
init.extend(string_to_u128_list("MTK".to_string()));
// Each binary is deployed in its own tx; each Cellpack is that tx's protostone.
// {3, AUTH_TOKEN_FACTORY_ID} deploys the auth-token factory; {1,0} deploys + calls
// owned-token, which lands at {2,1}.
let block = alkane_helpers::init_with_multiple_cellpacks_with_tx(
vec![
alkanes_std_auth_token_build::get_bytes(),
alkanes_std_owned_token_build::get_bytes(),
],
vec![
Cellpack { target: AlkaneId { block: 3, tx: AUTH_TOKEN_FACTORY_ID }, inputs: vec![100] },
Cellpack { target: AlkaneId { block: 1, tx: 0 }, inputs: init },
],
);
// Run it through the REAL indexer — the exact code path used on mainnet.
index_block(&block, 0)?;
// Call the view opcode 99 (GetName) with call_view — no state change, like eth_call.
let owned = AlkaneId { block: 2, tx: 1 };
let name = view::call_view(&owned, &vec![99u128], 100_000 /* fuel */)?;
assert_eq!(String::from_utf8(name)?, "MyToken");
Ok(())
}Run it with cargo test. You've now exercised a contract's wasmi execution and a view call in the precise environment it will see live on mainnet, no funds, no regtest.
To read the persisted execution trace of a real indexed call (rather than a stateless view), use view::trace(&outpoint). For a full worked contract, a custom alkane with its own build.rs, a state-changing mint, and trace assertions, see the free-mint and oyl-amm crates, which test against this harness the same way.
Notes
- Tests use
#[wasm_bindgen_test](not#[test]), because the suite compiles to and runs on wasm, the same target as the indexer. - The
test-utilsfeature is mandatory on thealkanes/protorune/metashrew-coredev-deps; it's what exposesalkanes::tests::helpers. - Deploy stock contracts with
alkanes::precompiled::*::get_bytes(); deploy your own with abuild.rs-generatedget_bytes().
alkanes-cli is the command-line client for the ALKANES protocol, Bitcoin, ordinals/runes, and the SUBFROST APIs. Build it from this repo:
cargo build --release -p alkanes-cli
# binary at target/release/alkanes-cliPoint it at any SUBFROST endpoint with -p <network> --jsonrpc-url <url>. The public mainnet JSON-RPC is free (rate-limited); for higher limits, put your API key in the path (/v4/<API_KEY>):
# current indexed block height
alkanes-cli -p mainnet --jsonrpc-url https://mainnet.subfrost.io/v4/jsonrpc metashrew height
# -> 958629
# inspect a deployed alkane's bytecode + code hash
alkanes-cli -p mainnet --jsonrpc-url https://mainnet.subfrost.io/v4/jsonrpc alkanes inspect 2:0 --codehash
# -> Bytecode Length: 262445 bytes
# Code Hash: 3b16eaa2d72f4695cb47fc58ecbbd2909d9c403a703e7db5a688ab3612fdbe00Command namespaces include wallet, alkanes (execute / simulate / trace / inspect / swap), ord, esplora, dataapi, metashrew, runestone, protorunes, subfrost, and brc20-prog. Full reference: https://api.subfrost.io/docs/cli/overview
@alkanes/ts-sdk is the TypeScript library for building on ALKANES, encrypted keystores, HD wallets, transaction construction, and typed clients (AlkanesRpcClient, AlkanesProvider) for the protocol and SUBFROST data APIs. Install the immutable, content-addressed build from pkg.alkanes.build:
npm install "https://pkg.alkanes.build/dist/@alkanes/ts-sdk?v=0.1.6-669e7c0"Create an encrypted keystore and a wallet:
import { createKeystore, createWallet } from '@alkanes/ts-sdk';
// ethers-style encrypted keystore (PBKDF2) + a BIP39 mnemonic
const { keystore, mnemonic } = await createKeystore('your-password', { network: 'mainnet' });
const wallet = await createWallet(keystore, 'your-password');The typed clients read from the same endpoints as the CLI (https://mainnet.subfrost.io/v4/jsonrpc). Full reference: https://api.subfrost.io/docs
Start building today, no indexer to host. SUBFROST serves live ALKANES + Bitcoin data over a public, rate-limited API; get an API key for higher limits.
- API access & docs, https://api.subfrost.io (mainnet JSON-RPC:
https://mainnet.subfrost.io/v4/jsonrpc)
Explore & build
- espo, open-source ALKANES indexer & explorer engine, https://espo.sh
- Block explorer, https://explorer.subfrost.io
- SUBFROST app (swaps, frBTC, AMM), https://app.subfrost.io
- Ecosystem, https://subfrost.io/ecosystem
- Governance (WIP), https://surtur.org
Wallets, ALKANES-enabled Bitcoin, everywhere
- Chrome extension, https://subfrost.io/download
- Android, Google Play (iOS coming soon, so ALKANES-enabled Bitcoin works on mobile)
- espo will soon release an open-source wallet for those running their own infrastructure.
Stay updated
- Articles & updates, https://subfrost.io/articles
ALKANES is carried forward by a community of builders, indexers, contract authors, and researchers who believe finance can be open, permissionless, and native to Bitcoin. To everyone writing code, running infrastructure, shipping contracts, filing issues, and stress-testing the protocol in the open, thank you. This work moves because you move it, toward a vision of free finance for everyone. 🧡
- flex
- v16
- butenprks
- clothic
- m3
MIT