はじめに
こんにちは。
symbol-sdkの3系、トランザクションシリーズ第六弾です。
今回はメタデータをやっていきます。
注意事項
書いてあるコードについて、正確性や完全性を保証するものではありません。あくまで参考程度として頂き、最新情報は公式ドキュメンテーションをご確認ください。
デッドラインなど
まずは各トランザクションで共通となる個所について。デッドラインと鍵ペアです。
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);
TextEncoder
も必要になりますので用意しておきます。
import { TextEncoder } from 'util';
const textEncoder = new TextEncoder();
メタデータのキー
メタデータはキーとバリューが必要になります。
キーの方はBigIntで4バイト。任意の値が設定できます。
const scopedMetadataKey = 0x0000000012345678n;
メタデータのバリュー
バリューのほうは、 metadataUpdateValue
という関数があるのでそれを使っていきます。
仕様的には古い値と新しい値でXORをとるみたいです。新しくメタデータを追加するなど、古い値がない場合はXORは考えずに値そのままで大丈夫です。
const value = symbolSdk.symbol.metadataUpdateValue(
textEncoder.encode("oldValue"),
textEncoder.encode("newValue")
);
ほかにも、Uint8Arrayをそのまま使うことができます。
const value = textEncoder.encode("fuga");
文字列でもよいです。
const value = "hoge";
valueSizeDelta
メタデータの値のバイトサイズに関するパラメータです。(英数字だと基本的に1文字1バイトです。)
古いメタデータの値と新しいメタデータの値で、サイズが異なる場合、それをトランザクションに記述する必要があります。
(確か)古い値が4バイトで、新しい値が5バイトだった場合、 valueSizeDelta = 1
となるはずです。
もし新しくメタデータを追加するなど、古い値がない場合は、新しいメタデータの値のサイズを指定します。
const valueSizeDelta = 4;
アカウントメタデータトランザクション
targetAddress
にメタデータを付けたいアドレスを指定します。
const accountMetadataTransaction = facade.transactionFactory.createEmbedded({
type: 'account_metadata_transaction_v1',
signerPublicKey: keyPair.publicKey.toString(),
targetAddress: network.publicKeyToAddress(keyPair.publicKey),
scopedMetadataKey,
valueSizeDelta,
value
});
モザイクメタデータトランザクション
モザイクのメタデータですが、 targetAddress
が必要になります。
targetMosaicId
にモザイクIDを指定します。これはBigIntで指定しています。
const mosaicMetadataTransaction = facade.transactionFactory.createEmbedded({
type: 'mosaic_metadata_transaction_v1',
signerPublicKey: keyPair.publicKey.toString(),
targetAddress: network.publicKeyToAddress(keyPair.publicKey),
scopedMetadataKey: 0x0000000012345678n,
targetMosaicId: 0x500952FEB9E8A682n,
valueSizeDelta: 4,
value: symbolSdk.symbol.metadataUpdateValue(textEncoder.encode(""), textEncoder.encode("fuga")),
});
targetMosaicId
は UnresolvedMosaicId
でも大丈夫です。
targetMosaicId: new symbolSdk.symbol.UnresolvedMosaicId(0x500952FEB9E8A682n),
ネームスペースメタデータトランザクション
ネームスペースのメタデータですが、 targetAddress
が必要になります。
targetNamespaceId
にネームスペースIDを指定します。これはBigIntです。
const namespaceMetadataTransaction = facade.transactionFactory.createEmbedded({
type: 'namespace_metadata_transaction_v1',
signerPublicKey: keyPair.publicKey.toString(),
targetAddress: network.publicKeyToAddress(keyPair.publicKey),
scopedMetadataKey: 0x0000000012345678n,
targetNamespaceId: 0xFBE54EC2AAA8EFDFn,
valueSizeDelta: 4,
value: symbolSdk.symbol.metadataUpdateValue(textEncoder.encode(""), textEncoder.encode("fuga")),
});
targetNamespaceId
は NamespaceId
でも大丈夫です。
targetNamespaceId: new symbolSdk.symbol.NamespaceId(0xFBE54EC2AAA8EFDFn),
共通
ここまでそれぞれメタデータのトランザクションを作りました。ここからは共通です。
アグリゲートトランザクションでラップ
送信するにあたっては、アグリゲートトランザクションでラップする必要があるみたいです。
(ラップしなかったら送信しても受理されなかった)
今回は、メタデータを付ける対象(targetAddress
)がすべてトランザクションの署名者と一致しているので、アグリゲート コンプリート トランザクションで行います。
もしそうでない場合は、アグリゲート ボンデッド トランザクションにする必要があるかと思います。
// 必要に応じてどれか選択
const innerTransactions = [ accountMetadataTransaction ];
const innerTransactions = [ mosaicMetadataTransaction ];
const innerTransactions = [ namespaceMetadataTransaction ];
今回はアグリゲートコンプリートトランザクションにします。
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
});
署名とハッシュ
署名をして、トランザクションにアタッチします。
ハッシュを計算します。
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);
送信
axios
を使います。
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)
});
おわりに
メタデータのトランザクションを作りました。 valueSizeDelta
を自分で計算するのは少し大変ですね。
シリーズ