# 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/ ## Requirements - Basic understanding of how to use command line tools - Know how to encode and decode extrinsics ## Prerequisites - Install nodejs https://nodejs.org/en ## Run Chopsticks ### Single Network `npx @acala-network/chopsticks@latest --config kusama` 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) ### Multiple Networks `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 ## Access the network 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. ## Dev RPCs Chopsticks offers multiple dev RPCs to modify the devnet ### dev_newBlock This can be used to build new blocks. Docs: https://acalanetwork.github.io/chopsticks/docs/chopsticks/README.html#newblock ### dev_setHead 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 ### dev_setStorage This can be used to modify storage. Docs: https://acalanetwork.github.io/chopsticks/docs/chopsticks/README.html#setstorage ```ts 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. ## Dispatch Call 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. ```ts 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 ```ts 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. ## Propose Bounty 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 schdule propose curator after the bounty is funded. To try it out: ```ts 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 blcok 20995201, the block after the current funding period ends. The schduled 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.