dapp

リポジトリにあるアプリはcairn-app/です。Tailwind v4を使い、ウォレットにはwagmiとviemを用いるNext.js 16のフロントエンド(App Router)で、ローカルではポート3100で配信されます。2026-10-02に、以前のReact + Viteのdappを置き換えました。アプリはプロジェクトの新しい名前であるCairn.funを冠しており、その名前のもとでプロトコルトークンは$CAIRNです。本書はStockFunという名前を使い続けます。

デプロイが設定されるまで、アプリは「Illustrative」と表示されたデモデータで動作し、何もデプロイされていません。このページでは、現状のままのアプリを説明します。

画面

ルート 画面
/ ランディング:次のドロップ、マーケットのティッカーテープ、ドロップの公開記録、手数料の計算ツール、バスケット、プロトコルトークン、FAQ
/markets 発見:トレジャリーの動向、プロトコルトークンの帯、フィルター、並べ替えと検索、マーケットの一覧表
/market/[slug] マーケット詳細:チャート、ドロップのパネル、取引履歴、署名前に手数料の配分を示す取引パネル、バンド1の進捗、コントラクト、ウォレットのポジション
/launch ローンチのフォーム:基本情報、バスケットの選択、ライブプレビュー、取り消せないことの確認チェックリスト。8回目の監査ループ以降、チェーンから読み取ったバスケットだけを提示し、送信の前に、選ばれたバスケットをもう一度チェーンと照合します
/claim ホルダーのエアドロップの請求(時間枠ごと)と、クリエイター手数料
/drops すべてのドロップ、時間枠ごと
/docs 仕組み、手数料、バスケット、ドロップ、プロトコルトークン、制限事項

エアドロップの決定以降は廃止されており、以前のdappが2026-09-28の時点でまだ表示していたTreasury Ratioは、リポジトリのアプリには現れません。それに代わる指標はまだ決まっていません。

請求画面

2026-10-05以降、/claimは、接続されたウォレットが請求できるすべての時間枠を、マーケットごとに一覧表示します:時間枠の終了時刻、その株式、金額とそのドル建ての価値です。金額はエアドロップのコントラクトだけから取得され(claimable)、ページが開いている間は60秒ごとに、そしてウォレットのトランザクションのたびに、改めて読み取られます。緊急移転の後でエアドロップのコントラクトが不足している株式には「awaiting settlement」と表示され、請求から外されます。

一覧の下には、合計と「Claim all」があります。「Claim all」は、claimManyを5個の時間枠ずつのバッチで送り(EthereumのGlamsterdamアップグレードが2026-10-06にSepoliaに到達するまでは10個ずつ。後述)、清算待ちの株式を含む時間枠については、他の株式を指定したclaimを送ります。すべての呼び出しはまずシミュレーションされます。失敗するはずのバッチはその時間枠ごとに分けられ、失敗するはずの時間枠はその他の株式に絞られるため、今は支払えない時間枠や株式はスキップされて名前が示され、残りを巻き込んで失敗させることは決してありません。レシートを読み取れないトランザクションはそのハッシュを保持し、確認がまだ読み取られていない送信済みのものとして表示されます。何も再送されず、ページは、そのトランザクション自体か、その代わりのトランザクションがマイニングされるまで、それを追跡します。10回目の監査ループ以降、ウォレットでの高速化は請求そのものとして扱われ、「View tx」はそれを指します。一方、キャンセルやその他の置き換えは、その請求を未完了として終わらせ、「View tx」はマイニングされたものを指します(後述)。結果には、支払われたもの、繰り延べられて支払われるべきものとして残る株式、そしてスキップされた時間枠とその理由が示されます。

