1

How can a multisig transaction be initiated, validated and completed, all using polkadotjs from javascript code?

The info I have currently is :

but I didn't find example code.

1 Answer 1

0

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:

enter image description here

Here was a resource I found which helped me complete the scenario:

https://github.com/polkadot-js/api/issues/2878

2
  • In the meantime I was pointed to github.com/allenzhong/polkadot-multisig-example which is similar to your code. But this does not work for me: const timepoint = timeinfo.unwrap().when; I get Property 'unwrap' does not exist on type 'Codec'.
    – ytresza
    May 16 at 7:30
  • You are getting this error with my sample or the sample you linked to?
    – Shawn Tabrizi
    2 days ago

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.