3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【NEM】ハーベスティングが無効になったことを通知しよう

Last updated at Posted at 2025-12-08

はじめに

NEMの記事第4弾です。
今回はNemKit関係なく、ただのGoogleスプレッドシートとGASを組み合わせてNEMのハーベスティングが無効になったことを通知する仕組みを作っていきます。

NEMの委任ハーベスティングはSymbolとは異なり、稼働中のノードのメモリ内に情報が記録されるらしいので、ノードがメンテナンスなどで停止するとハーベスティングの情報が消えてしまうので都度再設定が必要です。

無効になったかどうかを毎回ウォレットを開いて確認するのは面倒なので、GASとGoogleスプレッドシートを使って定期的にチェックして通知するようにしてみます。

今回やること

  • スプレッドシートに監視したいアドレスの情報を記載する
  • GASでハーベスティングチェックのコードを実装する
  • トリガーで定期的にGASを実行する
  • ハーベスティングが無効になってたらメールorSlack通知する

必要なもの

  1. ハーベスティングを委任したノードのURL
  2. リモートアドレス

1. ハーベストを委任したノードのURL

NanoWalletにログインし、サービスデリゲートハーベスティング(委任)デリゲートアカウント(委任アカウント)管理 で設定したノードのURLです。

なお、設定した時に覚えておかないと後から見る手段はないです。

image.png

わからない人は一旦ハーベスティングを無効にしてから再度ノードを選んで有効にしてください。
※Symbolと違って無効・有効とも手数料はかかりません。
※一応devToolsのnetworkタブを開きながらウォレットを操作すると見れますが、まあできる人はそれでやってくださいって感じです。

2. リモートアドレス

ハーベスティングしてるアドレスではなく委任アカウントのアドレスなのが面倒なポイント

デリゲートアカウントの有効化/無効化 を押すと表示されます。

image.png

詳細仕様

シートの記載内容

※1行目が見出しなので2行目から有効な末尾まで

image.png

  • A列: ノードURL
    • http://node.example.com:7890 のようにプロトコル名とポート番号まで書く
    • 末尾の / は消す
  • B列: 委任アドレス
    • 通常のアドレスではないので注意
    • 確認方法は後述
  • C列: [出力]最終チェック日時
    • 実行時に処理が終わった日時が更新されます
  • D列: 結果
    • 実行結果が更新されます

処理の流れ

1.http(s)://{委任したノードのurl}/account/get?address={委任アドレス}にGETリクエスト

  • 委任したノード宛にリクエストしないと正しい委任情報は取得できません。
  • もし、どこに委任したか忘れた場合は一度ハーベストが無効にを停止して再度ノードを選択して有効化してください
  • Symbolとは違ってトランザクションは飛ばさないので有効化に手数料はかかりません

2.以下のレスポンスが返る(必要なものだけ抜粋)

{
    "meta": {
        "status": "UNLOCKED",
        "remoteStatus": "REMOTE"
    }
}

3.C列に現在日時を更新する

4.値によってD列に結果を更新する

  • status === "UNLOCKED" && remoteStatus === "REMOTE" の場合、正常なので「有効」で更新
  • remoteStatus !== "REMOTE" の場合、設定を間違っているので「委任アカウントのアドレスを入力してください」と表示
  • status !== "UNLOCKED" の場合、エラーなので「無効」で更新し、通知を行う
  • ノードと通信できない場合もエラーなので「無効」で更新し、通知を行う

5.通知処理

  • Slackのincoming-webhookが設定されてたらメール通知はしない
  • メールアドレス宛にメールで通知する
  • どちら未入力なら通知を諦める

6.次の行の同様の処理を行う

コード

以下のGASのコードをスプレッドシートに紐づくApp Scriptに貼り付けて、設定を入力してください。

/**
 * 設定:通知先
 */
const SHEET_NAME = "シート1"; // 設定が記載されたシート名
const SLACK_WEBHOOK_URL = ""; // Slack URL (例 "https://hooks.slack.com/services/xxx/yyy/zzz")
const MAIL_TO = ""; // Slack 未設定時のみ使用


