Interacting with accounts

Creating and opening an account

You are going to create a new account and open it.

Prerequisites

Let’s get into some code

An account is a key pair (private and public key) associated to a mutable state stored on the NEM blockchain.

const account = Account.generateNewAccount(NetworkType.MIJIN_TEST);

console.log('Your new account address is:', account.address.pretty(), 'and its private key', account.privateKey);
const account = Account.generateNewAccount(NetworkType.MIJIN_TEST);

console.log('Your new account address is:', account.address.pretty(), 'and its private key', account.privateKey);
nem2-cli account generate --network MIJIN_TEST

The private key uniquely identifies a NEM account, holds all its power. It is a priority to make sure it is stored safely somewhere offline and do not share it with anyone.

The public key is cryptographically derived from the private key and safe to be shared. Although that, it is preferable to share the address, which contains further information such as network and validity check.

In case you already have a private key, not needing to generate a new account:

// Replace with a private key
const privateKey = process.env.PRIVATE_KEY as string;

const account = Account.createFromPrivateKey(privateKey, NetworkType.MIJIN_TEST);

console.log('Your account address is:', account.address.pretty(), 'and its private key', account.privateKey);
        // Replace with a private key
        final String privateKey = "AC4091B7B9127FB4009CE3EADFD89116F6A80AB768C4681B12CD55D7196078F4";

        final Account account = Account.createFromPrivateKey(privateKey, NetworkType.MIJIN_TEST);
// Replace with a private key
const privateKey = process.env.PRIVATE_KEY;

const account = Account.createFromPrivateKey(privateKey, NetworkType.MIJIN_TEST);

console.log('Your account address is:', account.address.pretty(), 'and its private key', account.privateKey);

Using a Wallet

If the programming language of the SDK you are using allows client-side development, you will be able to create a new account by creating a new wallet.

A wallet grants using an account for signing transactions, encrypting your credentials with a password.

const password = new Password('password');

const wallet = SimpleWallet.create('wallet-name', password, NetworkType.MIJIN_TEST);

const account = wallet.open(password);

console.log('Your new account address is:', account.address.pretty(), 'and its private key', account.privateKey);
const password = new Password('password');

const wallet = SimpleWallet.create('wallet-name', password, NetworkType.MIJIN_TEST);

const account = wallet.open(password);

console.log('Your new account address is:', account.address.pretty(), 'and its private key', account.privateKey);
nem2-cli account generate --network MIJIN_TEST --save --url http://localhost:3000 --profile test

In case you already have a private key, not needing to generate a new account:

const password = new Password('password');

// Replace with a private key
const privateKey = process.env.PRIVATE_KEY as string;

const wallet = SimpleWallet.createFromPrivateKey('wallet-name', password, privateKey, NetworkType.MIJIN_TEST);

const account = wallet.open(password);

console.log('Your account address is:', account.address.pretty(), 'and its private key', account.privateKey);
const password = new Password('password');

// Replace with a private key
const privateKey = process.env.PRIVATE_KEY as string;

const wallet = SimpleWallet.createFromPrivateKey('wallet-name', password, privateKey, NetworkType.MIJIN_TEST);

const account = wallet.open(password);

console.log('Your account address is:', account.address.pretty(), 'and its private key', account.privateKey);
nem2-cli profile create --privatekey your_private_key --network MIJIN_TEST --url http://localhost:3000 --profile test

Getting account information

You are going to develop a program to get the public key, height, balance and importance of an account.

Prerequisites

Let’s get into some code

Calling accountHttp.getAccountInfo you can get account information just passing address and network as a parameter.

const accountHttp = new AccountHttp('http://localhost:3000');

// Replace with address
const address = 'SD5DT3-CH4BLA-BL5HIM-EKP2TA-PUKF4N-Y3L5HR-IR54';

accountHttp.getAccountInfo(Address.createFromRawAddress(address)).subscribe(
    accountInfo => console.log(accountInfo),
    err => console.error(err)
);
        final AccountHttp accountHttp = new AccountHttp("http://localhost:3000");

        // Replace with address
        final String address = "SD5DT3-CH4BLA-BL5HIM-EKP2TA-PUKF4N-Y3L5HR-IR54";

        final AccountInfo accountInfo = accountHttp.getAccountInfo(Address.createFromRawAddress(address)).toFuture().get();

        System.out.println(accountInfo);
const accountHttp = new AccountHttp('http://localhost:3000');

// Replace with address
const address = 'SD5DT3-CH4BLA-BL5HIM-EKP2TA-PUKF4N-Y3L5HR-IR54';

accountHttp.getAccountInfo(Address.createFromRawAddress(address)).subscribe(
    accountInfo => console.log(accountInfo),
    err => console.error(err)
);
nem2-cli account info --address SD5DT3-CH4BLA-BL5HIM-EKP2TA-PUKF4N-Y3L5HR-IR54

Can you spot account’s public key?

publicKey: ‘F33152059827EAA4D7D67C6E27A59851AF09FBD0926C35150FA44D2A9A5E4CAA’,

Which block was the first one where account address and public key appeared?

publicKeyHeight: UInt64 { lower: 401575, higher: 0 }, addressHeight: UInt64 { lower: 288598, higher: 0 },

Does the account have an importance?

importance: UInt64 { lower: 1100282, higher: 0 },

Checking account’s balance

Check account’s balance using mosaicService. The balance is the amount of the different mosaics stored in the account.

const url = 'http://localhost:3000';

const accountHttp = new AccountHttp(url);
const mosaicHttp = new MosaicHttp(url);
const namespaceHttp = new NamespaceHttp(url);

const mosaicService = new MosaicService(accountHttp, mosaicHttp, namespaceHttp);

// Replace with address
const address = "SD5DT3-CH4BLA-BL5HIM-EKP2TA-PUKF4N-Y3L5HR-IR54";

mosaicService.mosaicsAmountViewFromAddress(Address.createFromRawAddress(address))
    .flatMap((_) => _)
    .subscribe(
        mosaic => console.log('You have', mosaic.relativeAmount(), mosaic.fullName()),
        err => console.error(err)
    );
const url = 'http://localhost:3000';

const accountHttp = new AccountHttp(url);
const mosaicHttp = new MosaicHttp(url);
const namespaceHttp = new NamespaceHttp(url);

const mosaicService = new MosaicService(accountHttp, mosaicHttp, namespaceHttp);

// Replace with address
const address = "SD5DT3-CH4BLA-BL5HIM-EKP2TA-PUKF4N-Y3L5HR-IR54";

mosaicService.mosaicsAmountViewFromAddress(Address.createFromRawAddress(address))
    .flatMap((_) => _)
    .subscribe(
        mosaic => console.log('You have', mosaic.relativeAmount(), mosaic.fullName()),
        err => console.error(err)
    );
