importjsonimportosimporttimeimporturllib.requestfromsymbolchain.CryptoTypesimportPrivateKeyfromsymbolchain.facade.SymbolFacadeimportSymbolFacadefromsymbolchain.scimportAmountfromsymbolchain.symbol.NetworkimportNetworkTimestampNODE_URL=os.environ.get('NODE_URL','https://reference.symboltest.net:3001')print(f'Using node {NODE_URL}')# 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)asresponse:print(f' Response: {response.read().decode()}')# Helper function to wait for transaction confirmationdefwait_for_confirmation(transaction_hash,label):print(f'Waiting for {label} confirmation...')forattemptinrange(60):time.sleep(1)try:url=f'{NODE_URL}/transactionStatus/{transaction_hash}'withurllib.request.urlopen(url)asresponse:status=json.loads(response.read().decode())print(f' Transaction status: {status["group"]}')ifstatus['group']=='confirmed':print(f'{label} confirmed in {attempt} seconds')returnifstatus['group']=='failed':raiseException(f'{label} failed: {status["code"]}')excepturllib.error.HTTPError:print(' Transaction status: unknown')raiseException(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)asresponse:status=json.loads(response.read().decode())cosignatories=status['multisig']['cosignatoryAddresses']print(f' Response: {cosignatories}')returncosignatoriesexcepturllib.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 (each is 104 bytes)# and calculate fee for the final transaction sizetransaction.fee=Amount(fee_mult*(transaction.size+104*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_mult*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))returntransactionfacade=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))kp=SymbolFacade.KeyPair(PrivateKey(COSIGNATORY_PRIVATE_KEY))cosignatory_key_pairs.append(kp)addr=facade.network.public_key_to_address(kp.public_key)cosignatory_addresses.append(addr)print(f'Cosignatory {i} address: {addr} (public key {kp.public_key})')try:# 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_mult=response_json['medianFeeMultiplier']minimum_mult=response_json['minFeeMultiplier']fee_mult=max(median_mult,minimum_mult)print(f' Fee multiplier: {fee_mult}')# Get current state of the multisig account and decide which# operation to performcosignatories=get_multisig_cosignatories(multisig_address)iflen(cosignatories)==0:# Enable the multisigtransaction=multisig_enable_transaction()# This operation must be signed by the multisig accountsigner_key_pair=multisig_key_pairelse:# Disable the multisigtransaction=multisig_disable_transaction()# This operation must be signed by one of the cosignerssigner_key_pair=cosignatory_key_pairs[0]json_payload=facade.transaction_factory.attach_signature(transaction,facade.sign_transaction(signer_key_pair,transaction))# Announce and wait for confirmationtransaction_hash=facade.hash_transaction(transaction)print(f'Built aggregate transaction with hash: {transaction_hash}')announce_transaction(json_payload,'aggregate transaction')wait_for_confirmation(transaction_hash,'aggregate transaction')exceptExceptionase:print(e)
import{PrivateKey}from'symbol-sdk';import{KeyPair,SymbolTransactionFactory,models,NetworkTimestamp,SymbolFacade}from'symbol-sdk/symbol';constNODE_URL=process.env.NODE_URL||'https://reference.symboltest.net:3001';console.log('Using node',NODE_URL);// 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;attempt<60;attempt++){awaitnewPromise(resolve=>setTimeout(resolve,1000));try{constresponse=awaitfetch(`${NODE_URL}/transactionStatus/${transactionHash}`);conststatus=awaitresponse.json();console.log(' Transaction status:',status.group);if(status.group==='confirmed'){console.log(`${label} confirmed in`,attempt,'seconds');return;}if(status.group==='failed'){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}`);try{constresponse=awaitfetch(`${NODE_URL}${multisigPath}`);constjson=awaitresponse.json();constcosignatories=json.multisig.cosignatoryAddresses;console.log(' Response:',JSON.stringify(cosignatories));returncosignatories;}catch{console.log(' Response: No cosignatories');return[];}}// Returns a transaction that turns a regular account into a multisigfunctionmultisigEnableTransaction(timestamp,feeMult){// 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 (each is 104 bytes)// and calculate fee for the final transaction sizetransaction.fee=newmodels.Amount(feeMult*(transaction.size+104*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,feeMult){// 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(feeMult*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;}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;i<2;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})`);}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();constmedianMult=feeJSON.medianFeeMultiplier;constminimumMult=feeJSON.minFeeMultiplier;constfeeMult=Math.max(medianMult,minimumMult);console.log(' Fee multiplier:',feeMult);// Get current state of the multisig account and decide which// operation to performconstcosignatories=awaitgetMultisigCosignatories(multisigAddress);lettransaction;letsignerKeyPair;if(cosignatories.length===0){// Enable the multisigtransaction=multisigEnableTransaction(timestamp,feeMult);// This operation must be signed by the multisig accountsignerKeyPair=multisigKeyPair;}else{// Disable the multisigtransaction=multisigDisableTransaction(timestamp,feeMult);// This operation must be signed by one of the cosignerssignerKeyPair=cosignatoryKeyPairs[0];}constpayload=SymbolTransactionFactory.attachSignature(transaction,facade.signTransaction(signerKeyPair,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))kp=SymbolFacade.KeyPair(PrivateKey(COSIGNATORY_PRIVATE_KEY))cosignatory_key_pairs.append(kp)addr=facade.network.public_key_to_address(kp.public_key)cosignatory_addresses.append(addr)print(f'Cosignatory {i} address: {addr} (public key {kp.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;i<2;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_mult=response_json['medianFeeMultiplier']minimum_mult=response_json['minFeeMultiplier']fee_mult=max(median_mult,minimum_mult)print(f' Fee multiplier: {fee_mult}')
# 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)asresponse:status=json.loads(response.read().decode())cosignatories=status['multisig']['cosignatoryAddresses']print(f' Response: {cosignatories}')returncosignatoriesexcepturllib.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}`);try{constresponse=awaitfetch(`${NODE_URL}${multisigPath}`);constjson=awaitresponse.json();constcosignatories=json.multisig.cosignatoryAddresses;console.log(' Response:',JSON.stringify(cosignatories));returncosignatories;}catch{console.log(' Response: No cosignatories');return[];}}
# Get current state of the multisig account and decide which# operation to performcosignatories=get_multisig_cosignatories(multisig_address)iflen(cosignatories)==0:# Enable the multisigtransaction=multisig_enable_transaction()# This operation must be signed by the multisig accountsigner_key_pair=multisig_key_pairelse:# Disable the multisigtransaction=multisig_disable_transaction()# This operation must be signed by one of the cosignerssigner_key_pair=cosignatory_key_pairs[0]json_payload=facade.transaction_factory.attach_signature(transaction,facade.sign_transaction(signer_key_pair,transaction))
// Get current state of the multisig account and decide which// operation to performconstcosignatories=awaitgetMultisigCosignatories(multisigAddress);lettransaction;letsignerKeyPair;if(cosignatories.length===0){// Enable the multisigtransaction=multisigEnableTransaction(timestamp,feeMult);// This operation must be signed by the multisig accountsignerKeyPair=multisigKeyPair;}else{// Disable the multisigtransaction=multisigDisableTransaction(timestamp,feeMult);// This operation must be signed by one of the cosignerssignerKeyPair=cosignatoryKeyPairs[0];}constpayload=SymbolTransactionFactory.attachSignature(transaction,facade.signTransaction(signerKeyPair,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 (each is 104 bytes)# and calculate fee for the final transaction sizetransaction.fee=Amount(fee_mult*(transaction.size+104*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 (each is 104 bytes)// and calculate fee for the final transaction sizetransaction.fee=newmodels.Amount(feeMult*(transaction.size+104*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_mult*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(feeMult*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 confirmationtransaction_hash=facade.hash_transaction(transaction)print(f'Built aggregate transaction with hash: {transaction_hash}')announce_transaction(json_payload,'aggregate transaction')wait_for_confirmation(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');