importjsonimportosimporttimeimporturllib.requestfromsymbolchain.CryptoTypesimportPrivateKeyfromsymbolchain.facade.SymbolFacadeimportSymbolFacadefromsymbolchain.scimportAmountfromsymbolchain.symbol.NetworkimportNetworkTimestampfromsymbolchain.symbol.IdGeneratorimportgenerate_mosaic_alias_idNODE_URL='https://reference.symboltest.net:3001'print(f'Using node {NODE_URL}')MULTISIG_PRIVATE_KEY=os.getenv('MULTISIG_PRIVATE_KEY','0000000000000000000000000000000000000000000000000000000000000001')multisig_key_pair=SymbolFacade.KeyPair(PrivateKey(MULTISIG_PRIVATE_KEY))print(f'Multisig public key: {multisig_key_pair.public_key}')COSIGNATORY0_PRIVATE_KEY=os.getenv('COSIGNATORY0_PRIVATE_KEY','0000000000000000000000000000000000000000000000000000000000000002')cosignatory_key_pair=SymbolFacade.KeyPair(PrivateKey(COSIGNATORY0_PRIVATE_KEY))print(f'Cosignatory public key: {cosignatory_key_pair.public_key}')facade=SymbolFacade('testnet')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())timestamp=NetworkTimestamp(int(response_json['communicationTimestamps']['receiveTimestamp']))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}')# Build the embedded transfer transactiontransfer_transaction=facade.transaction_factory.create_embedded({'type':'transfer_transaction_v1','signer_public_key':multisig_key_pair.public_key,'recipient_address':facade.network.public_key_to_address(multisig_key_pair.public_key),'mosaics':[{'mosaic_id':generate_mosaic_alias_id('symbol.xym'),'amount':1_000_000# 1 XYM}]})# Build the wrapper aggregate transactiontransaction=facade.transaction_factory.create({'type':'aggregate_complete_transaction_v3',# This is the account that will pay for the transaction'signer_public_key':cosignatory_key_pair.public_key,'deadline':timestamp.add_hours(2).timestamp,'transactions_hash':facade.hash_embedded_transactions([transfer_transaction]),'transactions':[transfer_transaction]})transaction.fee=Amount(fee_mult*transaction.size)# Sign the aggregate transaction using the cosignatory's signaturejson_payload=facade.transaction_factory.attach_signature(transaction,facade.sign_transaction(cosignatory_key_pair,transaction))print('Built transaction:')print(json.dumps(transaction.to_json(),indent=2))# Announce the transactionannounce_path='/transactions'print(f'Announcing transaction to {announce_path}')announce_request=urllib.request.Request(f'{NODE_URL}{announce_path}',data=json_payload.encode(),headers={'Content-Type':'application/json'},method='PUT')withurllib.request.urlopen(announce_request)asresponse:print(f' Response: {response.read().decode()}')# Wait for confirmationstatus_path=(f'/transactionStatus/{facade.hash_transaction(transaction)}')print(f'Waiting for confirmation from {status_path}')forattemptinrange(60):time.sleep(1)try:withurllib.request.urlopen(f'{NODE_URL}{status_path}')asresponse:status=json.loads(response.read().decode())print(f' Transaction status: {status['group']}')ifstatus['group']=='confirmed':print(f'Transaction confirmed in {attempt} seconds')breakifstatus['group']=='failed':print(f'Transaction failed: {status['code']}')breakexcepturllib.error.HTTPErrorase:print(f' Transaction status: unknown | Cause: ({e.msg})')else:print('Confirmation took too long.')excepturllib.error.URLErrorase:print(e.reason)
import{PrivateKey}from'symbol-sdk';import{SymbolFacade,NetworkTimestamp,models,generateMosaicAliasId}from'symbol-sdk/symbol';constNODE_URL='https://reference.symboltest.net:3001';console.log('Using node',NODE_URL);constMULTISIG_PRIVATE_KEY=process.env.MULTISIG_PRIVATE_KEY||('0000000000000000000000000000000000000000000000000000000000000001');constmultisigKeyPair=newSymbolFacade.KeyPair(newPrivateKey(MULTISIG_PRIVATE_KEY));console.log(`Multisig public key: ${multisigKeyPair.publicKey}`);constCOSIGNATORY0_PRIVATE_KEY=process.env.COSIGNATORY0_PRIVATE_KEY||('0000000000000000000000000000000000000000000000000000000000000002');constcosignatoryKeyPair=newSymbolFacade.KeyPair(newPrivateKey(COSIGNATORY0_PRIVATE_KEY));console.log(`Cosignatory public key: ${cosignatoryKeyPair.publicKey}`);constfacade=newSymbolFacade('testnet');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);// Build the embedded transfer transactionconsttransferTransaction=facade.transactionFactory.createEmbedded({type:'transfer_transaction_v1',signerPublicKey:multisigKeyPair.publicKey.toString(),recipientAddress:facade.network.publicKeyToAddress(multisigKeyPair.publicKey).toString(),mosaics:[{mosaicId:generateMosaicAliasId('symbol.xym'),amount:1_000_000n// 1 XYM}]});// Build the wrapper aggregate transactionconsttransaction=facade.transactionFactory.create({type:'aggregate_complete_transaction_v3',// This is the account that will pay for the transactionsignerPublicKey:cosignatoryKeyPair.publicKey.toString(),deadline:timestamp.addHours(2).timestamp,transactionsHash:facade.static.hashEmbeddedTransactions([transferTransaction]),transactions:[transferTransaction]});transaction.fee=newmodels.Amount(feeMult*transaction.size);// Sign the aggregate transaction using the cosignatory's signatureconstjsonPayload=facade.transactionFactory.static.attachSignature(transaction,facade.signTransaction(cosignatoryKeyPair,transaction));console.log('Built transaction:');console.dir(transaction.toJson(),{colors:true});// Announce the transactionconstannouncePath='/transactions';console.log('Announcing transaction to',announcePath);constannounceResponse=awaitfetch(`${NODE_URL}${announcePath}`,{method:'PUT',headers:{'Content-Type':'application/json'},body:jsonPayload});console.log(' Response:',awaitannounceResponse.text());// Wait for confirmationconsttransactionHash=facade.hashTransaction(transaction).toString();conststatusPath=`/transactionStatus/${transactionHash}`;console.log('Waiting for confirmation from',statusPath);letattempt=0;functionpollStatus(){attempt++;if(attempt>60){console.warn('Confirmation took too long.');return;}returnfetch(`${NODE_URL}${statusPath}`).then(response=>{if(!response.ok){console.log(' Transaction status: unknown | Cause:',response.statusText);// HTTP error: schedule a retryreturnnewPromise(resolve=>setTimeout(resolve,1000)).then(pollStatus);}returnresponse.json();}).then(status=>{// Skip if previous step scheduled a retryif(!status)return;console.log(' Transaction status:',status.group);if(status.group==='confirmed'){console.log('Transaction confirmed in',attempt,'seconds');}elseif(status.group==='failed'){console.log('Transaction failed:',status.code);}else{// Transaction unconfirmed: schedule a retryreturnnewPromise(resolve=>setTimeout(resolve,1000)).then(pollStatus);}});}pollStatus();}catch(e){console.error(e.message,'| Cause:',e.cause?.code??'unknown');}
MULTISIG_PRIVATE_KEY=os.getenv('MULTISIG_PRIVATE_KEY','0000000000000000000000000000000000000000000000000000000000000001')multisig_key_pair=SymbolFacade.KeyPair(PrivateKey(MULTISIG_PRIVATE_KEY))print(f'Multisig public key: {multisig_key_pair.public_key}')COSIGNATORY0_PRIVATE_KEY=os.getenv('COSIGNATORY0_PRIVATE_KEY','0000000000000000000000000000000000000000000000000000000000000002')cosignatory_key_pair=SymbolFacade.KeyPair(PrivateKey(COSIGNATORY0_PRIVATE_KEY))print(f'Cosignatory public key: {cosignatory_key_pair.public_key}')
constMULTISIG_PRIVATE_KEY=process.env.MULTISIG_PRIVATE_KEY||('0000000000000000000000000000000000000000000000000000000000000001');constmultisigKeyPair=newSymbolFacade.KeyPair(newPrivateKey(MULTISIG_PRIVATE_KEY));console.log(`Multisig public key: ${multisigKeyPair.publicKey}`);constCOSIGNATORY0_PRIVATE_KEY=process.env.COSIGNATORY0_PRIVATE_KEY||('0000000000000000000000000000000000000000000000000000000000000002');constcosignatoryKeyPair=newSymbolFacade.KeyPair(newPrivateKey(COSIGNATORY0_PRIVATE_KEY));console.log(`Cosignatory public key: ${cosignatoryKeyPair.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())timestamp=NetworkTimestamp(int(response_json['communicationTimestamps']['receiveTimestamp']))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}')
# Build the embedded transfer transactiontransfer_transaction=facade.transaction_factory.create_embedded({'type':'transfer_transaction_v1','signer_public_key':multisig_key_pair.public_key,'recipient_address':facade.network.public_key_to_address(multisig_key_pair.public_key),'mosaics':[{'mosaic_id':generate_mosaic_alias_id('symbol.xym'),'amount':1_000_000# 1 XYM}]})
// Build the embedded transfer transactionconsttransferTransaction=facade.transactionFactory.createEmbedded({type:'transfer_transaction_v1',signerPublicKey:multisigKeyPair.publicKey.toString(),recipientAddress:facade.network.publicKeyToAddress(multisigKeyPair.publicKey).toString(),mosaics:[{mosaicId:generateMosaicAliasId('symbol.xym'),amount:1_000_000n// 1 XYM}]});
# Build the wrapper aggregate transactiontransaction=facade.transaction_factory.create({'type':'aggregate_complete_transaction_v3',# This is the account that will pay for the transaction'signer_public_key':cosignatory_key_pair.public_key,'deadline':timestamp.add_hours(2).timestamp,'transactions_hash':facade.hash_embedded_transactions([transfer_transaction]),'transactions':[transfer_transaction]})transaction.fee=Amount(fee_mult*transaction.size)
// Build the wrapper aggregate transactionconsttransaction=facade.transactionFactory.create({type:'aggregate_complete_transaction_v3',// This is the account that will pay for the transactionsignerPublicKey:cosignatoryKeyPair.publicKey.toString(),deadline:timestamp.addHours(2).timestamp,transactionsHash:facade.static.hashEmbeddedTransactions([transferTransaction]),transactions:[transferTransaction]});transaction.fee=newmodels.Amount(feeMult*transaction.size);
# Sign the aggregate transaction using the cosignatory's signaturejson_payload=facade.transaction_factory.attach_signature(transaction,facade.sign_transaction(cosignatory_key_pair,transaction))print('Built transaction:')print(json.dumps(transaction.to_json(),indent=2))
// Sign the aggregate transaction using the cosignatory's signatureconstjsonPayload=facade.transactionFactory.static.attachSignature(transaction,facade.signTransaction(cosignatoryKeyPair,transaction));console.log('Built transaction:');console.dir(transaction.toJson(),{colors:true});
# Announce the transactionannounce_path='/transactions'print(f'Announcing transaction to {announce_path}')announce_request=urllib.request.Request(f'{NODE_URL}{announce_path}',data=json_payload.encode(),headers={'Content-Type':'application/json'},method='PUT')withurllib.request.urlopen(announce_request)asresponse:print(f' Response: {response.read().decode()}')# Wait for confirmationstatus_path=(f'/transactionStatus/{facade.hash_transaction(transaction)}')print(f'Waiting for confirmation from {status_path}')forattemptinrange(60):time.sleep(1)try:withurllib.request.urlopen(f'{NODE_URL}{status_path}')asresponse:status=json.loads(response.read().decode())print(f' Transaction status: {status['group']}')ifstatus['group']=='confirmed':print(f'Transaction confirmed in {attempt} seconds')breakifstatus['group']=='failed':print(f'Transaction failed: {status['code']}')breakexcepturllib.error.HTTPErrorase:print(f' Transaction status: unknown | Cause: ({e.msg})')else:print('Confirmation took too long.')
// Announce the transactionconstannouncePath='/transactions';console.log('Announcing transaction to',announcePath);constannounceResponse=awaitfetch(`${NODE_URL}${announcePath}`,{method:'PUT',headers:{'Content-Type':'application/json'},body:jsonPayload});console.log(' Response:',awaitannounceResponse.text());// Wait for confirmationconsttransactionHash=facade.hashTransaction(transaction).toString();conststatusPath=`/transactionStatus/${transactionHash}`;console.log('Waiting for confirmation from',statusPath);letattempt=0;functionpollStatus(){attempt++;if(attempt>60){console.warn('Confirmation took too long.');return;}returnfetch(`${NODE_URL}${statusPath}`).then(response=>{if(!response.ok){console.log(' Transaction status: unknown | Cause:',response.statusText);// HTTP error: schedule a retryreturnnewPromise(resolve=>setTimeout(resolve,1000)).then(pollStatus);}returnresponse.json();}).then(status=>{// Skip if previous step scheduled a retryif(!status)return;console.log(' Transaction status:',status.group);if(status.group==='confirmed'){console.log('Transaction confirmed in',attempt,'seconds');}elseif(status.group==='failed'){console.log('Transaction failed:',status.code);}else{// Transaction unconfirmed: schedule a retryreturnnewPromise(resolve=>setTimeout(resolve,1000)).then(pollStatus);}});}pollStatus();