importjsonimportosimporttimeimporturllib.requestfromsymbolchain.CryptoTypesimportPrivateKeyfromsymbolchain.facade.SymbolFacadeimportSymbolFacadefromsymbolchain.symbol.NetworkimportNetworkTimestampfromsymbolchain.symbol.IdGeneratorimport(generate_namespace_path,generate_mosaic_alias_id)fromsymbolchain.scimportAmountNODE_URL=os.getenv('NODE_URL','https://reference.symboltest.net:3001')print(f'Using node {NODE_URL}')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}')namespace_name=os.getenv('NAMESPACE_NAME','my_namespace')print(f'Namespace name: {namespace_name}')namespace_id=generate_namespace_path(namespace_name)[-1]print(f'Namespace ID: {namespace_id} ({hex(namespace_id)})')# Target mosaic ID to link the namespace tomosaic_id=int(os.getenv('MOSAIC_ID','0x45C8C3733983AAC2'),16)print(f'Mosaic ID: {mosaic_id} ({hex(mosaic_id)})')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}')# Build the alias transactiontransaction=facade.transaction_factory.create({'type':'mosaic_alias_transaction_v1','signer_public_key':signer_key_pair.public_key,'deadline':timestamp.add_hours(2).timestamp,'namespace_id':namespace_id,'mosaic_id':mosaic_id,'alias_action':'link'})transaction.fee=Amount(fee_mult*transaction.size)# Sign transaction and generate final payloadsignature=facade.sign_transaction(signer_key_pair,transaction)json_payload=facade.transaction_factory.attach_signature(transaction,signature)print('Built transaction:')print(json.dumps(transaction.to_json(),indent=2))transaction_hash=facade.hash_transaction(transaction)print(f'Transaction hash: {transaction_hash}')# Announce transactionprint('Announcing mosaic alias transaction to /transactions')request=urllib.request.Request(f'{NODE_URL}/transactions',data=json_payload.encode(),headers={'Content-Type':'application/json'},method='PUT')withurllib.request.urlopen(request)asresponse:print(f' Response: {response.read().decode()}')# Wait for confirmationprint('Waiting for transaction confirmation...')forattemptinrange(60):time.sleep(1)try:status_url=(f'{NODE_URL}/transactionStatus/{transaction_hash}')withurllib.request.urlopen(status_url)asresponse:status=json.loads(response.read().decode())print(f' Transaction status: {status["group"]}')ifstatus['group']=='confirmed':print('Mosaic alias transaction confirmed in',attempt,'seconds')breakifstatus['group']=='failed':raiseException('Mosaic alias transaction failed:',status['code'])excepturllib.error.HTTPError:print(' Transaction status: unknown')# Retrieve the namespace to verify the aliasnamespace_path=f'/namespaces/{namespace_id:x}'print(f'Fetching namespace information from {namespace_path}')withurllib.request.urlopen(f'{NODE_URL}{namespace_path}')asresponse:response_json=json.loads(response.read().decode())namespace_info=response_json['namespace']print('Alias information:')alias_type=namespace_info['alias']['type']print(f' Alias type: {alias_type}')ifalias_type==1:# MOSAIC typealiased_mosaic_id=namespace_info['alias']['mosaicId']print(f' Linked mosaic ID: {aliased_mosaic_id}')# Send a transfer using the alias instead of a raw mosaic IDprint(f'Using alias in transfer: {namespace_name}')# Convert namespace to mosaic alias IDmosaic_alias_id=generate_mosaic_alias_id(namespace_name)transfer_tx=facade.transaction_factory.create({'type':'transfer_transaction_v1','signer_public_key':signer_key_pair.public_key,'deadline':timestamp.add_hours(2).timestamp,'recipient_address':facade.network.public_key_to_address(signer_key_pair.public_key),'mosaics':[{'mosaic_id':mosaic_alias_id,'amount':1}]})print('Transfer transaction:')print(f' Mosaic ID (alias):'f' {mosaic_alias_id} ({hex(mosaic_alias_id)})')exceptExceptionase:print(e)
import{PrivateKey}from'symbol-sdk';import{SymbolFacade,NetworkTimestamp,models,generateNamespacePath,generateMosaicAliasId}from'symbol-sdk/symbol';constNODE_URL=process.env.NODE_URL||'https://reference.symboltest.net:3001';console.log('Using node',NODE_URL);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());constnamespaceName=process.env.NAMESPACE_NAME||'my_namespace';console.log('Namespace name:',namespaceName);constnsPath=generateNamespacePath(namespaceName);constnamespaceId=nsPath[nsPath.length-1];console.log('Namespace ID:',`${namespaceId} (0x${namespaceId.toString(16)})`);// Target mosaic ID to link the namespace toconstmosaicId=BigInt(process.env.MOSAIC_ID||'0x45C8C3733983AAC2');console.log('Mosaic ID:',`${mosaicId} (0x${mosaicId.toString(16)})`);try{// Fetch current network timeconsttimePath='/node/time';console.log('Fetching current network time from',timePath);consttimeResponse=awaitfetch(`${NODE_URL}${timePath}`);consttimeJSON=awaittimeResponse.json();constreceiveTimestamp=timeJSON.communicationTimestamps.receiveTimestamp;consttimestamp=newNetworkTimestamp(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 alias transactionconsttransaction=facade.transactionFactory.create({type:'mosaic_alias_transaction_v1',signerPublicKey:signerKeyPair.publicKey.toString(),deadline:timestamp.addHours(2).timestamp,namespaceId:namespaceId,mosaicId:mosaicId,aliasAction:'link'});transaction.fee=newmodels.Amount(feeMult*transaction.size);// Sign transaction and generate final payloadconstsignature=facade.signTransaction(signerKeyPair,transaction);constjsonPayload=facade.transactionFactory.static.attachSignature(transaction,signature);console.log('Built transaction:');console.dir(transaction.toJson(),{colors:true});consttransactionHash=facade.hashTransaction(transaction).toString();console.log('Transaction hash:',transactionHash);// Announce transactionconsole.log('Announcing mosaic alias transaction to /transactions');constannounceResponse=awaitfetch(`${NODE_URL}/transactions`,{method:'PUT',headers:{'Content-Type':'application/json'},body:jsonPayload});console.log(' Response:',awaitannounceResponse.text());// Wait for confirmationconsole.log('Waiting for transaction confirmation...');for(letattempt=0;attempt<60;attempt++){awaitnewPromise(resolve=>setTimeout(resolve,1000));try{conststatusUrl=`${NODE_URL}/transactionStatus/${transactionHash}`;conststatusResponse=awaitfetch(statusUrl);if(!statusResponse.ok){console.log(' Transaction status: unknown');continue;}conststatus=awaitstatusResponse.json();console.log(' Transaction status:',status.group);if(status.group==='confirmed'){console.log('Mosaic alias transaction confirmed in',attempt,'seconds');break;}if(status.group==='failed'){thrownewError(`Mosaic alias transaction failed: ${status.code}`);}}catch(error){if(error.message.includes('failed:')){throwerror;}console.log(' Transaction status: unknown');}}// Retrieve the namespace to verify the aliasconstnamespacePath=`/namespaces/${namespaceId.toString(16)}`;console.log('Fetching namespace information from',namespacePath);constnamespaceResponse=awaitfetch(`${NODE_URL}${namespacePath}`);constnamespaceJSON=awaitnamespaceResponse.json();constnamespaceInfo=namespaceJSON.namespace;console.log('Alias information:');console.log(' Alias type:',namespaceInfo.alias.type);if(namespaceInfo.alias.type===1){// MOSAIC typeconsole.log(' Linked mosaic ID:',namespaceInfo.alias.mosaicId);}// Send a transfer using the alias instead of a raw mosaic IDconsole.log('Using alias in transfer:',namespaceName);// Convert namespace to mosaic alias IDconstmosaicAliasId=generateMosaicAliasId(namespaceName);consttransferTx=facade.transactionFactory.create({type:'transfer_transaction_v1',signerPublicKey:signerKeyPair.publicKey.toString(),deadline:timestamp.addHours(2).timestamp,recipientAddress:facade.network.publicKeyToAddress(signerKeyPair.publicKey).toString(),mosaics:[{mosaicId:mosaicAliasId,amount:1n}]});console.log('Transfer transaction:');console.log(' Mosaic ID (alias):',`${mosaicAliasId} (0x${mosaicAliasId.toString(16)})`);}catch(e){console.error(e.message);}
The snippet reads the signer's private key from the SIGNER_PRIVATE_KEY environment variable, which defaults to a
test key if not set.
The signer's address is derived from the public key.
This account must own both the namespace and the mosaic being linked.
namespace_name=os.getenv('NAMESPACE_NAME','my_namespace')print(f'Namespace name: {namespace_name}')namespace_id=generate_namespace_path(namespace_name)[-1]print(f'Namespace ID: {namespace_id} ({hex(namespace_id)})')# Target mosaic ID to link the namespace tomosaic_id=int(os.getenv('MOSAIC_ID','0x45C8C3733983AAC2'),16)print(f'Mosaic ID: {mosaic_id} ({hex(mosaic_id)})')
constnamespaceName=process.env.NAMESPACE_NAME||'my_namespace';console.log('Namespace name:',namespaceName);constnsPath=generateNamespacePath(namespaceName);constnamespaceId=nsPath[nsPath.length-1];console.log('Namespace ID:',`${namespaceId} (0x${namespaceId.toString(16)})`);// Target mosaic ID to link the namespace toconstmosaicId=BigInt(process.env.MOSAIC_ID||'0x45C8C3733983AAC2');console.log('Mosaic ID:',`${mosaicId} (0x${mosaicId.toString(16)})`);
The code defines:
Namespace name: The namespace to link, read from the NAMESPACE_NAME environment variable,
which defaults to my_namespace if not set.
This name must match a namespace that your account already owns.
Namespace ID: The ID is generated from the namespace name using ,
which returns an array of IDs for each level in the hierarchy.
The last element is selected to get the final namespace ID.
Taking the last element works for both root namespaces and subnamespaces.
For a root namespace like foo, the array contains one element.
For a subnamespace like symbol.xym, it contains two elements, and the last one is the ID of xym under symbol.
Subnamespace IDs are unique
Subnamespace IDs are derived hierarchically, so two subnamespaces with the same leaf name but different parents
produce different IDs.
For example, the last element of the path for foo.xym and bar.xym will be different.
Mosaic ID: The hexadecimal identifier of the mosaic that the namespace will point to, read from
the MOSAIC_ID environment variable. If not set, a default test mosaic ID is used.
# 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}')
# Build the alias transactiontransaction=facade.transaction_factory.create({'type':'mosaic_alias_transaction_v1','signer_public_key':signer_key_pair.public_key,'deadline':timestamp.add_hours(2).timestamp,'namespace_id':namespace_id,'mosaic_id':mosaic_id,'alias_action':'link'})transaction.fee=Amount(fee_mult*transaction.size)
// Build the alias transactionconsttransaction=facade.transactionFactory.create({type:'mosaic_alias_transaction_v1',signerPublicKey:signerKeyPair.publicKey.toString(),deadline:timestamp.addHours(2).timestamp,namespaceId:namespaceId,mosaicId:mosaicId,aliasAction:'link'});transaction.fee=newmodels.Amount(feeMult*transaction.size);
The mosaic alias transaction specifies:
Type: Mosaic alias transactions use the type mosaic_alias_transaction_v1.
Signer public key: The account that owns the namespace and mosaic, and will pay the transaction fee.
Namespace ID: The identifier of the namespace being linked.
Mosaic ID: The identifier of the mosaic to link to the namespace.
Alias action: The value link creates the alias. To remove the alias later, use unlink instead.
Unlinking an alias
To unlink a namespace from a mosaic, announce another mosaic_alias_transaction_v1 transaction with the same
namespace ID and mosaic ID, but set the alias_action field to unlink.
The unlinking process does not remove the namespace or the mosaic, only the association between them.
After unlinking, the namespace can be linked to a different mosaic or address.
# Sign transaction and generate final payloadsignature=facade.sign_transaction(signer_key_pair,transaction)json_payload=facade.transaction_factory.attach_signature(transaction,signature)print('Built transaction:')print(json.dumps(transaction.to_json(),indent=2))transaction_hash=facade.hash_transaction(transaction)print(f'Transaction hash: {transaction_hash}')# Announce transactionprint('Announcing mosaic alias transaction to /transactions')request=urllib.request.Request(f'{NODE_URL}/transactions',data=json_payload.encode(),headers={'Content-Type':'application/json'},method='PUT')withurllib.request.urlopen(request)asresponse:print(f' Response: {response.read().decode()}')
// Sign transaction and generate final payloadconstsignature=facade.signTransaction(signerKeyPair,transaction);constjsonPayload=facade.transactionFactory.static.attachSignature(transaction,signature);console.log('Built transaction:');console.dir(transaction.toJson(),{colors:true});consttransactionHash=facade.hashTransaction(transaction).toString();console.log('Transaction hash:',transactionHash);// Announce transactionconsole.log('Announcing mosaic alias transaction to /transactions');constannounceResponse=awaitfetch(`${NODE_URL}/transactions`,{method:'PUT',headers:{'Content-Type':'application/json'},body:jsonPayload});console.log(' Response:',awaitannounceResponse.text());
# Wait for confirmationprint('Waiting for transaction confirmation...')forattemptinrange(60):time.sleep(1)try:status_url=(f'{NODE_URL}/transactionStatus/{transaction_hash}')withurllib.request.urlopen(status_url)asresponse:status=json.loads(response.read().decode())print(f' Transaction status: {status["group"]}')ifstatus['group']=='confirmed':print('Mosaic alias transaction confirmed in',attempt,'seconds')breakifstatus['group']=='failed':raiseException('Mosaic alias transaction failed:',status['code'])excepturllib.error.HTTPError:print(' Transaction status: unknown')
# Retrieve the namespace to verify the aliasnamespace_path=f'/namespaces/{namespace_id:x}'print(f'Fetching namespace information from {namespace_path}')withurllib.request.urlopen(f'{NODE_URL}{namespace_path}')asresponse:response_json=json.loads(response.read().decode())namespace_info=response_json['namespace']print('Alias information:')alias_type=namespace_info['alias']['type']print(f' Alias type: {alias_type}')ifalias_type==1:# MOSAIC typealiased_mosaic_id=namespace_info['alias']['mosaicId']print(f' Linked mosaic ID: {aliased_mosaic_id}')
// Retrieve the namespace to verify the aliasconstnamespacePath=`/namespaces/${namespaceId.toString(16)}`;console.log('Fetching namespace information from',namespacePath);constnamespaceResponse=awaitfetch(`${NODE_URL}${namespacePath}`);constnamespaceJSON=awaitnamespaceResponse.json();constnamespaceInfo=namespaceJSON.namespace;console.log('Alias information:');console.log(' Alias type:',namespaceInfo.alias.type);if(namespaceInfo.alias.type===1){// MOSAIC typeconsole.log(' Linked mosaic ID:',namespaceInfo.alias.mosaicId);}
To verify the alias was created, the code retrieves the namespace information from the network
using the /namespaces/{namespaceId}GET endpoint.
The response includes the alias type (mosaic) and the linked mosaic ID, confirming the namespace now points to the
specified mosaic.
# Send a transfer using the alias instead of a raw mosaic IDprint(f'Using alias in transfer: {namespace_name}')# Convert namespace to mosaic alias IDmosaic_alias_id=generate_mosaic_alias_id(namespace_name)transfer_tx=facade.transaction_factory.create({'type':'transfer_transaction_v1','signer_public_key':signer_key_pair.public_key,'deadline':timestamp.add_hours(2).timestamp,'recipient_address':facade.network.public_key_to_address(signer_key_pair.public_key),'mosaics':[{'mosaic_id':mosaic_alias_id,'amount':1}]})print('Transfer transaction:')print(f' Mosaic ID (alias):'f' {mosaic_alias_id} ({hex(mosaic_alias_id)})')
// Send a transfer using the alias instead of a raw mosaic IDconsole.log('Using alias in transfer:',namespaceName);// Convert namespace to mosaic alias IDconstmosaicAliasId=generateMosaicAliasId(namespaceName);consttransferTx=facade.transactionFactory.create({type:'transfer_transaction_v1',signerPublicKey:signerKeyPair.publicKey.toString(),deadline:timestamp.addHours(2).timestamp,recipientAddress:facade.network.publicKeyToAddress(signerKeyPair.publicKey).toString(),mosaics:[{mosaicId:mosaicAliasId,amount:1n}]});console.log('Transfer transaction:');console.log(' Mosaic ID (alias):',`${mosaicAliasId} (0x${mosaicAliasId.toString(16)})`);
Once the namespace is linked to a mosaic, the namespace can be used in place of the mosaic ID in transactions.
The code demonstrates creating a transfer transaction using the alias in the mosaics array instead of
the full hexadecimal mosaic ID.
For simplicity, this example sends the mosaic back to the sender's own address
and does not announce the transaction or wait for its confirmation.
To use a namespace as a mosaic ID, the namespace name is converted to its mosaic alias ID using
.
For more details on how to announce transfer transactions, see the
Transfer Transaction tutorial.
Mosaic Resolution Receipt
When the network processes a transaction that uses a namespace alias as a mosaic ID, it generates a
Mosaic Resolution Receipt.
This receipt records the actual mosaic ID the alias pointed to at the time the transaction was confirmed.
This is important for historical auditability: since aliases can be changed or removed at any time, the receipt
ensures that the resolved mosaic ID can always be verified, even if the alias has since been updated.
Using node https://reference.symboltest.net:3001
Signer address: TCHBDENCLKEBILBPWP3JPB2XNY64OE7PYHHE32I
Namespace name: nsmos_1770541301
Namespace ID: 13340994241872799248 (0xb924bb94515c2610)
Mosaic ID: 5028483883612744386 (0x45c8c3733983aac2)
Fetching current network time from /node/time
Network time: 103292001688 ms since nemesis
Fetching recommended fees from /network/fees/transaction
Fee multiplier: 100
Built transaction:
{
"signature": "538EC97E2E9BF4099A28BCA4E7A7C1FEE53F9D7B85C2EB8DC4690B61F4D0CA350F3DEA096F284C4E8E99B533E383CF6C5602E5DA121244A0742997673A9AC60B",
"signer_public_key": "3B6A27BCCEB6A42D62A3A8D02A6F0D73653215771DE243A63AC048A18B59DA29",
"version": 1,
"network": 152,
"type": 17230,
"fee": "14500",
"deadline": "103299201688",
"namespace_id": "13340994241872799248",
"mosaic_id": "5028483883612744386",
"alias_action": 1
}
Transaction hash: CAA9489E08AA66371D4342D420A75622E6394D6198BF130D01A0B31424AD6E38
Announcing mosaic alias transaction to /transactions
Response: {"message":"packet 9 was pushed to the network via /transactions"}
Waiting for transaction confirmation...
Transaction status: unconfirmed
Transaction status: confirmed
Mosaic alias transaction confirmed in 16 seconds
Fetching namespace information from /namespaces/b924bb94515c2610
Alias information:
Alias type: 1
Linked mosaic ID: 45C8C3733983AAC2
Using alias in transfer: nsmos_1770541301
Transfer transaction:
Mosaic ID (alias): 13340994241872799248 (0xb924bb94515c2610)
Some highlights from the output:
Namespace and target (lines 3, 5): The namespace nsmos_1770541301 is being linked to the target mosaic ID.
Transaction hash (line 23): The transaction hash can be used to search for the transaction in the
Symbol Testnet Explorer.
Alias verification (lines 32-33): The namespace information confirms the alias type is 1 (mosaic) and
shows the linked mosaic ID.
Using the alias (lines 36): A transfer transaction is created using the alias in the mosaics array,
demonstrating that it can be used in place of the full mosaic ID.
Different mosaic ID
The mosaic ID used in the transfer differs from the original mosaic ID because it is the
encoded namespace ID, not the mosaic ID itself.
The network resolves the alias to the linked mosaic when processing the transaction.