Assigning metadata to a namespace

Add custom data to a namespace.

Use case

Metadata can be attached to namespaces to help users verify the ownership of the registered name. The data attached could include information such as registrant, administrative, or technical contact information.

Imagine that the company ComfyClothingCompany wants to add their information to their namespace cc so that any user of the network can quickly identify the company’s details. In this guide, you are going to implement a program to add metadata entries to a namespace.

../../_images/metadata-namespace.png

Prerequisites

  • Complete the getting started section.

  • Create a new account.

  • Load the account with enough symbol.xym to pay for transaction fees, create mosaics and register namespaces.

Creating the namespace

  1. Create a new namespace cc with the ComfyClothingCompany’s account.

symbol-cli transaction namespace --sync

Enter namespace name: cc
Do you want to create a root namespace? [y/n]: y
Enter the namespace rental duration: 1000
Enter max_fee (absolute amount): 0
Transaction confirmed.

Method #01: Using the SDK

  1. Define the following NamespaceMetadataTransaction.

Key

Value

NAME

ComfyClothingCompany

EMAIL

info@comfyclothingcompany

ADDRESS

ComfyClothingCompany HQ

PHONE

000-0000

// replace with network type
const networkType = NetworkType.TEST_NET;
// replace with company private key
const companyPrivateKey =
  'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA';
const companyAccount = Account.createFromPrivateKey(
  companyPrivateKey,
  networkType,
);
// replace with namespace name
const namespaceId = new NamespaceId('cc');
const name = 'ComfyClothingCompany';
const email = 'info@comfyclothingcompany';
const address = 'ComfyClothingCompany HQ';
const phone = '000-0000';

const nameMetadataTransaction = NamespaceMetadataTransaction.create(
  Deadline.create(epochAdjustment),
  companyAccount.address,
  KeyGenerator.generateUInt64Key('NAME'),
  namespaceId,
  name.length,
  name,
  networkType,
);

const emailMetadataTransaction = NamespaceMetadataTransaction.create(
  Deadline.create(epochAdjustment),
  companyAccount.address,
  KeyGenerator.generateUInt64Key('EMAIL'),
  namespaceId,
  email.length,
  email,
  networkType,
);

const addressMetadataTransaction = NamespaceMetadataTransaction.create(
  Deadline.create(epochAdjustment),
  companyAccount.address,
  KeyGenerator.generateUInt64Key('ADDRESS'),
  namespaceId,
  address.length,
  address,
  networkType,
);

const phoneMetadataTransaction = NamespaceMetadataTransaction.create(
  Deadline.create(epochAdjustment),
  companyAccount.address,
  KeyGenerator.generateUInt64Key('PHONE'),
  namespaceId,
  phone.length,
  phone,
  networkType,
);
// replace with network type
const networkType = symbol_sdk_1.NetworkType.TEST_NET;
// replace with company private key
const companyPrivateKey = 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA';
const companyAccount = symbol_sdk_1.Account.createFromPrivateKey(companyPrivateKey, networkType);
// replace with namespace name
const namespaceId = new symbol_sdk_1.NamespaceId('cc');
const name = 'ComfyClothingCompany';
const email = 'info@comfyclothingcompany';
const address = 'ComfyClothingCompany HQ';
const phone = '000-0000';
const nameMetadataTransaction = symbol_sdk_1.NamespaceMetadataTransaction.create(symbol_sdk_1.Deadline.create(epochAdjustment), companyAccount.address, symbol_sdk_1.KeyGenerator.generateUInt64Key('NAME'), namespaceId, name.length, name, networkType);
const emailMetadataTransaction = symbol_sdk_1.NamespaceMetadataTransaction.create(symbol_sdk_1.Deadline.create(epochAdjustment), companyAccount.address, symbol_sdk_1.KeyGenerator.generateUInt64Key('EMAIL'), namespaceId, email.length, email, networkType);
const addressMetadataTransaction = symbol_sdk_1.NamespaceMetadataTransaction.create(symbol_sdk_1.Deadline.create(epochAdjustment), companyAccount.address, symbol_sdk_1.KeyGenerator.generateUInt64Key('ADDRESS'), namespaceId, address.length, address, networkType);
const phoneMetadataTransaction = symbol_sdk_1.NamespaceMetadataTransaction.create(symbol_sdk_1.Deadline.create(epochAdjustment), companyAccount.address, symbol_sdk_1.KeyGenerator.generateUInt64Key('PHONE'), namespaceId, phone.length, phone, networkType);

