skip to content
TTL ZERO
Table of Contents

調査対象: Hyperliquid Web フロントエンド(保存済みページ一式) リビジョン: 2026-08-01-40f25a22dREACT_APP_VERSION より) 主要バンドル: main.8c49febc.js(約 4.9 MB, minified) 手法: 静的解析(minified JavaScript のトークン抽出・文脈復元) 注意: 本稿はクライアント側コードの静的解析に基づく推定を含みます。真の強制はサーバー側で行われます。

概要(TL;DR)

Hyperliquid フロントエンドの規制判定は、本体アプリの TraderState プロバイダ(main.8c49febc.js 内の関数 L)に集約されており、次の 3 層構造 で構成されている。

判定軸 データ源
① IP ベース地域規制 アクセス元 IP の所在地 サーバー API legalCheck
② ウォレットアドレス規制 接続ウォレットが制裁リストに一致するか クライアント内蔵の固定 Set
③ ユーザー個別許可/規約同意 userAllowed / acceptedTerms 同じ legalCheck レスポンス

これら 3 層は状態機械 D に統合され、優先順位付きで評価されて 1 つの activeState 文字列に落とし込まれる。ブロック時の UI 文言は最終的に「Not available in your jurisdiction」に集約される。


① IP ベースの地域規制(サーバー legalCheck

API 呼び出し

プロバイダのマウント時に /info エンドポイントへ問い合わせる。

await c0t({
endpoint: "info",
request: { type: "legalCheck", user: l ?? i.I4A }
});
// l = 接続中ウォレットアドレス。未接続時は i.I4A(ダミーアドレス)で IP のみ判定

レスポンスはスキーマ T で検証され、形は以下のとおり。

T = { acceptedTerms: bool, userAllowed: bool, restrictions: enum }

IP の実際の地理判定は サーバー側 で行われ、フロントには「どう制限するか」を表す列挙値だけが返る。IP アドレスそのものは JavaScript 側では扱っていない。

restrictions 列挙(4 値)

A = {
NoRestrictions: "n",
BlockActions: "a",
HideOutcomes: "o",
Uk: "u"
};

IP 制限を適用する条件

J = ("Mainnet" === chain) || i.PA7; // performIpRestrictions
// env: REACT_APP_FORCE_MAINNET_IP_RESTRICTIONS でも強制可能

Mainnet のときだけ IP 制限が有効。Testnet / Local では performIpRestrictions = false となり、以降の判定はすべて「許可」に短絡する(if (!n) return true;)。

restrictions から 3 つのフラグを導出

プロバイダの戻り値(useMemo)で、restrictions を 3 つの独立した許可フラグに変換している。

return {
// ...
ipAllowed: X, // 取引・操作そのものの可否
outcomeMarketsAllowed: ee, // 予測 (outcome) 市場の表示可否
referralsAllowed: ne, // リファラルの可否
userAllowed: se
};

各フラグの真偽値(performIpRestrictions = true 時)は switch で決まり、まとめると次のようになる。

restrictions ipAllowed outcomeMarketsAllowed referralsAllowed 意味
NoRestrictions (n) 制限なし
BlockActions (a) 取引全面ブロック(制裁国・米国等を想定)
HideOutcomes (o) 予測 (outcome) 市場のみ非表示
Uk (u) 英国:取引可だがリファラル禁止(FCA の金融プロモーション規制に対応と推定)

このように、地域ごとに「全面禁止 / 特定商品だけ隠す / 英国向けリファラル無効化」を段階的に切り替える設計になっている。


② ウォレットアドレスの規制(クライアント内蔵ブロックリスト)

main.8c49febc.js にハードコードされた 24 件の小文字アドレスの Set(変数 j が存在する(制裁対象/凍結アドレスを想定)。

const j = new Set([
"0x6a01af210dce01f44b5067fc688641384a8bce5b",
"0xf587b821d609605b1b124d8e248088e058266784",
"0x00071360d75385c4c25dd8f217465693ffe91a69",
"0x333983eb213d132cf4f71751dd38802d0362e3fa",
"0x7dd0ddff0d845660a1244dcf120c7bb586785835",
"0x9bd3e7e58e9d660716a1116a8aeac77a454baad8",
"0x00c07d5370605db4d449f389d1aaad3b2ee78e15",
"0x4f0f2f8a3f359a704998074832ffff98ca48c28f",
"0x8bc964e36e3fcf9068d98e55a64a99d22c898afa",
"0x4f91e6b80cb21d8e6ddf21936806d4c03ab12c42",
"0x594cccf29daccdcdf36870fd02e50733ace09e49",
"0xcefc6c5f2193821db8a77b4a4e388e4e322da141",
"0x7899f00dd07a577d11dc9f4a845b46b71d5298a1",
"0x6d569cb3481412b57b824e01bc8583b6733e31ef",
"0xbb976cda4cdc8141c2fa36129545645bb97b0cc2",
"0x62ada4c40cfb35162c81b06f85997dbb48adeb37",
"0xa2c5a179777e5caceb6bb9d0cebe7e2a1826ad45",
"0x3711f5ce6e55692f1767a1152170efec59756909",
"0x49da31ffd54067ca069b7c02f145b3f2185a97e3",
"0x68da925495adb0ce3a3477213ac0390b80fb958a",
"0xc49acbbf8b98d1ec2296d90afaea14802e7ce4f3",
"0x4a8bf1b7bf0ceaa309b49359a111ab31381e8ede",
"0xa6fb971f3b7a9b9f76eda76bc89268fe26560189",
"0xa0c0e9f307b5a26ca3fb5891c19154fc7a02bef7"
]);
const he = j.has((l ?? "").toLowerCase()); // 接続ウォレットが一致するか
  • hetrue になると、後述の状態機械で 最優先ブロックuserIpBlocked)となる。
  • 別途、入出金履歴(CCTP 転送)の描画でも j.has(...) により該当アドレスのエントリを除外している。
// 入出金履歴の描画時
if (n.isCctp && j.has(t.toLowerCase())) return; // ブロック対象はスキップ

③ 最終判定 — 状態機械 D

①②③を 1 つの状態文字列に落とし込むのが関数 D。呼び出しは D(u, he, X, ie, se, ue, de, Z, pe)上から順に評価される優先順位 が規制の要である。

function D(e, t, n, r, i, o, s, a, c) {
return t // t = he : ウォレットが制裁リストに一致
? "userIpBlocked"
: (false === n) // n = X = ipAllowed が false(BlockActions)
? "ipBlocked"
: ("unknown" === n)
? "ipLoading"
: e // e = u : ウォレット接続済みか
? ("unknown" === r) ? "acceptTermsLoading"
: (false === r) ? "needToAcceptTerms" // r = acceptedTerms
: (s || N(a, c))
? (false === i) ? "userBlocked" // i = userAllowed が false
: o ? "needToDeposit"
: N(a, c) ? "readyToTrade"
: "noRegisteredAgent"
: "noPendingAgent"
: "disconnected";
}

評価の優先順位

  1. 制裁ウォレットuserIpBlocked
  2. IP で取引ブロック (ipAllowed = false) → ipBlocked
  3. IP 判定中 → ipLoading
  4. 未接続 → disconnected
  5. 規約未同意 → needToAcceptTerms
  6. 個別に不許可 (userAllowed = false) → userBlocked
  7. 以降は入金・エージェント登録・readyToTrade(正常系)

UI への反映(ヘルパー 3 種)

// (a) IP ブロック画面を出すか
Hn(e): ipLoading / ipBlocked / userIpBlocked → true
// (b) ユーザー向けメッセージ
Vn(e):
ipBlocked | userIpBlocked → "Not available in your jurisdiction"
needToAcceptTerms → "Need to accept terms"
ipLoading | acceptTermsLoading → "Loading..."
// (c) 取引ボタン等を無効化するか(performIpRestrictions が前提)
Wn(e): ipBlocked / userIpBlocked / needToAcceptTerms / userBlocked / ...true (= 操作不可)

→ 制裁アドレスも IP ブロックも、ユーザーには一律「Not available in your jurisdiction(お住まいの地域では利用できません)」として表示される。両者を UI 上は区別しない点が特徴的である。


補足:KYC / 制裁チェックの別レイヤー

別バンドル 1123-3d5494c697688080.js には、以下の検証スキーマ(Zod 系)が定義されている。

  • identity / residence / selfie
  • sanctions_check(制裁チェック)
  • pep_check(重要公人:Politically Exposed Person)
  • negative_news_check(ネガティブニュース)
  • tax_id

これは埋め込みウォレット(Privy)やフィアット導線向けの KYC / AML レイヤー であり、上記 ①〜③ の取引画面ゲートとは 別系統 である。


判定フロー図

legalCheck (サーバー) ──► restrictions {n,a,o,u}
┌─────────────────────┼─────────────────────┐
▼ ▼ ▼
ipAllowed(X) outcomeMarketsAllowed(ee) referralsAllowed(ne)
│ he = j.has(wallet) ← クライアント内蔵の制裁アドレス Set
│ │
▼ ▼
┌──────────────── 状態機械 D ────────────────┐
│ he → userIpBlocked (最優先) │
│ !ipAllowed → ipBlocked │
│ !connected → disconnected │
│ !acceptedTerms→ needToAcceptTerms │
│ !userAllowed → userBlocked │
│ ... → needToDeposit / readyToTrade │
└──────────────────────────────────────────────┘
Hn / Vn / Wn → UI(ブロック画面・文言・操作抑止)

まとめ

  • IP 規制 はサーバー legalCheck が返す 4 値の restrictionsn / a / o / u)で制御され、ipAllowedoutcomeMarketsAllowedreferralsAllowed の 3 フラグに展開される。Mainnet 時のみ有効
  • ウォレット規制 はクライアント内蔵の 24 アドレスの Set(変数 j)による完全一致ブロック。
  • 両者は状態機械 D に統合され、制裁ウォレット > IP ブロック > 規約同意 > 個別不許可 の優先順で評価される。ブロック時の UI 文言は「Not available in your jurisdiction」に集約される。
  • KYC / 制裁チェック(sanctions_check / pep_check 等)は埋め込みウォレット向けの独立レイヤーとして別バンドルに存在する。

⚠️ 重要: これらはいずれも クライアント側の表示・操作抑止 であり、真の強制はサーバー(legalCheck およびオーダー受付 API)側で行われる。クライアントコードの改変だけでは実際の取引制限は回避できない設計と考えられる。