nem2-cli account info --address SD5DT3-CH4BLA-BL5HIM-EKP2TA-PUKF4N-Y3L5HR-IR54

What’s next?

Try to repeat retrieving the balance by only filtering XEM amount.

Getting the amount of XEM sent to an account

You are going to develop a program to check the amount of XEM you have sent to some account.

Prerequisites

Let’s get into some code

After obtaining all outgoing transactions from an account, apply a filter to distinguish target recipient.

// Replace with public key
const originPublicKey = '7D08373CFFE4154E129E04F0827E5F3D6907587E348757B0F87D2F839BF88246';

// Replace with recipient address
const recipientAddress = 'SDG4WG-FS7EQJ-KFQKXM-4IUCQG-PXUW5H-DJVIJB-OXJG';

const originAccount = PublicAccount.createFromPublicKey(originPublicKey, NetworkType.MIJIN_TEST);

// Replace with address
const address = Address.createFromRawAddress(recipientAddress);

const accountHttp = new AccountHttp('http://localhost:3000');

accountHttp
    .outgoingTransactions(originAccount)
    .flatMap((_) => _) // Transform transaction array to single transactions to process them
    .filter((_) => _.type === TransactionType.TRANSFER) // Filter transfer transactions
    .map((_) => _ as TransferTransaction) // Map transaction as transfer transaction
    .filter((_) => _.recipient.equals(address)) // Filter transactions from to account
    .filter((_) => _.mosaics.length === 1 && _.mosaics[0].id.equals(XEM.MOSAIC_ID)) // Filter xem transactions
    .map((_) => _.mosaics[0].amount.compact() / Math.pow(10, XEM.DIVISIBILITY)) // Map only amount in xem
    .toArray() // Add all mosaics amounts into one array
    .map((_) => _.reduce((a, b) => a + b, 0))
    .subscribe(
        total => console.log('Total xem send to account', address.pretty(), 'is:', total),
        err => console.error(err)
    );
        // Replace with public key
        final String originPublicKey = "";

        // Replace with recipient address
        final String recipientAddress = "SB2RPH-EMTFMB-KELX2Y-Q3MZTD-RV7DQG-UZEADV-CYKC";

        // Replace with public key
        final PublicAccount originAccount = PublicAccount.createFromPublicKey(originPublicKey, NetworkType.MIJIN_TEST);

        // Replace with address
        final Address address = Address.createFromRawAddress(recipientAddress);

        final AccountHttp accountHttp = new AccountHttp("http://localhost:3000");

        final BigInteger total = accountHttp.outgoingTransactions(originAccount)
                .flatMapIterable(tx -> tx) // Transform transaction array to single transactions to process them
                .filter(tx -> tx.getType().equals(TransactionType.TRANSFER)) // Filter transfer transactions
                .map(tx -> (TransferTransaction) tx) // Map transaction as transfer transaction
                .filter(tx -> tx.getRecipient().equals(address)) // Filter transactions from to account
                .filter(tx -> tx.getMosaics().size() == 1 && tx.getMosaics().get(0).getId().equals(XEM.MOSAICID)) // Filter xem transactions
                .map(tx -> tx.getMosaics().get(0).getAmount().divide(BigDecimal.valueOf(Math.pow(10, XEM.DIVISIBILITY)).toBigInteger())) // Map only amount in xem
                .toList() // Add all mosaics amounts into one array
                .map(amounts -> amounts.stream().reduce(BigInteger.ZERO, BigInteger::add))
                .toFuture()
                .get();

        System.out.println("Total xem send to account " + address.pretty() + " is: " + total.toString());
// Replace with public key
const originPublicKey = '7D08373CFFE4154E129E04F0827E5F3D6907587E348757B0F87D2F839BF88246';

// Replace with recipient address
const recipientAddress = 'SDG4WG-FS7EQJ-KFQKXM-4IUCQG-PXUW5H-DJVIJB-OXJG';

const originAccount = PublicAccount.createFromPublicKey(originPublicKey, NetworkType.MIJIN_TEST);

// Replace with address
const address = Address.createFromRawAddress(recipientAddress);

const accountHttp = new AccountHttp('http://localhost:3000');

accountHttp
    .outgoingTransactions(originAccount)
    .flatMap((_) => _) // Transform transaction array to single transactions to process them
    .filter((_) => _.type === TransactionType.TRANSFER) // Filter transfer transactions
    .map((_) => _ as TransferTransaction) // Map transaction as transfer transaction
    .filter((_) => _.recipient.equals(address)) // Filter transactions from to account
    .filter((_) => _.mosaics.length === 1 && _.mosaics[0].id.equals(XEM.MOSAIC_ID)) // Filter xem transactions
    .map((_) => _.mosaics[0].amount.compact() / Math.pow(10, XEM.DIVISIBILITY)) // Map only amount in xem
    .toArray() // Add all mosaics amounts into one array
    .map((_) => _.reduce((a, b) => a + b, 0))
    .subscribe(
        total => console.log('Total xem send to account', address.pretty(), 'is:', total),
        err => console.error(err)
    );

The amount of sent XEM in transfer transactions is displayed.

What’s next?

Try to repeat the example by changing NEM filter for another mosaic .

Receiving transactions of an account

You are going to develop a program to get the list of transactions where an account is involved.

Background

By default, the SDK provides up to 10 transactions. This parameter is increasable up to 100 transactions.

Prerequisites

  • Finish the getting started section
  • Text editor or IDE
  • NEM2-SDK or CLI
  • An account that has received some transaction

Let’s get into some code

In this example, we will fetch confirmed transactions for a given account using accountHttp repository.

A transaction is confirmed if it is included in a block and validated by the network.

const accountHttp = new AccountHttp('http://localhost:3000');

// Replace with public key
const publicKey = '7D08373CFFE4154E129E04F0827E5F3D6907587E348757B0F87D2F839BF88246';

/**
 * Page size between 10 and 100, otherwise 10
 */
const pageSize = 10;

accountHttp.transactions(
    PublicAccount.createFromPublicKey(publicKey, NetworkType.MIJIN_TEST),
    new QueryParams(pageSize)
).subscribe(
    transactions => console.log(transactions),
    err => console.error(err)
);
        final AccountHttp accountHttp = new AccountHttp("http://localhost:3000");

        // Replace with public key
        final String publicKey = "";

        final PublicAccount publicAccount = PublicAccount.createFromPublicKey(publicKey, NetworkType.MIJIN_TEST);

        // Page size between 10 and 100, otherwise 10
        int pageSize = 20;

        final List<Transaction> transactions = accountHttp.transactions(publicAccount, new QueryParams(pageSize, null)).toFuture().get();

        System.out.print(transactions);
