Skip to content

feat(indexer): トランザクション流動量に基づく減衰ランクシステム v1 #104

@yu23ki14

Description

@yu23ki14

一行説明

トランザクションの流動量に基づき、時間減衰するユーザーランクを 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"
}

要件

  • UserRank エンティティが schema.graphql に定義されている
  • TransferWithDistribution イベント時に半減期ベースの減衰スコアが計算される
  • 金額の平方根による加算が実装されている
  • 各ランクの閾値タイムスタンプ(treeUntil, sproutUntil, seedUntil)が計算・保存される
  • ランクティアの閾値・半減期が定数として一箇所にまとまっている
  • フロントエンドで now との比較のみでランクを判定できる

補足

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions