Qucik Learning Symbol Section 6

Chapter 6 Namespaces

Namespaces are human-readable text strings that can be rented and linked with an address or a mosaic. The name has a maximum length of 64 characters (the only allowed characters are a through z, 0 through 9, _ and -).

6.1 Fee calculation

There is a rental fee associated with registering a namespace which is separate from the network fee. Rental fees fluctuate depending on network activity with costs increasing during busy network periods, therefore it is sensible to check fees before registering a namespace.

In the following example, the fees are calculated for a 365-day rental of a root namespace.


$config = new Configuration();
$config->setHost($NODE_URL);
$client = new GuzzleHttp\Client();

$networkApiInstance = new NetworkRoutesApi($client, $config);
$rootNsperBlock = $networkApiInstance->getRentalFees()->getEffectiveRootNamespaceRentalFeePerBlock();
$rentalDays = 365;
$rentalBlock = ($rentalDays * 24 * 60 * 60) / 30;
$rootNsRenatalFeeTotal = $rentalBlock * $rootNsperBlock;
echo "rentalBlock: " . $rentalBlock . PHP_EOL;
echo "Root Namespace Rental Fee: " . $rootNsRenatalFeeTotal . PHP_EOL;

Sample output


rentalBlock: 1051200
Root Namespace Rental Fee: 210240000 //約210XYM

The duration is specified by the number of blocks; one block is calculated as 30 seconds. There is a minimum rental period of 30 days (maximum 1825 days).

Calculate the fee for acquiring a sub namespace.


$childNamespaceRentalFee = $networkApiInstance->getRentalFees()->getEffectiveChildNamespaceRentalFee();
echo "Child Namespace Rental Fee: " . $childNamespaceRentalFee . PHP_EOL;

Sample output


Child Namespace Rental Fee: 10000000 //10XYM

There is no duration limit specified for the sub namespace. It can be used for as long as the root namespace is registered.

6.2 Rental

Rent a root namespace. (Example:xembook)


$name = "xembook";
$tx = new NamespaceRegistrationTransactionV1(
  network: new NetworkType(NetworkType::TESTNET),
  signerPublicKey: $aliceKey->publicKey,  // 署名者公開鍵
  deadline: new Timestamp($facade->now()->addHours(2)),
  duration: new BlockDuration(86400), // 有効期限
  id: new NamespaceId(IdGenerator::generateNamespaceId($name)), //必須
  name: $name,
);
$facade->setMaxFee($tx, 100);
// 署名
$sig = $aliceKey->signTransaction($tx);
$payload = $facade->attachSignature($tx, $sig);

// アナウンス
$config = new Configuration();
$config->setHost($NODE_URL);
$client = new GuzzleHttp\Client();
$apiInstance = new TransactionRoutesApi($client, $config);

try {
  $result = $apiInstance->announceTransaction($payload);
  echo $result . PHP_EOL;
} catch (Exception $e) {
  echo 'Exception when calling TransactionRoutesApi->announceTransaction: ', $e->getMessage(), PHP_EOL;
}

Rent a sub namespace. (Example:xembook.tomato)


$parnetNameId = IdGenerator::generateNamespaceId("xembook"); //ルートネームスペース名
$name = "tomato"; //サブネームスペース名

// Tx作成
$tx = new NamespaceRegistrationTransactionV1(
  network: new NetworkType(NetworkType::TESTNET),
  signerPublicKey: $aliceKey->publicKey,  // 署名者公開鍵
  deadline: new Timestamp($facade->now()->addHours(2)),
  duration: new BlockDuration(86400), // 有効期限
  parentId: new NamespaceId($parnetNameId),
  id: new NamespaceId(IdGenerator::generateNamespaceId($name, $parnetNameId)),
  registrationType: new NamespaceRegistrationType(NamespaceRegistrationType::CHILD),
  name: $name,
);
$facade->setMaxFee($tx, 200);

// 署名
$sig = $aliceKey->signTransaction($tx);
$payload = $facade->attachSignature($tx, $sig);

/**
 * アナウンス
 */
$config = new Configuration();
$config->setHost($NODE_URL);
$client = new GuzzleHttp\Client();
$apiInstance = new TransactionRoutesApi($client, $config);

