はじめに
前回に引き続き、Nemkitの既得バランス系の記事第3弾(最終回)です。
今回は 送金後の既得バランスを事前に計算する機能 を作ります。
「できるだけ多く送金したいけど、ハーベストは止めたくない…」そんな時に使えるやつです。
今はSuperNodeプログラムもあるので、ハーベスティング以外の用途でも使えますね。
この機能は、私が知る限り他に類似サービスが存在しなかったNemKitオリジナル機能だと思ってます。
あったらスマソ
必要な情報
| 項目 | 説明 | セル位置(例) |
|---|---|---|
| 残高 | 現在の残高(1 XEM 以上) | B2 |
| 既得バランス | 現在の既得残高(10,000 XEM 以上) | B3 |
計算ロジック
基本的な考え方は前回の「送金後の既得バランス計算」と同じですが、今回は「送金後の既得残高が 10,000 XEM になる送金額」を逆算します。
アルゴリズム
- 初期値として、送金可能な最大額
送金額 = 残高 - 既得残高を仮定 - この額で送金した場合の既得残高を計算
- 結果が 10,000 XEM より少ない場合:
- 送金額を 1 XEM ずつ減らして再計算(既得残高が増える方向)
- 結果が 10,000 XEM より多い場合:
- 送金額を 1 XEM ずつ増やして再計算(既得残高が減る方向)
- ちょうど 10,000 XEM 付近になったら終了
やってることはExcelのゴールシークですね。
ひたすら計算して結果を得るパワープレイです。
Google Spreadsheet 実装
この計算は反復処理(ループ)が必要なため、標準数式では実現できません。GAS(Google Apps Script)のカスタム関数を使います。
GASカスタム関数の作成
手順:
- スプレッドシートで「拡張機能」→「Apps Script」を開く
- 以下のコードを貼り付けて保存(Ctrl+S または Cmd+S)←これ忘れないように!
- プロジェクト名を「NEM Keep 10k」などに変更(任意)
/**
* 送金後の既得バランスを計算(内部関数)
*/
function computeAfterVested(balance, vestedBalance, amount) {
const vestedPercent = vestedBalance / balance;
const afterVested = vestedBalance - amount * vestedPercent;
return Math.floor(afterVested * 100000) / 100000;
}
/**
* 10,000 XEM の既得残高を維持する最大送金額を計算
* @param {number} balance 残高
* @param {number} vestedBalance 既得残高
* @return {number} 最大送金可能額
* @customfunction
*/
function KEEP_10K(balance, vestedBalance) {
// 入力チェック
const flooredBalance = Math.floor(balance);
const flooredVested = Math.floor(vestedBalance);
if (flooredBalance <= 0) {
return "エラー: 残高は1 XEM以上が必要です";
}
if (flooredVested < 10000) {
return "エラー: 既得残高が10,000 XEM未満です";
}
// 初期値:残高と既得残高の差額を送金すると仮定
let totalAmount = flooredBalance - flooredVested;
let afterVested = computeAfterVested(flooredBalance, flooredVested, totalAmount);
// 10,000 XEM より少ないか多いかで調整方向を決定
const shouldDecrease = afterVested < 10000;
const maxIterations = flooredBalance; // 安全弁
for (let i = 0; i < maxIterations; i++) {
if (shouldDecrease) {
// 既得残高が少なすぎる → 送金額を減らす
totalAmount = Math.max(totalAmount - 1, 0);
if (afterVested >= 10000 || totalAmount === 0) break;
} else {
// 既得残高が多すぎる → 送金額を増やす
totalAmount += 1;
if (Math.floor(afterVested) <= 10000) break;
}
afterVested = computeAfterVested(flooredBalance, flooredVested, totalAmount);
}
return totalAmount;
}
/**
* KEEP_10K実行後の既得残高を計算
* @param {number} balance 残高
* @param {number} vestedBalance 既得残高
* @return {number} 送金後既得残高
* @customfunction
*/
function KEEP_10K_AFTER(balance, vestedBalance) {
const amount = KEEP_10K(balance, vestedBalance);
if (typeof amount === "string") return amount; // エラーの場合
return computeAfterVested(Math.floor(balance), Math.floor(vestedBalance), amount);
}
GASのイメージ↓(実際は上記の長いコード全部貼り付けます)
スプレッドシートでの使い方
セル配置:
| A | B | |
|---|---|---|
| 1 | 項目 | 値 |
| 2 | 残高 | 55000 |
| 3 | 既得残高 | 20000 |
| 4 | ||
| 5 | 最大送金可能額 | =KEEP_10K(B2, B3) |
| 6 | 送金後既得残高 | =KEEP_10K_AFTER(B2, B3) |
これで最大送金額と送金後の既得残高が自動計算されます!
「#NAME?」「不明な関数」とでた場合
まず以下を確認してください。
- そのスプレッドシートの
拡張機能→App Scriptから開いたGASに記載しているか- スプレッドシートに紐づいたGASである必要があります。
- Google Driveなどに保存された単独のGASでは不可能です。
- 他のコードが入ってたら一旦消す
- GASおよびスプレッドシートをリロードする
上記でも直らない場合
よくわからないのですが、自分の場合は一旦ファイル名を code.gas とかに変更したりしてたらいつの間にか正しく実行されてました。
時間の問題かな。。。
実際の計算例
アカウント
- 残高: 38,390.104992XEM
- 既得バランス: 38,366.085511XEM
スプレッドシートでの計算結果
- 送金可能額: 28,383.00000XEM (※手数料含む)
- 送金後の既得バランス: 10,000.74399XEM
NemKitでの計算結果
※なぜか1xem単位で小数点が考慮されてないので今回のスプレッドシートとは誤差があります。
実際にやってみる
これミスってたらハーベスティング止まっちゃって明日の記事が書けなくなるやつw
結果
セーフ。若干既得バランスに誤差があるのは、どうしても計算の過程で細かい小数点の桁数などでズレてしまうためです。
見て頂けるとわかるように、誤差はより既得バランスが増えるように丸めていますので、うっかり1万xemを下回らないようにガードされています。
注意点
- この計算では手数料を 0 として扱っています(実際の送金時は別途 0.05 XEM 程度の手数料が必要)
- 1 XEM 刻みで調整するため、完全に 10,000.000000 XEM にはならず、若干上回る結果になります
- 計算に時間がかかる場合がありますが、通常は数秒以内に完了します
まとめ
NEM 懐古録シリーズ全3回、いかがでしたでしょうか?
どれもシンプルな計算式の組み合わせですが、当時のユーザーにとっては非常に便利なツールだったと思います。
Symbol では仕組みが大きく変わりましたが、NEM 1.0 の独特な設計を振り返る良い機会になれば幸いです。