const accountHttp = new AccountHttp('http://localhost:3000');

// Replace with public key
const publicKey = '7D08373CFFE4154E129E04F0827E5F3D6907587E348757B0F87D2F839BF88246';

/**
 * Page size between 10 and 100, otherwise 10
 */
const pageSize = 10;

accountHttp.transactions(
    PublicAccount.createFromPublicKey(publicKey, NetworkType.MIJIN_TEST),
    new QueryParams(pageSize)
).subscribe(
    transactions => console.log(transactions),
    err => console.error(err)
);
nem2-cli account transactions --publickey 7D08373CFFE4154E129E04F0827E5F3D6907587E348757B0F87D2F839BF88246 --numtransactions 10

Notice that you can also retrieve aggregate bonded, unconfirmed, incoming and outgoing transactions for a given account. See available accountHttp methods here.

What’s next

Try to get more than 10 transactions per request.

Converting an account to multisig

The purpose of this guide is to create a 1-of-2 multisig account, by adding two cosignatories.

Background

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

This shared account is in NEM translated as 1-of-2 multisig, meaning that one cosignatory needs to cosign the transaction to be included in a block.

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

1-of-2 multisig account example

Remember that a multisig account has cosignatories accounts, and it cannot start transactions itself. Only the cosignatories can initiate transactions.

Prerequisites

Let’s get into some code

The first step is to define who will be the cosignatories of the multisig account. Then, open the account that will be converted into multisig by providing its private key.

// Replace with the private key of the account that you want to convert into multisig
const privateKey = process.env.PRIVATE_KEY as string;

// Replace with cosignatories public keys
const cosignatory1PublicKey = '7D08373CFFE4154E129E04F0827E5F3D6907587E348757B0F87D2F839BF88246';
const cosignatory2PublicKey = 'F82527075248B043994F1CAFD965F3848324C9ABFEC506BC05FBCF5DD7307C9D';

const account = Account.createFromPrivateKey(privateKey, NetworkType.MIJIN_TEST);

const cosignatory1 = PublicAccount.createFromPublicKey(cosignatory1PublicKey, NetworkType.MIJIN_TEST);
const cosignatory2 = PublicAccount.createFromPublicKey(cosignatory2PublicKey, NetworkType.MIJIN_TEST);
        // Replace with the private key of the account that you want to convert into multisig
        final String privateKey = "";

        // Replace with cosignatories public keys
        final String cosignatory1PublicKey = "";
        final String cosignatory2PublicKey = "";

        final Account account = Account.createFromPrivateKey(privateKey, NetworkType.MIJIN_TEST);

        final PublicAccount cosignatory1PublicAccount = PublicAccount.createFromPublicKey(cosignatory1PublicKey, NetworkType.MIJIN_TEST);
        final PublicAccount cosignatory2PublicAccount = PublicAccount.createFromPublicKey(cosignatory2PublicKey, NetworkType.MIJIN_TEST);
// Replace with the private key of the account that you want to convert into multisig
const privateKey = process.env.PRIVATE_KEY;

// Replace with cosignatories public keys
const cosignatory1PublicKey = '7D08373CFFE4154E129E04F0827E5F3D6907587E348757B0F87D2F839BF88246';
const cosignatory2PublicKey = 'F82527075248B043994F1CAFD965F3848324C9ABFEC506BC05FBCF5DD7307C9D';

const account = Account.createFromPrivateKey(privateKey, NetworkType.MIJIN_TEST);

const cosignatory1 = PublicAccount.createFromPublicKey(cosignatory1PublicKey, NetworkType.MIJIN_TEST);
const cosignatory2 = PublicAccount.createFromPublicKey(cosignatory2PublicKey, NetworkType.MIJIN_TEST);

The next step is to convert the account into a multisig account by setting a modify multisig account transaction. As it is a 1-of-2 multisig account, set the minimum signatures to 1.

const convertIntoMultisigTransaction = ModifyMultisigAccountTransaction.create(
    Deadline.create(),
    1,
    1,
    [
        new MultisigCosignatoryModification(
            MultisigCosignatoryModificationType.Add,
            cosignatory1,
        ),
        new MultisigCosignatoryModification(
            MultisigCosignatoryModificationType.Add,
            cosignatory2,
        )],
    NetworkType.MIJIN_TEST
);
        final ModifyMultisigAccountTransaction convertIntoMultisigTransaction = ModifyMultisigAccountTransaction.create(
            Deadline.create(2, HOURS),
            1,
            1,
            Arrays.asList(
                new MultisigCosignatoryModification(
                    MultisigCosignatoryModificationType.ADD,
                    cosignatory1PublicAccount
                ),
                new MultisigCosignatoryModification(
                    MultisigCosignatoryModificationType.ADD,
                    cosignatory2PublicAccount
                )
            ),
            NetworkType.MIJIN_TEST
        );
const convertIntoMultisigTransaction = ModifyMultisigAccountTransaction.create(
    Deadline.create(),
    1,
    1,
    [
        new MultisigCosignatoryModification(
            MultisigCosignatoryModificationType.Add,
            cosignatory1,
        ),
        new MultisigCosignatoryModification(
            MultisigCosignatoryModificationType.Add,
            cosignatory2,
        )],
    NetworkType.MIJIN_TEST
);

Finally, multisig account signs and announces the transaction.

const signedTransaction = account.sign(convertIntoMultisigTransaction);

const transactionHttp = new TransactionHttp('http://localhost:3000');

transactionHttp.announce(signedTransaction).subscribe(
    x => console.log(x),
    err => console.error(err)
);
        final TransactionHttp transactionHttp = new TransactionHttp("http://localhost:3000");
        final SignedTransaction signedTransaction = account.sign(convertIntoMultisigTransaction);
        transactionHttp.announce(signedTransaction).toFuture().get();
const signedTransaction = account.sign(convertIntoMultisigTransaction);

const transactionHttp = new TransactionHttp('http://localhost:3000');

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

If everything goes well, Alice and Bob should be cosignatories of the multisig account.

const accountHttp = new AccountHttp('http://localhost:3000');

// Replace with address
const address = 'SCSGBN-HYJD6P-KJHACX-3R2BI3-QUMMOY-QSNW5J-ICLK';

accountHttp.getMultisigAccountInfo(Address.createFromRawAddress(address)).subscribe(
    accountInfo => console.log(accountInfo),
    err => console.error(err)
);
        final AccountHttp accountHttp = new AccountHttp("http://localhost:3000");

        // Replace with address
        final String addressRaw = "SB2RPH-EMTFMB-KELX2Y-Q3MZTD-RV7DQG-UZEADV-CYKC";

        final Address address = Address.createFromRawAddress(addressRaw);

        final MultisigAccountInfo multisigAccountInfo = accountHttp.getMultisigAccountInfo(address).toFuture().get();

        System.out.println(multisigAccountInfo);