try {
  $result = $apiInstance->announceTransaction($payload);
  echo $result . PHP_EOL;
} catch (Exception $e) {
  echo 'Exception when calling TransactionRoutesApi->announceTransaction: ', $e->getMessage(), PHP_EOL;
}

You can also create a tier 2 sub namespace, for example in this case, defining xembook.tomato.morning:


$rootName = IdGenerator::generateNamespaceId("xembook"); //ルートネームスペース名
$parnetNameId = IdGenerator::generateNamespaceId("tomato", $rootName); // 紐づけたい1階層目のサブネームスペース
$name = "morning"; //サブネームスペース名
// 以下はサブネームスペース作成と同じ

6.2.1 Calculation of expiry date

Calculates the expiry date of the rented root namespace.


$namespaceIds = IdGenerator::generateNamespacePath("xembook"); // ルートネームスペース
$namespaceId = new NamespaceId($namespaceIds[count($namespaceIds) - 1]);

$config = new Configuration();
$config->setHost($NODE_URL);
$client = new GuzzleHttp\Client();
$namespaceApiInstance = new NamespaceRoutesApi($client, $config);
try {
  $nsInfo = $namespaceApiInstance->getNamespace(substr($namespaceId, 2));
  // echo $nsInfo['namespace']. PHP_EOL;
} catch (Exception $e) {
  echo 'Exception when calling TransactionRoutesApi->announceTransaction: ', $e->getMessage(), PHP_EOL;
}

$chainApiInstance = new ChainRoutesApi($client, $config);
try {
  $chainInfo = $chainApiInstance->getChainInfo(substr($namespaceId, 2));
  // echo $chainInfo . PHP_EOL;
} catch (Exception $e) {
  echo 'Exception when calling TransactionRoutesApi->announceTransaction: ', $e->getMessage(), PHP_EOL;
}
$lastHeight = (int)$chainInfo['height'];

$blockApiInstance = new BlockRoutesApi($client, $config);
try {
  $lastBlock = $blockApiInstance->getBlockByHeight($lastHeight);
  // echo $lastBlock . PHP_EOL;
} catch (Exception $e) {
  echo 'Exception when calling TransactionRoutesApi->announceTransaction: ', $e->getMessage(), PHP_EOL;
}
$remainHeight = (int)$nsInfo['namespace']['end_height'] - $lastHeight;

$endDate = Carbon::createFromTimestampMs((int)$lastBlock['block']['timestamp'] + $remainHeight * 30000 + $epochAdjustment * 1000);
echo "End Date: " . $endDate . PHP_EOL;

Retrieve information about the namespace expiry and output the date and time of the remaining number of blocks subtracted from the current block height multiplied by 30 seconds (the average block generation interval). For testnet, the update deadline is postponed by about a day from the expiry date. And for the mainnet, this value is 30 days, please note it.

Sample output


End Date: 2024-09-22 04:02:26

6.3 Link

6.3.1 Link to an account


$namespaceId = IdGenerator::generateNamespaceId("xembook"); // ルートネームスペース
$address = $aliceKey->address;

//Tx作成
$tx = new AddressAliasTransactionV1(
  network: new NetworkType(NetworkType::TESTNET),
  signerPublicKey: $aliceKey->publicKey,
  deadline: new Timestamp($facade->now()->addHours(2)),
  namespaceId: new NamespaceId($namespaceId),
  address: new Address($address),
  aliasAction: new AliasAction(AliasAction::LINK),
);
$facade->setMaxFee($tx, 100);

//署名
$sig = $aliceKey->signTransaction($tx);
$payload = $facade->attachSignature($tx, $sig);

$apiInstance = new TransactionRoutesApi($client, $config);

try {
  $result = $apiInstance->announceTransaction($payload);
  echo $result . PHP_EOL;
} catch (Exception $e) {
  echo 'Exception when calling TransactionRoutesApi->announceTransaction: ', $e->getMessage(), PHP_EOL;
}

The linked address does not have to be owned by you.

6.3.2 Link to a mosaic


$namespaceIds = IdGenerator::generateNamespacePath("xembook.tomato"); // ルートネームスペース
$namespaceId = new NamespaceId($namespaceIds[count($namespaceIds) - 1]);
$mosaicId = new MosaicId("0x12679808DC2xxxx");