2026-10-06以降:

  • 実行を止めるのはウォレットの拒否だけで、10回目の監査ループ以降は、ウォレットでのキャンセルや置き換えも実行を止めます。失敗したトランザクションは数えられ(「did not go through」)、それがカバーしていたものは一覧に残り、他のトランザクションは送られます
  • まだ移動中の株式のための余裕。claimManyは、実行時点で時間枠が列挙しているすべての株式を支払います。配信をまだ受け付けている時間枠には、見積もりからブロックへの取り込みまでの間に、さらに1つの株式が計上されることがあります。そのため、アプリは、時間枠がまだ列挙していないバスケットの株式1つごとに400,000ガス(Glamsterdamアップグレードまでは100,000ガス)を追加します。使われなかったガスは請求されません
  • Glamsterdamに合わせたガス。Sepoliaは2026-10-06にEthereumのGlamsterdamアップグレードを有効にしました。これにより、新しいストレージスロットのガスは以前のおよそ5倍になります。メインネットにはまだ日程がありませんでした。請求が支払う各株式は新しいスロットを3つ書き込むことがあるため、アプリは、まだ移動中の株式1つごとに400,000ガスを残し、1トランザクションにつき5個の時間枠を請求します。5つの株式からなる時間枠10個では、最大でおよそ1,400万ガスかかることがありえます
  • 書き込みトランザクションごとのガス。このアップグレードのもとでは、ゼロから書き込まれるストレージスロットはおよそ110,000ガスかかり、取引は、その見積もりが一度も見ていないストレージへの書き込みに出会うことがあります。直前の請求によって空になったフックの手数料の積み立て(そのうち2種類の請求は誰でも呼び出せます)、次の時間の供給量のマーク、同じブロックで動いたトレーダー自身の記録です。9回目の監査ループ以降、アプリは各書き込みトランザクションに、その見積もりに50,000ガスを加えた値を与え、最新のブロックで見積もられた取引には、まだ起こりうるそのような書き込みそれぞれのガスも、同じブロックで読み取って加えます(積み立て1つにつき112,000、マークに150,000、記録に135,000。読み取りが失敗した場合はそのすべて)。見積もりが失敗した書き込みトランザクションは、最も重いケースから決めた固定の上限で送られ、余裕のないウォレット自身の見積もりが使われることは決してありません。その場合、ローンチは送られず、ページはやり直すよう伝えます。それまでは、すべての書き込みトランザクションに、見積もりを150,000ガス(アップグレード前は35,000)上回るガスが与えられていました。ウォレットは、署名の前に、上限に手数料を掛けた額を取り置きます。請求されるのは使われたガスだけです
  • 前回の請求で支払われなかった株式。トークンがこのウォレットへの転送を拒否したために(たとえば発行体による凍結)請求で繰り延べられた株式は、ブラウザに記憶され、「not paid at your last claim」と表示され、「Claim all」から除外されます。各請求は、まずその株式だけを単独で試し、その請求が通るようになった時点で再び含めます。他に支払われるべきものがない場合、ボタンには「Try the assets not paid again」と表示されます
  • 未確認は拒否ではない。シミュレーションが意味を持つのは、ノードが呼び出しの失敗する理由を示した場合だけです。確認の際にRPCエラーが起きた時間枠は、まだ確認されていないものとして一覧に残り、どのシミュレーションも応答しなかった場合は何も送られません
  • 読み取れないことはゼロではない。数値を読み取れなかったマーケットは名前が示され、何も負っていないかのように表示されることは決してありません。「Nothing to claim」が表示されるのはすべてのマーケットを読み取れた場合だけで、クリエイターのセクションは「You haven't launched a market」と表示する代わりに、そのマーケットを読み取れなかったことを示します

クリエイター手数料も、同じページでマーケットごとに請求します。

「Figures unavailable」の表示

2026-10-05以降、たとえば欠陥のあるアップグレードの後で、ボールトが自身のビュー関数に応答しないトレジャリーは、その数値の代わりに「Figures unavailable」と表示されます。マーケット一覧の行とカード、プロトコルトークンのバナー、そしてマーケットのページでです。その株式とETHは引き続き一覧に表示され、マーケットのページの合計は、いくつのトレジャリーを除外したかを示します。アプリはこれをLensから知ります。Lensは各ボールトを個別の呼び出しで読み取ります。アーキテクチャを参照してください。