const accountHttp = new AccountHttp('http://localhost:3000');

// Replace with address
const address = 'SCSGBN-HYJD6P-KJHACX-3R2BI3-QUMMOY-QSNW5J-ICLK';

accountHttp.getMultisigAccountInfo(Address.createFromRawAddress(address)).subscribe(
    accountInfo => console.log(accountInfo),
    err => console.error(err)
);

Note

You could also get the list of the multisig accounts where Alice or Bob are cosignatories using getMultisigAccountInfo method.

What’s next?

Try to modify the account, converting it into a 2-of-2 multisig following modifying a multisig account guide.

Modifying a multisig account

After reviewing this guide, you will learn how to modify an existing multisig account.

Specifically, you are going to convert a 1-of-2 into a 2-of-2 multisig account.

../_images/guides-accounts-multisig-2-of-2.png

2-of-2 multisig account example

Then, you will add a new cosignatory, becoming a 2-of-3.

../_images/guides-accounts-multisig-2-of-3.png

2-of-3 multisig account example

Prerequisites

Let’s get into some code

Alice and Bob are cosignatories of the 1-of-2 multisig account.

One of them, announces a modify multisig account transaction wrapped in an aggregate transaction.

  1. Create a modify multisig account transaction, increasing minAprovalDelta in one unit.
// Replace with the multisig public key
const cosignatoryPrivateKey = process.env.COSIGNATORY_1_PRIVATE_KEY as string;
const multisigAccountPublicKey = '202B3861F34F6141E120742A64BC787D6EBC59C9EFB996F4856AA9CBEE11CD31';

const cosignatoryAccount = Account.createFromPrivateKey(cosignatoryPrivateKey, NetworkType.MIJIN_TEST);
const multisigAccount = PublicAccount.createFromPublicKey(multisigAccountPublicKey, NetworkType.MIJIN_TEST);

const modifyMultisigAccountTransaction = ModifyMultisigAccountTransaction.create(
    Deadline.create(),
    1,
    0,
    [],
    NetworkType.MIJIN_TEST
);
        // Replace with the multisig public key
        final String cosignatoryPrivateKey = "";
        final String multisigAccountPublicKey = "";

        final Account cosignatoryAccount = Account.createFromPrivateKey(cosignatoryPrivateKey, NetworkType.MIJIN_TEST);
        final PublicAccount multisigAccount = PublicAccount.createFromPublicKey(multisigAccountPublicKey, NetworkType.MIJIN_TEST);

        final ModifyMultisigAccountTransaction modifyMultisigAccountTransaction = ModifyMultisigAccountTransaction.create(
                Deadline.create(2, HOURS),
                1,
                0,
                Collections.emptyList(),
                NetworkType.MIJIN_TEST
        );
// Replace with the multisig public key
const cosignatoryPrivateKey = process.env.COSIGNATORY_1_PRIVATE_KEY;
const multisigAccountPublicKey = '202B3861F34F6141E120742A64BC787D6EBC59C9EFB996F4856AA9CBEE11CD31';

const cosignatoryAccount = Account.createFromPrivateKey(cosignatoryPrivateKey, NetworkType.MIJIN_TEST);
const multisigAccount = PublicAccount.createFromPublicKey(multisigAccountPublicKey, NetworkType.MIJIN_TEST);

const modifyMultisigAccountTransaction = ModifyMultisigAccountTransaction.create(
    Deadline.create(),
    1,
    0,
    [],
    NetworkType.MIJIN_TEST
);
  1. Wrap the modify multisig account transaction under an aggregate transaction, attaching multisig public key as the signer.

An aggregate transaction is complete if before announcing it to the network, all required cosignatories have signed it. If valid, it will be included in a block.

const aggregateTransaction = AggregateTransaction.createComplete(
    Deadline.create(),
    [
        modifyMultisigAccountTransaction.toAggregate(multisigAccount),
    ],
    NetworkType.MIJIN_TEST,
    []
);

const signedTransaction = cosignatoryAccount.sign(aggregateTransaction);

const transactionHttp = new TransactionHttp('http://localhost:3000');

// announce signed transaction
transactionHttp.announce(signedTransaction).subscribe(
    x => console.log(x),
    err => console.error(err)
);
        final AggregateTransaction aggregateTransaction = AggregateTransaction.createComplete(
                Deadline.create(2, HOURS),
                Collections.singletonList(modifyMultisigAccountTransaction.toAggregate(multisigAccount)),
                NetworkType.MIJIN_TEST
        );

        final SignedTransaction signedTransaction = cosignatoryAccount.sign(aggregateTransaction);

        final TransactionHttp transactionHttp = new TransactionHttp("http://localhost:3000");

        transactionHttp.announce(signedTransaction).toFuture().get();
const aggregateTransaction = AggregateTransaction.createComplete(
    Deadline.create(),
    [
        modifyMultisigAccountTransaction.toAggregate(multisigAccount),
    ],
    NetworkType.MIJIN_TEST,
    []
);

const signedTransaction = cosignatoryAccount.sign(aggregateTransaction);

const transactionHttp = new TransactionHttp('http://localhost:3000');

// announce signed transaction
transactionHttp.announce(signedTransaction).subscribe(
    x => console.log(x),
    err => console.error(err)
);

Announce the aggregate transaction.

Adding a new cosignatory

Suddenly, Alice and Bob want to add Carol as a cosignatory of the multisig account.

Alice creates a modify multisig account transaction adding in a MultisigCosignatoryModification Carol as a cosignatory.

  1. Create a multisig cosignatory modification:
// Replace with the multisig public key
const cosignatoryPrivateKey = process.env.COSIGNATORY_1_PRIVATE_KEY as string;
const multisigAccountPublicKey = '202B3861F34F6141E120742A64BC787D6EBC59C9EFB996F4856AA9CBEE11CD31';
const newCosignatoryPublicKey = 'CD4EE677BD0642C93910CB93214954A9D70FBAAE1FFF1FF530B1FB52389568F1';

const cosignatoryAccount = Account.createFromPrivateKey(cosignatoryPrivateKey, NetworkType.MIJIN_TEST);
const newCosignatoryAccount = PublicAccount.createFromPublicKey(newCosignatoryPublicKey, NetworkType.MIJIN_TEST);
const multisigAccount = PublicAccount.createFromPublicKey(multisigAccountPublicKey, NetworkType.MIJIN_TEST);

