Create a Subnet with a Crowdloan
This page describes creating a subnet via crowdloan on a locally deployed Bittensor chain. We will use the Polkadot‑JS web app to submit extrinsics.
See also Crowdloans Overview
The following steps will take us through the lifecycle of a subnet creation crowdloan:
- First, we will create a crowdloan for a subnet. This is a special contract that will conditionally create the subnet if enough funds are raised (this threshold is called a crowdloan's cap).
- Next, we will contribute enough funds for the crowdloan to reach its cap.
- Next we must finalize the crowdloan, which executes the action wrapped inside the crowdloan—the creation of the subnet.
- Finally, we will verify the successful creation of the subnet by starting its emissions and observing the flow of liquidity to validator and creator hotkeys.
Prerequisites
- A locally running subtensor development chain. For more information, see run a local Bittensor blockchain instance.
- Polkadot‑JS browser app and Polkadot‑JS browser extension installed.
- An accessible 'Alice' wallet (see: Provision Wallets for Local Deploy)
Step 1: Connect Polkadot‑JS to your local chain
- Open the Polkadot‑JS app.
- In the network selector, choose Development → custom endpoint
ws://127.0.0.1:9944
. - Confirm your local chain metadata loads and your test accounts appear in the Accounts tab. To do this, see create and import accounts to the Polkadot-JS extension.
If the web app does not connect to your local chain, your browser’s privacy or security settings may be blocking it. Try adjusting those settings and reconnecting.
Step 2: Generate call hash
Before creating the crowdloan, you must first generate the hash that registers the subnet and creates a dedicated proxy for the designated beneficiary. To begin:
-
Go to Developer → Extrinsics.
-
Under “using the selected account”, pick the crowdloan "
creator
" account. -
Under “submit the following extrinsic”, choose module
subtensorModule
, callregisterLeasedNetwork(emissionsShare, endBlock)
. -
Fill the parameters:
emissionsShare
: choose a percentage, e.g, 30.endBlock
: leave as none.
-
Copy the hex code shown in the encoded call data field. You will use this to create the crowdloan in the next step.
Do not submit the transaction after entering the parameters. Only copy the encoded call data once all parameters are provided.
Step 3: Create a crowdloan
We will create a campaign whose purpose is to register a leased subnet on finalize.
-
Go to Developer → Extrinsics.
-
Under “using the selected account”, pick the crowdloan "
creator
" account. -
Under “submit the following extrinsic”, choose module
crowdloan
, callcreate
. -
Fill the parameters:
deposit
: choose an amount (e.g.,10,000,000,000
= 10 TAO on default dev config)min_contribution
: e.g.,100,000,000
(0.1 TAO)cap
: e.g.,2,000,000,000,000
(2000 TAO)end
: pick a block height in the near future (e.g., current + 5000)call
: put the hex code of the encoded call data saved from the previous step.target_address
: leave as None.
info- Set the
cap
value higher than the projected subnet lock cost plus proxy deposit (and a small fee buffer). On most dev setups the baseline lock cost is 1,000 TAO (1,000,000,000,000 RAO). Ifcap
equals the lock cost exactly, the lease coldkey may lack enough to pay proxy deposits and finalize can fail with insufficient balance. - If your local subtensor node uses non-fast blocks, the minimum duration for a crowdloan is one week (≈ 50,400 blocks). Therefore, the
end
value must be set at least 50,400 blocks after the current block. This limitation also applies on testnet and mainnet.
-
Click Submit Transaction and sign with the
creator
account.
Get the crowdloan ID
Crowdloan IDs are allocated sequentially, starting from 0
, with each new crowdloan assigned the next incremental ID. There is no extrinsic to list created crowdloans. Therefore, to check the identity of crowdloans created, you must use one of these methods.
-
From Events:
- Navigate to the block explorer after submitting the crowdload transaction.
- In the Explorer tab, find the block in which the transaction occured.
- In the Events panel, locate the
crowdloan.create
extrinsic. Thecrowdloan.Created
event payload includescrowdloanId
that represents the ID of the crowdloan.
-
From storage:
- From the Developer dropdown, navigate to Chain state → Storage.
- Click the selected state query menu and select
crowdloan.nextCrowdloanId
. - Click the + icon to run the query.
tipThis query returns the ID assigned to the next crowdloan that will be created. Subtract 1 from the returned value to determine the total number of crowdloans that currently exist.
-
From the JS console:
- From the Developer dropdown, navigate to Javascript.
- Next, paste the following code block in the editor and run:
// List all existing crowdloan ids
const keys = await api.query.crowdloan.crowdloans.keys();
console.log(keys.map((k) => k.args[0].toNumber()));
Step 4: Contribute to the crowdloan
All contributions must occur before the defined end
block and will be clipped to the cap
value provided.
To contribute to the crowdloan, repeat the following steps for each contributor account:
- From the Developer dropdown, navigate to Extrinsics
- Under “using the selected account”, select the crowdloan "contributor(s)" account.
- Under “submit the following extrinsic”, choose module
crowdloan
, callcontribute (crowdloan_id, amount)
. - Provide the
crowdloan_id
(typically 0 on a fresh chain) and an amount. - Submit and sign.
The crowdloan cap is the maximum total raise. If a contribution would push the total above this cap, the contribution is clipped to fit the remaining available amount. Once the cap is reached, any further contributions are rejected and a crowdloan.CapRaised
event is triggered.
Verify crowdloan contributions
To verify crowdload contributions:
-
From Events:
- Navigate to the block explorer after contributing to the crowdload.
- In the Explorer tab, find the block in which the transaction occured.
- In the Events panel, locate the
crowdloan.contribute
extrinsic. Thecrowdloan.Contributed
event payload contains thecrowdloanId
, the contributing account, and amount contributed.
-
From storage:
-
From the Developer dropdown, navigate to Chain state → Storage.
-
Click the selected state query menu and select one of the following:
crowdloan.Crowdloans(crowdloan_id)
to check details of the crowdloancrowdloan.Contributions(crowdloan_id, contributor)
to check contributions by an account.
-
Click the + icon to run the query.
-
Step 5: Finalize the crowdloan
The crowdload can be finalized by the creator when the end block has passed and the cap has been fully raised (raised == cap
).
- Wait for the chain to reach the
end
block. - From the Developer dropdown, go to Extrinsics.
- Under using the selected account, select the crowdloan creator account.
- Select
crowdloan.finalize(crowdloan_id)
and put the ID of the crowdload. - Submit and sign.
Show Event Output
system.ExtrinsicSuccess
balances.Withdraw (x2)
system.NewAccount (x2)
balances.Endowed
balances.Transfer (x2)
subtensorModule.RegistrationAllowed
subtensorModule.MaxAllowedUidsSet
subtensorModule.MaxAllowedValidatorsSet
subtensorModule.MinAllowedWeightSet
subtensorModule.MaxWeightLimitSet
subtensorModule.AdjustmentIntervalSet
subtensorModule.RegistrationPerIntervalSet
subtensorModule.AdjustmentAlphaSet
subtensorModule.ImmunityPeriodSet
subtensorModule.MinDifficultySet
subtensorModule.MaxDifficultySet
subtensorModule.NetworkAdded
balances.Reserved
proxy.ProxyAdded
subtensorModule.SubnetLeaseCreated
crowdloan.Finalized
balances.Deposit
transactionPayment.TransactionFeePaid
extrinsic event
- Even if the
cap
has been raised, the crowdloan cannot be finalized before theend
block. Finalizing before the contribution period ends fails with aContributionPeriodNotEnded
event. - If
target_address
was provided, the raised amount is transferred there. - The stored
subtensor.register_leased_network
call executes with creator origin, and the subnet lease is created. - The created subnet lease includes the coldkey and hotkey of the proxy wallet that manages the subnet. See Get the lease coldkey.
Verify the leased subnet
Finalizing the crowdloan registers a new subnet and creates a dedicated proxy for the designated beneficiary. Use one of the following methods to verify the creation of the leased subnet:
-
Using BTCLI:
You can verify the creation of the new subnet by running the following command in your terminal:
btcli subnets list --network local
This command lists all created subnets on the chain. Notice the addition of a new subnet among the listed subnets—netuid
2
in the following output.
Show Sample Output
Using the specified network local from config
[15:49:40] Warning: Verify your local subtensor is running on port 9944. subtensor_interface.py:89
Subnets
Network: local
┃ ┃ Price ┃ Market Cap ┃ ┃ ┃ ┃ ┃
Netuid ┃ Name ┃ (Τ_in/α_in) ┃ (α * Price) ┃ Emission (Τ) ┃ P (Τ_in, α_in) ┃ Stake (α_out) ┃ Supply (α) ┃ Tempo (k/n)
━━━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━━━━━╇━━━━━━━━━━━━━╇━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━╇━━━━━━━━━━━━━
0 │ τ root │ 1.0000 τ/Τ │ τ 0.00 │ τ 0.0000 │ -, - │ Τ 0.00 │ 0.00 Τ /21M │ -/-
2 │ β omron │ 0.0000 τ/β │ τ 0.00 │ τ 0.0000 │ τ 1.00k, 1.00k β │ 0.00 β │ 1.00k β /21M │ 3/10
1 │ α apex │ 0.0000 τ/α │ τ 0.00 │ τ 0.0000 │ τ 10.00, 10.00 α │ 1.00 α │ 11.00 α /21M │ 28/100
────────┼───────────┼─────────────┼─────────────┼──────────────┼────────────────────────┼───────────────┼──────────────┼─────────────
4 │ │ τ 0.0 │ │ τ 0.0 │ τ 2.01k/20.93k (9.60%) │ │ │
Step 6: Start the leased subnet (via proxy)
Before starting the subnet, you must first get the address of the proxy wallet specified in the subnet lease, as this wallet controls the subnet.
Get the lease coldkey
- From the Developer dropdown, navigate to Chain state → Storage.
- Click the selected state query menu and select
subtensorModule.SubnetLeases(lease_id)
to display details of the subnet lease, including the beneficiary, emission share, end block, lease coldkey and hotkey, netuid, and creation cost. - Click the + icon to run the query.
- Copy the value of the
lease.coldkey
in the response. You can add the lease coldkey to the address book on the Polkadot.js web app so that it's selectable in the UI.
- In your local environment, the
lease_id
would be the same as the ID of the crowdloan created. You can confirm thelease_id
by examining the block where the subnet lease was created for asubtensorModule.SubnetLeaseCreated
event.
Next, follow the following steps to start the subnet:
-
Go to Developer → Extrinsics.
-
Under “using the selected account”, pick the crowdloan "
creator
" account. -
Under “submit the following extrinsic”, choose module
proxy
, callproxy(real, forceProxyType, call)
. -
Fill the parameters:
real
: enter thelease.coldkey
gotten from the previous query.forceProxyType
: click the toggle and then choose theSubnetLeaseBeneficiary
option in the dropdown.call
: choosesubtensorModule.start_call
and then enter the netuid of the subnet you want to start.- Submit and sign.
Observe dividends distribution
Emissions accrue in Alpha (subnet share units), but are distributed in TAO. On distribution, the contributors' alpha is unstaked/swapped to TAO using the subnet pool; if swap/unstake cannot proceed (liquidity/price), the alpha is accumulated for later.
Owner emissions are periodically split among contributors and the beneficiary, but only when all of these are true:
- The subnet is leased and active (lease has not ended).
- A coinbase cycle paid an owner cut to the subnet owner for the given
netuid
. - Current block is an exact multiple of
LeaseDividendsDistributionInterval
(check in Constants). - There is sufficient liquidity to unstake the contributors’ cut from the subnet at or above the minimum swap price.
Balances credited go to each contributor’s coldkey and the beneficiary’s coldkey. You can observe changes by querying balances over time.
Alternative path: Refund and dissolve
If the cap is not reached by end
:
- Anyone can call
crowdloan.refund(crowdloan_id)
repeatedly until all contributors (except the creator) are refunded (batched per call). - After refunds complete (only the creator’s deposit remains), the
creator
can callcrowdloan.dissolve(crowdloan_id)
to clean up and recover the deposit.
Optional: Withdraw
Before finalization:
- Any contributor can
crowdloan.withdraw(crowdloan_id)
to recover their contribution. - The creator can only withdraw amounts above the kept deposit; the deposit itself remains until refund/dissolve.
Troubleshooting
- Call fails with
InvalidCrowdloadId
- Ensure that the crowdloan ID exists.
- Call fails with
InvalidOrigin
- Ensure that the selected account that is responsible for signing the transaction.
- Call fails with
BlockDurationTooShort
- Ensure that the crowdloan
end
is set at least one week away—~50,400 blocks.
- Ensure that the crowdloan
- Call fails with
BlockDurationTooLong
- Ensure that the crowdloan
end
is set between a week to 2 months away.
- Ensure that the crowdloan
- Contribution call fails with
ContributionPeriodEnded
- Extend the
end
value on the crowdloan using thecrowdloan.updateEnd
extrinsic.
- Extend the
- Finalize fails with
CapNotRaised
- Ensure total
raised
equalscap
. Add contributions or adjustcap
viaupdate_cap
(creator‑only) beforefinalize
.
- Ensure total
- Finalize fails with
ContributionPeriodNotEnded
- Wait until the
end
block is reached.
- Wait until the
- Finalize fails with
CallUnavailable
- Ensure the nested call was supplied during
create
. The pallet stores it as a preimage; if unavailable, it errors and drops the reference.
- Ensure the nested call was supplied during
- Refund does nothing
- Refunds only after
end
and only for non‑finalized campaigns. It processes up toRefundContributorsLimit
contributors per call.
- Refunds only after