一行説明
トランザクションの流動量に基づき、時間減衰するユーザーランクを Indexer で計算・閾値タイムスタンプとして保存し、フロントエンドで軽量に表示可能にする
詳細
設計思想
「保有量」ではなく「流動量と頻度」を評価する。一度ランクを上げても使い続けなければ下がる仕組みにより、継続的な利用(水やり)を動機づける。
v1 スコープ
多様性マルチプライヤー(新規相手ボーナス、Wash Trading 検出等)は v2 以降で実装する。v1 は減衰 + √Amount の基本ロジックに集中する。
ランクティア(仮)
| ランク |
閾値(仮) |
イメージ |
| Tree |
1000 |
大木 |
| Sprout |
300 |
芽 |
| Seed |
50 |
種 |
| None |
0 |
- |
※ 閾値は実際のユーザー行動を見て調整する。定数としてコード内で一箇所にまとめること。
アルゴリズム
A. 減衰(Decay)
半減期ベースの指数減衰。何もアクションを起こさない場合、スコアは半減期で半分になる。
$$S_{decayed} = S_{last} \times \left(\frac{1}{2}\right)^{\Delta t / T_{half}}$$
-
$S_{last}$: 前回アクション時のスコア
-
$\Delta t$: 前回アクションからの経過時間
-
$T_{half}$: 半減期(仮: 30日)
B. 加算(Growth)
$$S_{new} = S_{decayed} + \sqrt{Amount}$$
-
$\sqrt{Amount}$: 送金額の平方根。少額多頻度を重視する設計
- 例: 10,000 FoR × 1回 → +100 / 100 FoR × 10回 → +10 × 10 = +100(同等だが頻度で減衰をリセットできる分有利)
C. 閾値タイムスタンプの計算
各ランク閾値 $T_{rank}$ に対して、スコアがその閾値を下回る時刻を計算:
$$rankUntil = now + T_{half} \times \frac{\ln(S_{new} / T_{rank})}{\ln 2}$$
$S_{new} < T_{rank}$ の場合、その時点で既に未到達(rankUntil = 0)。
エンティティ
type UserRank @entity {
id: ID! # address
score: BigDecimal! # 実効スコア(最終更新時点)
lastUpdatedAt: BigInt! # 最終スコア更新時刻(UNIX秒)
treeUntil: BigInt! # この時刻まで Tree
sproutUntil: BigInt! # この時刻まで Sprout
seedUntil: BigInt! # この時刻まで Seed
}
mapping.ts 処理フロー
TransferWithDistribution イベント発生時:
1. UserRank を取得(なければ初期値で作成)
2. 減衰済みスコアを計算: decayed = lastScore × (1/2)^(Δt / T_half)
3. 加算: newScore = decayed + √totalAmount
4. 各ランクの閾値タイムスタンプを再計算
5. UserRank を保存
フロントエンド(参考)
function getRank(userRank: UserRank): string {
const now = Math.floor(Date.now() / 1000)
if (now < userRank.treeUntil) return "Tree"
if (now < userRank.sproutUntil) return "Sprout"
if (now < userRank.seedUntil) return "Seed"
return "None"
}
要件
補足
AssemblyScript での数学関数
サブグラフの mapping は AssemblyScript で実行される。exp / ln / sqrt の標準サポートに制約があるため:
- 半減期の減衰: 日単位の離散ステップで
score >> 1(1日ごとに減衰率を掛ける)か、BigDecimal での固定小数点演算
- 平方根: ニュートン法による整数近似
- ln: 閾値タイムスタンプ計算に必要。テイラー展開 or ルックアップテーブルで近似
将来の拡張(v2 以降)
- 多様性マルチプライヤー(新規相手 x2.0 / 常連 x1.0 / ケア x1.2)
- Wash Trading 検出(短期間の同一相手往復に x0.1)
UserPairInteraction エンティティの追加
依存 Issue
一行説明
トランザクションの流動量に基づき、時間減衰するユーザーランクを Indexer で計算・閾値タイムスタンプとして保存し、フロントエンドで軽量に表示可能にする
詳細
設計思想
「保有量」ではなく「流動量と頻度」を評価する。一度ランクを上げても使い続けなければ下がる仕組みにより、継続的な利用(水やり)を動機づける。
v1 スコープ
多様性マルチプライヤー(新規相手ボーナス、Wash Trading 検出等)は v2 以降で実装する。v1 は減衰 + √Amount の基本ロジックに集中する。
ランクティア(仮)
※ 閾値は実際のユーザー行動を見て調整する。定数としてコード内で一箇所にまとめること。
アルゴリズム
A. 減衰(Decay)
半減期ベースの指数減衰。何もアクションを起こさない場合、スコアは半減期で半分になる。
B. 加算(Growth)
C. 閾値タイムスタンプの計算
各ランク閾値$T_{rank}$ に対して、スコアがその閾値を下回る時刻を計算:
rankUntil = 0)。エンティティ
mapping.ts 処理フロー
TransferWithDistributionイベント発生時:フロントエンド(参考)
要件
UserRankエンティティがschema.graphqlに定義されているTransferWithDistributionイベント時に半減期ベースの減衰スコアが計算されるtreeUntil,sproutUntil,seedUntil)が計算・保存されるnowとの比較のみでランクを判定できる補足
AssemblyScript での数学関数
サブグラフの mapping は AssemblyScript で実行される。
exp/ln/sqrtの標準サポートに制約があるため:score >> 1(1日ごとに減衰率を掛ける)か、BigDecimalでの固定小数点演算将来の拡張(v2 以降)
UserPairInteractionエンティティの追加依存 Issue