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).