This guide will show you how to setup a joint account.
Imagine that Alice and Bob have separate accounts, but they also want to have a shared account to buy groceries. If one of them goes out shopping, they should be able to transact with using their joint account without requiring explicit authorization from the other person.
This shared account appears in Symbol as 1-of-2 multisig. Creating a Multisig account permits Alice and Bob to share funds in a separate account. Since the account is configured as a 1-of-2, it’s only required the signature from one of them to issue transactions from the new account.
symbol.xym
to pay for transaction fees.Note
To create create new accounts, follow this guide.
3. Click on “Add a cosignatory” and provide the address or public key of the account you want to add as a cosignatory of the multisig. Click on the “+” button. .. note:: Note: make sure potential cosignatory accounts have previously sent transactions on the network.
1. First, define the accounts that will become cosignatories of the multisig account. Following our example, these are Alice and Bob addresses. Then, open the account that will be converted into multisig using its private key.
// replace with network type
const networkType = NetworkType.TEST_NET;
// replace with candidate multisig private key
const privateKey = 'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF';
const account = Account.createFromPrivateKey(privateKey, networkType);
// replace with cosignatory 1 public key
const cosignatory1PublicKey = 'D04AB232742BB4AB3A1368BD4615E4E6D0224AB71A016BAF8520A332C9778737';
const cosignatory1 = PublicAccount.createFromPublicKey(cosignatory1PublicKey, networkType);
// replace with cosignatory 2 public key
const cosignatory2PublicKey = '462EE976890916E54FA825D26BDD0235F5EB5B6A143C199AB0AE5EE9328E08CE';
const cosignatory2 = PublicAccount.createFromPublicKey(cosignatory2PublicKey, networkType);
// replace with network type
const networkType = symbol_sdk_1.NetworkType.TEST_NET;
// replace with candidate multisig private key
const privateKey = 'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF';
const account = symbol_sdk_1.Account.createFromPrivateKey(privateKey, networkType);
// replace with cosignatory 1 public key
const cosignatory1PublicKey = 'D04AB232742BB4AB3A1368BD4615E4E6D0224AB71A016BAF8520A332C9778737';
const cosignatory1 = symbol_sdk_1.PublicAccount.createFromPublicKey(cosignatory1PublicKey, networkType);
// replace with cosignatory 2 public key
const cosignatory2PublicKey = '462EE976890916E54FA825D26BDD0235F5EB5B6A143C199AB0AE5EE9328E08CE';
const cosignatory2 = symbol_sdk_1.PublicAccount.createFromPublicKey(cosignatory2PublicKey, networkType);
2. Create a MultisigAccountModificationTransaction to convert the shared account into a multisig account.
Since we want to create a 1-of-2 multisig account, let’s set the minimum required signatures to 1
.
const multisigAccountModificationTransaction = MultisigAccountModificationTransaction.create(
Deadline.create(),
1,
1,
[cosignatory1.address, cosignatory2.address],
[],
networkType);
const multisigAccountModificationTransaction = symbol_sdk_1.MultisigAccountModificationTransaction.create(symbol_sdk_1.Deadline.create(), 1, 1, [cosignatory1.address, cosignatory2.address], [], networkType);
3. Create an AggregateBondedTransaction, wrapping the MultisigAccountModificationTransaction defined in the previous step. 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,
[],
UInt64.fromUint(2000000));
const aggregateTransaction = symbol_sdk_1.AggregateTransaction.createBonded(symbol_sdk_1.Deadline.create(), [multisigAccountModificationTransaction.toAggregate(account.publicAccount)], networkType, [], symbol_sdk_1.UInt64.fromUint(2000000));
Note
To make the transaction only valid for your network, you will need to pass the first network generation hash. Open nodeUrl + '/node/info'
in a new browser tab and copy the meta.networkGenerationHash
value.
// replace with meta.networkGenerationHash (nodeUrl + '/node/info')
const networkGenerationHash = '1DFB2FAA9E7F054168B0C5FCB84F4DEB62CC2B4D317D861F3168D161F54EA78B';
const signedTransaction = account.sign(aggregateTransaction, networkGenerationHash);
console.log(signedTransaction.hash);
// replace with meta.networkGenerationHash (nodeUrl + '/node/info')
const networkGenerationHash = '1DFB2FAA9E7F054168B0C5FCB84F4DEB62CC2B4D317D861F3168D161F54EA78B';
const signedTransaction = account.sign(aggregateTransaction, networkGenerationHash);
console.log(signedTransaction.hash);
5. Before sending an AggregateBondedTransaction, the future multisig account needs to lock at least 10
symbol.xym
.
This transaction is required to prevent spamming the network.
After the HashLockTransaction has been confirmed, announce the AggregateTransaction signed in (4).
// replace with symbol.xym id
const networkCurrencyMosaicId = new MosaicId('5E62990DCAC5BE8A');
// replace with network currency divisibility
const networkCurrencyDivisibility = 6;
const hashLockTransaction = HashLockTransaction.create(
Deadline.create(),
new Mosaic(networkCurrencyMosaicId,
UInt64.fromUint(10 * Math.pow(10, networkCurrencyDivisibility))),
UInt64.fromUint(480),
signedTransaction,
networkType,
UInt64.fromUint(2000000));
const signedHashLockTransaction = account.sign(hashLockTransaction, networkGenerationHash);
// replace with node endpoint
const nodeUrl = 'http://api-01.us-east-1.096x.symboldev.network:3000';
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());
});
// replace with symbol.xym id
const networkCurrencyMosaicId = new symbol_sdk_1.MosaicId('5E62990DCAC5BE8A');
// replace with network currency divisibility
const networkCurrencyDivisibility = 6;
const hashLockTransaction = symbol_sdk_1.HashLockTransaction.create(symbol_sdk_1.Deadline.create(), new symbol_sdk_1.Mosaic(networkCurrencyMosaicId, symbol_sdk_1.UInt64.fromUint(10 * Math.pow(10, networkCurrencyDivisibility))), symbol_sdk_1.UInt64.fromUint(480), signedTransaction, networkType, symbol_sdk_1.UInt64.fromUint(2000000));
const signedHashLockTransaction = account.sign(hashLockTransaction, networkGenerationHash);
// replace with node endpoint
const nodeUrl = 'http://api-01.us-east-1.096x.symboldev.network:3000';
const repositoryFactory = new symbol_sdk_1.RepositoryFactoryHttp(nodeUrl);
const listener = repositoryFactory.createListener();
const receiptHttp = repositoryFactory.createReceiptRepository();
const transactionHttp = repositoryFactory.createTransactionRepository();
const transactionService = new symbol_sdk_1.TransactionService(transactionHttp, receiptHttp);
listener.open().then(() => {
transactionService
.announceHashLockAggregateBonded(signedHashLockTransaction, signedTransaction, listener)
.subscribe((x) => console.log(x), (err) => console.log(err), () => listener.close());
});
6. Cosign the AggregateTransaction with the CLI using Alice’s account.
Replace the hash A6A374E66B32A3D5133018EFA9CD6E3169C8EEA339F7CCBE29C47D07086E068C
with the AggregateTransaction hash signed in (4).
symbol-cli transaction cosign --hash A6A374E66B32A3D5133018EFA9CD6E3169C8EEA339F7CCBE29C47D07086E068C --profile alice
symbol-cli transaction cosign --hash A6A374E66B32A3D5133018EFA9CD6E3169C8EEA339F7CCBE29C47D07086E068C --profile bob
8. If everything goes well, the account is now set as multisig, being Alice and Bob accounts their cosignatories.
You can get the list of the multisig accounts where Alice or Bob are cosignatories with the function MultisigHttp.getMultisigAccountInfo()
.
// replace with multisig address
const rawAddress = 'TAEG6L-KWXRA7-PSWUEE-ILQPG4-3V5CYZ-S5652T-JTUU';
const address = Address.createFromRawAddress(rawAddress);
// replace with node endpoint
const nodeUrl = 'http://api-01.us-east-1.096x.symboldev.network:3000';
const repositoryFactory = new RepositoryFactoryHttp(nodeUrl);
const multisigHttp = repositoryFactory.createMultisigRepository();
multisigHttp
.getMultisigAccountInfo(address)
.subscribe((multisigInfo) => console.log(multisigInfo), (err) => console.error(err));
// replace with multisig address
const rawAddress = 'TAEG6L-KWXRA7-PSWUEE-ILQPG4-3V5CYZ-S5652T-JTUU';
const address = symbol_sdk_1.Address.createFromRawAddress(rawAddress);
// replace with node endpoint
const nodeUrl = 'http://api-01.us-east-1.096x.symboldev.network:3000';
const repositoryFactory = new symbol_sdk_1.RepositoryFactoryHttp(nodeUrl);
const multisigHttp = repositoryFactory.createMultisigRepository();
multisigHttp
.getMultisigAccountInfo(address)
.subscribe((multisigInfo) => console.log(multisigInfo), (err) => console.error(err));
// replace with node endpoint
try (final RepositoryFactory repositoryFactory = new RepositoryFactoryVertxImpl(
"http://api-01.us-east-1.096x.symboldev.network:3000")) {
final MultisigRepository multisigRepository = repositoryFactory
.createMultisigRepository();
// replace with multisig address
final String rawAddress = "TAEG6L-KWXRA7-PSWUEE-ILQPG4-3V5CYZ-S5652T-JTUU";
final Address address = Address.createFromRawAddress(rawAddress);
final MultisigAccountInfo multisigAccountInfo = multisigRepository
.getMultisigAccountInfo(address).toFuture().get();
final JsonHelper helper = new JsonHelperJackson2();
System.out.println(helper.prettyPrint(multisigAccountInfo));
Follow the next guide to modify the number of required signatures.
Did you find what you were looking for? Give us your feedback.