importjsonimportosimporttimeimporturllib.requestfromsymbolchain.CryptoTypesimportPrivateKeyfromsymbolchain.facade.SymbolFacadeimportSymbolFacadefromsymbolchain.scimportAmount,Cosignaturefromsymbolchain.symbol.NetworkimportNetworkTimestampNODE_URL=os.getenv('NODE_URL','https://reference.symboltest.net:3001')print(f'Using node {NODE_URL}')facade=SymbolFacade('testnet')KEY_TEMPLATE='0'*63+'{}'# Setup the keys for the multisig account and its two cosignatoriesMULTISIG_PRIVATE_KEY=os.getenv('MULTISIG_PRIVATE_KEY',KEY_TEMPLATE.format(1))multisig_key_pair=SymbolFacade.KeyPair(PrivateKey(MULTISIG_PRIVATE_KEY))multisig_address=facade.network.public_key_to_address(multisig_key_pair.public_key)print(f'Multisig address: {multisig_address} 'f'(public key {multisig_key_pair.public_key})')cosignatory_key_pairs=[]cosignatory_addresses=[]foriinrange(2):COSIGNATORY_PRIVATE_KEY=os.getenv(f'COSIGNATORY{i}_PRIVATE_KEY',KEY_TEMPLATE.format(i+2))key_pair=SymbolFacade.KeyPair(PrivateKey(COSIGNATORY_PRIVATE_KEY))cosignatory_key_pairs.append(key_pair)addr=facade.network.public_key_to_address(key_pair.public_key)cosignatory_addresses.append(addr)print(f'Cosignatory {i} address: 'f'{addr} (public key {key_pair.public_key})')# Helper function to announce a transactiondefannounce_transaction(payload,label):print(f'Announcing {label} to /transactions')request=urllib.request.Request(f'{NODE_URL}/transactions',data=payload.encode(),headers={'Content-Type':'application/json'},method='PUT')withurllib.request.urlopen(request)asannounce_response:print(f' Response: {announce_response.read().decode()}')# Helper function to wait for transaction confirmationdefwait_for_confirmation(tx_hash,label):print(f'Waiting for {label} confirmation...')forattemptinrange(60):time.sleep(1)try:url=f'{NODE_URL}/transactionStatus/{tx_hash}'withurllib.request.urlopen(url)asconfirm_response:status=json.loads(confirm_response.read().decode())print(f' Transaction status: {status["group"]}')ifstatus['group']=='confirmed':print(f'{label} confirmed in {attempt} seconds')returnifstatus['group']=='failed':raiseRuntimeError(f'{label} failed: {status["code"]}')excepturllib.error.HTTPError:print(' Transaction status: unknown')raiseTimeoutError(f'{label} not confirmed after 60 seconds')# Returns the cosignatory addresses of the provided multisig account,# or an empty list if the account is not multisig or has never been useddefget_multisig_cosignatories(address):multisig_path=f'/account/{address}/multisig'print(f'Getting cosignatories from {multisig_path}')try:url=f'{NODE_URL}{multisig_path}'withurllib.request.urlopen(url)asmultisig_response:status=json.loads(multisig_response.read().decode())found_cosignatories=status['multisig']['cosignatoryAddresses']print(f' Response: {found_cosignatories}')returnfound_cosignatoriesexcepturllib.error.HTTPError:# The address has never been usedprint(' Response: No cosignatories')return[]# Returns a transaction that turns a regular account into a multisigdefmultisig_enable_transaction():# Create an embedded multisig account modification transaction# that adds two cosignatoriesembedded_transaction=facade.transaction_factory.create_embedded({'type':'multisig_account_modification_transaction_v1',# This is the account that will be turned into a multisig'signer_public_key':multisig_key_pair.public_key,# Increment of the number of signatures required for approvals'min_approval_delta':1,# Increment of the number of signatures required for removals'min_removal_delta':1,'address_additions':cosignatory_addresses})# Build the aggregate transactionembedded_transactions=[embedded_transaction]transaction=facade.transaction_factory.create({'type':'aggregate_complete_transaction_v3',# This is the account that will pay for this transaction'signer_public_key':multisig_key_pair.public_key,'deadline':timestamp.add_hours(2).timestamp,'transactions_hash':facade.hash_embedded_transactions(embedded_transactions),'transactions':embedded_transactions})# Reserve space for two cosignatures# and calculate fee for the final transaction sizecosignature_size=Cosignature().sizetransaction.fee=Amount(fee_multiplier*(transaction.size+cosignature_size*len(cosignatory_key_pairs)))print('Enabling the multisig with the aggregate transaction:')print(json.dumps(transaction.to_json(),indent=2))# Sign the aggregate transaction with the multisig's signaturefacade.transaction_factory.attach_signature(transaction,facade.sign_transaction(multisig_key_pair,transaction))# Append signatures from all cosignatoriesforcosignatory_key_pairincosignatory_key_pairs:transaction.cosignatures.append(facade.cosign_transaction(cosignatory_key_pair,transaction))returntransaction# Returns a transaction that turns a multisig into a regular accountdefmultisig_disable_transaction():# Create two embedded multisig account modification transactions# because cosignatories must be removed one by oneembedded_transaction_1=facade.transaction_factory.create_embedded({'type':'multisig_account_modification_transaction_v1',# This is the multisig account that will be modified'signer_public_key':multisig_key_pair.public_key,# Keep required signatures unchanged for this step'min_approval_delta':0,'min_removal_delta':0,'address_deletions':[cosignatory_addresses[1]]})embedded_transaction_2=facade.transaction_factory.create_embedded({'type':'multisig_account_modification_transaction_v1',# This is the multisig account that will be modified'signer_public_key':multisig_key_pair.public_key,# Decrease required signatures after final removal'min_approval_delta':-1,'min_removal_delta':-1,'address_deletions':[cosignatory_addresses[0]]})# Build the aggregate transactionembedded_transactions=[embedded_transaction_1,embedded_transaction_2]transaction=facade.transaction_factory.create({'type':'aggregate_complete_transaction_v3',# This is the account that will pay for all transactions'signer_public_key':cosignatory_key_pairs[0].public_key,'deadline':timestamp.add_hours(2).timestamp,'transactions_hash':facade.hash_embedded_transactions(embedded_transactions),'transactions':embedded_transactions})# Calculate fee for the final transaction size# (No need to reserve space for cosignatures, as there are none)transaction.fee=Amount(fee_multiplier*transaction.size)print('Disabling the multisig with the aggregate transaction:')print(json.dumps(transaction.to_json(),indent=2))# Sign the aggregate transaction using the first cosigner's signaturefacade.transaction_factory.attach_signature(transaction,facade.sign_transaction(cosignatory_key_pairs[0],transaction))returntransactiontry:# Fetch current network timetime_path='/node/time'print(f'Fetching current network time from {time_path}')withurllib.request.urlopen(f'{NODE_URL}{time_path}')asresponse:response_json=json.loads(response.read().decode())receive_timestamp=(response_json['communicationTimestamps']['receiveTimestamp'])timestamp=NetworkTimestamp(int(receive_timestamp))print(f' Network time: {timestamp.timestamp} ms since nemesis')# Fetch recommended feesfee_path='/network/fees/transaction'print(f'Fetching recommended fees from {fee_path}')withurllib.request.urlopen(f'{NODE_URL}{fee_path}')asresponse:response_json=json.loads(response.read().decode())median_multiplier=response_json['medianFeeMultiplier']minimum_multiplier=response_json['minFeeMultiplier']fee_multiplier=max(median_multiplier,minimum_multiplier)print(f' Fee multiplier: {fee_multiplier}')# Get current state of the multisig account and decide which# operation to performcosignatories=get_multisig_cosignatories(multisig_address)iflen(cosignatories)==0:# Enable the multisigagg_transaction=multisig_enable_transaction()else:# Disable the multisigagg_transaction=multisig_disable_transaction()json_payload=facade.transaction_factory.to_json(agg_transaction)# Announce and wait for confirmationagg_transaction_hash=facade.hash_transaction(agg_transaction)print(f'Built aggregate transaction with hash: {agg_transaction_hash}')announce_transaction(json_payload,'aggregate transaction')wait_for_confirmation(agg_transaction_hash,'aggregate transaction')exceptExceptionase:print(e)
import{PrivateKey}from'symbol-sdk';import{KeyPair,NetworkTimestamp,SymbolFacade,SymbolTransactionFactory,models}from'symbol-sdk/symbol';constNODE_URL=process.env.NODE_URL||'https://reference.symboltest.net:3001';console.log('Using node',NODE_URL);constfacade=newSymbolFacade('testnet');constKEY_PREFIX='0'.repeat(63);// Setup the keys for the multisig account and its two cosignatoriesconstMULTISIG_PRIVATE_KEY=process.env.MULTISIG_PRIVATE_KEY||(`${KEY_PREFIX}1`);constmultisigKeyPair=newKeyPair(newPrivateKey(MULTISIG_PRIVATE_KEY));constmultisigAddress=facade.network.publicKeyToAddress(multisigKeyPair.publicKey);console.log(`Multisig address: ${multisigAddress}`,`(public key ${multisigKeyPair.publicKey})`);constcosignatoryKeyPairs=[];constcosignatoryAddresses=[];for(leti=0;2>i;i++){constCOSIGNATORY_PRIVATE_KEY=process.env[`COSIGNATORY${i}_PRIVATE_KEY`]||(KEY_PREFIX+String(i+2));constkp=newKeyPair(newPrivateKey(COSIGNATORY_PRIVATE_KEY));cosignatoryKeyPairs.push(kp);constaddr=facade.network.publicKeyToAddress(kp.publicKey);cosignatoryAddresses.push(addr);console.log(`Cosignatory ${i} address: ${addr}`,`(public key ${kp.publicKey})`);}// Helper function to announce a transactionasyncfunctionannounceTransaction(payload,label){console.log(`Announcing ${label} to /transactions`);constresponse=awaitfetch(`${NODE_URL}/transactions`,{method:'PUT',headers:{'Content-Type':'application/json'},body:payload});console.log(' Response:',awaitresponse.text());}// Helper function to wait for transaction confirmationasyncfunctionwaitForConfirmation(transactionHash,label){console.log(`Waiting for ${label} confirmation...`);for(letattempt=0;60>attempt;attempt++){awaitnewPromise(resolve=>{setTimeout(resolve,1000);});try{constresponse=awaitfetch(`${NODE_URL}/transactionStatus/${transactionHash}`);conststatus=awaitresponse.json();console.log(' Transaction status:',status.group);if('confirmed'===status.group){console.log(`${label} confirmed in`,attempt,'seconds');return;}if('failed'===status.group)thrownewError(`${label} failed: ${status.code}`);}catch(e){if(e.message.includes('failed'))throwe;console.log(' Transaction status: unknown');}}thrownewError(`${label} not confirmed after 60 seconds`);}// Returns the cosignatory addresses of the provided multisig account,// or an empty list if the account is not multisig or has never been usedasyncfunctiongetMultisigCosignatories(address){constmultisigPath=`/account/${address}/multisig`;console.log(`Getting cosignatories from ${multisigPath}`);constresponse=awaitfetch(`${NODE_URL}${multisigPath}`);if(!response.ok){console.log(' Response: No cosignatories');return[];}constjson=awaitresponse.json();constcosignatories=json.multisig.cosignatoryAddresses;console.log(' Response:',JSON.stringify(cosignatories));returncosignatories;}// Returns a transaction that turns a regular account into a multisigfunctionmultisigEnableTransaction(timestamp,feeMultiplier){// Create an embedded multisig account modification transaction// that adds two cosignatoriesconstembeddedTransaction=facade.transactionFactory.createEmbedded({type:'multisig_account_modification_transaction_v1',// This is the account that will be turned into a multisigsignerPublicKey:multisigKeyPair.publicKey,// Delta of the number of signatures required for approvalsminApprovalDelta:1,// Delta of the number of signatures required for removalsminRemovalDelta:1,addressAdditions:cosignatoryAddresses});// Build the aggregate transactionconstembeddedTransactions=[embeddedTransaction];consttransaction=facade.transactionFactory.create({type:'aggregate_complete_transaction_v3',// This is the account that will pay for this transactionsignerPublicKey:multisigKeyPair.publicKey,deadline:timestamp.addHours(2).timestamp,transactionsHash:facade.static.hashEmbeddedTransactions(embeddedTransactions),transactions:embeddedTransactions});// Reserve space for two cosignatures// and calculate fee for the final transaction sizeconstcosignatureSize=newmodels.Cosignature().size;transaction.fee=newmodels.Amount(feeMultiplier*(transaction.size+(cosignatureSize*cosignatoryKeyPairs.length)));console.log('Enabling the multisig with the aggregate transaction:');console.log(JSON.stringify(transaction.toJson(),null,2));// Sign the aggregate transaction with the multisig's signatureSymbolTransactionFactory.attachSignature(transaction,facade.signTransaction(multisigKeyPair,transaction));// Append signatures from all cosignatoriesfor(constcosignatoryKeyPairofcosignatoryKeyPairs){transaction.cosignatures.push(facade.cosignTransaction(cosignatoryKeyPair,transaction));}returntransaction;}// Returns a transaction that turns a multisig into a regular accountfunctionmultisigDisableTransaction(timestamp,feeMultiplier){// Create two embedded multisig account modification transactions// because cosignatories must be removed one by oneconstembeddedTransaction1=facade.transactionFactory.createEmbedded({type:'multisig_account_modification_transaction_v1',// This is the multisig account that will be modifiedsignerPublicKey:multisigKeyPair.publicKey,// Keep required signatures unchanged for this stepminApprovalDelta:0,minRemovalDelta:0,addressDeletions:[cosignatoryAddresses[1]]});constembeddedTransaction2=facade.transactionFactory.createEmbedded({type:'multisig_account_modification_transaction_v1',// This is the multisig account that will be modifiedsignerPublicKey:multisigKeyPair.publicKey,// Decrease required signatures after final removalminApprovalDelta:-1,minRemovalDelta:-1,addressDeletions:[cosignatoryAddresses[0]]});// Build the aggregate transactionconstembeddedTransactions=[embeddedTransaction1,embeddedTransaction2];consttransaction=facade.transactionFactory.create({type:'aggregate_complete_transaction_v3',// This is the account that will pay for this transactionsignerPublicKey:cosignatoryKeyPairs[0].publicKey,deadline:timestamp.addHours(2).timestamp,transactionsHash:facade.static.hashEmbeddedTransactions(embeddedTransactions),transactions:embeddedTransactions});// Calculate fee for the final transaction size// (No need to reserve space for cosignatures, as there are none)transaction.fee=newmodels.Amount(feeMultiplier*transaction.size);console.log('Disabling the multisig with the aggregate transaction:');console.log(JSON.stringify(transaction.toJson(),null,2));// Sign the aggregate transaction using the first cosigner's signatureSymbolTransactionFactory.attachSignature(transaction,facade.signTransaction(cosignatoryKeyPairs[0],transaction));returntransaction;}try{// Fetch current network timeconsttimePath='/node/time';console.log('Fetching current network time from',timePath);consttimeResponse=awaitfetch(`${NODE_URL}${timePath}`);consttimeJSON=awaittimeResponse.json();consttimestamp=newNetworkTimestamp(timeJSON.communicationTimestamps.receiveTimestamp);console.log(' Network time:',timestamp.timestamp,'ms since nemesis');// Fetch recommended feesconstfeePath='/network/fees/transaction';console.log('Fetching recommended fees from',feePath);constfeeResponse=awaitfetch(`${NODE_URL}${feePath}`);constfeeJSON=awaitfeeResponse.json();constmedianMultiplier=feeJSON.medianFeeMultiplier;constminimumMultiplier=feeJSON.minFeeMultiplier;constfeeMultiplier=Math.max(medianMultiplier,minimumMultiplier);console.log(' Fee multiplier:',feeMultiplier);// Get current state of the multisig account and decide which// operation to performconstcosignatories=awaitgetMultisigCosignatories(multisigAddress);lettransaction;if(0===cosignatories.length){// Enable the multisigtransaction=multisigEnableTransaction(timestamp,feeMultiplier);}else{// Disable the multisigtransaction=multisigDisableTransaction(timestamp,feeMultiplier);}constpayload=SymbolTransactionFactory.toJson(transaction);// Announce and wait for confirmationconsttransactionHash=facade.hashTransaction(transaction).toString();console.log('Built aggregate transaction with hash:',transactionHash);awaitannounceTransaction(payload,'aggregate transaction');awaitwaitForConfirmation(transactionHash,'aggregate transaction');}catch(e){console.error(e.message,'| Cause:',e.cause?.code??'unknown');}
KEY_TEMPLATE='0'*63+'{}'# Setup the keys for the multisig account and its two cosignatoriesMULTISIG_PRIVATE_KEY=os.getenv('MULTISIG_PRIVATE_KEY',KEY_TEMPLATE.format(1))multisig_key_pair=SymbolFacade.KeyPair(PrivateKey(MULTISIG_PRIVATE_KEY))multisig_address=facade.network.public_key_to_address(multisig_key_pair.public_key)print(f'Multisig address: {multisig_address} 'f'(public key {multisig_key_pair.public_key})')cosignatory_key_pairs=[]cosignatory_addresses=[]foriinrange(2):COSIGNATORY_PRIVATE_KEY=os.getenv(f'COSIGNATORY{i}_PRIVATE_KEY',KEY_TEMPLATE.format(i+2))key_pair=SymbolFacade.KeyPair(PrivateKey(COSIGNATORY_PRIVATE_KEY))cosignatory_key_pairs.append(key_pair)addr=facade.network.public_key_to_address(key_pair.public_key)cosignatory_addresses.append(addr)print(f'Cosignatory {i} address: 'f'{addr} (public key {key_pair.public_key})')
constKEY_PREFIX='0'.repeat(63);// Setup the keys for the multisig account and its two cosignatoriesconstMULTISIG_PRIVATE_KEY=process.env.MULTISIG_PRIVATE_KEY||(`${KEY_PREFIX}1`);constmultisigKeyPair=newKeyPair(newPrivateKey(MULTISIG_PRIVATE_KEY));constmultisigAddress=facade.network.publicKeyToAddress(multisigKeyPair.publicKey);console.log(`Multisig address: ${multisigAddress}`,`(public key ${multisigKeyPair.publicKey})`);constcosignatoryKeyPairs=[];constcosignatoryAddresses=[];for(leti=0;2>i;i++){constCOSIGNATORY_PRIVATE_KEY=process.env[`COSIGNATORY${i}_PRIVATE_KEY`]||(KEY_PREFIX+String(i+2));constkp=newKeyPair(newPrivateKey(COSIGNATORY_PRIVATE_KEY));cosignatoryKeyPairs.push(kp);constaddr=facade.network.publicKeyToAddress(kp.publicKey);cosignatoryAddresses.push(addr);console.log(`Cosignatory ${i} address: ${addr}`,`(public key ${kp.publicKey})`);}
# Fetch current network timetime_path='/node/time'print(f'Fetching current network time from {time_path}')withurllib.request.urlopen(f'{NODE_URL}{time_path}')asresponse:response_json=json.loads(response.read().decode())receive_timestamp=(response_json['communicationTimestamps']['receiveTimestamp'])timestamp=NetworkTimestamp(int(receive_timestamp))print(f' Network time: {timestamp.timestamp} ms since nemesis')# Fetch recommended feesfee_path='/network/fees/transaction'print(f'Fetching recommended fees from {fee_path}')withurllib.request.urlopen(f'{NODE_URL}{fee_path}')asresponse:response_json=json.loads(response.read().decode())median_multiplier=response_json['medianFeeMultiplier']minimum_multiplier=response_json['minFeeMultiplier']fee_multiplier=max(median_multiplier,minimum_multiplier)print(f' Fee multiplier: {fee_multiplier}')
# Returns the cosignatory addresses of the provided multisig account,# or an empty list if the account is not multisig or has never been useddefget_multisig_cosignatories(address):multisig_path=f'/account/{address}/multisig'print(f'Getting cosignatories from {multisig_path}')try:url=f'{NODE_URL}{multisig_path}'withurllib.request.urlopen(url)asmultisig_response:status=json.loads(multisig_response.read().decode())found_cosignatories=status['multisig']['cosignatoryAddresses']print(f' Response: {found_cosignatories}')returnfound_cosignatoriesexcepturllib.error.HTTPError:# The address has never been usedprint(' Response: No cosignatories')return[]
// Returns the cosignatory addresses of the provided multisig account,// or an empty list if the account is not multisig or has never been usedasyncfunctiongetMultisigCosignatories(address){constmultisigPath=`/account/${address}/multisig`;console.log(`Getting cosignatories from ${multisigPath}`);constresponse=awaitfetch(`${NODE_URL}${multisigPath}`);if(!response.ok){console.log(' Response: No cosignatories');return[];}constjson=awaitresponse.json();constcosignatories=json.multisig.cosignatoryAddresses;console.log(' Response:',JSON.stringify(cosignatories));returncosignatories;}
# Get current state of the multisig account and decide which# operation to performcosignatories=get_multisig_cosignatories(multisig_address)iflen(cosignatories)==0:# Enable the multisigagg_transaction=multisig_enable_transaction()else:# Disable the multisigagg_transaction=multisig_disable_transaction()json_payload=facade.transaction_factory.to_json(agg_transaction)
// Get current state of the multisig account and decide which// operation to performconstcosignatories=awaitgetMultisigCosignatories(multisigAddress);lettransaction;if(0===cosignatories.length){// Enable the multisigtransaction=multisigEnableTransaction(timestamp,feeMultiplier);}else{// Disable the multisigtransaction=multisigDisableTransaction(timestamp,feeMultiplier);}constpayload=SymbolTransactionFactory.toJson(transaction);
# Create an embedded multisig account modification transaction# that adds two cosignatoriesembedded_transaction=facade.transaction_factory.create_embedded({'type':'multisig_account_modification_transaction_v1',# This is the account that will be turned into a multisig'signer_public_key':multisig_key_pair.public_key,# Increment of the number of signatures required for approvals'min_approval_delta':1,# Increment of the number of signatures required for removals'min_removal_delta':1,'address_additions':cosignatory_addresses})
// Create an embedded multisig account modification transaction// that adds two cosignatoriesconstembeddedTransaction=facade.transactionFactory.createEmbedded({type:'multisig_account_modification_transaction_v1',// This is the account that will be turned into a multisigsignerPublicKey:multisigKeyPair.publicKey,// Delta of the number of signatures required for approvalsminApprovalDelta:1,// Delta of the number of signatures required for removalsminRemovalDelta:1,addressAdditions:cosignatoryAddresses});
# Build the aggregate transactionembedded_transactions=[embedded_transaction]transaction=facade.transaction_factory.create({'type':'aggregate_complete_transaction_v3',# This is the account that will pay for this transaction'signer_public_key':multisig_key_pair.public_key,'deadline':timestamp.add_hours(2).timestamp,'transactions_hash':facade.hash_embedded_transactions(embedded_transactions),'transactions':embedded_transactions})# Reserve space for two cosignatures# and calculate fee for the final transaction sizecosignature_size=Cosignature().sizetransaction.fee=Amount(fee_multiplier*(transaction.size+cosignature_size*len(cosignatory_key_pairs)))print('Enabling the multisig with the aggregate transaction:')print(json.dumps(transaction.to_json(),indent=2))
// Build the aggregate transactionconstembeddedTransactions=[embeddedTransaction];consttransaction=facade.transactionFactory.create({type:'aggregate_complete_transaction_v3',// This is the account that will pay for this transactionsignerPublicKey:multisigKeyPair.publicKey,deadline:timestamp.addHours(2).timestamp,transactionsHash:facade.static.hashEmbeddedTransactions(embeddedTransactions),transactions:embeddedTransactions});// Reserve space for two cosignatures// and calculate fee for the final transaction sizeconstcosignatureSize=newmodels.Cosignature().size;transaction.fee=newmodels.Amount(feeMultiplier*(transaction.size+(cosignatureSize*cosignatoryKeyPairs.length)));console.log('Enabling the multisig with the aggregate transaction:');console.log(JSON.stringify(transaction.toJson(),null,2));
# Sign the aggregate transaction with the multisig's signaturefacade.transaction_factory.attach_signature(transaction,facade.sign_transaction(multisig_key_pair,transaction))# Append signatures from all cosignatoriesforcosignatory_key_pairincosignatory_key_pairs:transaction.cosignatures.append(facade.cosign_transaction(cosignatory_key_pair,transaction))
// Sign the aggregate transaction with the multisig's signatureSymbolTransactionFactory.attachSignature(transaction,facade.signTransaction(multisigKeyPair,transaction));// Append signatures from all cosignatoriesfor(constcosignatoryKeyPairofcosignatoryKeyPairs){transaction.cosignatures.push(facade.cosignTransaction(cosignatoryKeyPair,transaction));}
# Create two embedded multisig account modification transactions# because cosignatories must be removed one by oneembedded_transaction_1=facade.transaction_factory.create_embedded({'type':'multisig_account_modification_transaction_v1',# This is the multisig account that will be modified'signer_public_key':multisig_key_pair.public_key,# Keep required signatures unchanged for this step'min_approval_delta':0,'min_removal_delta':0,'address_deletions':[cosignatory_addresses[1]]})embedded_transaction_2=facade.transaction_factory.create_embedded({'type':'multisig_account_modification_transaction_v1',# This is the multisig account that will be modified'signer_public_key':multisig_key_pair.public_key,# Decrease required signatures after final removal'min_approval_delta':-1,'min_removal_delta':-1,'address_deletions':[cosignatory_addresses[0]]})
// Create two embedded multisig account modification transactions// because cosignatories must be removed one by oneconstembeddedTransaction1=facade.transactionFactory.createEmbedded({type:'multisig_account_modification_transaction_v1',// This is the multisig account that will be modifiedsignerPublicKey:multisigKeyPair.publicKey,// Keep required signatures unchanged for this stepminApprovalDelta:0,minRemovalDelta:0,addressDeletions:[cosignatoryAddresses[1]]});constembeddedTransaction2=facade.transactionFactory.createEmbedded({type:'multisig_account_modification_transaction_v1',// This is the multisig account that will be modifiedsignerPublicKey:multisigKeyPair.publicKey,// Decrease required signatures after final removalminApprovalDelta:-1,minRemovalDelta:-1,addressDeletions:[cosignatoryAddresses[0]]});
# Build the aggregate transactionembedded_transactions=[embedded_transaction_1,embedded_transaction_2]transaction=facade.transaction_factory.create({'type':'aggregate_complete_transaction_v3',# This is the account that will pay for all transactions'signer_public_key':cosignatory_key_pairs[0].public_key,'deadline':timestamp.add_hours(2).timestamp,'transactions_hash':facade.hash_embedded_transactions(embedded_transactions),'transactions':embedded_transactions})# Calculate fee for the final transaction size# (No need to reserve space for cosignatures, as there are none)transaction.fee=Amount(fee_multiplier*transaction.size)print('Disabling the multisig with the aggregate transaction:')print(json.dumps(transaction.to_json(),indent=2))# Sign the aggregate transaction using the first cosigner's signaturefacade.transaction_factory.attach_signature(transaction,facade.sign_transaction(cosignatory_key_pairs[0],transaction))
// Build the aggregate transactionconstembeddedTransactions=[embeddedTransaction1,embeddedTransaction2];consttransaction=facade.transactionFactory.create({type:'aggregate_complete_transaction_v3',// This is the account that will pay for this transactionsignerPublicKey:cosignatoryKeyPairs[0].publicKey,deadline:timestamp.addHours(2).timestamp,transactionsHash:facade.static.hashEmbeddedTransactions(embeddedTransactions),transactions:embeddedTransactions});// Calculate fee for the final transaction size// (No need to reserve space for cosignatures, as there are none)transaction.fee=newmodels.Amount(feeMultiplier*transaction.size);console.log('Disabling the multisig with the aggregate transaction:');console.log(JSON.stringify(transaction.toJson(),null,2));// Sign the aggregate transaction using the first cosigner's signatureSymbolTransactionFactory.attachSignature(transaction,facade.signTransaction(cosignatoryKeyPairs[0],transaction));
# Announce and wait for confirmationagg_transaction_hash=facade.hash_transaction(agg_transaction)print(f'Built aggregate transaction with hash: {agg_transaction_hash}')announce_transaction(json_payload,'aggregate transaction')wait_for_confirmation(agg_transaction_hash,'aggregate transaction')
// Announce and wait for confirmationconsttransactionHash=facade.hashTransaction(transaction).toString();console.log('Built aggregate transaction with hash:',transactionHash);awaitannounceTransaction(payload,'aggregate transaction');awaitwaitForConfirmation(transactionHash,'aggregate transaction');