symbol-sdk@3.0.7で作るモザイクとネームスペースのトランザクション

# はじめに

こんにちは。

symbol-sdkの3.0.7、トランザクションシリーズ第二弾です。

今回はモザイクとネームスペースをやっていきます。

https://www.npmjs.com/package/symbol-sdk/v/3.0.7

## シリーズ

https://qiita.com/planethouki/items/860d9f1095c087ccd98f

## 注意事項

書いてあるコードについて、正確性や完全性を保証するものではありません。あくまで参考程度として頂き、最新情報は公式ドキュメンテーションをご確認ください。

# 共通

まずはモザイクのトランザクションとネームスペースのトランザクションで共通となる個所について。
デッドラインと鍵ペアです。

```jsx
import symbolSdk from 'symbol-sdk';

const network = symbolSdk.symbol.Network.TESTNET;
const deadline = network.fromDatetime(new Date(Date.now() + 7200000)).timestamp;

const facade = new symbolSdk.facade.SymbolFacade(network.name);

const privateKey = new symbolSdk.PrivateKey(PRIVATE_KEY);
const keyPair = new facade.constructor.KeyPair(privateKey);
```

[参考: symbol-sdk@3.0.7を試してみる](https://qiita.com/planethouki/items/f6771a12c4ebc688d9ac#%E8%A3%9C%E8%B6%B3)

# モザイク発行のトランザクション

## モザイクフラグ

MosaicFlagsというものが用意されています。とりあえず全部つけておきます。

```jsx
const { MosaicFlags } = symbolSdk.symbol;

const mosaicFlags = MosaicFlags.SUPPLY_MUTABLE.value |
  MosaicFlags.TRANSFERABLE.value |
  MosaicFlags.RESTRICTABLE.value |
  MosaicFlags.REVOKABLE.value;
```

もしくは単純に数値でも大丈夫です。

```jsx
const mosaicFlags = 3;
const mosaicFlags = 1 | 2 | 4 | 8;    // 15
```

## モザイクナンス

モザイクIDは（確か）アドレスと乱数から生成されるので、その乱数がモザイクナンスです。

```jsx
const mosaicNonceValue = 123;
```

モザイクナンスは4バイト。なので4バイトの乱数でも良い。

```jsx
function getRandomInt(max) {
  return Math.floor(Math.random() * max);
}

const mosaicNonceValue = getRandomInt(0xffffffff);
```

## モザイクID

モザイクIDを生成するには、 `generateMosaicId` というヘルパーが用意されています。そして、トランザクションに渡す用に `UnresolvedMosaicId` にします。

```jsx
const ownerAddress = network.publicKeyToAddress(keyPair.publicKey);
const mosaicIdRaw = symbolSdk.symbol.generateMosaicId(ownerAddress, mosaicNonceValue);
const mosaicId = new symbolSdk.symbol.UnresolvedMosaicId(mosaicIdRaw);
console.log("mosaicId:", mosaicId.toString());
```

## トランザクション

モザイク定義と発行量変更。

以下はテスト用モザイクなので、モザイクの期間が有限です。 `duration` は `BlockDuration` にしてますが、たぶん `number` か `BigInt` でも大丈夫だと思います。

```jsx
const definitionTransaction = facade.transactionFactory.createEmbedded({
  type: 'mosaic_definition_transaction_v1',
  signerPublicKey: keyPair.publicKey.toString(),
  duration: new symbolSdk.symbol.BlockDuration(86400n),
  divisibility: 1,
  nonce: new symbolSdk.symbol.MosaicNonce(mosaicNonceValue),
  flags: new symbolSdk.symbol.MosaicFlags(mosaicFlags),
});

const supplyTransaction = facade.transactionFactory.createEmbedded({
  type: 'mosaic_supply_change_transaction_v1',
  signerPublicKey: keyPair.publicKey.toString(),
  mosaicId,
  delta: new symbolSdk.symbol.Amount(1000000000n),
  action: symbolSdk.symbol.MosaicSupplyChangeAction.INCREASE,
});
```

アグリゲートトランザクションにします。

```jsx
const innerTransactions = [ definitionTransaction, supplyTransaction ];
const transactionsHash = symbolSdk.facade.SymbolFacade.hashEmbeddedTransactions(innerTransactions)

const transaction = facade.transactionFactory.create({
  type: 'aggregate_complete_transaction_v2',
  signerPublicKey: keyPair.publicKey.toString(),
  fee: 1000000n,
  deadline,
  transactions: innerTransactions,
  transactionsHash,
});
```

# リボークトランザクション

他の人からモザイクを回収するトランザクションです。あまり凝った点はありません。

```jsx
const mosaicId = 0x13A98C75D38B2D59n;

const transaction = facade.transactionFactory.create({
  type: 'mosaic_supply_revocation_transaction_v1',
  signerPublicKey: keyPair.publicKey.toString(),
  fee: 1000000n,
  deadline,
  sourceAddress: ADDRESS_RECEIVER_1,
  mosaic: { mosaicId, amount: 100n },
});
```

`mosaicId` を `BigInt` で渡したり、 `UnresolvedMosaicId` で渡したり、どっちがいいんだろう。

# ネームスペースを作成するトランザクション

ネームスペース名は、 `string` もしくは `uint8array` で。

```jsx
const namespaceName = 'name1';
const namespaceName = new TextEncoder().encode('name1');
```

トランザクション。

```jsx
const transaction = facade.transactionFactory.create({
  type: 'namespace_registration_transaction_v1',
  signerPublicKey: keyPair.publicKey.toString(),
  fee: 1000000n,
  deadline,
  duration: new symbolSdk.symbol.BlockDuration(86400n),
  registrationType: symbolSdk.symbol.NamespaceRegistrationType.ROOT,
  name: namespaceName,
});
```

## チャイルドネームスペースを作成する

ネームスペース名は、 `string` もしくは `uint8array` で。

```jsx
const namespaceName = 'child1';
const namespaceName = new TextEncoder().encode('child1');
```

親のネームスペースはIDで指定します。

```jsx
const namespaceId = symbolSdk.symbol.generateNamespaceId('name1');
const namespaceId = new symbolSdk.symbol.NamespaceId(0xFBE54EC2AAA8EFDFn);
```

トランザクション。

```jsx
const transaction = facade.transactionFactory.create({
  type: 'namespace_registration_transaction_v1',
  signerPublicKey: keyPair.publicKey.toString(),
  fee: 1000000n,
  deadline,
  registrationType: symbolSdk.symbol.NamespaceRegistrationType.CHILD,
  parentId: namespaceId,
  name: namespaceName,
});
```

## さらにチャイルドネームスペースを作成する

ネームスペース名

```jsx
const namespaceName = 'grandchild1';
const namespaceName = new TextEncoder().encode('grandchild1');
```

親のネームスペースIDです。 `generateNamespaceId` では、ネームスペース名に`parent.child`のように`.`を入れられないので、代わりに `generateMosaicAliasId` を使います。

```jsx
const namespaceId = symbolSdk.symbol.generateMosaicAliasId('name1.child1');
const namespaceId = new symbolSdk.symbol.NamespaceId(0x83BCCBAA250528DFn);
```

あとは同様です。

## 署名と送信

```javascript
const signature = facade.signTransaction(keyPair, transaction);
const jsonPayload = facade.transactionFactory.constructor.attachSignature(transaction, signature);
const hash = facade.hashTransaction(transaction).toString();

console.log(jsonPayload);
console.log(hash);

const sendRes = await axios.put(`${NODE_URL}/transactions`, jsonPayload).then((res) => res.data);
console.log(sendRes);

await axios
  .get(`${NODE_URL}/transactionStatus/${hash}`).then((res) => res.data)
  .then((statusRes) => {
    console.log(statusRes)
  })
  .catch((e) => {
    console.log(e.message, e.response.data)
  });
```

# おわりに

今回はモザイクとネームスペースに関連するトランザクションについて書いていきました。