const multisigCosignatoryModification = new MultisigCosignatoryModification(MultisigCosignatoryModificationType.Add,newCosignatoryAccount);
        // Replace with the multisig public key
        final String cosignatoryPrivateKey = "";
        final String multisigAccountPublicKey = "";
        final String newCosignatoryPublicKey = "";

        final Account cosignatoryAccount = Account.createFromPrivateKey(cosignatoryPrivateKey, NetworkType.MIJIN_TEST);
        final PublicAccount newCosignatoryAccount = PublicAccount.createFromPublicKey(newCosignatoryPublicKey, NetworkType.MIJIN_TEST);
        final PublicAccount multisigAccount = PublicAccount.createFromPublicKey(multisigAccountPublicKey, NetworkType.MIJIN_TEST);

        final MultisigCosignatoryModification multisigCosignatoryModification = new MultisigCosignatoryModification(
            MultisigCosignatoryModificationType.ADD,
            newCosignatoryAccount
        );
// Replace with the multisig public key
const cosignatoryPrivateKey = process.env.COSIGNATORY_1_PRIVATE_KEY;
const multisigAccountPublicKey = '202B3861F34F6141E120742A64BC787D6EBC59C9EFB996F4856AA9CBEE11CD31';
const newCosignatoryPublicKey = 'CD4EE677BD0642C93910CB93214954A9D70FBAAE1FFF1FF530B1FB52389568F1';

const cosignatoryAccount = Account.createFromPrivateKey(cosignatoryPrivateKey, NetworkType.MIJIN_TEST);
const newCosignatoryAccount = PublicAccount.createFromPublicKey(newCosignatoryPublicKey, NetworkType.MIJIN_TEST);
const multisigAccount = PublicAccount.createFromPublicKey(multisigAccountPublicKey, NetworkType.MIJIN_TEST);

const multisigCosignatoryModification = new MultisigCosignatoryModification(MultisigCosignatoryModificationType.Add, newCosignatoryAccount);
  1. Create a modify multisig account transaction:
const modifyMultisigAccountTransaction = ModifyMultisigAccountTransaction.create(
    Deadline.create(),
    0,
    0,
    [
        multisigCosignatoryModification
    ],
    NetworkType.MIJIN_TEST
);
        final ModifyMultisigAccountTransaction modifyMultisigAccountTransaction = ModifyMultisigAccountTransaction.create(
            Deadline.create(2, HOURS),
            0,
            0,
            Collections.singletonList(multisigCosignatoryModification),
            NetworkType.MIJIN_TEST
        );
const modifyMultisigAccountTransaction = ModifyMultisigAccountTransaction.create(
    Deadline.create(),
    0,
    0,
    [
        multisigCosignatoryModification
    ],
    NetworkType.MIJIN_TEST
);
  1. Create an aggregate bonded transaction:
const aggregateTransaction = AggregateTransaction.createBonded(
    Deadline.create(),
    [
        modifyMultisigAccountTransaction.toAggregate(multisigAccount),
    ],
    NetworkType.MIJIN_TEST
);

const signedTransaction = cosignatoryAccount.sign(aggregateTransaction);
        final AggregateTransaction aggregateTransaction = AggregateTransaction.createBonded(
            Deadline.create(2, HOURS),
            Collections.singletonList(modifyMultisigAccountTransaction.toAggregate(multisigAccount)),
            NetworkType.MIJIN_TEST
        );

        final SignedTransaction signedTransaction = cosignatoryAccount.sign(aggregateTransaction);
const aggregateTransaction = AggregateTransaction.createBonded(
    Deadline.create(),
    [
        modifyMultisigAccountTransaction.toAggregate(multisigAccount),
    ],
    NetworkType.MIJIN_TEST
);

const signedTransaction = cosignatoryAccount.sign(aggregateTransaction);

Alice needs to lock at least 10 XEM.

Once Bob cosigns the transaction, the amount of XEM becomes available again on Alice’s account.

After lock funds transaction has been confirmed, Alice announces the aggregate transaction.

const lockFundsTransaction = LockFundsTransaction.create(
    Deadline.create(),
    XEM.createRelative(10),
    UInt64.fromUint(480),
    signedTransaction,
    NetworkType.MIJIN_TEST);

const lockFundsTransactionSigned = cosignatoryAccount.sign(lockFundsTransaction);

const transactionHttp = new TransactionHttp('http://localhost:3000');

// announce signed transaction
const listener = new Listener('http://localhost:3000');

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

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

    listener.confirmed(cosignatoryAccount.address)
        .filter((transaction) => transaction.transactionInfo !== undefined
            && transaction.transactionInfo.hash === lockFundsTransactionSigned.hash)
        .subscribe(ignored => {
                transactionHttp.announceAggregateBonded(signedTransaction).subscribe(
                    x => console.log(x),
                    err => console.error(err)
                );
            },
            err => console.error(err));
});
        final LockFundsTransaction lockFundsTransaction = LockFundsTransaction.create(
            Deadline.create(2, HOURS),
            XEM.createRelative(BigInteger.valueOf(10)),
            BigInteger.valueOf(480),
            signedTransaction,
            NetworkType.MIJIN_TEST
        );

        final SignedTransaction lockFundsTransactionSigned = cosignatoryAccount.sign(lockFundsTransaction);

        final TransactionHttp transactionHttp = new TransactionHttp("http://localhost:3000");

        transactionHttp.announce(lockFundsTransactionSigned).toFuture().get();

        // announce signed transaction
        final Listener listener = new Listener("http://localhost:3000");

        listener.open().get();

        final Transaction transaction = listener.confirmed(cosignatoryAccount.getAddress()).toFuture().get();

        transactionHttp.announceAggregateBonded(signedTransaction).toFuture().get();
const lockFundsTransaction = LockFundsTransaction.create(
    Deadline.create(),
    XEM.createRelative(10),
    UInt64.fromUint(480),
    signedTransaction,
    NetworkType.MIJIN_TEST);

const lockFundsTransactionSigned = cosignatoryAccount.sign(lockFundsTransaction);

const transactionHttp = new TransactionHttp('http://localhost:3000');

// announce signed transaction
const listener = new Listener('http://localhost:3000');

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

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

    listener.confirmed(cosignatoryAccount.address)
        .filter((transaction) => transaction.transactionInfo !== undefined
            && transaction.transactionInfo.hash === lockFundsTransactionSigned.hash)
        .subscribe(ignored => {
                transactionHttp.announceAggregateBonded(signedTransaction).subscribe(
                    x => console.log(x),
                    err => console.error(err)
                );
            },
            err => console.error(err));
});

What’s next?

Bob didn’t cosign the transaction yet. Follow signing announced aggregate bonded transactions guide.

