Upgrade to Pro — share decks privately, control downloads, hide ads and more …

ブロックチェーンアプリの 状態管理はなぜ難しいのか Cross-Chain Bridge「T...

Sponsored · Your Podcast. Everywhere. Effortlessly. Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.
Avatar for Datachain Datachain
February 27, 2026
76

ブロックチェーンアプリの 状態管理はなぜ難しいのか Cross-Chain Bridge「TOKI」での事例 / React Tokyo フェス 2026

2026年2月28日開催「React Tokyo フェス 2026」における株式会社Datachain フロントエンドエンジニア 伊藤 耕太による発表スライドです。

------------------
Website:https://www.datachain.jp/ja
採用情報:https://careers.datachain.jp/
GitHub:https://github.com/datachainlab
X:@datachain_jp, @datachain_en
Medium:日本語 https://medium.com/@datachain_jp, English https://medium.com/@datachain
note:https://note.datachain.jp/
募集職種一覧:https://herp.careers/v1/datachain
カジュアル面談お申し込み:https://herp.careers/v1/datachain/yobfg8YOkk4g

Avatar for Datachain

Datachain

February 27, 2026
Tweet

More Decks by Datachain

Transcript

  1. Cross-Chain Bridge「TOKI」 • 4 ネットワーク対応 • 異なるネットワーク間でトークンを移す Transfer機能 • 他、流動性追加‧削除のPool機能や

    コミュニティ向けの ポイントプログラム機能など • モバイルファーストなデザイン • ダークモード対応 ※ TOKIは2026年1⽉をもってクローズしました。
  2. ブロックチェーンアプリ(DApps)の状態管理は 状態の種類 ⽤途 Server State APIデータのキャッシュ‧再検証 URL State フィルター‧ページネーション‧検索クエリなど Client

    State UIステート(モーダル開閉、選択状態など) Form State フォーム⼊⼒‧バリデーション Blockchain State on-chainデータ‧トランザクションの状態など
  3. トランザクションとは? created ウォレットで署名、トランザクションハッシュが発⾏される pending ブロックチェーンノードの待機エリア(mempool)に保存 contained ブロックに取り込まれる finality トランザクションが確定する 数秒

    ブロックチェーン上で⾏われる取引の記録のこと。 TOKIでは Transfer や流動性の追加‧削除などを⾏うとトランザクションが⽣成されます。 TxHash: 0x123456… TxHash: 0xabcdef… ※Ethereumネットワークを例にしています。約 12秒ごとに1ブロック生成。 時間
  4. トランザクションとは? created ウォレットで署名、トランザクションハッシュが発⾏される pending ブロックチェーンノードの待機エリア( mempool)に保存 contained ブロックに取り込まれる ブロックチェーン上で⾏われる取引の記録のこと。 TOKIでは

    Transfer や流動性の追加‧削除などを⾏うとトランザクションが⽣成されます。 - TxHash: 0x123456… finality トランザクションが確定する 数秒 数⼗秒 ※Ethereumネットワークを例にしています。約 12秒ごとに1ブロック生成。 時間
  5. トランザクションとは? created ウォレットで署名、トランザクションハッシュが発⾏される pending ブロックチェーンノードの待機エリア( mempool)に保存 contained ブロックに取り込まれる ブロックチェーン上で⾏われる取引の記録のこと。 TOKIでは

    Transfer や流動性の追加‧削除などを⾏うとトランザクションが⽣成されます。 finality トランザクションが確定する 数秒 数⼗秒 約12分 ※Ethereumネットワークを例にしています。約 12秒ごとに1ブロック生成。 時間
  6. // Response [ { "type": "TRANSFER_POOL", "nonce": 1205, "txs": [

    { "status": "SUCCEEDED", "txHash": "0xc0f817b5dd499ba5f0259b3607bc3460afaef41ff48de64728b2828410909b6e", "chainId": 56, "createdTime": "2025-12-08T02:31:37Z", "estimatedTime": "2025-12-08T02:31:37Z", "order": 1, "steps": [ "contractAddress": “0x..”, "event": “SendPacket”, ] }, { …} ], "status": "COMPLETED", "statusDetail": "COMPLETED_DETAIL", "sourceTxHash": "0xc0f817b5dd499ba5f0259b3607bc3460afaef41ff48de64728b2828410909b6e", "sourceChainId": 56, "createdTime": "2025-12-08T02:31:37Z", .. }, API v2/operations トランザクションハッシュを保持していれば、 ブロックチェーンノードに問い合わせたり、バックエンドのAPI、 チェーンのエクスプローラに聞くことで状態を取得できます。 // Response { "blockHash": "0xf850331061196b8f2b67e1f43aaa9e69504c059d3d3fb9547b04f9ed4d141ab7", "blockNumber": "0xcf2420", "from": "0x00192fb10df37c9fb26829eb2cc623cd1bf599e8", "gas": "0x5208", "gasPrice": "0x19f017ef49", "maxFeePerGas": "0x1f6ea08600", "maxPriorityFeePerGas": "0x3b9aca00", "hash": "0xbc78ab8a9e9a0bca7d0321a27b2c03addeae08ba81ea98b03cd3dd237eabed44", "input": "0x", "nonce": "0x33b79d", "to": "0xc67f4e626ee4d3f272c2fb31bad60761ab55ed9f", "transactionIndex": "0x5b", "value": "0x19755d4ce12c00", "type": "0x2", "accessList": [], "chainId": "0x1", "v": "0x0", "r": "0xa681faea68ff81d191169010888bbbe90ec3eb903e31b0572cd34f13dae281b9", "s": "0x3f59b0fa5ce6cf38aff2cfeb68e7a503ceda2a72b4442c7e2844d63544383e3", "yParity": "0x0" } curl <RPC_URL> \ -X POST \ -H "Content-Type: application/json" \ -d '{"jsonrpc": "2.0", "method": "eth_getTransactionByHash", "params": ["0xbc78ab8a9e9a0bca7d0321a27b2c03addeae08ba81ea98b03cd3dd237eabed44"], "id": 1}'
  7. IndexedDBを採⽤(Dexie + Live Query)   どこに保持するか?主な要件は、 • トランザクション数によってデータが増えても安⼼な設計にしたい • [address +

    createdTime]によって絞り込み、ソートしたい • UIを⾮同期に更新したい localStorage IndexedDB 容量/履歴 △(厳しめ) ◎ 検索/ソート ✕(⾃前) ◎ 更新とUI △ ◎
  8. in-memoryを加えた⽐較 in-memory localStorage IndexedDB 容量/履歴 △ △(厳しめ) ◎ 検索/ソート △

    ✕(⾃前) ◎ 更新とUI ◎ △ ◎ リロード耐性 ✕ ◎ ◎ in-memoryはシンプルですが、リロードで消えます。 localStorageは永続化できますが、検索やソートが弱い。
  9. useLiveQueryの仕組み export const useOperations = (sender?: string, offset?: number, limit?:

    number) => { const operations = useLiveQuery (() => { if (!sender) return [] let query = dbOperations. operations .where('[sender+createdTime]' ) .between([sender, Dexie. minKey], [sender, Dexie. maxKey]) .reverse() if (!isUndefined (offset)) { query = query. offset(offset) } if (!isUndefined (limit)) { query = query. limit(limit) } return query.toArray() }, [sender, offset, limit]) ?? [] return { operations } }
  10. エッジケース対応 • WebSocket切断 → 再接続時にBackendから最新を取得して同期 • tx失敗 → RESUMABLE /

    UNRECOVERABLE の状態遷移で対応 • タイムアウト → ウォレット側で speed up / cancel、サポートへの導線