symbol-sdk@3を試してみる

symbol-sdk@3を試してみる

タグ
Symbol
公開日
May 10, 2023

はじめに

こんにちは。OpeningLIneの坂本です。symbol-sdkの3系が手軽に利用できるようになりましたね。

npmでは、執筆時点で3.0.7が公開されています。

では、少し触ってみようと思います。

インストール

今回はブラウザではなくて、Node.jsの環境で実施します。

以下

npm i symbol-sdk

のコマンドで、

"symbol-sdk": "^3.0.7"

が入りました。rxjsはいらないみたいです。

また、REST Gatewayとの通信のためにaxiosをインストールしておきます。

npm i axios

Typescriptは使えない

最初Typescriptで始めようとしたら、コンパイルでこけました。どうやらTypescriptは対応していないようです。

> tsc

src/index.ts:1:23 - error TS7016: Could not find a declaration file for module 'symbol-sdk'. '/node_modules/symbol-sdk/src/index.js' implicitly has an 'any' type.
  Try `npm i --save-dev @types/symbol-sdk` if it exists or add a new declaration (.d.ts) file containing `declare module 'symbol-sdk';`

Typescriptについては詳しくはないのですが、型定義を作成すると使えるようになるのかな、と思います。

ですので、以下はJavaScriptで進めようと思います。

転送トランザクションサンプル

情報を収集する

epoch adjustmentやモザイクID、ネットワークIDを取得していきます。

const node = axios.create({
  baseURL: NODE_URL,
  timeout: 1000,
  headers: { 'Content-Type': 'application/json' }
});

const networkProperties = await node.get('/network/properties').then((res) => res.data);

const epochAdjustment = BigInt(networkProperties.network.epochAdjustment.slice(0, -1)) * 1000n;
// 1667250467000n

const currencyMosaicId = BigInt(networkProperties.chain.currencyMosaicId.replace(/'/g, ''));
// 0x72C0212E67A08BCEn

const symbolNetwork = networkProperties.network.identifier;
// 'testnet'

Facadeを生成する

symbol用のfacadeを生成します。このfacadeを使ってsymbol固有の処理を実行していきます。

const facade = new symbolSdk.facade.SymbolFacade(symbolNetwork);

トランザクションを作成する

ここは2系のものと大きくは変わっていません。

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: BigInt(Date.now()) - epochAdjustment + 7200000n,
  recipientAddress: ADDRESS_RECEIVER_1,
  mosaics: [
    { mosaicId: currencyMosaicId, amount: 1000000n }
  ]
});

署名してハッシュを計算する

この辺りも2系のものと大きくは変わっていませんが、少し難しくなりましたかね。

jsonPayloadには、トランザクションのバイト列が入っています。これをそのままの形でREST Gatewayに送信することになります。

const signature = facade.signTransaction(keyPair, transaction);

const jsonPayload = facade.transactionFactory.constructor.attachSignature(transaction, signature);

const hash = facade.hashTransaction(transaction).toString();

console.log(jsonPayload);
// '{ "payload": "B000000000000000F514132000..." }'

console.log(hash);
// "D9140C1F85150ED9A0BE92DB18BBA02401AC6788B0D11DA190C50011FBD09C14"

ここでジェネレーションハッシュを入力していませんが、SDKの中に直に書かれていました。

送信する

REST Gatewayのエンドポイントへ送信します。

const response = await node.put("/transactions", jsonPayload).then((res) => res.data);

console.log(response);
// { message: 'packet 9 was pushed to the network via /transactions' }

確認する

SDKでは、情報を取得するためのものが無いため、自らREST Gatewayに問い合わせる必要があります。

トランザクションステータス

const statusRes = await node.get("/transactionStatus/" + hash).then((res) => res.data);

console.log(statusRes);

このようなデータが返ってきます。

{
  group: 'unconfirmed',
  code: 'Success',
  hash: 'F80F27E817288971AB533EAA9EF6DD846DECD0305734E047C04FC6BFEFCAE327',
  deadline: '16032354275',
  height: '0'
}

承認されていたら、こうなります。

{
  "group": "confirmed",
  "code": "Success",
  "hash": "F80F27E817288971AB533EAA9EF6DD846DECD0305734E047C04FC6BFEFCAE327",
  "deadline": "16032354275",
  "height": "436904"
}

トランザクション詳細

また、トランザクション承認後は、以下のエンドポイントが利用可能になりますね。

const hash = "F80F27E817288971AB533EAA9EF6DD846DECD0305734E047C04FC6BFEFCAE327"

const confirmedRes = await node.get("/transactions/confirmed/" + hash).then((res) => res.data);
console.log(confirmedRes);

コード全体

全体のコードです。
import symbolSdk from 'symbol-sdk';
import axios from 'axios';
import {
  NODE_URL,
  ADDRESS_RECEIVER_1,
  PRIVATE_KEY,
} from './env.js';

const node = axios.create({
  baseURL: NODE_URL,
  timeout: 1000,
  headers: { 'Content-Type': 'application/json' }
});

const networkProperties = await node.get('/network/properties').then((res) => res.data);

const epochAdjustment = BigInt(networkProperties.network.epochAdjustment.slice(0, -1)) * 1000n;
const currencyMosaicId = BigInt(networkProperties.chain.currencyMosaicId.replace(/'/g, ''));
const symbolNetwork = networkProperties.network.identifier;

const facade = new symbolSdk.facade.SymbolFacade(symbolNetwork);

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: BigInt(Date.now()) - epochAdjustment + 7200000n,
  recipientAddress: ADDRESS_RECEIVER_1,
  mosaics: [
    { mosaicId: currencyMosaicId, amount: 1000000n }
  ]
});

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 node.put("/transactions", jsonPayload).then((res) => res.data);
console.log(sendRes);


await new Promise((resolve) => setTimeout(resolve, 1000));

const statusRes = await node.get("/transactionStatus/" + hash).then((res) => res.data);
console.log(statusRes);

補足

ネットワーク情報やデッドラインの作成方法がいくつかあるみたいでした。

import symbolSdk from 'symbol-sdk';

const network = symbolSdk.symbol.Network.TESTNET;

とすることで、Symbolテストネットのネットネット情報にアクセスできるようです。

const network = symbolSdk.symbol.Network.MAINNET;

とすれば、メインネット。

他には、このような方法も見つかりました。

const network = symbolSdk.NetworkLocator.findByName(symbolSdk.symbol.Network.NETWORKS, 'testnet');

これを使って、2時間のデッドラインを計算するには、以下のようにします。

const deadline = network.fromDatetime(new Date(Date.now() + 7200000)).timestamp;
// 16034333219n

7200000は、2時間をミリ秒に直した数値です。

おわりに

新しくなったsymbol-sdkにて、転送トランザクションを送信することをやってみました。少し難しくなった印象がありますね。その分、SDK自体は軽くなっているような気がします。