importjsonimportosimporttimeimporturllib.requestfromsymbolchain.CryptoTypesimportPrivateKeyfromsymbolchain.facade.NemFacadeimportNemFacadefromsymbolchain.ncimportAmountfromsymbolchain.nem.FeeCalculatorimport(calculate_namespace_rental_fee,calculate_transaction_fee)NODE_URL=os.getenv('NODE_URL','http://libertalia.nemtest.net:7890')print(f'Using node {NODE_URL}')# Helper function to announce a transactiondefannounce_transaction(payload,label):announce_path='/transaction/announce'print(f'Announcing {label} to {announce_path}')request=urllib.request.Request(f'{NODE_URL}{announce_path}',data=payload.encode(),headers={'Content-Type':'application/json'},method='POST')withurllib.request.urlopen(request)asannounce_response:result=json.loads(announce_response.read().decode())print(f' Result: {result["message"]}')returnresult['message']# Helper function to wait for transaction confirmationdefwait_for_confirmation(tx_hash,label):status_path=f'/transaction/get?hash={tx_hash}'print(f'Waiting for {label} confirmation from {status_path}')is_confirmed=Falsefor_inrange(120):try:withurllib.request.urlopen(f'{NODE_URL}{status_path}')asstatus_response:confirmed=json.loads(status_response.read().decode())height=confirmed['meta']['height']print(f'{label} confirmed in block {height}')is_confirmed=Truebreakexcepturllib.error.HTTPError:print(' Transaction status: pending')time.sleep(1)ifnotis_confirmed:print(f'{label} confirmation took too long.')SIGNER_PRIVATE_KEY=os.getenv('SIGNER_PRIVATE_KEY','0000000000000000000000000000000000000000000000000000000000000000')signer_key_pair=NemFacade.KeyPair(PrivateKey(SIGNER_PRIVATE_KEY))facade=NemFacade('testnet')signer_address=facade.network.public_key_to_address(signer_key_pair.public_key)print(f'Signer address: {signer_address}')try:# Build the namespace namenamespace_name=os.getenv('ROOT_NAMESPACE',f'ns_{int(time.time())}')print(f'Creating root namespace: {namespace_name}')# Build the transactionrental_fee=calculate_namespace_rental_fee(True)print(f' Namespace lease fee: {rental_fee/1_000_000} XEM')transaction=facade.create_transaction_from_descriptor({'type':'namespace_registration_transaction_v1','rental_fee_sink':'TAMESPACEWH4MKFMBCVFERDPOOP4FK7MTDJEYP35','rental_fee':rental_fee,'name':namespace_name},signer_key_pair.public_key,0,2*60*60)# Calculate and attach the transaction feefee=calculate_transaction_fee(transaction)transaction.fee=Amount(fee)print(f' Transaction fee: {fee/1_000_000} XEM')# 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 the transactionannounce_result=announce_transaction(json_payload,'namespace registration')# Wait for confirmationif'SUCCESS'==announce_result:wait_for_confirmation(transaction_hash,'namespace registration')else:print(f'Transaction rejected: {announce_result}')# Retrieve the namespacenamespace_path=f'/namespace?namespace={namespace_name}'print(f'Fetching namespace information from {namespace_path}')withurllib.request.urlopen(f'{NODE_URL}{namespace_path}')asresponse:namespace_info=json.loads(response.read().decode())print('Namespace information:')print(f' Name: {namespace_info["fqn"]}')print(f' Owner: {namespace_info["owner"]}')print(f' Registration height: {namespace_info["height"]}')exceptExceptionaserror:print(error)
import{PrivateKey}from'symbol-sdk';import{Address,NemFacade,calculateNamespaceRentalFee,calculateTransactionFee,descriptors,models}from'symbol-sdk/nem';constNODE_URL=process.env.NODE_URL||'http://libertalia.nemtest.net:7890';console.log('Using node',NODE_URL);// Helper function to announce a transactionasyncfunctionannounceTransaction(payload,label){constannouncePath='/transaction/announce';console.log(`Announcing ${label} to ${announcePath}`);constresponse=awaitfetch(`${NODE_URL}${announcePath}`,{method:'POST',headers:{'Content-Type':'application/json'},body:payload});if(!response.ok)thrownewError(`HTTP ${response.status}`);constresult=awaitresponse.json();console.log(' Result:',result.message);returnresult.message;}// Helper function to wait for transaction confirmationasyncfunctionwaitForConfirmation(transactionHash,label){conststatusPath=`/transaction/get?hash=${transactionHash}`;console.log(`Waiting for ${label} confirmation from`,statusPath);letisConfirmed=false;for(letattempt=1;120>=attempt;++attempt){constresponse=awaitfetch(`${NODE_URL}${statusPath}`);if(!response.ok){console.log(' Transaction status: pending');awaitnewPromise(resolve=>{setTimeout(resolve,1000);});}else{constconfirmed=awaitresponse.json();console.log(`${label} confirmed in block`,confirmed.meta.height);isConfirmed=true;break;}}if(!isConfirmed)console.warn(`${label} confirmation took too long.`);}constSIGNER_PRIVATE_KEY=process.env.SIGNER_PRIVATE_KEY||'0000000000000000000000000000000000000000000000000000000000000000';constsignerKeyPair=newNemFacade.KeyPair(newPrivateKey(SIGNER_PRIVATE_KEY));constfacade=newNemFacade('testnet');constsignerAddress=facade.network.publicKeyToAddress(signerKeyPair.publicKey);console.log('Signer address:',signerAddress.toString());try{// Build the namespace nameconstnamespaceName=process.env.ROOT_NAMESPACE||`ns_${Math.floor(Date.now()/1000)}`;console.log('Creating root namespace:',namespaceName);// Build the transactionconstrentalFee=calculateNamespaceRentalFee(true);console.log(' Namespace lease fee:',`${Number(rentalFee)/1_000_000} XEM`);consttransaction=facade.createTransactionFromTypedDescriptor(newdescriptors.NamespaceRegistrationTransactionV1Descriptor(newAddress('TAMESPACEWH4MKFMBCVFERDPOOP4FK7MTDJEYP35'),newmodels.Amount(rentalFee),namespaceName),signerKeyPair.publicKey,0n,2*60*60);// Calculate and attach the transaction feeconstfee=calculateTransactionFee(transaction);transaction.fee=newmodels.Amount(fee);console.log(` Transaction fee: ${Number(fee)/1_000_000} XEM`);// 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 the transactionconstannounceResult=awaitannounceTransaction(jsonPayload,'namespace registration');// Wait for confirmationif('SUCCESS'===announceResult)awaitwaitForConfirmation(transactionHash,'namespace registration');elseconsole.log('Transaction rejected:',announceResult);// Retrieve the namespaceconstnamespacePath=`/namespace?namespace=${namespaceName}`;console.log('Fetching namespace information from',namespacePath);constnamespaceResponse=awaitfetch(`${NODE_URL}${namespacePath}`);constnamespaceInfo=awaitnamespaceResponse.json();console.log('Namespace information:');console.log(' Name:',namespaceInfo.fqn);console.log(' Owner:',namespaceInfo.owner);console.log(' Registration height:',namespaceInfo.height);}catch(e){console.error(e.message,'| Cause:',e.cause?.code??'unknown');}
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 will own the registered namespace.
// Build the namespace namefinalStringnamespaceName=System.getenv().getOrDefault("ROOT_NAMESPACE","ns_"+System.currentTimeMillis()/1000);System.out.printf("Creating root namespace: %s%n",namespaceName);
A namespace is identified by its name, which the transaction reserves on the network for one year.
See Name in the Textbook for the naming rules.
To avoid collisions across multiple runs of the tutorial, a timestamp is added to the name.
In practice, however, programs would use a fixed name for their namespaces.
You can force the tutorial to use a fixed name through the ROOT_NAMESPACE environment variable.
The network rejects transactions that send the lease fee to any other address.
: The lease fee, which is 100 XEM for root namespaces.
The SDK's helper returns the required amount.
The argument requests the fee for a root namespace.
The network rejects transactions that pay less than this fee.
Larger amounts are accepted, but the entire amount is transferred to the sink account.
: The name of the root namespace.
The facade adds the signer, which becomes the namespace owner, and derives the timestamp and deadline from the
two-hour deadline duration.
// Calculate and attach the transaction feefinallongfee=FeeCalculator.calculateTransactionFee(transaction);transaction.setFee(newAmount(fee));System.out.printf(" Transaction fee: %s XEM%n",(double)fee/1_000_000);
Finally, the transaction fee is calculated with and attached to the
transaction.
Unlike the lease fee, the transaction fee is paid to the harvester account.
Namespace registration transactions pay a fixed transaction fee of 0.15 XEM, as shown in the
fee schedule.
# 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 the transactionannounce_result=announce_transaction(json_payload,'namespace registration')
// 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 the transactionconstannounceResult=awaitannounceTransaction(jsonPayload,'namespace registration');
// Sign transaction and generate final payloadfinalCryptoTypes.Signaturesignature=facade.signTransaction(signerKeyPair,transaction);finalStringpayload=NemTransactionFactory.attachSignature(transaction,signature);System.out.println("Built transaction:");System.out.println(JSON_MAPPER.writerWithDefaultPrettyPrinter().writeValueAsString(transaction.toJson()));finalStringtransactionHash=facade.hashTransaction(transaction).toString();System.out.printf("Transaction hash: %s%n",transactionHash);// Announce the transactionfinalStringresult=announceTransaction(payload,"namespace registration");
The transaction is signed and announced following the same process as in the
Transfer XEM tutorial.
Namespace name (line 3): The chosen name ns_1783091378 includes a timestamp to ensure uniqueness.
Search for this name in the NEM testnet explorer to view the namespace details.
Lease fee and transaction fee (lines 4-5): The lease fee is 100 XEM because this is a root namespace
(Subnamespaces pay 10 XEM instead), while the transaction fee is 0.15 XEM.
Namespace information (lines 30-32): The registered namespace, its owner (the signer's address), and the
registration height, which is the block at which the lease began.