Once you have finished this guide, try to delete a cosignatory from the multisig. Notice that multisig accounts can be converted again to regular accounts by removing all cosignatories, just make sure you own the multisig private key!

The following shows how to remove a cosignatory of a 2-of-3 multisig account with minimum removal set to 1.

// Replace with the multisig public key
const multisigAccountPublicKey = '202B3861F34F6141E120742A64BC787D6EBC59C9EFB996F4856AA9CBEE11CD31';
const multisigAccount = PublicAccount.createFromPublicKey(multisigAccountPublicKey, NetworkType.MIJIN_TEST);

// Replace with the cosignatory public key to be deleted
const cosignatoryToRemovePublicKey = 'CD4EE677BD0642C93910CB93214954A9D70FBAAE1FFF1FF530B1FB52389568F1';
const cosignatoryToRemove = PublicAccount.createFromPublicKey(cosignatoryToRemovePublicKey, NetworkType.MIJIN_TEST);

// Replace with the cosignatory private key
const cosignatoryPrivateKey = process.env.COSIGNATORY_1_PRIVATE_KEY as string;
const cosignatoryAccount =  Account.createFromPrivateKey(cosignatoryPrivateKey, NetworkType.MIJIN_TEST);

const multisigCosignatoryModification = new MultisigCosignatoryModification(MultisigCosignatoryModificationType.Remove,cosignatoryToRemove);

const modifyMultisigAccountTransaction = ModifyMultisigAccountTransaction.create(
    Deadline.create(),
    0,
    0,
    [
        multisigCosignatoryModification
    ],
    NetworkType.MIJIN_TEST
);

const aggregateTransaction = AggregateTransaction.createComplete(
    Deadline.create(),
    [
        modifyMultisigAccountTransaction.toAggregate(multisigAccount),
    ],
    NetworkType.MIJIN_TEST,
    []
);

const signedTransaction = cosignatoryAccount.sign(aggregateTransaction);

const transactionHttp = new TransactionHttp('http://localhost:3000');

transactionHttp.announce(signedTransaction).subscribe(
    x => console.log(x),
    err => console.error(err)
);
        // Replace with the multisig public key
        final String multisigAccountPublicKey = "";

        // Replace with the cosignatory private key
        final String cosignatoryPrivateKey = "";

        final Account cosignatoryAccount = Account.createFromPrivateKey(cosignatoryPrivateKey, NetworkType.MIJIN_TEST);
        final PublicAccount multisigPublicAccount = PublicAccount.createFromPublicKey(multisigAccountPublicKey, NetworkType.MIJIN_TEST);

        final MultisigCosignatoryModification multisigCosignatoryModification = new MultisigCosignatoryModification(
            MultisigCosignatoryModificationType.REMOVE,
                PublicAccount.createFromPublicKey("", NetworkType.MIJIN_TEST)
        );

        final ModifyMultisigAccountTransaction modifyMultisigAccountTransaction = ModifyMultisigAccountTransaction.create(
            Deadline.create(2, HOURS),
            0,
            0,
            Collections.singletonList(multisigCosignatoryModification),
            NetworkType.MIJIN_TEST
        );

        final AggregateTransaction aggregateTransaction = AggregateTransaction.createComplete(
            Deadline.create(2, HOURS),
            Collections.singletonList(modifyMultisigAccountTransaction.toAggregate(multisigPublicAccount)),
            NetworkType.MIJIN_TEST
        );

        final SignedTransaction signedTransaction = cosignatoryAccount.sign(aggregateTransaction);

        final TransactionHttp transactionHttp = new TransactionHttp("http://localhost:3000");

        transactionHttp.announce(signedTransaction).toFuture().get();
// Replace with the multisig public key
const multisigAccountPublicKey = '202B3861F34F6141E120742A64BC787D6EBC59C9EFB996F4856AA9CBEE11CD31';
const multisigAccount = PublicAccount.createFromPublicKey(multisigAccountPublicKey, NetworkType.MIJIN_TEST);

// Replace with the cosignatory public key to be deleted
const cosignatoryToRemovePublicKey = 'CD4EE677BD0642C93910CB93214954A9D70FBAAE1FFF1FF530B1FB52389568F1';
const cosignatoryToRemove = PublicAccount.createFromPublicKey(cosignatoryToRemovePublicKey, NetworkType.MIJIN_TEST);

// Replace with the cosignatory private key
const cosignatoryPrivateKey = process.env.COSIGNATORY_1_PRIVATE_KEY;
const cosignatoryAccount =  Account.createFromPrivateKey(cosignatoryPrivateKey, NetworkType.MIJIN_TEST);

const multisigCosignatoryModification = new MultisigCosignatoryModification(MultisigCosignatoryModificationType.Remove,cosignatoryToRemove);

const modifyMultisigAccountTransaction = ModifyMultisigAccountTransaction.create(
    Deadline.create(),
    0,
    0,
    [
        multisigCosignatoryModification
    ],
    NetworkType.MIJIN_TEST
);

const aggregateTransaction = AggregateTransaction.createComplete(
    Deadline.create(),
    [
        modifyMultisigAccountTransaction.toAggregate(multisigAccount),
    ],
    NetworkType.MIJIN_TEST,
    []
);

const signedTransaction = cosignatoryAccount.sign(aggregateTransaction);

const transactionHttp = new TransactionHttp('http://localhost:3000');

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

Creating a multilevel multisig account (MLMA)

Following this tutorial, you will create a multi-level multisig account.

../_images/concepts-multisig-multilevel-11.png

Three-level multisig account example

Background

A multi-level multisig is a multisig that has a cosignatory that is another multisig.

Consider the following constraints:

  • The maximum number of levels is 3.
  • The maximum of no-multisig cosignatories (leaf) is 15.

Prerequisites

Let’s get into some code

  1. Creating multisig account #2
// Create multisig #2 (1-of-2)

// Replace with the private key of the account that you want to convert into multisig
const multisig2PrivateKey = process.env.MULTISIG_2_PRIVATE_KEY as string;

// Replace with cosignatories public keys
const cosignatoryAccount5PublicKey = '7D08373CFFE4154E129E04F0827E5F3D6907587E348757B0F87D2F839BF88246';
const cosignatoryAccount6PublicKey = '28AE57EC0E81967880C483BE99D4B6AF38E5DCD9F8B89D41F2E7619CFDB447C5';

const multisigAccount2 = Account.createFromPrivateKey(multisig2PrivateKey, NetworkType.MIJIN_TEST);

const cosignatory5 = PublicAccount.createFromPublicKey(cosignatoryAccount5PublicKey, NetworkType.MIJIN_TEST);
const cosignatory6 = PublicAccount.createFromPublicKey(cosignatoryAccount6PublicKey, NetworkType.MIJIN_TEST);

