ink! smart contracts for Bittensor Alpha and TAO over-the-counter markets.
The repository currently contains three deployable contracts:
| Contract | Purpose |
|---|---|
otc_contract |
Spot Alpha listings and TAO offers with market-relative pricing, fee collection, subnet listing freezes, pause controls, and owner-controlled upgrades. |
lockup_listings |
Alpha listings that sell into per-purchase lockup escrows. Supports partial fills, configurable lockup duration bounds, fee collection, pause controls, and owner-controlled upgrades. |
alpha_lockup |
Per-purchase escrow contract instantiated by lockup_listings. It holds purchased Alpha until unlock_block, then lets the buyer claim the Alpha plus any staking rewards. |
Shared Bittensor environment types, runtime calls, chain extensions, and fixed-point helpers live in shared.
.
|-- contracts/
| |-- alpha_lockup/ # Escrow contract used by lockup listings
| |-- lockup_listings/ # Lockup listing marketplace
| `-- otc/ # Spot OTC marketplace
|-- integration-tests/ # TypeScript integration tests for a local contracts node
|-- shared/ # Shared ink! environment, runtime types, and helpers
`-- Cargo.toml # Rust workspace
- Rust from
rust-toolchain.toml(1.89) withrust-src,rustfmt,clippy, andwasm32-unknown-unknown. cargo-contract5.x.- Node.js 22 LTS for the TypeScript integration tests.
- A Bittensor/Subtensor contracts development node with the expected chain extension methods.
Install the Rust contract tool if needed:
cargo install --force --locked cargo-contractcargo fmt -- --check
cargo clippy --all-targets --all-features -- -D warnings
cargo test
cargo contract build --release --manifest-path contracts/alpha_lockup/Cargo.toml
cargo contract build --release --manifest-path contracts/otc/Cargo.toml
cargo contract build --release --manifest-path contracts/lockup_listings/Cargo.tomlContract artifacts are written under target/ink/<contract_name>/.
The workspace keeps Cargo.lock tracked on purpose. These are deployable smart contracts, so reproducible dependency resolution is more important than treating the workspace like a reusable library crate.
The integration test suite expects a local contracts node at ws://127.0.0.1:9944 unless CONTRACTS_NODE_URL is set.
cd integration-tests
npm install
CONTRACTS_NODE_URL=ws://127.0.0.1:9944 npm test -- --runThe suite deploys or reuses local artifacts from:
target/ink/alpha_lockup/alpha_lockup.wasmtarget/ink/lockup_listings/lockup_listings.wasmtarget/ink/otc_contract/otc_contract.wasm
Local deployment cache files are ignored by Git:
integration-tests/.contract-addressintegration-tests/.lockup-listings-addressintegration-tests/.alpha-lockup-code-hash
- Build and upload
alpha_lockup; keep its code hash. - Instantiate
lockup_listingswith thealpha_lockupcode hash. - Instantiate
otc_contractfor spot listings and offers. - Before using listing or selling flows that move stake from a user, that user must authorize the marketplace contract as a proxy on the chain.
new(
owner: AccountId,
hotkey: AccountId,
fee_rate: u128,
min_listing_amount: u64,
min_offer_amount: u64,
min_listing_age: u32,
)new(
owner: AccountId,
hotkey: AccountId,
escrow_code_hash: Hash,
fee_rate: u128,
min_listing_amount: u64,
min_purchase_amount: u64,
min_lockup_duration: u32,
max_lockup_duration: u32,
)new(
buyer: AccountId,
netuid: u16,
alpha_amount: u64,
unlock_block: u32,
hotkey: AccountId,
)alpha_lockup is normally instantiated by lockup_listings when a buyer takes a lockup listing.
- TAO amounts are represented in rao:
1 TAO = 1_000_000_000 rao. - Alpha amounts are also represented in rao-style integer units.
- Fee rates are raw
U64F64fixed-point bits. For example,0.005or 0.5% is92233720368547758. - The chain extension returns Alpha market price as
TAO per Alpha * 1e9. price_offset_bpsis an offset from the live market price in basis points:0means market price.500means 5% above market.-500means 5% below market.- Values must be greater than
-10000.
Execution price is calculated at execution time:
executed_price = market_price * (10000 + price_offset_bps) / 10000
list_alpha(hotkey, netuid, amount, price_offset_bps) -> Result<u64>
#[payable] create_tao_offer(netuid, price_offset_bps) -> Result<u64>
#[payable] take_alpha_listing(netuid, seller, listing_id) -> Result<()>
take_tao_offer(netuid, buyer, offer_id, hotkey) -> Result<()>
cancel_alpha_listing(netuid, listing_id) -> Result<()>
cancel_tao_offer(netuid, offer_id) -> Result<()>
force_cancel_alpha_listing(netuid, seller, listing_id) -> Result<()>
claim_dividends(netuid) -> Result<()>list_alpha and take_tao_offer move Alpha from the caller through the Bittensor proxy flow. The caller must have authorized the contract as a proxy first.
take_alpha_listing accepts overpayment and refunds excess TAO after the trade executes.
claim_dividends is owner-only. It claims stake held by the contract above the Alpha reserved for open listings.
get_listing(netuid, seller, listing_id) -> Option<AlphaListing>
get_user_listings(seller, netuid) -> Vec<u64>
get_offer(netuid, buyer, offer_id) -> Option<TaoOffer>
get_user_offers(buyer, netuid) -> Vec<u64>
estimate_listing_price(netuid, seller, listing_id) -> Result<u64>
estimate_offer_price(netuid, buyer, offer_id) -> Result<u64>
get_reserved_alpha(netuid) -> u64
is_subnet_frozen(netuid) -> bool
get_pause_state() -> PauseStateUsers are limited to 25 active Alpha listings and 25 active TAO offers per subnet.
create_lockup_listing(hotkey, netuid, amount, price_offset_bps, lockup_duration) -> Result<u64>
#[payable] take_lockup_listing(netuid, seller, listing_id, amount) -> Result<u64>
cancel_lockup_listing(netuid, listing_id) -> Result<()>
force_cancel_lockup_listing(netuid, seller, listing_id) -> Result<()>create_lockup_listing moves Alpha from the seller through the Bittensor proxy flow. The seller must have authorized the contract as a proxy first.
take_lockup_listing can partially fill a listing. Each purchase instantiates a new alpha_lockup escrow and returns its purchase id. If a partial fill leaves a nonzero remainder below Bittensor's minimum stake amount of 2_000_000 rao, the purchase is rejected.
get_listing(netuid, seller, listing_id) -> Option<LockupListing>
get_user_listings(seller, netuid) -> Vec<u64>
get_escrow(netuid, listing_id, purchase_id) -> Option<AccountId>
estimate_lockup_price(netuid, seller, listing_id, amount) -> Result<u64>
get_reserved_alpha(netuid) -> u64
get_escrow_code_hash() -> Hash
get_lockup_duration_limits() -> (u32, u32)
get_pause_state() -> PauseStateUsers are limited to 25 active lockup listings per subnet.
claim() -> Result<()>
get_info() -> Result<LockupInfo>
get_buyer() -> AccountId
get_unlock_block() -> u32
is_claimed() -> bool
blocks_until_unlock() -> u32
account_id() -> AccountIdOnly the buyer can call claim. Claiming before unlock_block fails. A successful claim transfers all current escrow stake to the buyer, including any staking rewards, emits AlphaClaimed, and terminates the escrow contract.
Both marketplace contracts have owner-only controls for:
- owner transfer
- hotkey updates
- fee rate updates
- minimum listing, offer, or purchase amount updates
- pause and resume
set_codeupgrades
Additional owner controls:
otc_contract:set_subnet_listing_status(netuid, frozen)prevents new Alpha listings on a subnet while preserving cancellation and other allowed maintenance flows.lockup_listings:update_escrow_code_hashandupdate_lockup_duration_limits.
Pause states are shared:
| State | Effect |
|---|---|
NotPaused |
Normal operation. |
TradingPaused |
New trading actions are blocked while maintenance and cancellation flows remain available where implemented. |
FullyPaused |
Trading and most state-changing flows are blocked except owner recovery actions such as resume. |
- These contracts are not presented here as externally audited.
- The contracts depend on Bittensor runtime calls and chain extension behavior for stake movement, stake queries, and live Alpha pricing.
- Stake-moving seller flows require proxy authorization from the user before they can succeed.
- Market-relative prices are evaluated at execution time, not listing creation time.
- State is updated before external transfers in trade and claim flows.
- Stake transfer verification allows a 10 rao tolerance for Subtensor rounding or micro-fees.
- Keep owner keys and upgrade authority operationally separate from test keys and development accounts.
Report suspected vulnerabilities privately using the instructions in SECURITY.md.
This project is licensed under the MIT License. See LICENSE for details.