-
Notifications
You must be signed in to change notification settings - Fork 84
Governance fun with Chopsticks
How to use Chopsticks to have a mainnet fork of Polkadot/Kusama locally for OpenGov experiments.
The developer docs can be found at https://acalanetwork.github.io/chopsticks/docs/
- Basic understanding of how to use command line tools
- Know how to encode and decode extrinsics
- Install nodejs: https://nodejs.org/en
- OR install bun: https://bun.sh
npx @acala-network/chopsticks@latest --config kusama
Or use bunx
instead of npx
if using bun.
List of available networks can be found here: https://github.com/AcalaNetwork/chopsticks/tree/master/configs
You may submit a PR to add new networks (or ask a dev to do it if you don't know how to submit a PR)
npx @acala-network/chopsticks@latest xcm --relaychain kusama --parachain karura
Multiple --parachain CHAIN_NAME
can be specified. Check the log output for the port of each chain
The Chopsticks RPC url will be ws://127.0.0.1:8000
or with port 800X for additional instances. You may use any tools to connect to this endpoint and start using it.
The easiest way is use pjs apps https://polkadot.js.org/apps/?rpc=ws%3A%2F%2F127.0.0.1%3A8000#/explorer
Note: if it isn't loading, make sure you have configured your browser to support insecure localhost.
You may start using pjs apps to send transactions and browse the network. By default, the network will produce new block only when received a transaction.
Chopsticks offers multiple dev RPCs to modify the devnet
This can be used to build new blocks. Docs: https://acalanetwork.github.io/chopsticks/docs/chopsticks/README.html#newblock
This can be used to revert block, in case you want to undo something, you can do await api.rpc('dev_setHead', -1)
. Docs: https://acalanetwork.github.io/chopsticks/docs/chopsticks/README.html#sethead
This can be used to modify storage. Docs: https://acalanetwork.github.io/chopsticks/docs/chopsticks/README.html#setstorage
api.rpc('dev_setStorage', {
system: { account: [[['FoQJpPyadYccjavVdTWxpxU7rUEaYhfLCPwXgkfD6Zat9QP'], { providers: 1, data: { free: '10000000000000'}}]] }
})
Run the above code in https://polkadot.js.org/apps/?rpc=ws%3A%2F%2F127.0.0.1%3A8000#/js with Kusama will set the balance of Bob
to 10 KSM.
For the networks that have sudo pallet, we can simply use dev_setStorage
to set the sudo key to Alice and use sudo to dispatch calls. However, most of the networks does not have sudo pallet, including Kusama and Polkadot. In this case, we can use scheduler pallet to dispatch the call.
This is an example of using root to perform force transfer. Note: it can take up to a few minutes for the block to be produced.
const number = (await api.rpc.chain.getHeader()).number.toNumber()
await api.rpc('dev_setStorage', {
scheduler: {
agenda: [
[
[number + 1], [
{
call: {
Inline: '0x040200d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d008eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48070010a5d4e8'
},
origin: {
system: 'Root'
}
}
]
]
]
}
})
await api.rpc('dev_newBlock')
Note that we can specify the dispatch origin. This means we can simulate the outcome of a call when dispatched by a particular OpenGov tracks.
For example, this simulates to spend 100 KSM from SmallTipper
track
const number = (await api.rpc.chain.getHeader()).number.toNumber()
await api.rpc('dev_setStorage', {
scheduler: {
agenda: [
[
[number + 1], [
{
call: {
Inline: '0x12030b00407a10f35a008eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48'
},
origin: {
origins: 'SmallTipper'
}
}
]
]
]
}
})
await api.rpc('dev_newBlock')
and it will fail with treasury.InsufficientPermission
error indicate SmallTipper
cannot spend 100 KSM.
A bounty have multiple phases: Proposed
, Approved
, Funded
, CuratorProposed
, Active
, PendingPayout
. (Code: https://github.com/paritytech/polkadot-sdk/blob/048a9c2744ca83ef6faeadd2cd19466295d52b9d/substrate/frame/bounties/src/lib.rs#L147-L175)
It is only possible to propose a curator after the bounty is approved and fund and treasury only fund bounty and other spending proposals every funding period (6 days in Kusama and 24 days in Polkadot). This means it is not possible to have a single referendum to approve a bounty and propose a curator. Except with Treasurer origin, which have access to scheduler.schedule
call. The original intention is to allow treasurer origin to schedule reoccurring payouts, but in this case, can also be used to schedule propose curator after the bounty is funded.
To try it out:
const number = (await api.rpc.chain.getHeader()).number.toNumber()
await api.rpc('dev_setStorage', {
scheduler: {
agenda: [
[
[number + 1], [
{
call: {
Inline: '0x1802082301641d00815c400100fe230264008eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48070010a5d4e8'
},
origin: {
origins: 'Treasurer'
}
}
]
]
]
}
})
await api.rpc('dev_newBlock')
This will dispatch a proposal using Treasurer origin to approve bounty 25, and schedule propose curator at block 20995201, the block after the current funding period ends.
The scheduled call can be found at https://polkadot.js.org/apps/?rpc=ws%3A%2F%2F127.0.0.1%3A8000#/scheduler
Then we can do a time warp with await api.rpc('dev_newBlock', { count: 2, unsafeBlockHeight: 20995200 })
to directly build block 20995200 and 20995201. Note this is unsafe as it skips all the block between current block and 20995200. But we shall see the bounty status for bounty 25 is CuratorProposed
now.