本文へ移動

オラクルの小数桁不一致

小数桁の不一致により、オラクル回答、トークン数量、ラッパー交換レート、またはプロトコル値へ誤った単位倍率が適用されると、借入、発行、償還、清算に桁違いの誤差が生じます。

更新日

本資料は教育目的に限られ、投資助言ではありません。投資では損失が生じる可能性があります。

要点

オラクルの小数桁不一致とは、コントラクトが生の整数値を誤った単位指数、価格方向、または内部スケールで解釈することです。フィードの小数桁、トークンの小数桁、クォートトークンの小数桁、ラッパーまたはシェアの交換レート尺度、プロトコルの WAD や RAY の規約は、それぞれ独立しています。フロントエンドの表示が正しく見えても、利用側コントラクトが同じ演算を行っているとは限りません。

基準トークンの生数量 A_raw の小数桁数を d_t、基準トークン 1 単位をクォートトークン建てで示す正のフィード回答 P_raw の小数桁数を d_p とします。小数桁数が d_q のクォートトークン生数量は V_raw = round(A_raw * P_raw * 10^d_q / 10^(d_t + d_p)) です。この式が有効なのは、記載した価格方向について、フィード、単位、型、丸め規則を検証した場合に限られます。アダプターがすでに値を正規化しているなら、再度倍率を掛けてはいけません。

ERC-20 の decimals() は任意の表示用メタデータであり、すべてが 18 桁である保証ではありません。フィードの decimals() が表すのは、そのフィード回答の小数桁であって、トークンやプロトコルの尺度ではありません。検査付き算術はオーバーフロー時にリバートできますが、次元を誤った式は検出できません。また、中間積のオーバーフローによって、本来表現可能な最終結果までサービス拒否の原因になる場合があります。

仕組み

  1. チェーン、ブロック、利用側コントラクト、アダプター、フィードプロキシとアグリゲーター、トークンまたはボールト、コンパイラーと数学ライブラリーのバージョン、およびアップグレード状態を固定します。
  2. すべての整数について型と単位を棚卸しします。対象は、トークンの生数量、トークンの小数桁、符号付きフィード回答、フィードの小数桁、基準資産とクォート資産の方向、ラッパーまたはシェアの交換レート、プロトコル内部尺度、出力トークンの小数桁です。
  3. 変換前に情報源を検証します。正しいアドレスと方向、正の回答、タイムスタンプと状態、許容できる経過時間と範囲、フォールバックの意味、および該当する場合の L2 シーケンサー猶予規則を確認します。
  4. 入力の生単位から出力の生単位まで、次元の整合する式を一つ導きます。倍率を上げる場合と下げる場合を明示的に分岐し、10 の累乗指数をすべて制限し、各項を一度だけ正規化します。
  5. 検証済みの全精度乗除算、または上限を設けた約分手法を使います。除算先行による切り捨て、検査付き中間積のオーバーフロー、未検査のラップアラウンド、危険な符号変換や狭い型へのキャストを避けます。
  6. 経済行為ごとに切り捨て、切り上げ、最近接丸めを指定します。担保、債務、借入、発行、償還、手数料、清算、ボールトシェア変換では、保守的な丸め方向が異なる場合があります。
  7. 極端な数量、小数桁の組み合わせ、逆数、複合フィード、ゼロ、負値、古い回答、アップグレード、端数についてゴールデンベクトルと性質テストを行います。オンチェーン結果を独立した高精度モデルと照合し、影響を受け得るエクスポージャーに上限を設けます。

正規化は書式設定ではなく次元解析です。BTC/USDUSD/BTC には逆数の式が必要であり、ラベルを変えるだけでは価格は反転しません。複合フィードでは各項の尺度とタイムスタンプが必要です。Solidity の整数除算はゼロ方向へ切り捨てるため、代数的に同値な式の並べ替えでもオンチェーン結果が変わり得ます。全精度の mulDiv は中間値のビット幅の問題を解決しますが、呼び出し側は依然として正しい分子、分母、単位、境界、丸め方向を指定する必要があります。