const convertMultisigAccount2Transaction = ModifyMultisigAccountTransaction.create(
    Deadline.create(),
    1,
    1,
    [
        new MultisigCosignatoryModification(
            MultisigCosignatoryModificationType.Add,
            cosignatory5,
        ),
        new MultisigCosignatoryModification(
            MultisigCosignatoryModificationType.Add,
            cosignatory6,
        )],
    NetworkType.MIJIN_TEST
);

const transactionHttp = new TransactionHttp('http://localhost:3000');

const signedTransaction2 = multisigAccount2.sign(convertMultisigAccount2Transaction);

transactionHttp.announce(signedTransaction2).subscribe(
    x => console.log(x),
    err => console.error(err)
);
        // Create multisig #2 (1-of-2)

        // Replace with the private key of the account that you want to convert into multisig
        final String multisig2PrivateKey = "";

        // Replace with cosignatories public keys
        final String cosignatory5PublicKey = "";
        final String cosignatory6PublicKey = "";

        final Account multisigAccount2 = Account.createFromPrivateKey(multisig2PrivateKey, NetworkType.MIJIN_TEST);

        final PublicAccount cosignatory5PublicAccount = PublicAccount.createFromPublicKey(cosignatory5PublicKey, NetworkType.MIJIN_TEST);
        final PublicAccount cosignatory6PublicAccount = PublicAccount.createFromPublicKey(cosignatory6PublicKey, NetworkType.MIJIN_TEST);

        final ModifyMultisigAccountTransaction convertMultisigAccount2Transaction = ModifyMultisigAccountTransaction.create(
                Deadline.create(2, HOURS),
                1,
                1,
                Arrays.asList(
                    new MultisigCosignatoryModification(
                        MultisigCosignatoryModificationType.ADD,
                        cosignatory5PublicAccount
                    ),
                    new MultisigCosignatoryModification(
                        MultisigCosignatoryModificationType.ADD,
                        cosignatory6PublicAccount
                    )
                ),
                NetworkType.MIJIN_TEST
        );


        final SignedTransaction signedTransaction2 = multisigAccount2.sign(convertMultisigAccount2Transaction);

        transactionHttp.announce(signedTransaction2).toFuture().get();
// Create multisig #2 (1-of-2)

// Replace with the private key of the account that you want to convert into multisig
const multisig2PrivateKey = process.env.MULTISIG_2_PRIVATE_KEY;

// Replace with cosignatories public keys
const cosignatoryAccount5PublicKey = '7D08373CFFE4154E129E04F0827E5F3D6907587E348757B0F87D2F839BF88246';
const cosignatoryAccount6PublicKey = '28AE57EC0E81967880C483BE99D4B6AF38E5DCD9F8B89D41F2E7619CFDB447C5';

const multisigAccount2 = Account.createFromPrivateKey(multisig2PrivateKey, NetworkType.MIJIN_TEST);

const cosignatory5 = PublicAccount.createFromPublicKey(cosignatoryAccount5PublicKey, NetworkType.MIJIN_TEST);
const cosignatory6 = PublicAccount.createFromPublicKey(cosignatoryAccount6PublicKey, NetworkType.MIJIN_TEST);

const convertMultisigAccount2Transaction = ModifyMultisigAccountTransaction.create(
    Deadline.create(),
    1,
    1,
    [
        new MultisigCosignatoryModification(
            MultisigCosignatoryModificationType.Add,
            cosignatory5,
        ),
        new MultisigCosignatoryModification(
            MultisigCosignatoryModificationType.Add,
            cosignatory6,
        )],
    NetworkType.MIJIN_TEST
);

const transactionHttp = new TransactionHttp('http://localhost:3000');

const signedTransaction2 = multisigAccount2.sign(convertMultisigAccount2Transaction);

transactionHttp.announce(signedTransaction2).subscribe(
    x => console.log(x),
    err => console.error(err)
);
  1. Creating multisig account #3
// Create multisig #3 (2-of-3)

// Replace with the private key of the account that you want to convert into multisig
const multisig3PrivateKey = process.env.MULTISIG_3_PRIVATE_KEY as string;

// Replace with cosignatories public keys
const cosignatoryAccount7PublicKey = 'DAD5B5B7F7AE4ACEAB3F6A5FE05EA3186208D219A04B6C047C39A2B0EFF49511';
const cosignatoryAccount8PublicKey = 'E29302E0AF530292EABEDADF2DE2953BBFBB0BDD9A1F51FA0C857E87828BABA9';
const cosignatoryAccount4PublicKey = '473233D6B89671DCA4D334CF1059C31356CBF18120E484E33EEA9BDC09EEA515';

const multisigAccount3 = Account.createFromPrivateKey(multisig3PrivateKey, NetworkType.MIJIN_TEST);

const cosignatory7 = PublicAccount.createFromPublicKey(cosignatoryAccount7PublicKey, NetworkType.MIJIN_TEST);
const cosignatory8 = PublicAccount.createFromPublicKey(cosignatoryAccount8PublicKey, NetworkType.MIJIN_TEST);
const cosignatory4 = PublicAccount.createFromPublicKey(cosignatoryAccount4PublicKey, NetworkType.MIJIN_TEST);

const convertMultisigAccount3Transaction = ModifyMultisigAccountTransaction.create(
    Deadline.create(),
    2,
    1,
    [
        new MultisigCosignatoryModification(
            MultisigCosignatoryModificationType.Add,
            cosignatory7,
        ),
        new MultisigCosignatoryModification(
            MultisigCosignatoryModificationType.Add,
            cosignatory8,
        ),
        new MultisigCosignatoryModification(
            MultisigCosignatoryModificationType.Add,
            cosignatory4,
        )],
    NetworkType.MIJIN_TEST
);

const signedTransaction3 = multisigAccount3.sign(convertMultisigAccount3Transaction);

