importjsonimportosimporttimeimporturllib.requestfromsymbolchain.CryptoTypesimportPrivateKeyfromsymbolchain.facade.SymbolFacadeimportSymbolFacadefromsymbolchain.scimportAmountfromsymbolchain.symbol.Metadataimport(metadata_generate_key,metadata_update_value)fromsymbolchain.symbol.NetworkimportNetworkTimestampNODE_URL=os.getenv('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')SIGNER_PRIVATE_KEY=os.getenv('SIGNER_PRIVATE_KEY','0000000000000000000000000000000000000000000000000000000000000000')signer_key_pair=SymbolFacade.KeyPair(PrivateKey(SIGNER_PRIVATE_KEY))facade=SymbolFacade('testnet')signer_address=facade.network.public_key_to_address(signer_key_pair.public_key)print(f'Signer address: {signer_address}')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}')# --- ADDING NEW METADATA ---print('\n--- Adding new metadata ---')# Define metadata key and valuekey_string=f'username_{int(time.time())}'scoped_metadata_key=metadata_generate_key(key_string)metadata_value='alice'.encode('utf8')# Create the embedded metadata transactionembedded_transaction=facade.transaction_factory.create_embedded({'type':'account_metadata_transaction_v1','signer_public_key':signer_key_pair.public_key,'target_address':signer_address,'scoped_metadata_key':scoped_metadata_key,# When creating new metadata, value_size_delta# equals the value length'value_size_delta':len(metadata_value),'value':metadata_value})print('Created embedded metadata transaction:')print(json.dumps(embedded_transaction.to_json(),indent=2))# Build the aggregate transactionembedded_transactions=[embedded_transaction]transaction=facade.transaction_factory.create({'type':'aggregate_complete_transaction_v3','signer_public_key':signer_key_pair.public_key,'deadline':timestamp.add_hours(2).timestamp,'transactions_hash':facade.hash_embedded_transactions(embedded_transactions),'transactions':embedded_transactions})transaction.fee=Amount(fee_mult*transaction.size)# Sign and generate final payloadsignature=facade.sign_transaction(signer_key_pair,transaction)json_payload=facade.transaction_factory.attach_signature(transaction,signature)# 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')# --- MODIFYING EXISTING METADATA ---print('\n--- Modifying existing metadata ---')# Fetch current metadata value from networkmetadata_path=(f'/metadata?sourceAddress={signer_address}'f'&targetAddress={signer_address}'f'&scopedMetadataKey={scoped_metadata_key:016X}''&metadataType=0')print(f'Fetching current metadata from {metadata_path}')withurllib.request.urlopen(f'{NODE_URL}{metadata_path}')asresponse:response_json=json.loads(response.read().decode())# Get the metadata entryifnotresponse_json['data']:raiseException('Metadata entry not found')metadata_entry=response_json['data'][0]['metadataEntry']current_value=bytes.fromhex(metadata_entry['value'])print(f' Current value: {current_value.decode("utf8")}')# XOR the current and new valuesnew_value='bob'.encode('utf8')update_value=metadata_update_value(current_value,new_value)# Create the update transaction with XOR'd valueembedded_update=facade.transaction_factory.create_embedded({'type':'account_metadata_transaction_v1','signer_public_key':signer_key_pair.public_key,'target_address':signer_address,'scoped_metadata_key':scoped_metadata_key,# value_size_delta is the difference in length# (can be negative)'value_size_delta':len(new_value)-len(current_value),'value':update_value})# Build the aggregate for the updateembedded_transactions=[embedded_update]update_transaction=facade.transaction_factory.create({'type':'aggregate_complete_transaction_v3','signer_public_key':signer_key_pair.public_key,'deadline':timestamp.add_hours(2).timestamp,'transactions_hash':facade.hash_embedded_transactions(embedded_transactions),'transactions':embedded_transactions})update_transaction.fee=Amount(fee_mult*update_transaction.size)# Sign and announce the updatesignature=facade.sign_transaction(signer_key_pair,update_transaction)json_payload=facade.transaction_factory.attach_signature(update_transaction,signature)# Announce and wait for confirmationupdate_hash=facade.hash_transaction(update_transaction)print(f'Built aggregate transaction with hash: {update_hash}')announce_transaction(json_payload,'aggregate transaction')wait_for_confirmation(update_hash,'aggregate transaction')exceptExceptionase:print(e)
import{PrivateKey}from'symbol-sdk';import{metadataGenerateKey,metadataUpdateValue,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`);}constSIGNER_PRIVATE_KEY=process.env.SIGNER_PRIVATE_KEY||('0000000000000000000000000000000000000000000000000000000000000000');constsignerKeyPair=newSymbolFacade.KeyPair(newPrivateKey(SIGNER_PRIVATE_KEY));constfacade=newSymbolFacade('testnet');constsignerAddress=facade.network.publicKeyToAddress(signerKeyPair.publicKey);console.log('Signer address:',signerAddress.toString());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);// --- ADDING NEW METADATA ---console.log('\n--- Adding new metadata ---');// Define metadata key and valueconstkeyString=`username_${Date.now()}`;constscopedMetadataKey=metadataGenerateKey(keyString);constmetadataValue=newTextEncoder().encode('alice');// Create the embedded metadata transactionconstembeddedTransaction=facade.transactionFactory.createEmbedded({type:'account_metadata_transaction_v1',signerPublicKey:signerKeyPair.publicKey.toString(),targetAddress:signerAddress.toString(),scopedMetadataKey,// When creating new metadata, valueSizeDelta// equals value lengthvalueSizeDelta:metadataValue.length,value:metadataValue});console.log('Created embedded metadata transaction:');console.log(JSON.stringify(embeddedTransaction.toJson(),null,2));// Build the aggregate transactionconstembeddedTransactions=[embeddedTransaction];consttransaction=facade.transactionFactory.create({type:'aggregate_complete_transaction_v3',signerPublicKey:signerKeyPair.publicKey.toString(),deadline:timestamp.addHours(2).timestamp,transactionsHash:facade.static.hashEmbeddedTransactions(embeddedTransactions),transactions:embeddedTransactions});transaction.fee=newmodels.Amount(feeMult*transaction.size);// Sign and generate final payloadconstsignature=facade.signTransaction(signerKeyPair,transaction);constjsonPayload=facade.transactionFactory.static.attachSignature(transaction,signature);// Announce and wait for confirmationconsttransactionHash=facade.hashTransaction(transaction).toString();console.log('Built aggregate transaction with hash:',transactionHash);awaitannounceTransaction(jsonPayload,'aggregate transaction');awaitwaitForConfirmation(transactionHash,'aggregate transaction');// --- MODIFYING EXISTING METADATA ---console.log('\n--- Modifying existing metadata ---');// Fetch current metadata value from networkconstscopedKeyHex=scopedMetadataKey.toString(16).toUpperCase().padStart(16,'0');constmetadataPath=`/metadata?sourceAddress=${signerAddress}`+`&targetAddress=${signerAddress}`+`&scopedMetadataKey=${scopedKeyHex}`+'&metadataType=0';console.log('Fetching current metadata from',metadataPath);constmetadataResponse=awaitfetch(`${NODE_URL}${metadataPath}`);constmetadataJSON=awaitmetadataResponse.json();// Get the metadata entryif(!metadataJSON.data.length){thrownewError('Metadata entry not found');}constmetadataEntry=metadataJSON.data[0].metadataEntry;constcurrentValue=Buffer.from(metadataEntry.value,'hex');console.log(' Current value:',currentValue.toString('utf8'));// XOR the current and new valuesconstnewValue=newTextEncoder().encode('bob');constupdateValue=metadataUpdateValue(currentValue,newValue);// Create the update transaction with XOR'd valueconstembeddedUpdate=facade.transactionFactory.createEmbedded({type:'account_metadata_transaction_v1',signerPublicKey:signerKeyPair.publicKey.toString(),targetAddress:signerAddress.toString(),scopedMetadataKey,// valueSizeDelta is the difference in length// (can be negative)valueSizeDelta:newValue.length-currentValue.length,value:updateValue});// Build the aggregate for the updateconstupdateEmbedded=[embeddedUpdate];constupdateTransaction=facade.transactionFactory.create({type:'aggregate_complete_transaction_v3',signerPublicKey:signerKeyPair.publicKey.toString(),deadline:timestamp.addHours(2).timestamp,transactionsHash:facade.static.hashEmbeddedTransactions(updateEmbedded),transactions:updateEmbedded});updateTransaction.fee=newmodels.Amount(feeMult*updateTransaction.size);// Sign and announce the updateconstupdateSignature=facade.signTransaction(signerKeyPair,updateTransaction);constupdatePayload=facade.transactionFactory.static.attachSignature(updateTransaction,updateSignature);// Announce and wait for confirmationconstupdateHash=facade.hashTransaction(updateTransaction).toString();console.log('Built aggregate transaction with hash:',updateHash);awaitannounceTransaction(updatePayload,'aggregate transaction');awaitwaitForConfirmation(updateHash,'aggregate transaction');}catch(e){console.error(e.message,'| Cause:',e.cause?.code??'unknown');}
# 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}')
# Create the embedded metadata transactionembedded_transaction=facade.transaction_factory.create_embedded({'type':'account_metadata_transaction_v1','signer_public_key':signer_key_pair.public_key,'target_address':signer_address,'scoped_metadata_key':scoped_metadata_key,# When creating new metadata, value_size_delta# equals the value length'value_size_delta':len(metadata_value),'value':metadata_value})print('Created embedded metadata transaction:')print(json.dumps(embedded_transaction.to_json(),indent=2))
// Create the embedded metadata transactionconstembeddedTransaction=facade.transactionFactory.createEmbedded({type:'account_metadata_transaction_v1',signerPublicKey:signerKeyPair.publicKey.toString(),targetAddress:signerAddress.toString(),scopedMetadataKey,// When creating new metadata, valueSizeDelta// equals value lengthvalueSizeDelta:metadataValue.length,value:metadataValue});console.log('Created embedded metadata transaction:');console.log(JSON.stringify(embeddedTransaction.toJson(),null,2));
# Build the aggregate transactionembedded_transactions=[embedded_transaction]transaction=facade.transaction_factory.create({'type':'aggregate_complete_transaction_v3','signer_public_key':signer_key_pair.public_key,'deadline':timestamp.add_hours(2).timestamp,'transactions_hash':facade.hash_embedded_transactions(embedded_transactions),'transactions':embedded_transactions})transaction.fee=Amount(fee_mult*transaction.size)
// Build the aggregate transactionconstembeddedTransactions=[embeddedTransaction];consttransaction=facade.transactionFactory.create({type:'aggregate_complete_transaction_v3',signerPublicKey:signerKeyPair.publicKey.toString(),deadline:timestamp.addHours(2).timestamp,transactionsHash:facade.static.hashEmbeddedTransactions(embeddedTransactions),transactions:embeddedTransactions});transaction.fee=newmodels.Amount(feeMult*transaction.size);
# Sign and generate final payloadsignature=facade.sign_transaction(signer_key_pair,transaction)json_payload=facade.transaction_factory.attach_signature(transaction,signature)# 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')
// Sign and generate final payloadconstsignature=facade.signTransaction(signerKeyPair,transaction);constjsonPayload=facade.transactionFactory.static.attachSignature(transaction,signature);// Announce and wait for confirmationconsttransactionHash=facade.hashTransaction(transaction).toString();console.log('Built aggregate transaction with hash:',transactionHash);awaitannounceTransaction(jsonPayload,'aggregate transaction');awaitwaitForConfirmation(transactionHash,'aggregate transaction');
# Fetch current metadata value from networkmetadata_path=(f'/metadata?sourceAddress={signer_address}'f'&targetAddress={signer_address}'f'&scopedMetadataKey={scoped_metadata_key:016X}''&metadataType=0')print(f'Fetching current metadata from {metadata_path}')withurllib.request.urlopen(f'{NODE_URL}{metadata_path}')asresponse:response_json=json.loads(response.read().decode())# Get the metadata entryifnotresponse_json['data']:raiseException('Metadata entry not found')metadata_entry=response_json['data'][0]['metadataEntry']current_value=bytes.fromhex(metadata_entry['value'])print(f' Current value: {current_value.decode("utf8")}')
// Fetch current metadata value from networkconstscopedKeyHex=scopedMetadataKey.toString(16).toUpperCase().padStart(16,'0');constmetadataPath=`/metadata?sourceAddress=${signerAddress}`+`&targetAddress=${signerAddress}`+`&scopedMetadataKey=${scopedKeyHex}`+'&metadataType=0';console.log('Fetching current metadata from',metadataPath);constmetadataResponse=awaitfetch(`${NODE_URL}${metadataPath}`);constmetadataJSON=awaitmetadataResponse.json();// Get the metadata entryif(!metadataJSON.data.length){thrownewError('Metadata entry not found');}constmetadataEntry=metadataJSON.data[0].metadataEntry;constcurrentValue=Buffer.from(metadataEntry.value,'hex');console.log(' Current value:',currentValue.toString('utf8'));
# XOR the current and new valuesnew_value='bob'.encode('utf8')update_value=metadata_update_value(current_value,new_value)# Create the update transaction with XOR'd valueembedded_update=facade.transaction_factory.create_embedded({'type':'account_metadata_transaction_v1','signer_public_key':signer_key_pair.public_key,'target_address':signer_address,'scoped_metadata_key':scoped_metadata_key,# value_size_delta is the difference in length# (can be negative)'value_size_delta':len(new_value)-len(current_value),'value':update_value})
// XOR the current and new valuesconstnewValue=newTextEncoder().encode('bob');constupdateValue=metadataUpdateValue(currentValue,newValue);// Create the update transaction with XOR'd valueconstembeddedUpdate=facade.transactionFactory.createEmbedded({type:'account_metadata_transaction_v1',signerPublicKey:signerKeyPair.publicKey.toString(),targetAddress:signerAddress.toString(),scopedMetadataKey,// valueSizeDelta is the difference in length// (can be negative)valueSizeDelta:newValue.length-currentValue.length,value:updateValue});
既存のメタデータエントリを更新するには、前述のようにネットワークから取得した現在の値が必要です。
メタデータの更新を実演するために、コードは同じメタデータキーを使用して別の account_metadata_transaction_v1 トランザクションを作成し、ユーザー名を alice から bob に変更します。
# Build the aggregate for the updateembedded_transactions=[embedded_update]update_transaction=facade.transaction_factory.create({'type':'aggregate_complete_transaction_v3','signer_public_key':signer_key_pair.public_key,'deadline':timestamp.add_hours(2).timestamp,'transactions_hash':facade.hash_embedded_transactions(embedded_transactions),'transactions':embedded_transactions})update_transaction.fee=Amount(fee_mult*update_transaction.size)# Sign and announce the updatesignature=facade.sign_transaction(signer_key_pair,update_transaction)json_payload=facade.transaction_factory.attach_signature(update_transaction,signature)# Announce and wait for confirmationupdate_hash=facade.hash_transaction(update_transaction)print(f'Built aggregate transaction with hash: {update_hash}')announce_transaction(json_payload,'aggregate transaction')wait_for_confirmation(update_hash,'aggregate transaction')
// Build the aggregate for the updateconstupdateEmbedded=[embeddedUpdate];constupdateTransaction=facade.transactionFactory.create({type:'aggregate_complete_transaction_v3',signerPublicKey:signerKeyPair.publicKey.toString(),deadline:timestamp.addHours(2).timestamp,transactionsHash:facade.static.hashEmbeddedTransactions(updateEmbedded),transactions:updateEmbedded});updateTransaction.fee=newmodels.Amount(feeMult*updateTransaction.size);// Sign and announce the updateconstupdateSignature=facade.signTransaction(signerKeyPair,updateTransaction);constupdatePayload=facade.transactionFactory.static.attachSignature(updateTransaction,updateSignature);// Announce and wait for confirmationconstupdateHash=facade.hashTransaction(updateTransaction).toString();console.log('Built aggregate transaction with hash:',updateHash);awaitannounceTransaction(updatePayload,'aggregate transaction');awaitwaitForConfirmation(updateHash,'aggregate transaction');