//Tx作成
$tx = new MosaicAliasTransactionV1(
  network: new NetworkType(NetworkType::TESTNET),
  signerPublicKey: $aliceKey->publicKey,
  deadline: new Timestamp($facade->now()->addHours(2)),
  namespaceId: new NamespaceId($namespaceId),
  mosaicId: $mosaicId,
  aliasAction: new AliasAction(AliasAction::LINK),
);
$facade->setMaxFee($tx, 100);

//署名
$sig = $aliceKey->signTransaction($tx);
$payload = $facade->attachSignature($tx, $sig);

$apiInstance = new TransactionRoutesApi($client, $config);

try {
  $result = $apiInstance->announceTransaction($payload);
  echo $result . PHP_EOL;
} catch (Exception $e) {
  echo 'Exception when calling TransactionRoutesApi->announceTransaction: ', $e->getMessage(), PHP_EOL;
}

Mosaics can only be linked if it is identical to the address at which the mosaic was created.

6.4 Use as an UnresolvedAccount

Designate the destination as UnresolvedAccount to sign and announce the transaction without identifying the address. Transaction is executed for an account resolved on the chain side.

Since the namespace cannot be specified directly in v3, data must be processed when operating without specifying the address.



// UnresolvedAccount 導出
$namespaceId = IdGenerator::generateNamespaceId("xembook"); // ルートネームスペース
$address = Address::fromNamespaceId(new NamespaceId($namespaceId), $facade->network->identifier);

// Tx作成
$tx = new TransferTransactionV1(
  signerPublicKey: $aliceKey->publicKey,
  network: new NetworkType($networkType),
  deadline: new Timestamp($facade->now()->addHours(2)),
  recipientAddress: new UnresolvedAddress($unresolvedAccount),
  message: ''
);
$facade->setMaxFee($tx, 100);

//署名
$sig = $aliceKey->signTransaction($tx);
$payload = $facade->attachSignature($tx, $sig);

$apiInstance = new TransactionRoutesApi($client, $config);

try {
  $result = $apiInstance->announceTransaction($payload);
  echo $result . PHP_EOL;
} catch (Exception $e) {
  echo 'Exception when calling TransactionRoutesApi->announceTransaction: ', $e->getMessage(), PHP_EOL;
}

Designate the sending mosaic as an UnresolvedMosaic to sign and announce the transaction without identifying the mosaic ID.


$namespaceIds = IdGenerator::generateNamespacePath("xembook.tomato"); // ルートネームスペース
$namespaceId = new NamespaceId($namespaceIds[count($namespaceIds) - 1]);

$tx = new TransferTransactionV1(
  network: new NetworkType(NetworkType::TESTNET),
  signerPublicKey: $aliceKey->publicKey,
  deadline: new Timestamp($facade->now()->addHours(2)),
  recipientAddress: $aliceKey->address,
  mosaics: [
    new UnresolvedMosaic(
      mosaicId: new UnresolvedMosaicId($namespaceId),
      amount: new Amount(100)
    ),
  ],
);
$facade->setMaxFee($tx, 100);

//署名
$sig = $aliceKey->signTransaction($tx);
$payload = $facade->attachSignature($tx, $sig);

$apiInstance = new TransactionRoutesApi($client, $config);

try {
  $result = $apiInstance->announceTransaction($payload);
  echo $result . PHP_EOL;
} catch (Exception $e) {
  echo 'Exception when calling TransactionRoutesApi->announceTransaction: ', $e->getMessage(), PHP_EOL;
}

To use XYM in a namespace, specify as follows.


$namespaceIds = IdGenerator::generateNamespacePath("symbol.xym");
$namespaceId = new NamespaceId($namespaceIds[count($namespaceIds) - 1]);
var_dump($namespaceId);

Sample output


object(SymbolSdk\Symbol\Models\NamespaceId)#101 (2) {
  ["size"]=>
  int(8)
  ["value"]=>
  int(-1780160202445377554)
}

6.5 Reference

Refer to the namespace linked to the address.


$namespaceId = new NamespaceId(IdGenerator::generateNamespaceId("xembook"));
$namespaceInfo = $namespaceApiInstance->getNamespace(substr($namespaceId, 2));
var_dump($namespaceInfo);

Sample output


