Skip to content

Modify multisig account transaction

Chun Lam edited this page Feb 21, 2024 · 4 revisions
import { Deadline, Account, NetworkType, ModifyMultisigAccountTransaction, 
    MultisigCosignatoryModification, MultisigCosignatoryModificationType, 
    TransferTransaction, PlainMessage, CosignatureTransaction, AggregateTransaction, 
    HashLockTransaction, Mosaic, UInt64, MosaicId, NamespaceId 
} from 'tsjs-xpx-chain-sdk';
import { Network } from '../utils';

const network = new Network('http://localhost:3000');
const NETWORK_TYPE = NetworkType.MIJIN_TEST;

const seeding = Account.createFromPrivateKey(
    '28FCECEA252231D2C86E1BCF7DD541552BDBBEFBB09324758B3AC199B4AA7B78',
    NetworkType.MIJIN_TEST, 1);

const toBecomeMultisig = Account.createFromPrivateKey(
    '0123456789012345678901234567890123456789012345678901234567890123',
    NetworkType.MIJIN_TEST, 1);
const cosignatory1 = Account.createFromPrivateKey(
    '1123456789012345678901234567890123456789012345678901234567890123',
    NetworkType.MIJIN_TEST, 1);
const cosignatory2 = Account.createFromPrivateKey(
    '2123456789012345678901234567890123456789012345678901234567890123',
    NetworkType.MIJIN_TEST, 1);
const cosignatory3 = Account.createFromPrivateKey(
    '3123456789012345678901234567890123456789012345678901234567890123',
    NetworkType.MIJIN_TEST, 1);

const hundredXpx = new Mosaic(new NamespaceId("prx.xpx"), UInt64.fromUint(100 * 1000000));

network.networkProperties.then(networkProperties => {
    const modifyMultisigAccountTx = ModifyMultisigAccountTransaction.create(
        Deadline.create(),
        2,
        2,
        [
            new MultisigCosignatoryModification(MultisigCosignatoryModificationType.Add, cosignatory1.publicAccount),
            new MultisigCosignatoryModification(MultisigCosignatoryModificationType.Add, cosignatory2.publicAccount),
            new MultisigCosignatoryModification(MultisigCosignatoryModificationType.Add, cosignatory3.publicAccount),
        ],
        NetworkType.MIJIN_TEST
    )

    const aggregateBonded = AggregateTransaction.createBondedV1(
        Deadline.create(),
        [modifyMultisigAccountTx.toAggregateV1(toBecomeMultisig.publicAccount)],
        NETWORK_TYPE);
    const signedAggregateBonded = toBecomeMultisig.preV2Sign(aggregateBonded, networkProperties.generationHash);

    const hashLock = HashLockTransaction.create(
        Deadline.create(),
        new Mosaic(new NamespaceId("prx.xpx"), UInt64.fromUint(10 * 1000000)),
        UInt64.fromUint(50),
        signedAggregateBonded,
        NETWORK_TYPE
    )
    const signedHashLock = toBecomeMultisig.preV2Sign(hashLock, networkProperties.generationHash);

    // make sure all soon-to-be cosignatories have public keys and some xpx, so they can be added as cosignatories
    network.api.listener.open().then(() => {
        Promise.all([
            network.announceAndWaitForConfirmation(
                seeding.preV2Sign(
                    TransferTransaction.create(Deadline.create(), toBecomeMultisig.address, [hundredXpx], PlainMessage.create('Good luck!'), NETWORK_TYPE),
                    networkProperties.generationHash
                )
            ),
            network.announceAndWaitForConfirmation(
                seeding.preV2Sign(
                    TransferTransaction.create(Deadline.create(), cosignatory1.address, [hundredXpx], PlainMessage.create('Good luck!'), NETWORK_TYPE),
                    networkProperties.generationHash
                )
            ),
            network.announceAndWaitForConfirmation(
                seeding.preV2Sign(
                    TransferTransaction.create(Deadline.create(), cosignatory2.address, [hundredXpx], PlainMessage.create('Good luck!'), NETWORK_TYPE),
                    networkProperties.generationHash
                )
            ),
            network.announceAndWaitForConfirmation(
                seeding.preV2Sign(
                    TransferTransaction.create(Deadline.create(), cosignatory3.address, [hundredXpx], PlainMessage.create('Good luck!'), NETWORK_TYPE),
                    networkProperties.generationHash
                )
            )]
        ).then(_ => {
            Promise.all([
                network.announceAndWaitForConfirmation(
                    cosignatory1.preV2Sign(
                        TransferTransaction.create(Deadline.create(), cosignatory1.address, [], PlainMessage.create('get public key'), NETWORK_TYPE),
                        networkProperties.generationHash
                    )
                ),
                network.announceAndWaitForConfirmation(
                    cosignatory2.preV2Sign(
                        TransferTransaction.create(Deadline.create(), cosignatory2.address, [], PlainMessage.create('get public key'), NETWORK_TYPE),
                        networkProperties.generationHash
                    )
                ),
                network.announceAndWaitForConfirmation(
                    cosignatory3.preV2Sign(
                        TransferTransaction.create(Deadline.create(), cosignatory3.address, [], PlainMessage.create('get public key'), NETWORK_TYPE),
                        networkProperties.generationHash
                    )
                )
            ]).then(_ => {
                // following listener code should be executed by other clients - one for each opt-in confirmation
                network.api.listener.open().then(() => {
                    const sub1 = network.api.listener.aggregateBondedAdded(cosignatory1.address).subscribe(aggregateTransaction => {
                        if (! aggregateTransaction.signedByAccount(cosignatory1.publicAccount)) {
                            sub1.unsubscribe();
                            network.api.transaction.announceAggregateBondedCosignature(
                                cosignatory1.preV2SignCosignatureTransaction(CosignatureTransaction.create(aggregateTransaction))
                            );
                            console.log('Cosignatory 1 signed and announced.')
                        }
                    });
                    const sub2 = network.api.listener.aggregateBondedAdded(cosignatory2.address).subscribe(aggregateTransaction => {
                        if (! aggregateTransaction.signedByAccount(cosignatory2.publicAccount)) {
                            sub2.unsubscribe();
                            network.api.transaction.announceAggregateBondedCosignature(
                                cosignatory2.preV2SignCosignatureTransaction(CosignatureTransaction.create(aggregateTransaction))
                            );
                            console.log('Cosignatory 2 signed and announced.')
                        }
                    });
                    const sub3 = network.api.listener.aggregateBondedAdded(cosignatory3.address).subscribe(aggregateTransaction => {
                        if (! aggregateTransaction.signedByAccount(cosignatory3.publicAccount)) {
                            sub3.unsubscribe();
                            network.api.transaction.announceAggregateBondedCosignature(
                                cosignatory3.preV2SignCosignatureTransaction(CosignatureTransaction.create(aggregateTransaction))
                            );
                            console.log('Cosignatory 3 signed and announced.')
                        }
                    });
                });

                // first announce the lockfunds
                network.announceAndWaitForConfirmation(signedHashLock).then(_ => {
                    // now announce the modify msig
                    network.announceAndWaitForConfirmation(signedAggregateBonded, true). then(_ => {
                        // will be confirmed only after all soon-to-be cosigners confirm the transaction (opt-in)
                        console.log('Converted to multisig.');
                    });
                });
            });
        });
    });
})