2026-10-06以降、ボールトが支払いを拒否した後にフックと流動性のロックがそのボールトのために保持しているETHは、そのトレジャリー、その価値、そして合計に含まれます。「ETH owed」という行として表示され、まだボールトには入っておらず、ボールトが受け取った時点でボールトに移るという注記が付きます。これはフックとロック上で読み取られるため、ボールトが応答しない間も最新に保たれます。応答せず、一度も読み取られたことのないボールトには、プロトコルのブリッジ設定が示すレールが当てはめられ、ドロップのパネルにはそのことが示されます。その株式がどこにあるかは推定であり、その保有資産は不完全かもしれません。Lensがプロトコルトークンのトレジャリーを読み取れない場合、プロトコルトークンのマーケットは、未ローンチとして表示されるのではなく、最後に読み取った状態のまま、古いものとして印を付けて画面に残ります。

マーケットのページに表示されるもの

2026-10-06以降:

  • 回収されたプールにはチャートがない。ロックの終了モードがプールの流動性を取り出した後は、誰でもその価格を無料で動かせます。ページはすでにそのプールについて価格も取引も表示していませんでしたが、今では24時間の変動もチャートも表示せず、その理由を示します。また、Workerはその価格のサンプリングをやめます
  • 取引のうち株式に回る分は、その取引自身のタックスの各項目がトレジャリーへ送った分であり、今日の設定ではなく、その取引が支払った時点の設定によるものです。不明な場合は「—」と表示されます
  • 時価総額は、流通供給量、つまり総量からバーンアドレスの保有分を差し引いたものを数えます。ヘッダーでもチャートでも同じです。7回目の監査ループ以降、供給量を読み取れなかった場合は、$0ではなく「—」を表示します
  • 読み取れなかった残高がゼロになることは決してありません。ポジションのパネルは読み取れなかったことを示し、取引パネルは最後に読み取った残高を「last read」と印を付けて表示し、それを上回る売却を拒否しません。実際の残高不足は、トランザクションのシミュレーションが拒否します
  • 最後に読み取った状態のまま残されたプロトコルトークンのマーケットは、そのことを示す。Lensがそのトレジャリーを読み取れない間、その価格と時価総額は、表示されるすべての場所で「(last read)」と印が付けられ、24時間の変動は表示されず、チャートには「Now」の代わりに「Last read」と表示されます。Lensが再び応答するまで、Workerはその価格を記録しないため、この停止によってチャートに点が加わることはありません。その時点のタックスは不明です。ページにはアンチスナイプのタグは表示されず、取引パネルは超過分の行なしで、有効な通常のタックスを表示します。そのトークン自身の数値は引き続きトークン上で読み取られるため、ランディングの「Burned by buyback」は最新に保たれます。その時価総額は最後に読み取ったものです。7回目の監査ループ以降、この停止はチャート上で途切れとして表示され、最後に読み取った価格にはその経過時間が示されます。トークンの数値は、マーケットが読み取られている場合も最後に読み取った状態のまま残されている場合も、応答がないときは最後に読み取った値を使い、ゼロや空の名前になることは決してありません。欠陥のあるアップグレードの後でプロトコルのコントラクトが失敗しても、それらが空白になることはもうありません

