## Overview This document is just a dump of some different usecases for Chopsticks as and when I came across them. My aim was to fill the gap which existed when I first started using Chopsticks where there were no actual demonstrations of how it can be used, or at least none that I could find. I have used it for a lot of other stuff than what is listed here, but this demonstrates enough of the high level workflows such that you can tweak them slightly to cover a lot of normal usecases. This is all just manual poking with PolkadotJS + chopsticks, but you can automate things that will run repeatedly and talk to it/submit transactions/check state any way you want, as it's just a local RPC. This means you can have a nice chopsticks + PAPI setup to automate stuff, or even just using PAPI instead of clicking around in PJS Apps. CI or automated testing suites are probably better suited for the Polkadot Ecosystem tests repo, which Alexandre has written about [here](https://hackmd.io/_nI34GUiS82To1FKxpKHGw). ## Chopsticks setup ### First run ```shell npx @acala-network/chopsticks@latest -c polkadot ``` And that's it - polkadot is forked locally using the config pulled from the acala chopsticks repo. Get it at [port 8000](https://polkadot.js.org/apps/?rpc=ws%3A%2F%2F127.0.0.1%3A8000#/explorer) Nice... but what now? ### Running an extrinsic Everything works as normal from signed and unsigned origins. An alice account is injected in with 10m tokens so you can use that, or use your own account if you have enough tokens for whatever you're simulating. If the default configs don't start you off with the network you need, you can curl them: ``` curl -OL https://raw.githubusercontent.com/AcalaNetwork/chopsticks/master/configs/polkadot.yml ``` or whatever other config. you can then edit it locally and fork the network with those options with the following: ```shell npx @acala-network/chopsticks@latest -c ./polkadot.yml ``` You can fiddle the storage when you start the chain by editing the yaml file to add an `import-storage` section. Check out the [Polkadot Collectives config](https://github.com/AcalaNetwork/chopsticks/blob/master/configs/polkadot-collectives.yml) for an example of this. You can clear entries in storage using `$removePrefix` and add ones using the pallet and key. Anything that needs to be called from a privileged origin needs some of the Chopsticks-specific functions that you can call from within the apps Javascript tab. There's a handy pattern that you can use to inject a call from any origin into the scheduler for the next block, then execute that block for the extrinsics to be executed. This only works on chains which include the scheduler pallet (Just relay and collectives of the system chains, but soon to also include asset hub) but you can execute anything else via an XCM injected into the scheduler state on the relay to the chain in question. ## Relay runtime upgrade with authorize_upgrade flow Get the hash of your code and generate the call hash. We'll use Kusama [1.2.1](https://github.com/polkadot-fellows/runtimes/releases/tag/v1.2.1) for this. The hash is `0xc17b4fe5bf581aac865de728590080b9efe9d9fdce957dfadafb78f288c312bc`, therefore we form the following call: [0x0009c17b4fe5bf581aac865de728590080b9efe9d9fdce957dfadafb78f288c312bc](https://polkadot.js.org/apps/?rpc=ws%3A%2F%2F127.0.0.1%3A8000#/extrinsics/decode/0x0009c17b4fe5bf581aac865de728590080b9efe9d9fdce957dfadafb78f288c312bc) If we submit this we get rejected with a BadOrigin as we're not root. There's no sudo on Kusama, so we need another way to submit this. ### The scheduler Go to the [Javascript](https://polkadot.js.org/apps/?rpc=ws%3A%2F%2F127.0.0.1%3A8000#/js) tab and clear the code in there and add: ```javascript // Grab the block number of the current head // api is already imported, no need to add anything but the following. const number = (await api.rpc.chain.getHeader()).number.toNumber() // use chopsticks dev_setStorage to inject the call into the scheduler state for the next block. The value of `Inline` is just the call data. Crucially with this call we can set the origin to `Root` await api.rpc('dev_setStorage', { scheduler: { agenda: [ [ [number + 1], [ { call: { Inline: '0x0009c17b4fe5bf581aac865de728590080b9efe9d9fdce957dfadafb78f288c312bc' }, origin: { system: 'Root' } } ] ] ] } }) // Make a block to include the extrinsic await api.rpc('dev_newBlock', { count: 1 }) ``` When you run this the window on the right won't update, but we can see the new block in the terminal ```log ❯ npx @acala-network/chopsticks@latest -c ./kusama.yml [10:18:09.642] INFO: Kusama RPC listening on port 8000 app: "chopsticks" [10:39:09.849] INFO (block-builder): Kusama building #22,991,035 app: "chopsticks" number: 22991035 extrinsics: [] umpCount: 0 [10:39:16.705] INFO (block-builder): Kusama new head #22,991,035 app: "chopsticks" number: 22991035 hash: "0x923db5a5023c00de9e49a93b09e956a45343011b396ae453b432d496c7fa403a" extrinsics: [] pendingExtrinsics: [] ump: {} ``` Note that no extrinsics are registered, as we've used a sneaky backdoor to give it a `Root` origin. If we check the block in the block explorer we can see that the following event was emitted: ![Screenshot 2024-05-02 at 12.15.27](https://hackmd.io/_uploads/r1GkLe-GC.png) Great, and that is the same hash that we noted above. We can also check this in the `system.authorizedUpgrade` chain state: ![system.authorizedUpgrade](https://hackmd.io/_uploads/SyoyWyZM0.png) Now we should be able to upload [the code](https://github.com/polkadot-fellows/runtimes/releases/download/v1.2.1/kusama_runtime-v1002001.compact.compressed.wasm) from an unsigned origin. Let's just do that right from the extrinsics tab with a call to the System pallet's `apply_authorized_upgrade`. ![Screenshot 2024-05-02 at 10.49.45](https://hackmd.io/_uploads/Syv6bJWGR.png) Hit "Submit Unsigned" then "Submit (no signature)" in the popup. Now in the chopsticks terminal we see a new block has just been created with an extrinsic included: ```log [10:50:49.514] INFO (block-builder): Kusama building #22,991,039 app: "chopsticks" number: 22991039 extrinsics: [ "0x623f670004000b463f670052bc537646…744a91e3048176230137c5282a7f8e01" ] umpCount: 0 [10:50:51.520] INFO (block-builder): Kusama new head #22,991,039 app: "chopsticks" number: 22991039 hash: "0x83743b9eb0f8274f00a6f161a98718ae76d64b6c02c7ef3236209f8143612a25" extrinsics: [ "0x623f670004000b463f670052bc537646…744a91e3048176230137c5282a7f8e01" ] pendingExtrinsics: [] ump: {} ``` Now since we're already monitoring `system.authorizedUpgrade` in the chain state we see that it has changed to `<none>`, so it looks like our upgrade was successful. Let's check in the block: ![Screenshot 2024-05-02 at 11.02.56](https://hackmd.io/_uploads/rJy1Sk-zC.png) Yes! But it doesn't appear in the `system.lastRuntimeUpgrade`, which is still at `1002000`. However, this is only set by the `on_runtime_upgrade` hook after the migrations have run. So far we have updated code, but we are still running with the old runtime. Advancing one block past the upgrade, the new code will execute, migrations will run and it will be updated and correctly show `1002001`: ```javascript await api.rpc('dev_newBlock', { count: 1 }) ``` This doesn't include the XCMs that we expect to be sent to the People chain with this upgrade, even in the DMP pallet chain state. The 1004 downward message queue does not exist yet as the People Chain registration has not happened. Let's start again and go right from the referendum to register and onboard people chain so we get the downward message queue and can see the XCMs being queued. ## Flow from registering people chain The referendum for registering People Chain has been submitted and its preimage is in state. We can use the same trick to inject calls into the scheduler state and force the referendum to enact. TODO ## Chopsticks in XCM mode Let's start again with Chopsticks in XCM mode to ensure the XCMs are actually propagated to the people chain and that the Transacts execute properly. > This will only work when the People Chain has an RPC we can fork from Starting Chopsticks in XCM mode allows us to track XCMs and check their execution on the target chain. We just need to create a config file for the people chain, as one doesn't exist yet in the Acala Chopsticks repo. Use the following: ```yaml endpoint: - wss://kusama-people-rpc.polkadot.io mock-signature-host: true block: ${env.KUSAMA_PEOPLE_BLOCK_NUMBER} db: ./db.sqlite import-storage: System: Account: - - - 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY - providers: 1 data: free: 1000000000000000 ``` The endpoint, block and db are the most important points here. Adding tokens to Alice in storage is irrelevant for this test, but might be useful for further tests. Fork these two chains by using the following command: ```shell npx @acala-network/chopsticks@latest xcm -r kusama.yml -p ./kusama-people.yml ``` This gives us [Kusama Relay]() and Kusama People Chain ### 1.2.4 release testing Here the preimages are in storage and referenda are already live, we want to set up the entire core network (relay + system paras) in XCM mode and check that the execution of the calls is successful and that the code upgrades to the new version. We don't need to inject anything additional in storage, so let's just use the base configs for: - Polkadot Relay - Asset Hub - Bridge Hub - Collectives ```log ❯ npx @acala-network/chopsticks@latest xcm -r polkadot -p polkadot-asset-hub -p polkadot-bridge-hub -p polkadot-collectives [12:32:36.080] INFO: Loading config file https://raw.githubusercontent.com/AcalaNetwork/chopsticks/master/configs/polkadot-asset-hub.yml app: "chopsticks" [12:32:37.570] INFO: Polkadot Asset Hub RPC listening on port 8000 app: "chopsticks" [12:32:37.570] INFO: Loading config file https://raw.githubusercontent.com/AcalaNetwork/chopsticks/master/configs/polkadot-bridge-hub.yml app: "chopsticks" [12:32:38.698] INFO: Polkadot BridgeHub RPC listening on port 8001 app: "chopsticks" [12:32:38.698] INFO: Loading config file https://raw.githubusercontent.com/AcalaNetwork/chopsticks/master/configs/polkadot-collectives.yml app: "chopsticks" [12:32:39.677] INFO: Collectives RPC listening on port 8002 app: "chopsticks" [12:32:40.067] INFO (xcm): Connected parachains [1000,1001,1002] app: "chopsticks" [12:32:40.067] INFO: Loading config file https://raw.githubusercontent.com/AcalaNetwork/chopsticks/master/configs/polkadot.yml app: "chopsticks" [12:32:41.782] INFO: Polkadot RPC listening on port 8003 app: "chopsticks" [12:32:42.015] INFO (xcm): Connected relaychain 'Polkadot' with parachain 'Polkadot Asset Hub' app: "chopsticks" [12:32:42.217] INFO (xcm): Connected relaychain 'Polkadot' with parachain 'Polkadot BridgeHub' app: "chopsticks" [12:32:42.422] INFO (xcm): Connected relaychain 'Polkadot' with parachain 'Collectives' app: "chopsticks" ``` Looks good, I've independently regenerated the submissions and preimages using `opengov-cli build-upgrade`: ```log ❯ opengov-cli build-upgrade --relay-version v1.2.4 --network polkadot ... ❯ opengov-cli submit-referendum --at 21169000 \ --proposal "./upgrade-polkadot-1.2.4/polkadot-1.2.4.call" \ --network "polkadot" --track whitelistedcaller ... Batch to submit on Polkadot Relay Chain: https://polkadot.js.org/apps/?rpc=wss%3A%2F%2Fpolkadot-rpc.dwellir.com#/extrinsics/decode/0x1a04080a00490317031a0410630004000100a10f04082f0000060202a4fa3301108c01029161b599ff28393665dc275a06222f7c485494604bf5335bb9cac0830d68edcc01630004000100a50f04082f0000060242d8173901108c010226cb564aac635f951717d8b96ddc4b3fa9a00784e72848e144aa825a7e45a5f801630004000100a90f04082f00000602028bfb3701108c0102cb51f86c6a8779a468f09990cc2a0d61a5ddf868535625eac0c4931c63267944010009ac8f4dda4d4eab0e5d776e3ae717904696e5e57de89e5e5bf5efc7347f1e88a41500160d02ea83ee65f02ff6400f159c7db601d18875f32dd03f6cede8dd747a958d8cd0fbd20000000068034301 Batch to submit on Polkadot Collectives Chain: https://polkadot.js.org/apps/?rpc=wss%3A%2F%2Fpolkadot-collectives-rpc.polkadot.io#/extrinsics/decode/0x2804082b00d41f0004010004082f0000060302fab374216f881700f31effce49fe706736518f25b7e0f76cad0646c63c6d40c2d4512452bf8ed4b43d003e0202f7b8093d5fde61d517dd126af255beb02318f0ed4fba71c895b0fbb8b1706d5a35000000010a000000 ``` We can get the preimage hashes and call data from the two batches returned by opengov-cli. Clickable: [1](https://polkadot.js.org/apps/?rpc=ws%3A%2F%2F127.0.0.1%3A8002#/extrinsics/decode/0x2804082b00d41f0004010004082f0000060302fab374216f881700f31effce49fe706736518f25b7e0f76cad0646c63c6d40c2d4512452bf8ed4b43d003e0202f7b8093d5fde61d517dd126af255beb02318f0ed4fba71c895b0fbb8b1706d5a35000000010a000000), [2](https://polkadot.js.org/apps/?rpc=ws%3A%2F%2F127.0.0.1%3A8003#/extrinsics/decode/0x1a04080a00490317031a0410630004000100a10f04082f0000060202a4fa3301108c01029161b599ff28393665dc275a06222f7c485494604bf5335bb9cac0830d68edcc01630004000100a50f04082f0000060242d8173901108c010226cb564aac635f951717d8b96ddc4b3fa9a00784e72848e144aa825a7e45a5f801630004000100a90f04082f00000602028bfb3701108c0102cb51f86c6a8779a468f09990cc2a0d61a5ddf868535625eac0c4931c63267944010009ac8f4dda4d4eab0e5d776e3ae717904696e5e57de89e5e5bf5efc7347f1e88a41500160d02ea83ee65f02ff6400f159c7db601d18875f32dd03f6cede8dd747a958d8cd0fbd20000000068034301). Go to the forked collectives chain and run the following JS: ```javascript // Grab the block number of the current head // api is already imported, no need to add anything but the following. const number = (await api.rpc.chain.getHeader()).number.toNumber() // use chopsticks dev_setStorage to inject the call into the scheduler state for the next block. The value of `Inline` is just the call data. Crucially with this call we can set the origin to `Root` await api.rpc('dev_setStorage', { scheduler: { agenda: [ [ [number + 1], [ { call: { Lookup: { hash: '0xf7b8093d5fde61d517dd126af255beb02318f0ed4fba71c895b0fbb8b1706d5a', len: 53 } }, origin: { FellowshipOrigins: 'Fellows' } } ] ] ] } }) // Make a block to include the extrinsic await api.rpc('dev_newBlock', { count: 1 }) ``` We get a UMP sent to the relay from collectives. Go to the relay window where we can see that the message is processed successfully. We also see that an event has been logged which whitelists the call. ![Screenshot 2024-05-24 at 18.42.37](https://hackmd.io/_uploads/r1rjWU0QR.png) Let's action the referendum now using the Lookup to get the preimage referenced in the ```javascript // Grab the block number of the current head // api is already imported, no need to add anything but the following. const number = (await api.rpc.chain.getHeader()).number.toNumber() // use chopsticks dev_setStorage to inject the call into the scheduler state for the next block. The value of `Inline` is just the call data. Crucially with this call we can set the origin to `Root` await api.rpc('dev_setStorage', { scheduler: { agenda: [ [ [number + 1], [ { call: { Lookup: { hash: '0xea83ee65f02ff6400f159c7db601d18875f32dd03f6cede8dd747a958d8cd0fb', len: 210 } }, origin: { Origins: 'WhitelistedCaller' } } ] ] ] } }) // Make a block to include the extrinsic await api.rpc('dev_newBlock', { count: 1 }) ``` Looks like it was successful, blocks have been produced and the XCMs have been sent to all three parachains. ![Screenshot 2024-05-24 at 18.45.26](https://hackmd.io/_uploads/ByOBMUC7A.png) Checking system.authorizedUpgrade in the state we should find the correct hashes (matching `opengov-cli`): ``` Polkadot Relay Chain Runtime Hash: 0xac8f4dda4d4eab0e5d776e3ae717904696e5e57de89e5e5bf5efc7347f1e88a4 ✅ Polkadot Asset Hub Runtime Hash: 0x9161b599ff28393665dc275a06222f7c485494604bf5335bb9cac0830d68edcc ✅ Polkadot Collectives Runtime Hash: 0x26cb564aac635f951717d8b96ddc4b3fa9a00784e72848e144aa825a7e45a5f8 ✅ Polkadot Bridge Hub Runtime Hash: 0xcb51f86c6a8779a468f09990cc2a0d61a5ddf868535625eac0c4931c63267944 ✅ ``` They all match. Let's try to upgrade one by one by just submitting unsigned. This should work in any order All extrinsics were successful, and the relay has upgraded to 1002004 when we check the core.version() runtime call. Let's check the other chains: AH, BH and Collectives all have the event that the validation function is stored, and are 20.53%, 22.18% and 21.86% of the block weight respectively. Now let's churn some blocks! We need to pass an hour until the system chains are upgraded. Send an empty XCM to all system paras so they keep making blocks and don't timeout call hash: `0x1a000c630004000100a10f0400630001000100a50f0400630004000100a90f0400` ```javascript await api.rpc('dev_newBlock', { count: 600 }) ``` ## Kusama `npx @acala-network/chopsticks@latest xcm -r kusama -p kusama-asset-hub -p kus ama-bridge-hub -p kusama-coretime -p ./kusama-people.yml -p encointer-kusama` ## Change core count to 100 on Kusama Our referendum will send an XCM to the coretime chain with a transact containing [`request_core_count` with 100 cores](https://polkadot.js.org/apps/?rpc=wss%3A%2F%2Fkusama-coretime-rpc.polkadot.io#/extrinsics/decode/0x32126400). [This](https://polkadot.js.org/apps/?rpc=wss%3A%2F%2Fkusama-rpc.dwellir.com#/extrinsics/decode/0x630004000100b50f04082f00000602028c8f84f1551032126400) is the call for the referendum (including ~1.5x safety factor), which has the hash: `0x630004000100b50f04082f00000602028c8f84f1551032126400`. We can fire up chopsticks and use the scheduler to execute this. ```shell npx @acala-network/chopsticks@latest xcm -r kusama.yml -p ./kusama-people.yml ``` Open the [Coretime Chain](https://polkadot.js.org/apps/?rpc=ws%3A%2F%2F127.0.0.1%3A8000) to watch the events. Go to the [Javascript](https://polkadot.js.org/apps/?rpc=ws%3A%2F%2F127.0.0.1%3A8001#/js) tab on the relay chain, clear the code and add: ```javascript // Grab the block number of the current head // api is already imported, no need to add anything but the following. const number = (await api.rpc.chain.getHeader()).number.toNumber() // use chopsticks dev_setStorage to inject the call into the scheduler state for the next block. The value of `Inline` is just the call data. Crucially with this call we can set the origin to `Root` await api.rpc('dev_setStorage', { scheduler: { agenda: [ [ [number + 1], [ { call: { Inline: '0x630004000100b50f04082f00000602028c8f84f1551032126400' }, origin: { system: 'Root' } } ] ] ] } }) // Make a block to include the extrinsic await api.rpc('dev_newBlock', { count: 1 }) ``` Now we can see from the events that the message was sent on the relay, received and executed on the coretime chain and we have a `broker.CoreCountRequested` event with 100 cores. Back to the relay chain, we can check the chain state `configuration.pending_config` and scroll down to find `coretimeCores: 100`. Sweet! now let's make some blocks on the relay to get through the two session boundaries and make sure there are no problems with the coretime chain when we apply the config. This number depends on when you forked the chain, but you can watch the chain state `session.index` and wait until it is equal to the one that the `pendingConfig` containing the 100 cores is at. Go to the [Javascript](https://polkadot.js.org/apps/?rpc=ws%3A%2F%2F127.0.0.1%3A8001#/js) tab on the relay chain, clear the code and add: ``` await api.rpc('dev_newBlock', { count: 1200 }) ``` My laptop makes around 2 blocks per second, so this won't take long. We're looking for the new session, then we can double check that the configuration has changes the `coretimeCores: 100`, then go to the coretime chain and we see that a message has been processed, and we get a `broker.CoreCountChanged` event with a value of 100. Also checking the coretime chain state `broker.status` should show `coreCount: 100`. If you want you can keep spinning out cores on the coretime chain to check that the cores are offered for sale at the next sale boundary, then buy them all and assign them, but I'll not go that far. ## Upgrade to 1.2.6 Happened at [this block](https://polkadot.subscan.io/block/21558000), I'd like to check the head state generated by the script with chopsticks before making the referendum. Check out Nacho's branch with the identity script, pull the most recent changes and yarn install. ```shell git checkout nacho/people-chain-spec-with-migation && git pull cd cumulus/scripts/migrate_storage_to_genesis/ bun install ``` Run the script ```shell bun run migrate -c ws://127.0.0.1:8000 -f ../../../../people-release/people-polkadot.json -p Identity -m ./identityMigration.js ``` Once that has completed, we should end up with a chainspec with which we can create another instance of chopsticks for the people chain genesis. Save this config as `polkadot-people.yml`: ```yaml mock-signature-host: true block: ${env.KUSAMA_PEOPLE_BLOCK_NUMBER} db: ./db.sqlite genesis: ./people-polkadot.json wasm-override: ./people-polkadot_runtime-v1002006.compact.compressed.wasm import-storage: System: Account: - - - 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY - providers: 1 data: free: 1000000000000000 ``` and run ```shell npx @acala-network/chopsticks@latest -c ./polkadot-people.yml -p 8001 ``` This can then be used along with the following comparison script: ```python """ SPDX-License-Identifier: GPL-3.0-only Compare that the Polkadot identity pallet's storage `IdentityOf` is the same as on the Polkadot People chain. """ import json from substrateinterface import SubstrateInterface chain = SubstrateInterface( url="wss://polkadot-rpc.dwellir.com" ) print(f"Connected to {chain.name}: {chain.chain} v{chain.version}") kusama_identity = {} query = chain.query_map('Identity', 'IdentityOf') for acc, i in query: if i.value[0]['info']['additional']: for additional in i.value[0]['info']['additional']: if additional[0]['Raw'].lower() == 'github' or additional[0]['Raw'].lower() == 'discord': print(acc.value) print(i.value[0]['info']['additional']) del i.value[0]['deposit'] # should be zero (different) del i.value[0]['info']['additional'] # not in new type i.value[0]['info']['matrix'] = i.value[0]['info'].pop('riot') # renamed to matrix kusama_identity[acc.value] = i.value kusama_subs = {} query = chain.query_map('Identity', 'SubsOf') for acc, i in query: zero_deposit_value = (0, i.value[1]) kusama_subs[acc.value] = zero_deposit_value kusama_super = {} super_query = chain.query_map('Identity', 'SuperOf') for acc, i in super_query: kusama_super[acc.value] = i.value # People chain = SubstrateInterface( url="ws://127.0.0.1:8000" ) print(f"Connected to {chain.name}: {chain.chain} v{chain.version}") people_identity = {} query = chain.query_map('Identity', 'IdentityOf') for acc, i in query: assert(i.value[0]['deposit'] == 0) # should be zero del i.value[0]['deposit'] del i.value[0]['info']['github'] # not in old del i.value[0]['info']['discord'] # not in old people_identity[acc.value] = i.value people_subs = {} query = chain.query_map('Identity', 'SubsOf') for acc, i in query: people_subs[acc.value] = i.value people_super = {} super_query = chain.query_map('Identity', 'SuperOf') for acc, i in super_query: people_super[acc.value] = i.value # Sort and compare kusama_super = dict(sorted(kusama_super.items())) people_super = dict(sorted(people_super.items())) with open("kusama_super.json", "w") as f: json.dump(kusama_super, f) with open("people_super.json", "w") as f: json.dump(people_super, f) print("Wrote Kusama and People SuperOf to files.") if kusama_super == people_super: print('✅ SuperOf') else: print('❌ SuperOf') kusama_subs = dict(sorted(kusama_subs.items())) people_subs = dict(sorted(people_subs.items())) with open("kusama_subs.json", "w") as f: json.dump(kusama_subs, f) with open("people_subs.json", "w") as f: json.dump(people_subs, f) print("Wrote Kusama and People SubsOf to files.") if kusama_super == people_super: print('✅ SubsOf') else: print('❌ SubsOf') kusama_identity = dict(sorted(kusama_identity.items())) people = dict(sorted(people_identity.items())) with open("kusama_identity.json", "w") as f: json.dump(kusama_identity, f) with open("people_identity.json", "w") as f: json.dump(people_identity, f) print("Wrote Kusama and People IdentityOf to files.") if kusama_identity == people_identity: print('✅ IdentityOf') else: print('❌ IdentityOf') ``` ## Upgrade to 1.2.7 ``` 0x1a040800095300f1f0bddedee260228911bf8b3eaadbe16ffa520d4c6c7593b086fe5486ae0104c0a800000000630004000100b10f04082f0000060202286bee01408800091e50ce48d0527e436ce7a12499ad2ab314a3b2c6b863a1a1859256bd3155f2cc ``` should authorize an upgrade of the relay chain directly then schedule an authorized upgrade of the people chain for 3 days later ``` npx @acala-network/chopsticks -c polkadot -p 8000 ``` Go to the [Javascript](https://polkadot.js.org/apps/?rpc=ws%3A%2F%2F127.0.0.1%3A8000#/js) tab on the relay chain, clear the code and add: ```javascript // Grab the block number of the current head // api is already imported, no need to add anything but the following. const number = (await api.rpc.chain.getHeader()).number.toNumber() // use chopsticks dev_setStorage to inject the call into the scheduler state for the next block. The value of `Inline` is just the call data. Crucially with this call we can set the origin to `Root` await api.rpc('dev_setStorage', { scheduler: { agenda: [ [ [number + 1], [ { call: { Inline: '0x1a040800095300f1f0bddedee260228911bf8b3eaadbe16ffa520d4c6c7593b086fe5486ae0104c0a800000000630004000100b10f04082f0000060202286bee01408800091e50ce48d0527e436ce7a12499ad2ab314a3b2c6b863a1a1859256bd3155f2cc' }, origin: { system: 'Root' } } ] ] ] } }) // Make a block to include the extrinsic await api.rpc('dev_newBlock', { count: 1 }) ``` events are correct: ![Screenshot 2024-07-11 at 07.49.35](https://hackmd.io/_uploads/S1puMgTPR.png) And now we can check the upgrade hash in storage at System->authorizedUpgrade ``` { codeHash: 0x5300f1f0bddedee260228911bf8b3eaadbe16ffa520d4c6c7593b086fe5486ae checkVersion: true } ``` which is the same as the hash of the 1.2.7 upgrade ![Screenshot 2024-07-11 at 07.52.55](https://hackmd.io/_uploads/H1BH7gpD0.png) Also check the scheduled call in storage at scheduler->agenda with the number that was in the event (for me 21639031) ``` [ { maybeId: null priority: 0 call: { Inline: 0x630004000100b10f04082f0000060202286bee01408800091e50ce48d0527e436ce7a12499ad2ab314a3b2c6b863a1a1859256bd3155f2cc } maybePeriodic: null origin: { system: Root } } ] ``` Root origin, containing a v4 XCM to para id 1004. Unlimited weight and a transact containing the following call: `0x00091e50ce48d0527e436ce7a12499ad2ab314a3b2c6b863a1a1859256bd3155f2cc` with weight 1bil/4096 This call is an authorize upgrade with the code hash `0x1e50ce48d0527e436ce7a12499ad2ab314a3b2c6b863a1a1859256bd3155f2cc` which is the same as the hash of the people 1.2.7 release: ![Screenshot 2024-07-11 at 07.57.12](https://hackmd.io/_uploads/ryIHVgpP0.png) ## Actually sending the message We need to get the people chain to register and onboard so that the DMP channel is opened, since at this stage the chain is not actually running. this will require churning some blocks. The preimage is at 0x973ad11408c2844944382b41c1ac5ec5c03ad77fe9baac786bc1ae4c3572ef4f with length 853,640 So if we fire up a fresh chopsticks in xcm mode with people at genesis: ``` npx @acala-network/chopsticks@latest xcm -r polkadot.yml -p ./polkadot-people.yml -p polkadot-collectives.yml ``` Go to the forked collectives chain and run the following JS to whitelist the call: ```javascript // Grab the block number of the current head // api is already imported, no need to add anything but the following. const number = (await api.rpc.chain.getHeader()).number.toNumber() // use chopsticks dev_setStorage to inject the call into the scheduler state for the next block. The value of `Inline` is just the call data. Crucially with this call we can set the origin to `Root` await api.rpc('dev_setStorage', { scheduler: { agenda: [ [ [number + 1], [ { call: { Lookup: { hash: '0xf1b9cf6b791e5af5154fde8d7b193e6000ebdaef067ac3c5ae30cae7afa3711a', len: 53 } }, origin: { FellowshipOrigins: 'Fellows' } } ] ] ] } }) // Make a block to include the extrinsic await api.rpc('dev_newBlock', { count: 1 }) ``` Go to the [Javascript](https://polkadot.js.org/apps/?rpc=ws%3A%2F%2F127.0.0.1%3A8001#/js) tab on the relay chain, clear the code and add: ```javascript // Grab the block number of the current head // api is already imported, no need to add anything but the following. const number = (await api.rpc.chain.getHeader()).number.toNumber() // use chopsticks dev_setStorage to inject the call into the scheduler state for the next block. The value of `Inline` is just the call data. Crucially with this call we can set the origin to `Root` await api.rpc('dev_setStorage', { scheduler: { agenda: [ [ [number + 1], [ { call: { Lookup: { hash: '0x973ad11408c2844944382b41c1ac5ec5c03ad77fe9baac786bc1ae4c3572ef4f', len: 853640 } }, origin: { Origins: 'WhitelistedCaller' } } ] ] ] } }) // Make a block to include the extrinsic await api.rpc('dev_newBlock', { count: 1 }) ``` The people chain is making blocks! OK now the DMP channel is open so we can try the new ref. Go to the [Javascript](https://polkadot.js.org/apps/?rpc=ws%3A%2F%2F127.0.0.1%3A8002#/js) tab on the relay chain, clear the code and add: ```javascript // Grab the block number of the current head // api is already imported, no need to add anything but the following. const number = (await api.rpc.chain.getHeader()).number.toNumber() // use chopsticks dev_setStorage to inject the call into the scheduler state for the next block. The value of `Inline` is just the call data. Crucially with this call we can set the origin to `Root` await api.rpc('dev_setStorage', { scheduler: { agenda: [ [ [number + 1], [ { call: { Inline: '0x1a040800095300f1f0bddedee260228911bf8b3eaadbe16ffa520d4c6c7593b086fe5486ae01040a0000000000630004000100b10f04082f0000060202286bee01408800091e50ce48d0527e436ce7a12499ad2ab314a3b2c6b863a1a1859256bd3155f2cc' }, origin: { system: 'Root' } } ] ] ] } }) // Make a block to include the extrinsic await api.rpc('dev_newBlock', { count: 1 }) ``` now make 11 blocks ``` await api.rpc('dev_newBlock', { count: 1 }) ``` check people chain, it has processed an XCM and checking the state we see that it has an authorized upgrade whose hash matches the release: ``` { codeHash: 0x1e50ce48d0527e436ce7a12499ad2ab314a3b2c6b863a1a1859256bd3155f2cc checkVersion: true } ``` ## Via whitelisted caller That was from root though, really it will be a whitelistedcaller, so we can whitelist it and then run it. rerun the above but run the following two steps instead of the previous last step. Go to the [Javascript](https://polkadot.js.org/apps/?rpc=ws%3A%2F%2F127.0.0.1%3A8000#/js) tab on the collectives chain, clear the code and add: ```javascript // Grab the block number of the current head // api is already imported, no need to add anything but the following. const number = (await api.rpc.chain.getHeader()).number.toNumber() // use chopsticks dev_setStorage to inject the call into the scheduler state for the next block. The value of `Inline` is just the call data. Crucially with this call we can set the origin to `Root` await api.rpc('dev_setStorage', { scheduler: { agenda: [ [ [number + 1], [ { call: { Inline: '0x17001eb42fa45df3e498440559cf1419abf1369785bb181d04ab7d1b70b0d3c5fe3b' }, origin: { FellowshipOrigins: 'Fellows' } } ] ] ] } }) // Make a block to include the extrinsic await api.rpc('dev_newBlock', { count: 1 }) ``` Go to the [Javascript](https://polkadot.js.org/apps/?rpc=ws%3A%2F%2F127.0.0.1%3A8002#/js) tab on the relay chain, clear the code and add: ```javascript // Grab the block number of the current head // api is already imported, no need to add anything but the following. const number = (await api.rpc.chain.getHeader()).number.toNumber() // use chopsticks dev_setStorage to inject the call into the scheduler state for the next block. The value of `Inline` is just the call data. Crucially with this call we can set the origin to `Root` await api.rpc('dev_setStorage', { scheduler: { agenda: [ [ [number + 1], [ { call: { Inline: '0x1a040800095300f1f0bddedee260228911bf8b3eaadbe16ffa520d4c6c7593b086fe5486ae01040a0000000000630004000100b10f04082f0000060202286bee01408800091e50ce48d0527e436ce7a12499ad2ab314a3b2c6b863a1a1859256bd3155f2cc' }, origin: { system: 'WhitelistedCaller' } } ] ] ] } }) // Make a block to include the extrinsic await api.rpc('dev_newBlock', { count: 1 }) ``` ## Testing invulnerable referenda July 2024 Referenda were posted on Kusama and Polkadot to correct some community invulnerables and change the number of permissionless slots. [Polkadot referendum](https://polkadot.subsquare.io/referenda/1008) [Kusama referendum](https://kusama.subsquare.io/referenda/421) Let's test them using Chopsticks. Taking Polkadot first we see it's on the staking admin track and can inspect the call to see what it involves: ![Screenshot 2024-07-19 at 16.38.31](https://hackmd.io/_uploads/rJ4j_-_OC.png) So it's an XCM to the people chain (1004). We can fire up chopsticks in a terminal in XCM mode with polkadot relay and polkadot people chain: Currently chopsticks has no config for polkadot-people, but I've made a PR to add it. Soon you'll be able to run the bottom without creating your own file. Create the following as ./polkadot-people.yml: ``` endpoint: wss://polkadot-people-rpc.polkadot.io mock-signature-host: true block: ${env.POLKADOT_PEOPLE_BLOCK_NUMBER} db: ./db.sqlite import-storage: System: Account: - - - 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY - providers: 1 data: free: 1000000000000000 ``` Now we can fork the network in xcm mode - this gives us both the polkadot relay and polkadot people chain to play around with: ``` npx @acala-network/chopsticks@latest xcm -r polkadot -p ./polkadot-people.yml ``` We can use PJS apps with these two chains at ports 8000 and 8001 (relay is always last unless you manually specify port number) [Polkadot Relay](https://polkadot.js.org/apps/?rpc=ws%3A%2F%2F127.0.0.1%3A8001%2F#/explorer) [Polkadot People Chain](https://polkadot.js.org/apps/?rpc=ws%3A%2F%2F127.0.0.1%3A8000%2F#/explorer) We get the preimage hash and length from subscan (linked on subsquare/polkassembly referendum page): https://polkadot.subscan.io/block/21714510?tab=event&event=21714510-51 From here we see: ![Screenshot 2024-07-19 at 16.37.27](https://hackmd.io/_uploads/HkVSdWOdC.png) Now we can go to the [JS tab](https://polkadot.js.org/apps/?rpc=ws%3A%2F%2F127.0.0.1%3A8001%2F#/js) of the relay and enact the preimage with the staking admin origin. This runs the referendum's exact preimage in our forked network: ```javascript // Grab the block number of the current head // api is already imported, no need to add anything but the following. const number = (await api.rpc.chain.getHeader()).number.toNumber() // use chopsticks dev_setStorage to inject the call into the scheduler state for the next block. The value of `Lookup` is just the preimage hash and its length. Crucially with this call we can set the origin to `StakingAdmin` await api.rpc('dev_setStorage', { scheduler: { agenda: [ [ [number + 1], [ { call: { Lookup: { hash: '0x7b69697edc70cac0681a4210c6176a63c7b0ca20b88211fa62b6421bdecd88c3', len: 53 } }, origin: { Origins: 'StakingAdmin' } } ] ] ] } }) // Make a block to include the extrinsic await api.rpc('dev_newBlock', { count: 1 }) ``` In the terminal you'll see a new relay block being made, then the people chain making a block when it spots the XCM waiting for it. We can check the events and chain state to make sure it has done what we want: ![Screenshot 2024-07-19 at 17.10.00](https://hackmd.io/_uploads/SJD1gfOOA.png) So we see that an event has been emitted to show the new values of candidacy bond (7) and desired candidates (1000 EXP10) and since dot has 10 decimals, this looks correct. The test for the kusama call is exactly analagous and is left the the reader 🙂 ## Checking call for Hydra [This call](https://polkadot.js.org/apps/?rpc=wss%3A%2F%2Fpolkadot-rpc.dwellir.com#/extrinsics/decode/0x1a0208130503000100a10f000002043205011f0b0038da5d250103010200a50f044a00130504000100a10f01000f0000e941cc6b0104010200a50f044b00) So looking at this call I'm guessing it'll be called as a referendum on one of the treasury spend tracks. Looking at the amounts maybe the medium spender (due to the second amount) Let's try this with root first and see if the call itself works, before checking out the origins. Looks like we need parachains 1000 and 1001 up as well as the relay. This is asset hub and collectives. Now we can fork the network in xcm mode - this gives us the polkadot relay and polkadot collectives and AH to play around with: ``` npx @acala-network/chopsticks@latest xcm -r polkadot -p polkadot-asset-hub -p polkadot-collectives ``` Now to submit this as if it were a referendum. Let's try just inlining it in the relay chain [JS tab](https://polkadot.js.org/apps/?rpc=ws%3A%2F%2F127.0.0.1%3A8002%2F#/js) ```javascript // Grab the block number of the current head // api is already imported, no need to add anything but the following. const number = (await api.rpc.chain.getHeader()).number.toNumber() // use chopsticks dev_setStorage to inject the call into the scheduler state for the next block. The value of `Inline` is just the call data. We set the origin to `Root` await api.rpc('dev_setStorage', { scheduler: { agenda: [ [ [number + 1], [ { call: { Inline: '0x1a0208130503000100a10f000002043205011f0b0038da5d250103010200a50f044a00130504000100a10f01000f0000e941cc6b0104010200a50f044b00' }, origin: { system: 'Root' } } ] ] ] } }) // Make a block to include the extrinsic await api.rpc('dev_newBlock', { count: 1 }) ``` Then go to extrinsics on the relay chain `treasury.payout` with the correct indices (in this case 8 and 9) then sign and submit them one by one. This gives us some XCMs sent from the relay for the first: ![Screenshot 2024-07-30 at 20.05.09](https://hackmd.io/_uploads/HkTyqnLKC.png) And the second: ![Screenshot 2024-07-30 at 20.05.20](https://hackmd.io/_uploads/H1Nx52UKR.png) Which are received on Asset Hub processed, and the funds transferred (first one): ![Screenshot 2024-07-30 at 20.06.02](https://hackmd.io/_uploads/rJrl5hLKC.png) ![Screenshot 2024-07-30 at 20.06.10](https://hackmd.io/_uploads/ryVx5n8tR.png) And for the second one: ![Screenshot 2024-07-30 at 20.06.33](https://hackmd.io/_uploads/BkHg53UY0.png) ## Re-run a block An XCM Transact failed to process on rococo coretime, so I'll rerun the block with log set to trace to see what went wrong ``` chopsticks run-block -c ./rococo-coretime.yml -b 0x89843c0022d1ba39f61b761fabf43699cb6f8260e3b9f5ba9bbfe37cbe75e117 --runtime-log-level 5 ``` It's saying: ``` { message: "Barrier blocked execution origin=Location { parents: 1, interior: Here } message=Xcm([Transact { origin_kind: Superuser, require_weight_at_most: Weight { ref_time: 200000000, proof_size: 20000 }, call: \"0x3214d059b20000000000000000000000000000000000\" }, SetTopic([246, 215, 195, 119, 196, 246, 86, 42, 202, 18, 43, 243, 228, 136, 159, 86, 165, 10, 25, 88, 147, 83, 137, 238, 132, 41, 249, 241, 191, 137, 210, 183])]) properties=Properties { weight_credit: Weight { ref_time: 0, proof_size: 0 }, message_id: Some([246, 215, 195, 119, 196, 246, 86, 42, 202, 18, 43, 243, 228, 136, 159, 86, 165, 10, 25, 88, 147, 83, 137, 238, 132, 41, 249, 241, 191, 137, 210, 183]) } error=Unsupported", level: 4, target: "xcm::execute", } ``` `0x3214d059b20000000000000000000000000000000000` 0x32 is the broker, 0x14 is notify revenue. It's definitely in the pallet, so something is blocking that from being called from a transact 🧐 Let's just check the `require_weight_at_most` first to rule out the classic gotcha: `ref_time: 200000000, proof_size: 20000` in the transact, looking at the weights file I can see it's definitely lower than that: ```rust Weight::from_parts(1_991_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) ``` 100mil ref time for the write only takes us to `ref_time: 101_991_000, proof_size: 0`, which is well within the limit. Aaand I've got it - looking again at the last barrier to fail I notice the XCM is missing UnpaidExecution so it fails all barriers: https://github.com/paritytech/polkadot-sdk/blob/master/polkadot/runtime/parachains/src/coretime/mod.rs#L401 pushes notifyrevenue onto an [empty vec](https://github.com/paritytech/polkadot-sdk/blob/master/polkadot/runtime/parachains/src/coretime/mod.rs#L361) when `revenue == 0`, so it never explicitly requests unpaid execution, because that happens only in [the block where revenue is `> 0`](https://github.com/paritytech/polkadot-sdk/blob/master/polkadot/runtime/parachains/src/coretime/mod.rs#L387) ## Dry run Polkadot Coretime Referendum We need a chopsticks config for the coretime chain on Polkadot in genesis mode: ``` # polkadot-coretime.yml mock-signature-host: true block: ${env.POLKADOT_CORETIME_BLOCK_NUMBER} db: ./db.sqlite genesis: ./coretime-polkadot.json import-storage: System: Account: - - - 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY - providers: 1 data: free: 1000000000000000 ``` Now we spin up a forked network in xcm mode with the three relevant chains: ``` chopsticks xcm -r polkadot -p ./polkadot-coretime.yml -p polkadot-collectives.yml ``` We have the two preimages which we need to note on the correct chain - On the relay go to the extrinsics tab of PJS and use `preimage.note_preimage` and then upload the file output by opengov-cli. You'll need about 1096 dot free to be reserved - For the collectives chain it's short enough to inline it. Go to the [js tab](https://polkadot.js.org/apps/?rpc=ws%3A%2F%2F127.0.0.1%3A8001%2F#/js) of PJS on the collectives chain and add the following code and run it. ```javascript // Grab the block number of the current head // api is already imported, no need to add anything but the following. const number = (await api.rpc.chain.getHeader()).number.toNumber() // use chopsticks dev_setStorage to inject the call into the scheduler state for the next block. The value of `Inline` is just the call data. Crucially with this call we can set the origin to `Root` await api.rpc('dev_setStorage', { scheduler: { agenda: [ [ [number + 1], [ { call: { Inline: '0x1f0004010004082f0000060302fab374216f88170064c65cfa6a5f6bbd54286060c126c6e68603bfd760d41ad546ef25b795d4c322' }, origin: { FellowshipOrigins: 'Fellows' } } ] ] ] } }) // Make a block to include the extrinsic await api.rpc('dev_newBlock', { count: 1 }) ``` Where the call hash is taken from the link for the fellowship referendum preimage output by opengov-cli. This sends an XCM which whitelists the call on the relay. We can check the relay explorer to see the events from this: ![Screenshot 2024-08-21 at 00.22.24](https://hackmd.io/_uploads/rJFtdqzj0.png) Now go to the [js tab](https://polkadot.js.org/apps/?rpc=ws%3A%2F%2F127.0.0.1%3A8002%2F#/js) of PJS on the relay chain and add the following code and run it. ```javascript // Grab the block number of the current head // api is already imported, no need to add anything but the following. const number = (await api.rpc.chain.getHeader()).number.toNumber() // use chopsticks dev_setStorage to inject the call into the scheduler state for the next block. The value of `Inline` is just the call data. Crucially with this call we can set the origin to `Root` await api.rpc('dev_setStorage', { scheduler: { agenda: [ [ [number + 1], [ { call: { Lookup: { hash: '0x06d6197ac4a5fc0aacdaa1cd89f55263197c9f414ade65f660cdd2b4c1eaa86a', len: 1054967 } }, origin: { Origins: 'WhitelistedCaller' } } ] ] ] } }) // Make a block to include the extrinsic await api.rpc('dev_newBlock', { count: 1 }) ``` We can see the side effects in the explorer: ![Screenshot 2024-08-21 at 00.26.12](https://hackmd.io/_uploads/SJYtu9zoC.png) ![Screenshot 2024-08-21 at 00.26.31](https://hackmd.io/_uploads/SJcY_9foC.png) And the parachain appears in the "network->parathreads" tab: ![Screenshot 2024-08-21 at 00.27.21](https://hackmd.io/_uploads/SkKtuczoA.png) And we can double check that the hash in the `paras.PvfCheckStarted` event (in the screenshots above) is the same as the hash in the release: ![Screenshot 2024-08-21 at 00.38.47](https://hackmd.io/_uploads/Hy2Zj5Gi0.png) This is enough to be satisfied that the referendum has been configured correctly, but just to be certain we can churn blocks for the new parachain to go through the parachain lifecycle and start making blocks. For this to happen between 4 and 8 hours need to pass on chain to go through two session boundaries (depending on when you fork the chain) until the parachain makes blocks. You'll likely need to add some more blocks to the below command to get to the second session boundary. To get the proper count, take the time left in the epoch as the forked chain sees it divided by 6 seconds and add it to the `count` below. If you undershoot it just run a similar command with the count you need to get to it. Run this in the [js tab](https://polkadot.js.org/apps/?rpc=ws%3A%2F%2F127.0.0.1%3A8002%2F#/js) on the relay chain. ```javascript await api.rpc('dev_newBlock', { count: 2400 }) ``` 2400 is four hours (this will take some time to run, about 2 blocks per second on my laptop so ~20mins) ## Test the Polkadot update to v1.3.0 ```shell chopsticks xcm -r polkadot -p polkadot-asset-hub -p polkadot-collectives -p polkadot-bridge-hub -p polkadot-people ``` ``` // Grab the block number of the current head // api is already imported, no need to add anything but the following. const number = (await api.rpc.chain.getHeader()).number.toNumber() // use chopsticks dev_setStorage to inject the call into the scheduler state for the next block. The value of `Inline` is just the call data. Crucially with this call we can set the origin to `Root` await api.rpc('dev_setStorage', { scheduler: { agenda: [ [ [number + 1], [ { call: { Inline: '0x1a0410630004000100a10f04082f0000060202a4fa330110880009754c316ef45e67d8d142ae2d7c3cc9312667bff286ca3f4de844d5676346ca3e630004000100a50f04082f0000060242d817390110880009787ebbdd1d03b93fbbfd2ec92967f31c79ed916b51b8fa3b14c22a14c762dcb7630004000100a90f04082f00000602028bfb370110880009b1396335188f4a829a53fd47274328929412080f8f3fef7bae1683f2cd4461ce00092a9eafb8537a2bfbc19968522a256d2c21fcbd9f3ab1ef945011858a39a1daef' }, origin: { system: 'Root' } } ] ] ] } }) // Make a block to include the extrinsic await api.rpc('dev_newBlock', { count: 1 }) ``` # Test ref to reimburse for XCM bug Reimburse accounts affected by an XCM bug The three accounts on this proposal made extrinsics in the past that lost funds due to bugs in the runtime at that time. The extrinsics were: https://kusama.subscan.io/extrinsic/21678807-20 https://kusama.subscan.io/extrinsic/21577634-3 https://kusama.subscan.io/extrinsic/21734857-2 https://kusama.subscan.io/extrinsic/21605077-2 You can see in the polkadotXcm.Attempted event that it failed, however, there should be a polkadotXcm.AssetsTrapped event. It's a bug that the assets were not trapped and therefore lost. The bug was present in the version used in these extrinsics, 1.1.0 of Kusama. The bug had to do with delivery fees. The accounts attempted to cross-chain transfer all their funds and didn't have enough to pay for delivery fees, since they were taking from the account itself. This was fixed in version 1.1.2: https://github.com/paritytech/polkadot-sdk/pull/3085. This proposal is for these extrinsics where funds were lost due to a bug. They are not for extrinsics where assets were trapped, since in that case, they can be reclaimed. Call: 0x18021012030ba16151158b45002e81638652ca92e659f1fd3218b2b36054ff4bf9a45a89cf48a4688b2e0e5e55120307c08caa3de800be240a9677de05bf887a07c2fc7cf936d635f938715a3fe8e38ca470f36e893512030bcb13645cfc0b00c47fb31c8c5e5521ce1cc4355738f51d3b89f2b0c1ad3cf6f5ead68706b2f87c12030b407dc2b201080070dcd0d3990e46a1073934b302c2caf2ce095765058d2ae9bdb85c527b9d4645 Preimage hash: 0x65fb808e0a7928429935fbf0b2e2bfebae3095b19e0f99762ce40b4acbebfd8d Preimage length: 170 Track: SmallSpender Fork Kusama locally and upload the preimage Go to the js tab and paste the following to run the referendum: ``` // Grab the block number of the current head // api is already imported, no need to add anything but the following. const number = (await api.rpc.chain.getHeader()).number.toNumber() // use chopsticks dev_setStorage to inject the call into the scheduler state for the next block. The value of `Inline` is just the call data. Crucially with this call we can set the origin to `Root` await api.rpc('dev_setStorage', { scheduler: { agenda: [ [ [number + 1], [ { call: { Lookup: { hash: '0x65fb808e0a7928429935fbf0b2e2bfebae3095b19e0f99762ce40b4acbebfd8d', len: 170 } }, origin: { Origins: 'SmallSpender' } } ] ] ] } }) // Make a block to include the extrinsic await api.rpc('dev_newBlock', { count: 1 }) ``` ref submission call is 0x15002b0a0265fb808e0a7928429935fbf0b2e2bfebae3095b19e0f99762ce40b4acbebfd8daa000000010a000000 hash 0xd1395c60ba5e8d3c4d4c05055303e5d1a0e20d0b6e3e3265503bccaa2a74a3fd # Send an XCM from relay as root to SP People chain -> identity -> addRegistrar Wrapped in a transact executed as superuser Included in an xcm 0x630004000100b10f04082f000006020208af2fe12e8c3200008638bcb5e5ae5e04954d7fa2d29ccedf7f323482573198732af0b3fe32f8da03 ```shell chopsticks xcm -r polkadot -p polkadot-people ``` Execute from js tab on polkadot relay apps page / dispatch from code in a test ``` // Grab the block number of the current head // api is already imported, no need to add anything but the following. const number = (await api.rpc.chain.getHeader()).number.toNumber() // use chopsticks dev_setStorage to inject the call into the scheduler state for the next block. The value of `Inline` is just the call data. Crucially with this call we can set the origin to `Root` await api.rpc('dev_setStorage', { scheduler: { agenda: [ [ [number + 1], [ { call: { Inline: '0x630004000100b10f04082f000006020208af2fe12e8c3200008638bcb5e5ae5e04954d7fa2d29ccedf7f323482573198732af0b3fe32f8da03' }, origin: { system: 'Root' } } ] ] ] } }) // Make a block to include the extrinsic await api.rpc('dev_newBlock', { count: 1 }) ``` Expect from this that the xcm is sent Xcm received by people chain Processed Call is executed Registrar added ## Inject USDT into an account You need to ensure the total issuance of the asset is large enough for your account to contain that amount, not just putting the amount in the account. You can achieve this with the following two entries for USDT on westend (exists as an asset but with zero suppy). ``` import-storage: ... Assets: Account: - [[1984, 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY], { balance: 1000000000 }] Asset: [[[1984], { supply: 1000000000 }]] ``` You can then send a message like: ``` const msg = Enum('V5', [ XcmV4Instruction.WithdrawAsset([ { id: { parents: 1, interior: XcmV3Junctions.Here(), }, fun: XcmV3MultiassetFungibility.Fungible(5_000_000_000_000n), }, { id: { parents: 0, interior: XcmV3Junctions.X2([ XcmV3Junction.PalletInstance(50), XcmV3Junction.GeneralIndex(1984n)]), }, fun: XcmV3MultiassetFungibility.Fungible(1_000_000_000n), }, ]), Enum('PayFees', { asset: { id: { parents: 1, interior: XcmV3Junctions.Here(), }, fun: XcmV3MultiassetFungibility.Fungible(2_000_000_000_000n), } }), ]); ```