Qucik Learning Symbol Section 5
Chapter 5 Mosaics
This chapter describes the Mosaic settings and how they are generated. In Symbol, a token is called as a Mosaic.
According to Wikipedia, Tokens are 'objects of various shapes made of clay with a diameter of around 1 cm, excavated from Mesopotamian strata from around 8000 BC to 3000 BC'. On the other hand, mosaic, is "a technique of decorative art in which small pieces are assembled and embedded to form a picture (image) or pattern. Stone, ceramics (mosaic tiles), coloured and colourless glass, shells and wood are used to decorate the floors and walls of buildings or crafts.". In Symbol, mosaics can be thought of as the various components that represent aspects of the ecosystem created by the Symbol blockchain.
5.1 Mosaic generation
For mosaic generation, define the mosaic to be created.
$f = MosaicFlags::NONE;
$f += MosaicFlags::SUPPLY_MUTABLE; // 供給量変更可能
// $f += MosaicFlags::TRANSFERABLE; // 第三者への譲渡可否
$f += MosaicFlags::RESTRICTABLE; //制限設定の可否
$f += MosaicFlags::REVOKABLE; //発行者からの還収可否
$flags = new MosaicFlags($f);
$mosaicId = IdGenerator::generateMosaicId($aliceKey->address);
// 桁数のチェック(15桁なら先頭に0を付ける)
$hexMosaicId = strtoupper(dechex($mosaicId['id']));
if (strlen($hexMosaicId) === 15) {
$hexMosaicId = '0' . $hexMosaicId;
}
echo $hexMosaicId . PHP_EOL;
Sample output
222EF7A74ED6A71C
// モザイク定義
$mosaicDefTx = new EmbeddedMosaicDefinitionTransactionV1(
network: new NetworkType(NetworkType::TESTNET),
signerPublicKey: $aliceKey->publicKey, // 署名者公開鍵
id: new MosaicId($mosaicId['id']), // モザイクID
divisibility: 2, // 分割可能性
duration: new BlockDuration(0), //duration:有効期限
nonce: new MosaicNonce($mosaicId['nonce']),
flags: $flags,
);
※ All Inner Transaction classes of AggregateTransaction are marked with Embedded.
MosaicFlags are as follows.
MosaicFlags {
supplyMutable: false, transferable: false, restrictable: false, revokable: false
}
Permissions of supply changes, transferability to third parties, application of Mosaic Global Restrictions and revocability from the issuer can be specified. Once set these properties cannot be changed at a later date.
divisibility: DivisibilityDivisibility determines to what number of decimal places the quantity can be measured. Data is held as integer values.
- divisibility:0 = 1
- divisibility:1 = 1.0
- divisibility:2 = 1.00
If specified as 0, it cannot be subdivided into smaller units. If a mosaic expiry date is set, the data will not disappear after the expiry date. Please note that you can own up to 1,000 mosaics per account.
Next, change the quantity.
//モザイク変更
$mosaicChangeTx = new EmbeddedMosaicSupplyChangeTransactionV1(
network: new NetworkType(NetworkType::TESTNET),
signerPublicKey: $aliceKey->publicKey, // 署名者公開鍵
mosaicId: new UnresolvedMosaicId($mosaicId['id']),
delta: new Amount(10000),
action: new MosaicSupplyChangeAction(MosaicSupplyChangeAction::INCREASE),
);
If supplyMutable:false, the quantity can only be changed if the entire supply of the mosaic is in the issuers account. If divisibility > 0, define it as an integer value with the smallest unit being 1. (Specify divisibility:2 if you want to value 100 units as 1.00)
MosaicSupplyChangeAction is as follows.
{0: 'DECREASE', 1: 'INCREASE'}
Specify Increase if you want to increase it. Merge two transactions above into an aggregate transaction.
// マークルハッシュの算出
$embeddedTransactions = [$mosaicDefTx, $mosaicChangeTx];
$merkleHash = $facade->hashEmbeddedTransactions($embeddedTransactions);
// アグリゲートTx作成
$aggregateTx = new AggregateCompleteTransactionV2(
network: new NetworkType(NetworkType::TESTNET),
signerPublicKey: $aliceKey->publicKey,
deadline: new Timestamp($facade->now()->addHours(2)),
transactionsHash: $merkleHash,
transactions: $embeddedTransactions
);
$facade->setMaxFee($aggregateTx, 100); // 手数料
// 署名
$sig = $aliceKey->signTransaction($aggregateTx);
$payload = $facade->attachSignature($aggregateTx, $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;
}
Note that a feature of the aggregate transaction is that it attempts to change the quantity of a mosaic that does not yet exist. When arrayed, if there are no inconsistencies, they can be handled without problems within a single block.
5.1.1 Confirmation
Confirm the mosaic information held by the account which created the mosaic.
// 3.3 アカウント情報の確認 - 所有モザイク一覧の取得 を事前に実施する
$accountApiInstance = new AccountRoutesApi($client, $config);
$mosaicApiInstance = new MosaicRoutesApi($client, $config);
$account = $accountApiInstance->getAccountInfo($aliceKey->address);
foreach($account->getAccount()->getMosaics() as $mosaic) {
$mocaisInfo = $mosaicApiInstance->getMosaic($mosaic->getId());
echo "\n===モザイク情報===" . PHP_EOL;
var_dump($mocaisInfo);
}
Sample output
===モザイク情報===
object(SymbolRestClient\Model\MosaicInfoDTO)#131 (2) {
["openAPINullablesSetToNull":protected]=>
array(0) {
}
["container":protected]=>
array(2) {
["id"]=>
string(24) "669E4D9884E82060AFBD9C2E"
["mosaic"]=>
object(SymbolRestClient\Model\MosaicDTO)#121 (2) {
["openAPINullablesSetToNull":protected]=>
array(0) {
}
["container":protected]=>
array(9) {
["version"]=>
int(1)
["id"]=>
string(16) "12679808DC2A1493"
["supply"]=>
string(5) "10000"
["start_height"]=>
string(7) "1596556"
["owner_address"]=>
string(48) "98E521BD0F024F58E670A023BF3A14F3BECAF0280396BED0"
["revision"]=>
int(1)
["flags"]=>
int(13)
["divisibility"]=>
int(2)
["duration"]=>
string(1) "0"
}
}
}
}
5.2 Mosaic transfer
Transfer the created mosaic. Those new to blockchain often imagine mosaic transferring as "sending a mosaic stored on a client terminal to another client terminal", but mosaic information is always shared and synchronised across all nodes, and it is not about transferring mosaic information to the destination. More precisely, it refers to the operation of recombining token balances between accounts by 'sending transactions' to the blockchain.
// 受信アカウント作成
$bobKey = $facade->createAccount(PrivateKey::random());
$bobAddress = $bobKey->address;
$tx = new TransferTransactionV1(
network: new NetworkType(NetworkType::TESTNET),
signerPublicKey: $aliceKey->publicKey, // 署名者公開鍵
recipientAddress: $bobAddress, // 受信者アドレス
mosaics: [
new UnresolvedMosaic(
mosaicId: new UnresolvedMosaicId("0x12679808DC2A1493"), //5.1で作成したモザイクID
amount: new Amount(100) //過分性が2のため、100を指定することで送信量が1モザイクとなる
)
],
message: "\0モザイク送信",
deadline: new Timestamp($facade->now()->addHours(2)),
);
$facade->setMaxFee($tx, 100); // 手数料
// 署名とアナウンス
$sig = $aliceKey->signTransaction($tx);
$payload = $facade->attachSignature($tx, $sig);
try {
$result = $apiInstance->announceTransaction($payload);
echo $result . PHP_EOL;
} catch (Exception $e) {
echo 'Exception when calling TransactionRoutesApi->announceTransaction: ', $e->getMessage(), PHP_EOL;
}
Multiple mosaics can be transferred in a single transaction. To transfer XYM, specify the following mosaic ID.
- Mainnet:6BED913FA20223F8
- Testnet:72C0212E67A08BCE
Amount
All decimal points are also specified as integers. XYM is divisibility 6, so it is specified as 1XYM=1000000.
5.2.1 Confirmation of transaction
$txInfo = $apiInstance->getConfirmedTransaction(
$hash // 送信トランザクションのハッシュ
);
Sample output
object(SymbolRestClient\Model\TransactionInfoDTO)#127 (2) {
["openAPINullablesSetToNull":protected]=>
array(0) {
}
["container":protected]=>
array(3) {
["id"]=>
string(24) "669E5AAB527B051AC20A7DA6"
["meta"]=>
object(SymbolRestClient\Model\TransactionInfoDTOMeta)#120 (2) {
["openAPINullablesSetToNull":protected]=>
array(0) {
}
["container":protected]=>
array(8) {
["height"]=>
string(7) "1596649"
["hash"]=>
string(64) "2781E0DD5346405AC7B5CEC0DF6C8A52D164C86F9D75BA024ED8824E6B516F2F"
["merkle_component_hash"]=>
string(64) "2781E0DD5346405AC7B5CEC0DF6C8A52D164C86F9D75BA024ED8824E6B516F2F"
["index"]=>
int(0)
["timestamp"]=>
string(11) "54403463949"
["fee_multiplier"]=>
int(100)
["aggregate_hash"]=>
NULL
["aggregate_id"]=>
NULL
}
}
["transaction"]=>
object(SymbolRestClient\Model\TransactionInfoDTOTransaction)#138 (2) {
["openAPINullablesSetToNull":protected]=>
array(0) {
}
["container":protected]=>
array(58) {
["size"]=>
int(211)
["signature"]=>
string(128) "6467909E4563D3BDF83400FD955D0F5CCE3CAAC7E9D9CB35037CDF4DE4BE2857CEAB9ED120BB38882076B8DC13E9F11E270A05821663B71D38E1696A5CA60C06"
["signer_public_key"]=>
string(64) "25189135BF2307DCBCD1657A34ABC3FDEEC04A126D4572876BCA4F514DB5AC9B"
["version"]=>
int(1)
["network"]=>
int(152)
["type"]=>
int(16724)
["max_fee"]=>
string(5) "21100"
["deadline"]=>
string(11) "54410638989"
["linked_public_key"]=>
NULL
["link_action"]=>
NULL
["start_epoch"]=>
NULL
["end_epoch"]=>
NULL
["transactions_hash"]=>
NULL
["cosignatures"]=>
NULL
["transactions"]=>
NULL
["mosaic_id"]=>
NULL
["amount"]=>
NULL
["duration"]=>
NULL
["hash"]=>
NULL
["recipient_address"]=>
string(48) "98387B89DFD6F53C8FC960A509392E527278E1C71CDAAF74"
["secret"]=>
NULL
["hash_algorithm"]=>
NULL
["proof"]=>
NULL
["target_address"]=>
NULL
["scoped_metadata_key"]=>
NULL
["value_size_delta"]=>
NULL
["value_size"]=>
NULL
["value"]=>
NULL
["target_mosaic_id"]=>
NULL
["target_namespace_id"]=>
NULL
["id"]=>
NULL
["nonce"]=>
NULL
["flags"]=>
NULL
["divisibility"]=>
NULL
["delta"]=>
NULL
["action"]=>
NULL
["source_address"]=>
NULL
["parent_id"]=>
NULL
["registration_type"]=>
NULL
["name"]=>
NULL
["namespace_id"]=>
NULL
["address"]=>
NULL
["alias_action"]=>
NULL
["min_removal_delta"]=>
NULL
["min_approval_delta"]=>
NULL
["address_additions"]=>
NULL
["address_deletions"]=>
NULL
["restriction_flags"]=>
NULL
["restriction_additions"]=>
NULL
["restriction_deletions"]=>
NULL
["reference_mosaic_id"]=>
NULL
["restriction_key"]=>
NULL
["previous_restriction_value"]=>
NULL
["new_restriction_value"]=>
NULL
["previous_restriction_type"]=>
NULL
["new_restriction_type"]=>
NULL
["mosaics"]=>
array(2) {
[0]=>
object(SymbolRestClient\Model\UnresolvedMosaic)#139 (2) {
["openAPINullablesSetToNull":protected]=>
array(0) {
}
["container":protected]=>
array(2) {
["id"]=>
string(16) "12679808DC2A1493"
["amount"]=>
string(3) "100"
}
}
[1]=>
object(SymbolRestClient\Model\UnresolvedMosaic)#140 (2) {
["openAPINullablesSetToNull":protected]=>
array(0) {
}
["container":protected]=>
array(2) {
["id"]=>
string(16) "72C0212E67A08BCE"
["amount"]=>
string(7) "1000000"
}
}
}
["message"]=>
string(38) "00E383A2E382B6E382A4E382AFE98081E4BFA1"
}
}
}
}
It can be seen that two types of mosaics have been transferred in the Mosaic of the TransferTransaction. You can also find information on the approved blocks in the TransactionInfo.
5.3 Tips for use
5.3.1 Proof of ownership
Proof of existence by transaction was explained in the previous chapter. The transferring instructions created by an account can be left as an indelible record, so that a ledger can be created that is absolutely consistent. As a result of the accumulation of 'absolute, indelible transaction instructions' for all accounts, each account can prove its own mosaic ownership. As a result of the accumulation of 'indelible transaction instructions' for all accounts, each account can prove its own mosaic ownership. (In this document, possession is defined as "the state of being able to give it up at will". Slightly off topic, but the meaning of 'state of being able to give it up at will' may make sense if you look at the fact that ownership is not legally recognised for digital data, at least in Japan yet, and that once you know the data, you cannot prove to others that you have forgotten it of your own will. The blockchain allows you to clearly indicate the relinquishment of that data, but I'll leave the details to the legal experts.)
NFT(non fungible token)By limiting the number of tokens total supply to 1 and setting supplyMutable to false, only one token can be issued and no more can ever exist.
Mosaics store information about the account address that issued the mosaic and this data cannot be tampered with. Therefore, transactions from the account that issued the mosaic can be treated as metadata.
Note that there is also a way to register metadata to the mosaic, described in Chapter 7, which can be updated by the multi signature of the registered account and the mosaic issuer.
There are many ways to create NFTs, an example of the process is given below (please set the nonce and flag information appropriately for execution).
$f = MosaicFlags::NONE;
// $f += MosaicFlags::SUPPLY_MUTABLE; // 供給量変更可能
$f += MosaicFlags::TRANSFERABLE; // 第三者への譲渡可否
$f += MosaicFlags::RESTRICTABLE; //制限設定の可否
$f += MosaicFlags::REVOKABLE; //発行者からの還収可否
$flags = new MosaicFlags($f);
$mosaicId = IdGenerator::generateMosaicId($aliceKey->address);
// モザイク定義
$mosaicDefTx = new EmbeddedMosaicDefinitionTransactionV1(
network: new NetworkType(NetworkType::TESTNET),
signerPublicKey: $aliceKey->publicKey, // 署名者公開鍵
id: new MosaicId($mosaicId['id']), // モザイクID
divisibility: 0, // 分割可能性
duration: new BlockDuration(0), //duration:有効期限
nonce: new MosaicNonce($mosaicId['nonce']),
flags: $flags,
);
//モザイク変更
$mosaicChangeTx = new EmbeddedMosaicSupplyChangeTransactionV1(
network: new NetworkType(NetworkType::TESTNET),
signerPublicKey: $aliceKey->publicKey, // 署名者公開鍵
mosaicId: new UnresolvedMosaicId($mosaicId['id']),
delta: new Amount(1),
action: new MosaicSupplyChangeAction(MosaicSupplyChangeAction::INCREASE),
);
//NFTデータ
$nftTx = new EmbeddedTransferTransactionV1(
network: new NetworkType(NetworkType::TESTNET),
signerPublicKey: $aliceKey->publicKey, // 署名者公開鍵
recipientAddress: $bobAddress, // 受信者アドレス
message: "\0NFT送信", //NFTデータ実態
);
// マークルハッシュの算出
$embeddedTransactions = [$mosaicDefTx, $mosaicChangeTx, $nftTx];
$merkleHash = $facade->hashEmbeddedTransactions($embeddedTransactions);
// モザイクの生成とNFTデータをアグリゲートしてブロックに登録
$aggregateTx = new AggregateCompleteTransactionV2(
network: new NetworkType(NetworkType::TESTNET),
signerPublicKey: $aliceKey->publicKey,
deadline: new Timestamp($facade->now()->addHours(2)),
transactionsHash: $merkleHash,
transactions: $embeddedTransactions
);
$facade->setMaxFee($aggregateTx, 100); // 手数料
// 署名
$sig = $aliceKey->signTransaction($aggregateTx);
$payload = $facade->attachSignature($aggregateTx, $sig);
/**
* アナウンス
*/
try {
$result = $apiInstance->announceTransaction($payload);
echo $result . PHP_EOL;
} catch (Exception $e) {
echo 'Exception when calling TransactionRoutesApi->announceTransaction: ', $e->getMessage(), PHP_EOL;
}
※All Inner Transaction classes of AggregateTransaction are Embedded, so EmbeddedTransferTransactionV1 is used.
※The block height and creation account at the time of mosaic generation are included in the mosaic information, so by searching for transactions in the same block, the NFT data associated with the mosaic can be retrieved. The NFT data associated with the transactions in the same block can be retrieved.
■Note In case that the creator of the mosaic owns the entire quantity, the total supply can be changed. If the data is split into transactions and recorded, it cannot be tampered with, but data can be appended. When managing an NFT, please take care to manage it appropriately, for example by strictly managing or discarding the mosaic creator's private key.
Revocable point service operations Setting transferable to false restricts resale, making it possible to define points that are less susceptible to the act on settlement laws or regulations. Setting revokable to true enables centrally managed point service operations where the user does not need to manage the private key to collect the amount used.
$f = MosaicFlags::NONE;
$f += MosaicFlags::SUPPLY_MUTABLE; // 供給量変更可能
// $f += MosaicFlags::TRANSFERABLE; // 第三者への譲渡可否
$f += MosaicFlags::RESTRICTABLE; //制限設定の可否
$f += MosaicFlags::REVOKABLE; //発行者からの還収可否
$flags = new MosaicFlags($f);
A transaction is described as follows
$revocationTx = new MosaicSupplyRevocationTransactionV1(
network: new NetworkType(NetworkType::TESTNET),
signerPublicKey: $aliceKey->publicKey,
deadline: new Timestamp($facade->now()->addHours(2)),
mosaic: new UnresolvedMosaic(
mosaicId: new UnresolvedMosaicId("0x12679808DC2A1493"), //5.1で作成したモザイクID
amount: new Amount(100) //可分性が2のため、100を指定することで送信量が1モザイクとなる
),
sourceAddress: new UnresolvedAddress("TDZ46RYMP6XTRQLOGI3AWULOHV56LBUE7M43MCI"), //回収ターゲット
);
$facade->setMaxFee($revocationTx, 100); // 手数料