2. All metadata is attached only with the consent of the namespace creator through Aggregate Transactions. Wrap the metadata transactions inside an AggregateCompleteTransaction and sign the aggregate with the company’s account.

const aggregateTransaction = AggregateTransaction.createComplete(
  Deadline.create(epochAdjustment),
  [
    nameMetadataTransaction.toAggregate(companyAccount.publicAccount),
    emailMetadataTransaction.toAggregate(companyAccount.publicAccount),
    addressMetadataTransaction.toAggregate(companyAccount.publicAccount),
    phoneMetadataTransaction.toAggregate(companyAccount.publicAccount),
  ],
  networkType,
  [],
  UInt64.fromUint(2000000),
);
const aggregateTransaction = symbol_sdk_1.AggregateTransaction.createComplete(symbol_sdk_1.Deadline.create(epochAdjustment), [
    nameMetadataTransaction.toAggregate(companyAccount.publicAccount),
    emailMetadataTransaction.toAggregate(companyAccount.publicAccount),
    addressMetadataTransaction.toAggregate(companyAccount.publicAccount),
    phoneMetadataTransaction.toAggregate(companyAccount.publicAccount),
], networkType, [], symbol_sdk_1.UInt64.fromUint(2000000));

Note

If a namespace was owned by a different account, you would need to set the aggregate as bonded. Then, the namespace creator needs to accept the metadata request by cosigning the transaction.

  1. Sign and announce the AggregateTransaction to the network.

// replace with meta.networkGenerationHash (nodeUrl + '/node/info')
const networkGenerationHash =
  '1DFB2FAA9E7F054168B0C5FCB84F4DEB62CC2B4D317D861F3168D161F54EA78B';
const signedTransaction = companyAccount.sign(
  aggregateTransaction,
  networkGenerationHash,
);
console.log(signedTransaction.hash);

const nodeUrl = 'NODE_URL';
const repositoryFactory = new RepositoryFactoryHttp(nodeUrl);
const transactionHttp = repositoryFactory.createTransactionRepository();

transactionHttp.announce(signedTransaction).subscribe(
  (x) => console.log(x),
  (err) => console.error(err),
);
// replace with meta.networkGenerationHash (nodeUrl + '/node/info')
const networkGenerationHash = '1DFB2FAA9E7F054168B0C5FCB84F4DEB62CC2B4D317D861F3168D161F54EA78B';
const signedTransaction = companyAccount.sign(aggregateTransaction, networkGenerationHash);
console.log(signedTransaction.hash);
const nodeUrl = 'NODE_URL';
const repositoryFactory = new symbol_sdk_1.RepositoryFactoryHttp(nodeUrl);
const transactionHttp = repositoryFactory.createTransactionRepository();
transactionHttp.announce(signedTransaction).subscribe((x) => console.log(x), (err) => console.error(err));
  1. When the transaction gets confirmed, try to fetch the namespace’s metadata entries.

Method #02: Using the CLI

Open a terminal window and run the following command.

Replace TCM6YD-BC3BW2-ZYXOXC-HHIRDV-MEZUIP-BRISYI-TPQ with the namespace owner address, 85BBEA6CC462B244 with the target namespace id. Then, set the key-value pairs you want to attatch as metadata.

Note

You can use the command symbol-cli converter stringtokey to transform an string (e.g. NAME) into a valid UInt64 key (8B5DD479E6AB718A).

symbol-cli transaction namespacemetadata --target-address TCM6YD-BC3BW2-ZYXOXC-HHIRDV-MEZUIP-BRISYI-TPQ --namespace-id 85BBEA6CC462B244 --key 8B5DD479E6AB718A --value ComfyClothingCompany
symbol-cli transaction namespacemetadata --target-address TCM6YD-BC3BW2-ZYXOXC-HHIRDV-MEZUIP-BRISYI-TPQ --namespace-id 85BBEA6CC462B244 --key 802FE471ADA04D9D --value info@comfyclothingcompany
symbol-cli transaction namespacemetadata --target-address TCM6YD-BC3BW2-ZYXOXC-HHIRDV-MEZUIP-BRISYI-TPQ --namespace-id 85BBEA6CC462B244 --key D14E8FE298386BF5 --value ComfyClothingCompanyHQ
symbol-cli transaction namespacemetadata --target-address TCM6YD-BC3BW2-ZYXOXC-HHIRDV-MEZUIP-BRISYI-TPQ --namespace-id 85BBEA6CC462B244 --key FAAE8FBA0227A914 --value 000-0000