7回目の監査ループ以降(2026-10-06):

  • チャートは各点をその時刻に置く。各価格サンプルは、その時刻が範囲の中で当たる位置に置かれます。1H、24H、7Dは現在で終わり、「All」はマーケットの経過期間からではなく、最初のサンプルから始まります。ホバーした点にはその経過時間が示され(「3h 20m ago」)、サンプルが欠けているところ(停止、誰もページを開いていなかった夜)では線が途切れます。静止時、チャートはマーケットの価格、「Now」、または最後に読み取った状態のまま残されたマーケットについては「Last read」を表示します。それまでは、点は均等に並べられ、その位置によって日時が付けられていたため、停止の前に最後に読み取った価格が現在の価格として表示されていました
  • 24時間の出来高は、数えられない間は「—」を表示する。Workerによる取引ログの読み取りが失敗し続けると、その取引ウィンドウは進まなくなります。その場合、出来高は、マーケットごとにも合計でも、取引が止まったかのように縮んでいく数値の代わりに「—」を表示し、追いつく最初の読み取りで元に戻ります

8回目の監査ループ以降(2026-10-06):

  • ホワイトリストのウォレットには通常のタックスが表示される。マーケットのアンチスナイプの期間中、そのマーケットのホワイトリストに載っている接続済みのウォレットは、取引パネルが取引に使うStockFunのルーターを通じて、通常のタックスを支払います。パネルはそのウォレットについてアンチスナイプの超過分を表示しなくなり、その理由を注記で示します。見積もりはもともと正しく、超過分の行がそれと矛盾していました。ホワイトリストまたはウォレットが不明な間は、パネルは免除を考慮しないタックスを表示します
  • 株式に価格がない理由。Robinhood Chain上では、オラクルは、コーポレートアクションの間はその株式の価格を差し止め、シーケンサーが停止しているか、復旧した直後の間はすべての株式の価格を差し止めます。後者のチェックは、Chainlinkがそのチェーンのアップタイムフィードを公開するまではオフです(Robinhoodレールを参照してください)。その理由で株式の価値が欠けている場合、アプリはそう示します。「No price for NVDA right now: corporate action in progress」、またはRobinhood Chainのシーケンサーが停止中、復旧中、あるいはその状態が不明であることを、ドロップのパネルのバスケットの表の下とランディングのドロップのカードの下、そして請求ページではその株式の隣に表示します。ランディングのカードは、そのような株式を$0.00と表示していましたが、今は「—」と表示します
  • 24時間の出来高は各ブロックを一度だけ数える。前回より数ブロック遅れたノードが応答したWorkerの読み取りによって、取引ウィンドウが同じブロックを二度数えることはなくなりました。その1回の読み取りでは、最新の取引がフィードから欠けることがありますが、次の読み取りでそれらは元に戻ります

9回目の監査ループ以降(2026-10-06):

  • 推定値であるトレジャリーの額は、そのことを示す。トレジャリーの一部を読み取れないか価格を付けられない場合(オラクルが価格を差し止めている株式、古いETH/USDフィード、最後に読み取った状態のまま残されたミラーボールト)、その数値は価格を付けられた分を数え、今では表示されるすべての場所で「+」が付きます。マーケット一覧の行とカード、プロトコルトークンのカード、トレジャリーの動向、ランディング、ドロップのパネル、そしてホルダーの取り分です。マーケット一覧は、そのような額が価格を付けられた分で並べ替えられることを示し、トレジャリーの動向は、いくつのトレジャリーをそのように数えたかを示します。それまでは、ドロップのパネルだけが「an estimate」と示していました
  • 読み取れなかった承認はゼロではない。StockFunのルーターを通じた売却には、まずルーターへの承認額(アローワンス)が必要です。以前は、その読み取りに失敗すると、承認がないものとして数えられていました。すでに承認していたホルダーは、2回の署名でもう一度承認するよう求められ、2回目の失敗では無意味な承認が送られていました。取引パネルは今では、承認を読み取れなかったことを示し、何も見積もらず、読み取れるまで承認も売却も送りません。10回目の監査ループ以降、承認の直後の売却は、承認のブロック以降のブロックで承認額を読み取り、価格を見積もり、チェックを受けるため(後述)、1ブロック遅れたノードがゼロと答えることはもうありません

