5
2

symbol-sdk@3.0.7で作る転送トランザクション

Last updated at Posted at 2023-07-04

はじめに

こんにちは。
symbol-sdkの3.0.7がついに出ました。学習のために、様々なトランザクションを作ってみようと思います。

全部いっぺんに書くと長くなってしまうので、少しずつ分けて書いていく予定です。

今回は、転送トランザクションについて書いてみたいと思います。

注意事項

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

Transfer Transaction

まずはデッドラインと鍵ペアです。

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

シンプル

最もシンプルな形です。モザイクもメッセージもありません。

const transaction = facade.transactionFactory.create({
  type: 'transfer_transaction_v1',
  signerPublicKey: keyPair.publicKey.toString(),
  fee: 1000000n,
  deadline,
  recipientAddress: 'TARDV42KTAIZEF64EQT4NXT7K55DHWBEFIXVJQY',
});

モザイク

モザイクを送信してみます。

このように、モザイクIDをBigIntで指定して、

const currencyMosaicId = 0x72C0212E67A08BCEn;

mosaics として渡します。

const transaction = facade.transactionFactory.create({
  type: 'transfer_transaction_v1',
  signerPublicKey: keyPair.publicKey.toString(),
  fee: 1000000n,
  deadline,
  recipientAddress: 'TARDV42KTAIZEF64EQT4NXT7K55DHWBEFIXVJQY',
  mosaics: [
    { mosaicId: currencyMosaicId, amount: 1000000n },
  ],
});

ネームスペース

ネームスペースで送る場合は、以下のようなユーティリティが提供されていました。

const xymNamespaceId = symbolSdk.symbol.generateMosaicAliasId('symbol.xym');

これを使って、以下のようにします。

  mosaics: [
    { mosaicId: currencyMosaicId, amount: 1000000n },
		{ mosaicId: xymNamespaceId, amount: 1000000n },
  ],

メッセージ

文字列をutf-8にするために、Node.jsのパッケージをインポートします。

import { TextEncoder } from 'util';

const textEncoder = new TextEncoder();

平文

先頭に0x00をつけてエンコードしたデータを用います。

const message = new Uint8Array([0x00, ...textEncoder.encode('あいうえお')]);

トランザクション作成は以下のようになります。

const transaction = facade.transactionFactory.create({
  type: 'transfer_transaction_v1',
  signerPublicKey: keyPair.publicKey.toString(),
  fee: 1000000n,
  deadline,
  recipientAddress: 'TARDV42KTAIZEF64EQT4NXT7K55DHWBEFIXVJQY',
  mosaics: [
    { mosaicId: currencyMosaicId, amount: 1000000n },
  ],
  message,
});

暗号化メッセージ

メッセージを暗号化するクラスを使います。このとき、暗号化メッセージを送る際には、受信者の公開鍵が必要になります。

const messageEncoder = new symbolSdk.symbol.MessageEncoder(keyPair);
const message = messageEncoder.encode(
  new symbolSdk.PublicKey(PUBLIC_KEY_RECEIVER),
  textEncoder.encode('message')
)

メッセージを復号するには、以下のようにします。

const [result, decodedMessage] = messageEncoder.tryDecode(
  new symbolSdk.PublicKey(PUBLIC_KEY_RECEIVER),
  message
)

ただ、これを使ってトランザクションを送ってみたものの、Symbolデスクトップウォレットではうまく復号できませんでした。原因はまだ追い切れてません。何か知ってる方おられましたら教えてください。

受取人

アドレス

受取人をアドレスで指定する場合は、文字列で指定すればいいようです。

const transaction = facade.transactionFactory.create({
  type: 'transfer_transaction_v1',
  signerPublicKey: keyPair.publicKey.toString(),
  fee: 1000000n,
  deadline,
  recipientAddress: 'TARDV42KTAIZEF64EQT4NXT7K55DHWBEFIXVJQY',
});

ネームスペース

受取人をネームスペースで指定する場合は、ちょっとうまいやり方がわかりませんでした。

とりあえず、 recipientAddressUnresolvedAddress 型が使われているのはわかりました。なので、データを作ってすごく強引にトランザクションに入れ込んだら、とりあえずは動きました。

const addressAliasId = symbolSdk.symbol.generateMosaicAliasId('address.alias');
const hexByteArray = addressAliasId.toString(16).toUpperCase().match(/../g);
hexByteArray.reverse();
const addressAliasIdLE = hexByteArray.join('');
const addressAlias = `99${addressAliasIdLE}`.padEnd(48, '0');
const unresolvedAddress = new symbolSdk.symbol.UnresolvedAddress(symbolSdk.utils.hexToUint8(addressAlias));

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

transaction.recipientAddress = unresolvedAddress;

アグリゲートトランザクション

転送トランザクションをアグリゲートトランザクションにしてみます。

詳細は別にアグリゲートトランザクションの回でやってみたいと思いますので、今回はシンプルに。

まずは createEmbedded で内部トランザクションを作成。

const innerTransaction1 = facade.transactionFactory.createEmbedded({
  type: 'transfer_transaction_v1',
  signerPublicKey: keyPair.publicKey.toString(),
  recipientAddress: ADDRESS_RECEIVER_1,
  mosaics: [
    { mosaicId: currencyMosaicId, amount: 1000000n },
  ],
  message,
});

const innerTransaction2 = facade.transactionFactory.createEmbedded({
  type: 'transfer_transaction_v1',
  signerPublicKey: keyPair.publicKey.toString(),
  recipientAddress: ADDRESS_RECEIVER_2,
  mosaics: [
    { mosaicId: currencyMosaicId, amount: 2000000n },
  ],
  message,
});

ここで、内部トランザクションのマークルルートを計算します。

const innerTransactions = [ innerTransaction1, innerTransaction2 ];
const transactionsHash = symbolSdk.facade.SymbolFacade.hashEmbeddedTransactions(innerTransactions)

あとはこれらをアグリゲートトランザクションに渡します。

const transaction = facade.transactionFactory.create({
  type: 'aggregate_complete_transaction_v2',
  signerPublicKey: keyPair.publicKey.toString(),
  fee: 1000000n,
  deadline: BigInt(Date.now()) - epochAdjustment + 7200000n,
  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);

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)
  });

おわりに

今回は転送トランザクションについて書いてみました。

これだけでもまぁまぁな量がありました。まだうまく行ってない個所もありますが、また進捗ありましたら更新したいと思います。

5
2
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
2