symbol-sdk@3.0.7を試してみる

# はじめに

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

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

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

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

# インストール

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

以下

```shell
npm i symbol-sdk
```

のコマンドで、

```json
"symbol-sdk": "^3.0.7"
```

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

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

```bash
npm i axios
```

# Typescriptは使えない

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

```bash
> 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を取得していきます。

```javascript
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固有の処理を実行していきます。

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

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

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

```javascript
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に送信することになります。

```javascript
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のエンドポイントへ送信します。

```javascript
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に問い合わせる必要があります。

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

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

console.log(statusRes);
```

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

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

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

```jsx
{
  group: 'confirmed',
  code: 'Success',
  hash: 'F80F27E817288971AB533EAA9EF6DD846DECD0305734E047C04FC6BFEFCAE327',
  deadline: '16032354275',
  height: '436904'
}
```

### トランザクション詳細

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

```javascript
const hash = "F80F27E817288971AB533EAA9EF6DD846DECD0305734E047C04FC6BFEFCAE327"

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

## コード全体

- 全体のコードです。
    
```javascript
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);
```
    

# 補足

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

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

const network = symbolSdk.symbol.Network.TESTNET;
```

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

```javascript
const network = symbolSdk.symbol.Network.MAINNET;
```

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

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

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

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

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

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

# おわりに

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