10回目の監査ループ以降(2026-10-06):

  • ウォレットがキャンセルしたトランザクションは、決して完了とは表示されない。ウォレットは、保留中のトランザクションをキャンセルする(同じnonceで自分自身に何も送らない送金)ことも、高速化する(同じ呼び出しをより高い手数料で送る)こともできます。アプリは今では、その代わりにマイニングされたものを読み取ります。高速化はその操作そのものとして扱われ、「View tx」はそれを指します。キャンセルやその他の置き換えは、そのフローを未完了として終わらせ、「Cancelled in your wallet.」または「Replaced by another transaction in your wallet.」と表示し、「View tx」はマイニングされたものを指し、ウォレットの拒否と同じように請求の実行を止めます。それまでは、キャンセルされた取引は「Done」と表示され、キャンセルされた承認は与えられたものとして数えられ、キャンセルされたクリエイターの請求は「Claimed」と表示されてそのセッションの間ETHを隠し、キャンセルされたローンチは「$TICKER is live.」とデモの架空のトークンアドレスを表示していました。通常の3分間の待機の後に高速化されたトランザクションは決して確認されず、パネルはページが再読み込みされるまでロックされたままでした
  • 待機の後も追跡される。通常の待機が3分後に打ち切られると、アプリはトランザクションをそのnonceで追跡します。ウォレットの送信済みトランザクションの数がそのnonceを超え、トランザクションにレシートがない場合、そのnonceでマイニングされたものを探し(送信より前のブロックでは決して探しません)、同じ規則を適用します。キャンセルや置き換えは、実際にブロックの中で見つかったトランザクションからだけ判断され、レシートがないことからは決して判断されません
  • ローンチが成立するのは、チェーンがそう示すときだけ。ローンチは、マーケットの作成をファクトリー自身のイベントから読み取ります。それがなければ、トランザクションのリンクとともにフォームに戻り、デモのアドレスがデモの外に表示されることは決してありません
  • 最後のトランザクションのブロックを、1分間。自身のトランザクションの1つがマイニングされた後の60秒間、アプリは、そのチェーンで次に送るもののチェック、ガスの見積もり、価格の見積もりを、そのブロック以降のブロックで行い、売却の承認額もそこで読み取ります。負荷分散されたエンドポイントは、1ブロック遅れたノードから応答することがあります。承認の直後の売却は、以前はそこで拒否され(「Approve it first」)、2回目の試行で2つ目の承認が送られることがありました。そのブロックに達していないノードは、今では遅れているものとして数えられ、再度問い合わせられます。数秒以内にどのノードもそこに達しなければ、何も送られません。承認の直後の売却については、ページはそのとき「Your approval went through, but no quote could be read from the pool, so the sale was not sent…」と表示し(その価格の見積もりが、そこで行われる最初の読み取りです)、自身のチェックがそのブロックに達しない書き込みトランザクション(承認、請求、ローンチ)については「This could not be checked against the block of your last transaction just now, so nothing was sent. Try again in a moment.」と表示します
  • 残るもの。誤った「Replaced」は、次のすべてが成り立つという1つの狭いケースで、まだ起こりえます。どのノードも売却を示さず、そのためアプリがウォレットの数からそのnonceを推測したこと。他のノードより遅れているものの、送信前に読み取ったブロックよりは遅れていないノードが、その数を答えたこと。そのノードが見ていなかった同じウォレットの別のトランザクションが、推測したnonceを使ったこと。そして、プライベートリレーを通じて送られた売却が、3分と約36秒の猶予の後もまだマイニングされていなかったこと。キャンセルが完了と表示されることは決してなく、ロックされたままになるものもありません

ローンチ