object(SymbolRestClient\Model\NamespaceInfoDTO)#124 (2) {
  ["openAPINullablesSetToNull":protected]=>
  array(0) {
  }
  ["container":protected]=>
  array(3) {
    ["id"]=>
    string(24) "66A048C3527B051AC20A9E78"
    ["meta"]=>
    object(SymbolRestClient\Model\NamespaceMetaDTO)#129 (2) {
      ["openAPINullablesSetToNull":protected]=>
      array(0) {
      }
      ["container":protected]=>
      array(2) {
        ["active"]=>
        bool(true)
        ["index"]=>
        int(1)
      }
    }
    ["namespace"]=>
    object(SymbolRestClient\Model\NamespaceDTO)#120 (2) {
      ["openAPINullablesSetToNull":protected]=>
      array(0) {
      }
      ["container":protected]=>
      array(11) {
        ["version"]=>
        int(1)
        ["registration_type"]=>
        int(0)
        ["depth"]=>
        int(1)
        ["level0"]=>
        string(16) "E6707B3A003BDDD3"
        ["level1"]=>
        NULL
        ["level2"]=>
        NULL
        ["alias"]=>
        object(SymbolRestClient\Model\AliasDTO)#132 (2) {
          ["openAPINullablesSetToNull":protected]=>
          array(0) {
          }
          ["container":protected]=>
          array(3) {
            ["type"]=>
            int(2)
            ["mosaic_id"]=>
            NULL
            ["address"]=>
            string(48) "98E521BD0F024F58E670A023BF3A14F3BECAF0280396BED0"
          }
        }
        ["parent_id"]=>
        string(16) "0000000000000000"
        ["owner_address"]=>
        string(48) "98E521BD0F024F58E670A023BF3A14F3BECAF0280396BED0"
        ["start_height"]=>
        string(7) "1597929"
        ["end_height"]=>
        string(7) "1773609"
      }
    }
  }
}

AliasType is as follows.

{0: 'None', 1: 'Mosaic', 2: 'Address'}

NamespaceRegistrationType is as follows.

{0: 'RootNamespace', 1: 'SubNamespace'}

Refer to the namespace linked to the mosaic.


$namespaceIds = IdGenerator::generateNamespacePath("xembook.tomato");
$namespaceId = new NamespaceId($namespaceIds[count($namespaceIds) - 1]);
$namespaceInfo = $namespaceApiInstance->getNamespace(substr($namespaceId, 2));
var_dump($namespaceInfo);

Sample output


object(SymbolRestClient\Model\NamespaceInfoDTO)#104 (2) {
  ["openAPINullablesSetToNull":protected]=>
  array(0) {
  }
  ["container":protected]=>
  array(3) {
    ["id"]=>
    string(24) "66A048C3527B051AC20A9E7D"
    ["meta"]=>
    object(SymbolRestClient\Model\NamespaceMetaDTO)#133 (2) {
      ["openAPINullablesSetToNull":protected]=>
      array(0) {
      }
      ["container":protected]=>
      array(2) {
        ["active"]=>
        bool(true)
        ["index"]=>
        int(1)
      }
    }
    ["namespace"]=>
    object(SymbolRestClient\Model\NamespaceDTO)#128 (2) {
      ["openAPINullablesSetToNull":protected]=>
      array(0) {
      }
      ["container":protected]=>
      array(11) {
        ["version"]=>
        int(1)
        ["registration_type"]=>
        int(1)
        ["depth"]=>
        int(2)
        ["level0"]=>
        string(16) "E6707B3A003BDDD3"
        ["level1"]=>
        string(16) "9EFE1CF171B6C81E"
        ["level2"]=>
        NULL
        ["alias"]=>
        object(SymbolRestClient\Model\AliasDTO)#136 (2) {
          ["openAPINullablesSetToNull":protected]=>
          array(0) {
          }
          ["container":protected]=>
          array(3) {
            ["type"]=>
            int(1)
            ["mosaic_id"]=>
            string(16) "12679808DC2A1493"
            ["address"]=>
            NULL
          }
        }
        ["parent_id"]=>
        string(16) "E6707B3A003BDDD3"
        ["owner_address"]=>
        string(48) "98E521BD0F024F58E670A023BF3A14F3BECAF0280396BED0"
        ["start_height"]=>
        string(7) "1597929"
        ["end_height"]=>
        string(7) "1773609"
      }
    }
  }
}

