This guide will show you how to connect multiple multisig accounts to achieve advanced on-chain authorization logic.
Multisig accounts can have as cosignatories other multisig accounts and add “AND/OR” logic to multi-signature transactions.
In this guide, we are going to create a complex 3-level multisig account.
For example, if the account #5 initiates an AggregateBondedTransaction involving the account #1, the accounts #7 or #8 and #4 should cosign the transaction in order to be included in a block.
Complete converting an account to multisig guide.
Load the root multisig account with enough bitxor
to pay for the transaction fee.
Define the multisig account #2.
// replace with network type
const networkType = NetworkType.TEST_NET;
// replace with private key
const multisig2PrivateKey =
'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF';
const multisigAccount2 = Account.createFromPrivateKey(
multisig2PrivateKey,
networkType,
);
// replace with public key
const cosignatoryAccount5PublicKey =
'17E42BDF5B7FF5001DC96A262A1141FFBE3F09A3A45DE7C095AAEA14F45C0DA0';
const cosignatory5 = PublicAccount.createFromPublicKey(
cosignatoryAccount5PublicKey,
networkType,
);
// replace with public key
const cosignatoryAccount6PublicKey =
'D04AB232742BB4AB3A1368BD4615E4E6D0224AB71A016BAF8520A332C9778737';
const cosignatory6 = PublicAccount.createFromPublicKey(
cosignatoryAccount6PublicKey,
networkType,
);
const convertMultisigAccount2Transaction = MultisigAccountModificationTransaction.create(
Deadline.create(epochAdjustment),
1,
1,
[cosignatory5.address, cosignatory6.address],
[],
networkType,
);
// replace with network type
const networkType = bitxor_sdk_1.NetworkType.TEST_NET;
// replace with private key
const multisig2PrivateKey =
'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF';
const multisigAccount2 = bitxor_sdk_1.Account.createFromPrivateKey(
multisig2PrivateKey,
networkType,
);
// replace with public key
const cosignatoryAccount5PublicKey =
'17E42BDF5B7FF5001DC96A262A1141FFBE3F09A3A45DE7C095AAEA14F45C0DA0';
const cosignatory5 = bitxor_sdk_1.PublicAccount.createFromPublicKey(
cosignatoryAccount5PublicKey,
networkType,
);
// replace with public key
const cosignatoryAccount6PublicKey =
'D04AB232742BB4AB3A1368BD4615E4E6D0224AB71A016BAF8520A332C9778737';
const cosignatory6 = bitxor_sdk_1.PublicAccount.createFromPublicKey(
cosignatoryAccount6PublicKey,
networkType,
);
const convertMultisigAccount2Transaction = bitxor_sdk_1.MultisigAccountModificationTransaction.create(
bitxor_sdk_1.Deadline.create(epochAdjustment),
1,
1, [cosignatory5.address, cosignatory6.address], [],
networkType,
);
Define the multisig account #3.
// replace with private key
const multisig3PrivateKey =
'1111111111111111111111111111111111111111111111111111111111111111';
const multisigAccount3 = Account.createFromPrivateKey(
multisig3PrivateKey,
networkType,
);
// replace with public key
const cosignatoryAccount7PublicKey =
'38C22255DE39952C5D18803EC305A888D5DDE2C59BF3D4EFFAE6FC5FFCBF4F5D';
const cosignatory7 = PublicAccount.createFromPublicKey(
cosignatoryAccount7PublicKey,
networkType,
);
// replace with public key
const cosignatoryAccount8PublicKey =
'9F784BF20318AE3CA6246C0EC2207FE095FFF7A84B6787E7E3C2CE4C3B92A2EA';
const cosignatory8 = PublicAccount.createFromPublicKey(
cosignatoryAccount8PublicKey,
networkType,
);
// replace with public key
const cosignatoryAccount4PublicKey =
'EB2B065D27C6A6FB322F2E568E1AAD9CD6C0F155675E2837058D4811F5C0247D';
const cosignatory4 = PublicAccount.createFromPublicKey(
cosignatoryAccount4PublicKey,
networkType,
);
const convertMultisigAccount3Transaction = MultisigAccountModificationTransaction.create(
Deadline.create(epochAdjustment),
2,
1,
[cosignatory7.address, cosignatory8.address, cosignatory4.address],
[],
networkType,
);
// replace with private key
const multisig3PrivateKey =
'1111111111111111111111111111111111111111111111111111111111111111';
const multisigAccount3 = bitxor_sdk_1.Account.createFromPrivateKey(
multisig3PrivateKey,
networkType,
);
// replace with public key
const cosignatoryAccount7PublicKey =
'38C22255DE39952C5D18803EC305A888D5DDE2C59BF3D4EFFAE6FC5FFCBF4F5D';
const cosignatory7 = bitxor_sdk_1.PublicAccount.createFromPublicKey(
cosignatoryAccount7PublicKey,
networkType,
);
// replace with public key
const cosignatoryAccount8PublicKey =
'9F784BF20318AE3CA6246C0EC2207FE095FFF7A84B6787E7E3C2CE4C3B92A2EA';
const cosignatory8 = bitxor_sdk_1.PublicAccount.createFromPublicKey(
cosignatoryAccount8PublicKey,
networkType,
);
// replace with public key
const cosignatoryAccount4PublicKey =
'EB2B065D27C6A6FB322F2E568E1AAD9CD6C0F155675E2837058D4811F5C0247D';
const cosignatory4 = bitxor_sdk_1.PublicAccount.createFromPublicKey(
cosignatoryAccount4PublicKey,
networkType,
);
const convertMultisigAccount3Transaction = bitxor_sdk_1.MultisigAccountModificationTransaction.create(
bitxor_sdk_1.Deadline.create(epochAdjustment),
2,
1, [cosignatory7.address, cosignatory8.address, cosignatory4.address], [],
networkType,
);
Define the multisig account #1.
// replace with private key
const multisig1PrivateKey =
'0000000000000000000000000000000000000000000000000000000000000000';
const multisigAccount1 = Account.createFromPrivateKey(
multisig1PrivateKey,
networkType,
);
const convertMultisigAccount1Transaction = MultisigAccountModificationTransaction.create(
Deadline.create(epochAdjustment),
3,
1,
[
multisigAccount2.publicAccount.address,
multisigAccount3.publicAccount.address,
cosignatory4.address,
],
[],
networkType,
);
// replace with private key
const multisig1PrivateKey =
'0000000000000000000000000000000000000000000000000000000000000000';
const multisigAccount1 = bitxor_sdk_1.Account.createFromPrivateKey(
multisig1PrivateKey,
networkType,
);
const convertMultisigAccount1Transaction = bitxor_sdk_1.MultisigAccountModificationTransaction.create(
bitxor_sdk_1.Deadline.create(epochAdjustment),
3,
1, [
multisigAccount2.publicAccount.address,
multisigAccount3.publicAccount.address,
cosignatory4.address,
], [],
networkType,
);
4. Announce the transactions together using an AggregateBondedTransaction.
The account #1 must lock 10
bitxor
to announce the transaction.
const aggregateTransaction = AggregateTransaction.createBonded(
Deadline.create(epochAdjustment),
[
convertMultisigAccount2Transaction.toAggregate(
multisigAccount2.publicAccount,
),
convertMultisigAccount3Transaction.toAggregate(
multisigAccount3.publicAccount,
),
convertMultisigAccount1Transaction.toAggregate(
multisigAccount1.publicAccount,
),
],
networkType,
[],
UInt64.fromUint(2000000),
);
// replace with meta.networkGenerationHash (nodeUrl + '/node/info')
const networkGenerationHash =
'1DFB2FAA9E7F054168B0C5FCB84F4DEB62CC2B4D317D861F3168D161F54EA78B';
const signedTransaction = multisigAccount1.sign(
aggregateTransaction,
networkGenerationHash,
);
console.log(signedTransaction.hash);
// replace with bitxor id
const networkCurrencyTokenId = new TokenId('5E62990DCAC5BE8A');
// replace with network currency divisibility
const networkCurrencyDivisibility = 6;
const hashLockTransaction = HashLockTransaction.create(
Deadline.create(epochAdjustment),
new Token(
networkCurrencyTokenId,
UInt64.fromUint(10 * Math.pow(10, networkCurrencyDivisibility)),
),
UInt64.fromUint(480),
signedTransaction,
networkType,
UInt64.fromUint(2000000),
);
const signedHashLockTransaction = multisigAccount1.sign(
hashLockTransaction,
networkGenerationHash,
);
// replace with node endpoint
const nodeUrl = 'NODE_URL';
const repositoryFactory = new RepositoryFactoryHttp(nodeUrl);
const listener = repositoryFactory.createListener();
const receiptHttp = repositoryFactory.createReceiptRepository();
const transactionHttp = repositoryFactory.createTransactionRepository();
const transactionService = new TransactionService(transactionHttp, receiptHttp);
listener.open().then(() => {
transactionService
.announceHashLockAggregateBonded(
signedHashLockTransaction,
signedTransaction,
listener,
)
.subscribe(
(x) => console.log(x),
(err) => console.log(err),
() => listener.close(),
);
});
const aggregateTransaction = bitxor_sdk_1.AggregateTransaction.createBonded(
bitxor_sdk_1.Deadline.create(epochAdjustment), [
convertMultisigAccount2Transaction.toAggregate(
multisigAccount2.publicAccount,
),
convertMultisigAccount3Transaction.toAggregate(
multisigAccount3.publicAccount,
),
convertMultisigAccount1Transaction.toAggregate(
multisigAccount1.publicAccount,
),
],
networkType, [],
bitxor_sdk_1.UInt64.fromUint(2000000),
);
// replace with meta.networkGenerationHash (nodeUrl + '/node/info')
const networkGenerationHash =
'1DFB2FAA9E7F054168B0C5FCB84F4DEB62CC2B4D317D861F3168D161F54EA78B';
const signedTransaction = multisigAccount1.sign(
aggregateTransaction,
networkGenerationHash,
);
console.log(signedTransaction.hash);
// replace with bitxor id
const networkCurrencyTokenId = new bitxor_sdk_1.TokenId('5E62990DCAC5BE8A');
// replace with network currency divisibility
const networkCurrencyDivisibility = 6;
const hashLockTransaction = bitxor_sdk_1.HashLockTransaction.create(
bitxor_sdk_1.Deadline.create(epochAdjustment),
new bitxor_sdk_1.Token(
networkCurrencyTokenId,
bitxor_sdk_1.UInt64.fromUint(
10 * Math.pow(10, networkCurrencyDivisibility),
),
),
bitxor_sdk_1.UInt64.fromUint(480),
signedTransaction,
networkType,
bitxor_sdk_1.UInt64.fromUint(2000000),
);
const signedHashLockTransaction = multisigAccount1.sign(
hashLockTransaction,
networkGenerationHash,
);
// replace with node endpoint
const nodeUrl = 'NODE_URL';
const repositoryFactory = new bitxor_sdk_1.RepositoryFactoryHttp(nodeUrl);
const listener = repositoryFactory.createListener();
const receiptHttp = repositoryFactory.createReceiptRepository();
const transactionHttp = repositoryFactory.createTransactionRepository();
const transactionService = new bitxor_sdk_1.TransactionService(
transactionHttp,
receiptHttp,
);
listener.open().then(() => {
transactionService
.announceHashLockAggregateBonded(
signedHashLockTransaction,
signedTransaction,
listener,
)
.subscribe(
(x) => console.log(x),
(err) => console.log(err),
() => listener.close(),
);
});
5. The potential cosignatories must opt-in to become cosignatories. Cosign the announced AggregateTransaction with the accounts #5, #6, #7, #8, and #4.
bitxor-cli transaction cosign --hash A6A374E66B32A3D5133018EFA9CD6E3169C8EEA339F7CCBE29C47D07086E068C --profile <account>