import { ApiPromise, WsProvider } from "@polkadot/api"; import { Keyring } from "@polkadot/api"; import { cryptoWaitReady } from "@polkadot/util-crypto"; const filterConsole = require("filter-console"); // Hide some warning messages that are coming from Polkadot JS API. // TODO: Make configurable. filterConsole([ `code: '1006' reason: 'connection failed'`, `Unhandled promise rejections`, `UnhandledPromiseRejectionWarning:`, `Unknown types found`, ]); // Connect to a local Substrate node. This function wont resolve until connected. // TODO: Add a timeout where we know something went wrong so we don't wait forever. export async function connect(port: number, types: any) { const provider = new WsProvider("ws://127.0.0.1:" + port); const api = await ApiPromise.create({ provider, types, throwOnConnect: false, }); return api; } // Get the genesis header of a node. Used for registering a parachain on the relay chain. export async function getHeader(api: ApiPromise) { let genesis_hash = await api.rpc.chain.getBlockHash(0); let genesis_header = await api.rpc.chain.getHeader(genesis_hash); return genesis_header.toHex(); } // Submit an extrinsic to the relay chain to register a parachain. // Uses the Alice account which is known to be Sudo for the relay chain. export async function registerParachain( api: ApiPromise, id: string, wasm: string, header: string, finalization: boolean = false ) { return new Promise<void>(async (resolvePromise, reject) => { await cryptoWaitReady(); const keyring = new Keyring({ type: "sr25519" }); const alice = keyring.addFromUri("//Alice"); let paraGenesisArgs = { genesis_head: header, validation_code: wasm, parachain: true, }; let genesis = api.createType("ParaGenesisArgs", paraGenesisArgs); const nonce = Number((await api.query.system.account(alice.address)).nonce); console.log( `--- Submitting extrinsic to register parachain ${id}. (nonce: ${nonce}) ---` ); const unsub = await api.tx.sudo .sudo(api.tx.parasSudoWrapper.sudoScheduleParaInitialize(id, genesis)) .signAndSend(alice, { nonce: nonce, era: 0 }, (result) => { console.log(`Current status is ${result.status}`); if (result.status.isInBlock) { console.log( `Transaction included at blockHash ${result.status.asInBlock}` ); if (finalization) { console.log("Waiting for finalization..."); } else { unsub(); resolvePromise(); } } else if (result.status.isFinalized) { console.log( `Transaction finalized at blockHash ${result.status.asFinalized}` ); unsub(); resolvePromise(); } else if (result.isError) { console.log(`Transaction Error`); reject(`Transaction Error`); } }); }); } // Set the balance of an account on the relay chain. export async function setBalance( api: ApiPromise, who: string, value: string, finalization: boolean = false ) { return new Promise<void>(async (resolvePromise, reject) => { await cryptoWaitReady(); const keyring = new Keyring({ type: "sr25519" }); const alice = keyring.addFromUri("//Alice"); const nonce = Number((await api.query.system.account(alice.address)).nonce); console.log( `--- Submitting extrinsic to set balance of ${who} to ${value}. (nonce: ${nonce}) ---` ); const unsub = await api.tx.sudo .sudo(api.tx.balances.setBalance(who, value, 0)) .signAndSend(alice, { nonce: nonce, era: 0 }, (result) => { console.log(`Current status is ${result.status}`); if (result.status.isInBlock) { console.log( `Transaction included at blockHash ${result.status.asInBlock}` ); if (finalization) { console.log("Waiting for finalization..."); } else { unsub(); resolvePromise(); } } else if (result.status.isFinalized) { console.log( `Transaction finalized at blockHash ${result.status.asFinalized}` ); unsub(); resolvePromise(); } else if (result.isError) { console.log(`Transaction Error`); reject(`Transaction Error`); } }); }); } export async function sendHrmpMessage( api: ApiPromise, recipient: string, data: string, finalization: boolean = false ) { return new Promise<void>(async (resolvePromise, reject) => { await cryptoWaitReady(); const keyring = new Keyring({ type: "sr25519" }); const alice = keyring.addFromUri("//Alice"); const nonce = Number((await api.query.system.account(alice.address)).nonce); let hrmpMessage = { recipient: recipient, data: data, }; let message = api.createType("OutboundHrmpMessage", hrmpMessage); console.log(`--- Sending a message to ${recipient}. (nonce: ${nonce}) ---`); const unsub = await api.tx.sudo .sudo(api.tx.messageBroker.sudoSendHrmpMessage(message)) .signAndSend(alice, { nonce: nonce, era: 0 }, (result) => { console.log(`Current status is ${result.status}`); if (result.status.isInBlock) { console.log( `Transaction included at blockHash ${result.status.asInBlock}` ); if (finalization) { console.log("Waiting for finalization..."); } else { unsub(); resolvePromise(); } } else if (result.status.isFinalized) { console.log( `Transaction finalized at blockHash ${result.status.asFinalized}` ); unsub(); resolvePromise(); } else if (result.isError) { console.log(`Transaction Error`); reject(`Transaction Error`); } }); }); }