6.5.1 Reverse lookup

Check all namespaces linked to the address.


$addresses = ["addresses"=> ["TBIL6D6RURP45YQRWV6Q7YVWIIPLQGLZQFHWFEQ"]];
$accountNames = $namespaceApiInstance->getAccountsNames($addresses);
var_dump($accountNames);

Check all namespaces linked to the mosaic.


$mosaicIds = ["mosaicIds"=> ["72C0212E67A08BCE"]];
$mosaicNames = $namespaceApiInstance->getMosaicsNames($mosaicIds);
var_dump($mosaicNames);

6.5.2 Receipt reference

Check how the blockchain has resolved the namespace used for the transaction.

For address


$receiptApiInstance = new ReceiptRoutesApi($client, $config);

$state = $receiptApiInstance->searchAddressResolutionStatements(
  height: 1600481
);
echo $state;

Sample output


{
    "data": [
        {
            "id": "66A07563527B051AC20AA1FE",
            "meta": {
                "timestamp": "54541377011"
            },
            "statement": {
                "height": "1600481",
                "unresolved": "99D3DD3B003A7B70E6000000000000000000000000000000",
                "resolutionEntries": [
                    {
                        "source": {
                            "primaryId": 1,
                            "secondaryId": 0
                        },
                        "resolved": "98E521BD0F024F58E670A023BF3A14F3BECAF0280396BED0"
                    }
                ]
            }
        }
    ],
    "pagination": {
        "pageNumber": 1,
        "pageSize": 10
    }
}

For mosaic


$state = $receiptApiInstance->searchMosaicResolutionStatements(
  height: 1601155
);
echo $state;

Sample output


object(stdClass)#142 (2) {
  ["data"]=>
  array(1) {
    [0]=>
    object(stdClass)#139 (3) {
      ["statement"]=>
      object(stdClass)#143 (3) {
        ["height"]=>
        string(7) "1600481"
        ["unresolved"]=>
        string(48) "99D3DD3B003A7B70E6000000000000000000000000000000"
        ["resolutionEntries"]=>
        array(1) {
          [0]=>
          object(stdClass)#144 (2) {
            ["source"]=>
            object(stdClass)#140 (2) {
              ["primaryId"]=>
              int(1)
              ["secondaryId"]=>
              int(0)
            }
            ["resolved"]=>
            string(48) "98E521BD0F024F58E670A023BF3A14F3BECAF0280396BED0"
          }
        }
      }
      ["id"]=>
      string(24) "66A07563527B051AC20AA1FE"
      ["meta"]=>
      object(stdClass)#95 (1) {
        ["timestamp"]=>
        string(11) "54541377011"
      }
    }
  }
  ["pagination"]=>
  object(stdClass)#124 (2) {
    ["pageNumber"]=>
    int(1)
    ["pageSize"]=>
    int(10)
  }
}
{
    "data": [
        {
            "id": "66A0D267527B051AC20AA8AB",
            "meta": {
                "timestamp": "54565187345"
            },
            "statement": {
                "height": "1601155",
                "unresolved": {},
                "resolutionEntries": [
                    {
                        "source": {
                            "primaryId": 1,
                            "secondaryId": 0
                        },
                        "resolved": {}
                    }
                ]
            }
        }
    ],
    "pagination": {
        "pageNumber": 1,
        "pageSize": 10
    }
}

Note

As the namespace itself is rented, the link to the namespace used in past transactions may differ from the link to the current namespace.

Always refer to your receipt if you want to know which account you were linked to at the time, e.g. when referring to historical data.

6.6 Tips for use

6.6.1 Reciprocal links with external domains

As duplicate namespaces are restricted by protocol, user can build the brand valuation of one's account on the Symbol by acquiring a namespace that is identical to an internet domain or a well-known trademark name in the real world, and by promoting recognition of the namespace from external sources like official websites, printed materials, etc. (For legal validity, please seek expert opinion.) Beware of hacking external domains and renewing your own Symbol namespaces duration.

Note on accounts acquiring a namespace

Namespaces are rented for a specified duration . At the moment, options for acquired namespaces are only abandonment or duration extension. In case of utilising a namespace in a system where operational transfers, etc. are considered, we recommend acquiring a namespace with a multisig account (Chapter 9).