---
title: "Solanaトークンの過去の残高機能を発表"
description: "getTokenAccountsByOwnerAtSlotは、過去の任意の時点でウォレットが保有していたすべての情報を、正確な残高とともに1回の呼び出しで返します。"
---

# Solanaトークンの過去の残高機能を発表

<ImageBlock
  src="https://media.alchemy.com/gTABOAS.png"
  alt="getTokenAccountsByOwnerAtSlot — Solanaトークン残高の履歴取得"
  width={1920}
  height={900}
  priority
/>

Solanaのウォレットに今何を保有しているか聞けば、どのノードでも一回の呼び出しで答えが返ってくる。しかし去年の春に何を保有していたか、あるいは資金が抜かれる直前の正確な瞬間に何を保有していたかを聞くと、その簡単な方法は使えなくなる。ノードが提供するのは現在の残高だけだ。誰がいつ何を保有していたかという履歴は記録していない。つまり「そのとき何を保有していたか」という問いに対する正直な答えは、これまで「自分で再構築する」しかなかった。

今日、それを変える。[`getTokenAccountsByOwnerAtSlot`](https://www.alchemy.com/docs/chains/solana/solana-api-endpoints/get-token-accounts-by-owner-at-slot)は、過去の任意の時点でウォレットが保有していたすべてを、正確な残高とともに一回の呼び出しで返す。

## 履歴を再構築することの問題

標準の[`getTokenAccountsByOwner`](https://www.alchemy.com/docs/chains/solana/solana-api-endpoints/get-token-accounts-by-owner)が答えられる問いは一つだけだ。このウォレットが今何を保有しているか、というものだ。ライブアプリのフローの大半はこれでカバーできるが、答えられない問いの種類がまるごと残る。

過去の残高を自分で再構築するには、そのアカウントの生涯を再生する必要がある。[`getSignaturesForAddress`](https://www.alchemy.com/docs/chains/solana/solana-api-endpoints/get-signatures-for-address)でウォレットの全署名履歴を取得し、[`getTransaction`](https://www.alchemy.com/docs/chains/solana/solana-api-endpoints/get-transaction)で各トランザクションを取得し、目的のスロットまでスロットごとに残高を再導出する。アクティブなウォレットだと、これは数千回の呼び出しになり、大量のパースが必要で、計算を誤る余地も多い。アーカイブノードを使えば過去の状態を読めるようにはなるが、それでも「スロットXにおけるこのオーナーのトークンアカウントを一覧する」という問いには直接答えてくれない。

## 仕組み

このメソッドは`getTokenAccountsByOwner`ですでに馴染みのある構文を保ちつつ、一つだけ要素を加える。`slot`だ。スロットはSolanaの時計であり、おおよそ1ブロックに相当する。つまりスロットを渡すということは「その履歴上の正確な時点において、このウォレットがどうだったかを教えてくれ」という意味になる。

呼び出すと、そのウォレットの**トークンアカウント**が返ってくる。これは、ウォレットが保有する各トークンを保持している個々のレコードだ。Solanaではウォレットが残高を直接保持するわけではないため、これらのアカウントを一覧することが保有状況を読む方法になる。それぞれのアカウントには残高と**lamports**が付随する。lamportsとは、すべてのアカウントがオンチェーンに存在するために持つ少額のSOL(1コインの端数で、1 SOLは10億lamports)のことだ。このメソッドはSolanaの2つのトークン規格の両方をカバーしている。**SPL Token**は、USDCのような事実上あらゆる有名トークンの背後にあるオリジナルの規格であり、**Token-2022**は、転送手数料や残高の秘匿化といった追加機能を持つ新しい規格だ。これらの規格は異なるプログラムID配下に存在するため、一つの`programId`フィルターはそのプログラムのアカウントしか返さない。

この呼び出しの背後には、継続的に構築されている履歴インデックスがある。これは時間経過にわたるすべてのトークンアカウントのバージョン管理された記録であり、誰がどのアカウントをどれだけ、どのスロットの間保有していたかを、新しいブロックが取り込まれるたびに最新の状態に保っている。クエリのスコープの取り方は2通りある。一つはmintによるもので、過去のスロットにおける単一トークンの残高を読む場合。もう一つはprogramによるもので、そのスロットにおいて、そのプログラム配下で保有されているすべてのトークンアカウントを一覧する場合だ。両規格にまたがる完全なポートフォリオが必要な場合は、SPL TokenのプログラムIDで一度、Token-2022のプログラムIDで一度呼び出し、結果を結合する。以下の例はSPL Tokenのみにスコープしている。

<CodeSnippet
  language="javascript"
  code={`const res = await fetch("https://solana-mainnet.g.alchemy.com/v2/{ALCHEMY_API_KEY}", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    jsonrpc: "2.0",
    id: 1,
    method: "getTokenAccountsByOwnerAtSlot",
    params: [
      "89zoESAF7JqYQz5uhf5eYwg3QsKXQy5QFs4WZQ4hMPnb",
      { programId: "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" },
      { encoding: "jsonParsed", slot: 80000000 }
    ]
  })
});
console.log(await res.json());`}
/>

`pageKey`が指定したスロットを固定するため、ページングは一貫性を保つ。チェーンの先端はページング中も進んでいくことがあるが、結果は依然として同じ時点を記述し続ける。1ページあたり最大10,000アカウントまで対応する。

## なぜ重要なのか

任意の時点での保有状況が、ファーストクラスのクエリになる。本来であれば自分で構築・維持しなければならないインデックス作成パイプラインが、RPCの一回の呼び出しに収束する。これにより、チームは配管работではなく、製品そのものに取り組めるようになる。

これまで面倒だった作業が可能になる。

- 任意の過去時点でのポートフォリオおよびPnLスナップショット
- 特定のスロットや日付に紐づくコストベースおよび税務レポート
- 紛争対応、フォレンジック、インシデント対応。例えば、資金が抜かれる直前のブロックでウォレットが何を保有していたかを示す場合
- 再生処理なしに正確な過去の状態を必要とする分析やインデックス作成パイプライン

テスト中、結果をオンチェーンの正解データと照合して検証した。サンプリングした過去のクエリは、KINのスロット80,000,000時点での残高のような深い履歴を含め、チェーン上の残高およびlamportsと正確に一致した。

## はじめに

このメソッドは標準のSolanaメインネットエンドポイントで利用できるため、すでにAlchemy経由でSolanaのRPCを送信しているなら、既存のAPIキーで呼び出せる。`https://solana-mainnet.g.alchemy.com/v2/{ALCHEMY_API_KEY}`を指定し、`slot`を追加して、保有状況を読み取ればよい。

キーを設定して最初のリクエストを送るには、[Solana APIクイックスタート](https://www.alchemy.com/docs/reference/solana-api-quickstart)を参照のこと。

## FAQ

### getTokenAccountsByOwnerAtSlotは何をするものですか？

指定した過去のスロット時点における、Solanaウォレットのトークンアカウントを、残高とlamportsとともに返す。これは`getTokenAccountsByOwner`と同じ形状に、`slot`パラメータを加えたものだ。

### SPL TokenとToken-2022とは何ですか？

Solanaの2つのトークン規格だ。SPL Tokenはオリジナルの規格で、ネットワーク上の事実上あらゆる有名トークンをカバーしている。Token-2022はより新しい規格で、転送手数料や残高の秘匿化といったオプション機能を追加する。このメソッドは両方をカバーしている。

### getTokenAccountsByOwnerとの違いは何ですか？

`getTokenAccountsByOwner`は現在の保有状況のみを返す。`getTokenAccountsByOwnerAtSlot`は同じ問いに対して、ライブのノードの状態ではなく履歴インデックスから読み取ることで、任意の過去のスロットについて答える。

### アーカイブノードやカスタムインデクサーは必要ですか？

不要だ。このメソッドは事前に構築された履歴インデックスを読み取るため、アーカイブインフラを運用したり、トランザクション履歴を自分で再生したりする必要はない。

### 単一トークンだけでなく、ウォレットの全ポートフォリオをクエリできますか？

可能だが、ウォレットが両規格を保有している場合、一回の`programId`呼び出しでは完結しない。単一トークンの場合は`mint`でスコープし、あるスロットにおけるそのプログラム配下の全アカウントの場合は`programId`でスコープする。SPL TokenとToken-2022は異なるプログラムIDを使うため、完全なポートフォリオを得るには両方をクエリしてレスポンスをマージする必要がある。

### チェーンが進んでいく中で、ページングはどのように正確さを保つのですか？

`pageKey`がクエリのスロットを固定するため、新しいブロックが届いてもページ間で結果は一貫性を保つ。1ページあたり最大10,000アカウントまで対応する。