transactionHttp.announce(signedTransaction3).subscribe(
    x => console.log(x),
    err => console.error(err)
);
        // Replace with the private key of the account that you want to convert into multisig
        final String multisig3PrivateKey = "";

        // Replace with cosignatories public keys
        final String cosignatory7PublicKey = "";
        final String cosignatory8PublicKey = "";
        final String cosignatory4PublicKey = "";

        final Account multisigAccount3 = Account.createFromPrivateKey(multisig3PrivateKey, NetworkType.MIJIN_TEST);

        final PublicAccount cosignatory7PublicAccount = PublicAccount.createFromPublicKey(cosignatory7PublicKey, NetworkType.MIJIN_TEST);
        final PublicAccount cosignatory8PublicAccount = PublicAccount.createFromPublicKey(cosignatory8PublicKey, NetworkType.MIJIN_TEST);
        final PublicAccount cosignatory4PublicAccount = PublicAccount.createFromPublicKey(cosignatory4PublicKey, NetworkType.MIJIN_TEST);

        final ModifyMultisigAccountTransaction convertMultisigAccount3Transaction = ModifyMultisigAccountTransaction.create(
                Deadline.create(2, HOURS),
                2,
                1,
                Arrays.asList(
                        new MultisigCosignatoryModification(
                                MultisigCosignatoryModificationType.ADD,
                                cosignatory7PublicAccount
                                ),
                        new MultisigCosignatoryModification(
                                MultisigCosignatoryModificationType.ADD,
                                cosignatory8PublicAccount
                                ),
                        new MultisigCosignatoryModification(
                                MultisigCosignatoryModificationType.ADD,
                                cosignatory4PublicAccount
                                )
                ),
                NetworkType.MIJIN_TEST
        );

        final SignedTransaction signedTransaction3 = multisigAccount3.sign(convertMultisigAccount3Transaction);

        transactionHttp.announce(signedTransaction3).toFuture().get();
// Create multisig #3 (2-of-3)

// Replace with the private key of the account that you want to convert into multisig
const multisig3PrivateKey = process.env.MULTISIG_3_PRIVATE_KEY;

// Replace with cosignatories public keys
const cosignatoryAccount7PublicKey = 'DAD5B5B7F7AE4ACEAB3F6A5FE05EA3186208D219A04B6C047C39A2B0EFF49511';
const cosignatoryAccount8PublicKey = 'E29302E0AF530292EABEDADF2DE2953BBFBB0BDD9A1F51FA0C857E87828BABA9';
const cosignatoryAccount4PublicKey = '473233D6B89671DCA4D334CF1059C31356CBF18120E484E33EEA9BDC09EEA515';

const multisigAccount3 = Account.createFromPrivateKey(multisig3PrivateKey, NetworkType.MIJIN_TEST);

const cosignatory7 = PublicAccount.createFromPublicKey(cosignatoryAccount7PublicKey, NetworkType.MIJIN_TEST);
const cosignatory8 = PublicAccount.createFromPublicKey(cosignatoryAccount8PublicKey, NetworkType.MIJIN_TEST);
const cosignatory4 = PublicAccount.createFromPublicKey(cosignatoryAccount4PublicKey, NetworkType.MIJIN_TEST);

const convertMultisigAccount3Transaction = ModifyMultisigAccountTransaction.create(
    Deadline.create(),
    2,
    1,
    [
        new MultisigCosignatoryModification(
            MultisigCosignatoryModificationType.Add,
            cosignatory7,
        ),
        new MultisigCosignatoryModification(
            MultisigCosignatoryModificationType.Add,
            cosignatory8,
        ),
        new MultisigCosignatoryModification(
            MultisigCosignatoryModificationType.Add,
            cosignatory4,
        )],
    NetworkType.MIJIN_TEST
);

const signedTransaction3 = multisigAccount3.sign(convertMultisigAccount3Transaction);

transactionHttp.announce(signedTransaction3).subscribe(
    x => console.log(x),
    err => console.error(err)
);
  1. Creating multisig account #1
// Create multisig #1 (3-of-3)

// Replace with the private key of the account that you want to convert into multisig
const multisig1PrivateKey = process.env.MULTISIG_1_PRIVATE_KEY as string;

const multisigAccount1 = Account.createFromPrivateKey(multisig1PrivateKey, NetworkType.MIJIN_TEST);

const convertMultisigAccount1Transaction = ModifyMultisigAccountTransaction.create(
    Deadline.create(),
    3,
    1,
    [
        new MultisigCosignatoryModification(
            MultisigCosignatoryModificationType.Add,
            multisigAccount2.publicAccount,
        ),
        new MultisigCosignatoryModification(
            MultisigCosignatoryModificationType.Add,
            multisigAccount3.publicAccount,
        ),
        new MultisigCosignatoryModification(
            MultisigCosignatoryModificationType.Add,
            cosignatory4,
        )],
    NetworkType.MIJIN_TEST
);


const signedTransaction1 = multisigAccount1.sign(convertMultisigAccount1Transaction);

transactionHttp.announce(signedTransaction1).subscribe(
    x => console.log(x),
    err => console.error(err)
);
        // Replace with the private key of the account that you want to convert into multisig
        final String multisig1PrivateKey = "";

        final Account multisigAccount1 = Account.createFromPrivateKey(multisig1PrivateKey, NetworkType.MIJIN_TEST);

        final ModifyMultisigAccountTransaction convertMultisigAccount1Transaction = ModifyMultisigAccountTransaction.create(
                Deadline.create(2, HOURS),
                3,
                1,
                Arrays.asList(
                        new MultisigCosignatoryModification(
                                MultisigCosignatoryModificationType.ADD,
                                multisigAccount2.getPublicAccount()
                        ),
                        new MultisigCosignatoryModification(
                                MultisigCosignatoryModificationType.ADD,
                                multisigAccount3.getPublicAccount()
                        ),
                        new MultisigCosignatoryModification(
                                MultisigCosignatoryModificationType.ADD,
                                cosignatory4PublicAccount
                        )
                ),
                NetworkType.MIJIN_TEST
        );

        final SignedTransaction signedTransaction1 = multisigAccount1.sign(convertMultisigAccount1Transaction);

        transactionHttp.announce(signedTransaction1).toFuture().get();
// Create multisig #1 (3-of-3)

// Replace with the private key of the account that you want to convert into multisig
const multisig1PrivateKey = process.env.MULTISIG_1_PRIVATE_KEY;

const multisigAccount1 = Account.createFromPrivateKey(multisig1PrivateKey, NetworkType.MIJIN_TEST);

const convertMultisigAccount1Transaction = ModifyMultisigAccountTransaction.create(
    Deadline.create(),
    3,
    1,
    [
        new MultisigCosignatoryModification(
            MultisigCosignatoryModificationType.Add,
            multisigAccount2.publicAccount,
        ),
        new MultisigCosignatoryModification(
            MultisigCosignatoryModificationType.Add,
            multisigAccount3.publicAccount,
        ),
        new MultisigCosignatoryModification(
            MultisigCosignatoryModificationType.Add,
            cosignatory4,
        )],
    NetworkType.MIJIN_TEST
);


const signedTransaction1 = multisigAccount1.sign(convertMultisigAccount1Transaction);

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

What’s next?

Who should cosign the transaction if Account #5 initiates an aggregate bonded transaction? Notice that multisig accounts are not capable of cosigning transactions, being responsible for doing so cosignatories.

../_images/concepts-multisig-multilevel-21.png

Sending an aggregate bonded transaction from a MLMA