計算例

  • 8 桁と 18 桁。 BTC/USD フィードが 6,000,000,000,000 を返し、その小数桁数が d_p = 8 なら、価格は 60,000 USD/BTC です。生の回答を 18 桁として扱うと 0.000006 USD/BTC となり、過小評価の倍率は 10^10 であって 10^9 ではありません。価格を WAD に変換すると 6,000,000,000,000 * 10^(18 - 8) = 60,000,000,000,000,000,000,000 です。
  • 数量、価格、出力単位。 A_raw = 2,500,0002.5 トークンを表し、その小数桁数は d_t = 6 です。P_raw = 200,000,0002 クォートトークンを表し、その小数桁数は d_p = 8 です。18 桁の内部クォート値なら、2,500,000 * 200,000,000 * 10^18 / 10^(6 + 8) = 5,000,000,000,000,000,000、すなわち 5 quote tokens です。トークン側の分母を省くと、ポジションは 10^6 倍に過大評価されます。
  • 逆数と切り捨て。 WAD 尺度の ETH/USD は 2,000 * 10^18 です。同じ尺度の USD/ETH は floor(10^36 / (2,000 * 10^18)) = 500,000,000,000,000、すなわち 0.0005 ETH/USD です。別の例として、A_raw = 999,999d_t = 6、価格が 2 * 10^18 なら、全精度乗除算の結果は 1,999,998,000,000,000,000 です。数量を先に 10^6 で割ると 0 となり、価値をすべて失います。
  • 中間値のオーバーフローと丸め。 x = 2^200y = 2^100、分母を 2^100 とします。正確な結果は 2^200 で、これは uint256 に収まりますが、x * y = 2^300 は収まりません。検査付き乗算はリバートし、未検査乗算はラップしますが、全精度の mulDiv2^200 を返します。整数の 5 / 2 は切り捨てで 2、切り上げなら 3 になるため、丸めは経済的不変条件の一部です。

リスク

  • チェーン、フィード、プロキシ、アダプター、トークン、ボールト、または利用側コントラクトのアドレスが誤っている。
  • 逆数変換をせずに基準資産とクォート資産の方向を反転している。
  • トークンの小数桁を 18 と仮定する、または任意メタデータが取得不能もしくは誤っている。
  • フィードから読み取って固定せず、小数桁を 8 と仮定している。
  • クォートトークンの小数桁と、プロトコルの WAD、RAY、市場、会計尺度を混同している。
  • ラッパー、シェア、指数、または交換レートの小数桁を省いている。
  • アダプターが正規化した後に倍率を重ねて適用している。
  • 必要な倍率または分母を省いている。
  • 正であることを確認する前に符号付き回答を符号なしへキャストしている。
  • ゼロ、古い、未完了、上限張り付き、または無効なフィード結果を受け入れている。
  • 小数桁指数または 10 の累乗計算がアンダーフロー、オーバーフロー、もしくは境界超過を起こす。
  • 最終商は収まるのに、乗算の中間値がオーバーフローする。
  • 未検査算術、ビットシフト、または明示的な縮小変換が黙ってラップまたは切り捨てを起こす。
  • 乗算前の除算が精度を失わせ、端数をゼロにする。
  • 経済行為に対して、切り捨て、切り上げ、最近接丸めの選択が誤っている。
  • 変換を繰り返すことで精度損失や体系的な価値流出が累積する。
  • 価格、上限、比率、百分率、ベーシスポイント、WAD、RAY を異なる単位のまま比較する。
  • フィード、トークン、プロキシ、アダプター、ボールトのアップグレードにより、キャッシュした小数桁の前提が無効になる。
  • フロントエンド、ウォレット、RPC、インデクサーの表示が、異なるオンチェーン計算を覆い隠す。
  • 誤評価が借入、発行、償還、清算、上限、不良債権、または不公平なシェア発行を増幅する。

よくある誤解

  • 「すべての ERC-20 トークンは小数桁が 18 である。」 ERC-20 のメタデータ関数は任意であり、実際の資産は異なる値と挙動を採用しています。
  • 「すべての USD 価格フィードは小数桁が 8 である。」 フィード精度は個々のデプロイのインターフェース属性であり、読み取ってバージョンを固定する必要があります。
  • 「生の整数値が大きければ操作の証拠である。」 単位、方向、小数桁、タイムスタンプ、利用側の尺度がなければ、生の値の大きさに意味はありません。
  • 「Solidity 0.8 ならスケーリングも正しくなる。」 検査付きオーバーフローはリバートできますが、誤った単位、切り捨て、キャスト、丸め方針は修正しません。
  • 「常に先に掛ける、または小数桁を増やせば精度が上がる。」 オーバーフロー、二重スケーリング、誤った単位の温存を招き得ます。全精度演算にも正しい式が必要です。

関連トピック

出典

ナビゲーション

Wiki を検索...