Converting an account to multisig

Create a 1-of-2 multisig account.

Background

Alice and Bob have separate accounts. They also want to have a shared account to buy groceries, so that if Bob is out shopping, he can buy groceries for both himself and Alice.

This shared account appears in NEM as 1-of-2 multisig. Multisig accounts permit Alice and Bob sharing funds in a separate account, requiring only the signature from one of them to transact.

../../_images/multisig-1-of-2.png

1-of-2 multisig account example

In this guide, you are going to create a 1-of-2 multisig account. In future guides, you will learn how to increase the minimum number of cosignatures required, as well as invite and remove cosignatories from the multisig account.

Prerequisites

Getting into some code

  1. First, define the accounts that will be cosginatories of the multisig account. In our case, these are Alice and Bob addresses. Then, open the account that will be converted into multisig using its private key.
const nodeUrl = 'http://localhost:3000';
const transactionHttp = new TransactionHttp(nodeUrl);
const listener = new Listener(nodeUrl);

const privateKey = process.env.MULTISIG_ACCOUNT_PUBLIC_KEY as string;
const account = Account.createFromPrivateKey(privateKey, NetworkType.MIJIN_TEST);

const cosignatory1PublicKey = process.env.COSIGNATORY_1_PUBLIC_KEY as string;
const cosignatory1 = PublicAccount.createFromPublicKey(cosignatory1PublicKey, NetworkType.MIJIN_TEST);
const cosignatory2PublicKey = process.env.COSIGNATORY_2_PUBLIC_KEY as string;
const cosignatory2 = PublicAccount.createFromPublicKey(cosignatory2PublicKey, NetworkType.MIJIN_TEST);
  1. Create a MultisigAccountModificationTransaction to convert the shared account into a multisig account. As you want to create a 1-of-2 multisig account, set the minimum signatures required to 1.
const multisigAccountModificationTransaction = MultisigAccountModificationTransaction.create(
    Deadline.create(),
    1,
    1,
    [
        new MultisigCosignatoryModification(
            CosignatoryModificationAction.Add,
            cosignatory1,
        ),
        new MultisigCosignatoryModification(
            CosignatoryModificationAction.Add,
            cosignatory2,
        )],
    NetworkType.MIJIN_TEST);
  1. Create an AggregateBondedTransaction, wrapping the MultisigAccountModificationTransaction. This action is necessary because Alice and Bob must opt-in to become cosignatories of the new multisig account.
const aggregateTransaction = AggregateTransaction.createBonded(
    Deadline.create(),
    [multisigAccountModificationTransaction.toAggregate(account.publicAccount)],
    NetworkType.MIJIN_TEST);
  1. Sign the AggregateTransaction using the private key of the multisig account.

Note

To make the transaction only valid for your network, include the first block generation hash. Open http://localhost:3000/block/1 in a new tab and copy the meta.generationHash value.

const networkGenerationHash = process.env.NETWORK_GENERATION_HASH as string;
const signedTransaction = account.sign(aggregateTransaction, networkGenerationHash);
console.log(signedTransaction.hash);
  1. Before sending an AggregateBondedTransaction, the future multisig account needs to lock at least 10 cat.currency. This transaction is required to prevent spamming the network. After the HashLockTransaction has been confirmed, announce the AggregateTransaction signed in (4).
const hashLockTransaction = HashLockTransaction.create(
    Deadline.create(),
    NetworkCurrencyMosaic.createRelative(10),
    UInt64.fromUint(480),
    signedTransaction,
    NetworkType.MIJIN_TEST);

const hashLockTransactionSigned = account.sign(hashLockTransaction, networkGenerationHash);

listener.open().then(() => {

    transactionHttp
        .announce(hashLockTransactionSigned)
        .subscribe(x => console.log(x), err => console.error(err));

    listener
        .confirmed(account.address)
        .pipe(
            filter((transaction) => transaction.transactionInfo !== undefined
                && transaction.transactionInfo.hash === hashLockTransactionSigned.hash),
            mergeMap(ignored => transactionHttp.announceAggregateBonded(signedTransaction))
        )
        .subscribe(announcedAggregateBonded => console.log(announcedAggregateBonded),
            err => console.error(err));
});
  1. Cosign the AggregateTransaction with Alice’s account.
nem2-cli transaction cosign --hash A6A374E66B32A3D5133018EFA9CD6E3169C8EEA339F7CCBE29C47D07086E068C --profile alice
  1. Cosign the AggregateTransaction with Bob’s account.
nem2-cli transaction cosign --hash A6A374E66B32A3D5133018EFA9CD6E3169C8EEA339F7CCBE29C47D07086E068C --profile bob
  1. If everything goes well, the account is now multisig, being Alice and Bob cosignatories. You can get the list of the multisig accounts where Alice or Bob are cosignatories using getMultisigAccountInfo function.
const accountHttp = new AccountHttp('http://localhost:3000');

const rawAddress = process.env.ADDRESS as string;
const address = Address.createFromRawAddress(rawAddress);

accountHttp
    .getMultisigAccountInfo(address)
    .subscribe(accountInfo => console.log(accountInfo), err => console.error(err));
const accountHttp = new AccountHttp('http://localhost:3000');

const rawAddress = process.env.ADDRESS;
const address = Address.createFromRawAddress(rawAddress);
accountHttp
    .getMultisigAccountInfo(address)
    .subscribe(accountInfo => console.log(accountInfo), err => console.error(err));

What’s next?

Modify the multisig account you just created, converting it into a 2-of-2 multisig following the next guide.