Here is an end to end example of creating and executing a multisig with Polkadot JS:
const { ApiPromise, WsProvider } = require('@polkadot/api');
const { Keyring } = require('@polkadot/keyring');
const {
cryptoWaitReady,
createKeyMulti,
encodeAddress,
sortAddresses,
} = require('@polkadot/util-crypto');
const SS58Prefix = 2;
async function multisigTransfer() {
await cryptoWaitReady();
// create a keyring with some non-default values specified
const keyring = new Keyring({ type: 'sr25519', ss58Format: SS58Prefix });
// Connect to the local Polkadot node
const wsProvider = new WsProvider('ws://localhost:9944');
const api = await ApiPromise.create({ provider: wsProvider });
// Retrieve the chain & node information via rpc calls
const [chain, nodeName, nodeVersion] = await Promise.all([
api.rpc.system.chain(),
api.rpc.system.name(),
api.rpc.system.version()
]);
console.log(`You are connected to chain ${chain} using ${nodeName} v${nodeVersion}`);
// create accounts based on the development seed
const alice = keyring.addFromUri('//Alice');
const bob = keyring.addFromUri('//Bob');
const charlie = keyring.addFromUri('//Charlie');
// Define the multisig transfer details
const multisigCall = api.tx.balances.transfer(alice.address, 1_234_567);
const addresses = [alice.address, bob.address, charlie.address]
const threshold = 2;
const { weight: maxWeight } = await multisigCall.paymentInfo(alice);
// Address as a byte array.
const multisigAddress = encodeAddress(createKeyMulti(addresses, threshold), SS58Prefix);
console.log(multisigAddress);
await fundAddress(api, alice, multisigAddress, 1_000_000_000_000)
await approveMultisigWithHash(api, alice, threshold, addresses, null, multisigCall.method.hash, maxWeight);
const info = await api.query.multisig.multisigs(multisigAddress, multisigCall.method.hash);
const timepoint = info.unwrap().when;
await approveMultisigWithHash(api, bob, threshold, addresses, timepoint, multisigCall.method.hash, maxWeight);
await finalizeMultisig(api, charlie, threshold, addresses, timepoint, multisigCall, maxWeight);
}
async function fundAddress(api, sender, to, amount) {
return new Promise((resolve, reject) => {
api.tx.balances
.transfer(to, amount)
.signAndSend(sender, async (result) => {
console.log(`Current status is ${result.status}`);
if (result.status.isInBlock) {
console.log(`Transfer completed at blockHash ${result.status.asInBlock}`);
resolve();
}
}).catch((error) => {
reject(error);
});
});
}
async function approveMultisigWithHash(api, sender, threshold, addresses, timepoint, callHash, maxWeight) {
return new Promise((resolve, reject) => {
// Take addresses and remove the sender.
const otherSignatories = addresses.filter((who) => who !== sender.address);
// Sort them by public key.
const otherSignatoriesSorted = sortAddresses(otherSignatories, SS58Prefix);
api.tx.multisig
.approveAsMulti(threshold, otherSignatoriesSorted, timepoint, callHash, maxWeight)
.signAndSend(sender, async (result) => {
console.log(`Current status is ${result.status}`);
if (result.status.isInBlock) {
console.log(`Transaction included at blockHash ${result.status.asInBlock}`);
resolve();
}
}).catch((error) => {
reject(error);
});
});
}
async function finalizeMultisig(api, sender, threshold, addresses, timepoint, multisigCall, maxWeight) {
return new Promise((resolve, reject) => {
// Take addresses and remove the sender.
const otherSignatories = addresses.filter((who) => who !== sender.address);
// Sort them by public key.
const otherSignatoriesSorted = sortAddresses(otherSignatories, SS58Prefix);
api.tx.multisig
.asMulti(threshold, otherSignatoriesSorted, timepoint, multisigCall, maxWeight)
.signAndSend(sender, async (result) => {
console.log(`Current status is ${result.status}`);
if (result.status.isInBlock) {
console.log(`Transaction included at blockHash ${result.status.asInBlock}`);
resolve();
}
}).catch((error) => {
reject(error);
});
});
}
multisigTransfer().catch(console.error);
Here you can see all the events emitted, showing the end to end scenario works on a local polkadot dev envrionment:
Here was a resource I found which helped me complete the scenario:
https://github.com/polkadot-js/api/issues/2878