ローンチのフォームは、チェーンのレジストリから読み取ったバスケットだけを、レジストリ自身の番号付けのまま提示します。最初の読み取りの前は何も提示せず(「Reading the baskets from the chain…」)、コントラクトがデプロイされる前は、それらが稼働した時点でローンチが可能になると示します。送信の直前に、フォームはファクトリーから作成手数料と選ばれたバスケット(その名前と、ウェイト付きの株式)をもう一度読み取り、どちらかが表示しているものと異なる場合は、何も送らずに送信を拒否します。そのメッセージは、チェーンがその番号で保持しているバスケットを示します。8回目の監査ループまでは、フォームは最初の読み取りの前に、設定されたバスケットを設定された番号で提示していたため、レジストリがバスケットを別の番号で並べるデプロイでは、表示されたものとは別のバスケットでマーケットがローンチされることがありえました。フォームが支払うのは作成手数料だけです。クリエイター自身の購入はなく、アンチスナイプのホワイトリストも指定せず、そのことを示します(ファクトリーは直接の呼び出しであればその両方を受け付けます。マーケットのローンチを参照してください)。確認画面は、送信時に照合したバスケットを示します。

アートディレクション

アプリのデザインルールは、アプリ自身のDESIGN.mdにあります:白いキャンバスの上の温かみのあるライトテーマ、ドロップと主要な数値にはテラコッタ、インターフェースにはPoppins、大きな金額表示にはInstrument Serif、表では等幅数字です。

データの出どころ

手数料、ローンチのパラメータ、供給量、バスケットの構成は、コンポーネント内に直接書かれることは決してありません。これらはアプリの設定の中にあるか、チェーンから取得され、手数料の項目は、起動時に、コントラクトを反映した@stockfun/sharedから生成された定数と照合されます。ページに手入力されたパーセンテージこそが、コントラクトが実装していない配分表をプロダクトが宣伝してしまう原因になります。2026-10-05以降、これらの数値はオーナーの設定です。sharedパッケージはそのデフォルト値を保持し、現在の値は、それを保持するコントラクトから読み取られます。

プロトコルの状態はチェーンから取得されます。インデクサーはありません。Workerであるcairn-worker/が、開いているすべてのページのためにそれを一度だけ読み取ります:まずスナップショットを送り、その後は変更だけをプッシュするWebSocketです。Workerが失敗した場合、ページは公開RPCを通じて自らチェーンを読み取ります。残高から請求できるものまで、ウォレット自身のデータはブラウザが読み取ります。7回目の監査ループ以降、Workerは、自身のチェーンに対応していると答えるエンドポイントだけを読み取ります。別のチェーンにあるエンドポイントは停止しているものとして数えられるため、誤ったネットワークにあるバックアップによって、すべてのマーケットが消えることは決してありません。8回目以降、Workerは8番目のデータスキーマを公開しており、それは株式の価格が欠けている理由を追加します。古いスキーマを読み取るアプリも、その理由なしで引き続き動作します。9回目の監査ループ以降、Workerのウォッチャーは、自身のRPCについて得た情報(どのエンドポイントがいつから失敗しているか、どれだけ待つべきか、それぞれがどのチェーンに対応しているか)を、Cloudflareのオブジェクトが2回の読み取りの間にスリープする間も保持しますが、エンドポイントのアドレスを保存することは決してありません。プライマリーRPCの停止にかかるのは、読み取りのたびに3つのリクエストではなく、倍々に延びる待機ごとに1回のプローブであり、正常な読み取りは何も二度尋ねません。Workerはまた、Robinhood Chain自身のブロック番号を記録します。以前は、Robinhood Chainが決済を行うチェーンのブロック番号を記録していました。

文言監査

pnpm audit:copyは、バックエンド、キーパー、shared、コントラクトのソースをスキャンします。禁止語が1つでもあれば、あるいは静的にチェックできる視覚上の禁止事項が1つでもあれば、失敗します。必要に応じて実行するもので、pnpm buildには含まれていません。以前のdappがリポジトリから外れて以降、フロントエンドはスキャンしません。アプリでは、禁止語彙はレビューのルールであり、アプリのPRODUCT.mdに書かれています。

これはスタイルのリンターではありません。禁止語彙は法的な制約です。