/**
 * メイン処理
 */
function checkAccounts() {
  const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(SHEET_NAME);
  const lastRow = sheet.getLastRow();

  for (let row = 2; row <= lastRow; row++) {
    const host = sheet.getRange(row, 1).getValue();
    const address = sheet.getRange(row, 2).getValue();

    if (!host || !address) continue;

    const url = `${host}/account/get?address=${address}`;
    const result = fetchAccountStatus(url);

    const now = new Date();
    // C列
    sheet.getRange(row, 3).setValue(now); 

    let message = "";

    // API 呼び出し失敗 → エラー扱い
    if (!result) {
      message = "無効";
      sheet.getRange(row, 4).setValue(message);
      notify(address);
      continue;
    }

    const status = result.meta?.status;
    const remoteStatus = result.meta?.remoteStatus;

    if (status === "UNLOCKED" && remoteStatus === "REMOTE") {
      message = "有効";
    } else if (remoteStatus !== "REMOTE") {
      message = "委任アカウントのアドレスを入力してください";
    } else if (status !== "UNLOCKED") {
      message = "無効";
      notify(address);
    }

    sheet.getRange(row, 4).setValue(message);
  }
}


/**
 * API 呼び出し
 * - 10秒タイムアウト
 * - HTTP >= 400 はエラー扱い
 * - 例外(connection refused, timeout 他)もすべて null を返す
 */
function fetchAccountStatus(url) {
  try {
    const resp = UrlFetchApp.fetch(url, {
      method: "get",
      muteHttpExceptions: true,
      timeout: 10 * 1000 // 10秒
    });

    const code = resp.getResponseCode();

    // 正常以外はエラー扱い
    if (code < 200 || code >= 400) {
      return null;
    }

    return JSON.parse(resp.getContentText());

  } catch (e) {
    // あらゆる通信失敗をエラー扱い
    return null;
  }
}


/**
 * エラー通知
 * Slack 優先 → Slack未設定ならメール → 両方なしなら何もしない
 */
function notify(address) {
  const message = buildErrorMessage(address);

  if (SLACK_WEBHOOK_URL) {
    sendSlack(message);
    return;
  }

  if (MAIL_TO) {
    sendMail(message);
  }
}


/**
 * 通知本文テンプレート
 */
function buildErrorMessage(address) {
  return `
:rotating_light: ハーベストが無効になりました :rotating_light:

アドレス: ${address}
ウォレットから再設定してください。
`;
}


/**
 * Slack 通知
 */
function sendSlack(text) {
  UrlFetchApp.fetch(SLACK_WEBHOOK_URL, {
    method: "post",
    contentType: "application/json",
    payload: JSON.stringify({ text })
  });
}


/**
 * メール通知
 */
function sendMail(body) {
  const subject = "ハーベストが無効になりました"; // 絵文字なし
  MailApp.sendEmail({
    to: MAIL_TO,
    subject,
    body
  });
}

テスト

image.png

実行対象が checkAccounts になっていることを確認します。

今回は正常系・異常系全部テストしてみました↓

image.png

結果

ハーベストが無効になっていた場合、指定したメール or Slackに通知が来ると思います

image.png

なお、パラメータ設定ミスの場合は通知しない仕様ですので通知してほしい場合はコードを修正してください。

定期チェックを有効化

image.png

GASのトリガーから時間手動型で checkAccounts が実行されるように設定します。
頻度はお好みでいいですが、まあそんなに頻繁にチェックしなくて良いので今回は6時間にしてます。

まとめ

以上でNEMのハーベスティングの監視ができました。

実はこの仕組みを応用することで委任が切れたら自動で違うノードに再度委任する仕組みというのも安全かつ比較的簡単に実装できるのですが、この記事でそこまで書くのは長すぎるのでまた機会があればどこかで。

3
0
0

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
3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?