本文へスキップ
BecomeCoder

Solanaコース · 第8章 トークンとNFT · レッスン31

Associated Token Account(ATA)

ローカル実施

導入

「あるウォレットが、あるトークンをいくら持っているか」を調べたいとき、そのToken Accountのアドレスをどうやって知ればよいでしょうか。毎回相手に「あなたのToken Accountのアドレスは?」と聞くのは非現実的です。この問題を解決するのが Associated Token Account(ATA) です。

説明

ATAは、「(ウォレット, Mint)の組み合わせが決まれば、Token Accountのアドレスも一意に決まる」ように標準化された仕組みです。第5章で学んだ PDA(Program Derived Address) の一種で、ウォレットのアドレスとMintのアドレスをシードにして導出されます。

flowchart LR
    W["ウォレットのアドレス"] --> D["find_program_addressで導出<br/>seeds = [wallet, token_program, mint]"]
    Mi["Mintのアドレス"] --> D
    D --> ATA["ATAのアドレス<br/>(誰でも同じ計算で求められる)"]

PDAなので秘密鍵は存在せず、誰でも同じ計算式(ウォレットのアドレスとMintのアドレスから)で同じアドレスを再現できます。これにより、次のようなことが可能になります。

  • 相手に「あなたのToken Accountは?」と聞かなくても、相手のウォレットアドレスとMintのアドレスさえ分かれば、送金先のToken Accountを自分で計算できる。
  • まだToken Accountを持っていない相手にも、ATAを作成しながら送金できる(createtransfer を1つのトランザクションにまとめられる)。

Anchorでは anchor_spl::associated_token::AssociatedToken を使って、この標準化されたアカウントを制約の中で扱えます。

use anchor_spl::associated_token::AssociatedToken;
use anchor_spl::token::{Mint, Token, TokenAccount};

#[derive(Accounts)]
pub struct SendToken<'info> {
    #[account(mut)]
    pub mint: Account<'info, Mint>,
    #[account(
        init_if_needed,
        payer = payer,
        associated_token::mint = mint,
        associated_token::authority = recipient,
    )]
    pub recipient_ata: Account<'info, TokenAccount>,
    pub recipient: SystemAccount<'info>,
    #[account(mut)]
    pub payer: Signer<'info>,
    pub token_program: Program<'info, Token>,
    pub associated_token_program: Program<'info, AssociatedToken>,
    pub system_program: Program<'info, System>,
}

特定のシードから決まるアドレス」というPDAの性質が、ここでは「誰でも同じToken Accountを見つけられる標準化」という実用的な形で活躍しています。

演習

もしATAという標準がなく、ユーザーが「Mintごとに好きなアドレスでToken Accountを作ってよい」ルールだったら、ウォレットアプリはどうやってユーザーの残高を全部見つければよいでしょうか。ATAがある場合と比べて考えてみましょう。

ヒント1を見る

ATAがなければ、ユーザーが持つすべてのToken Accountのアドレスをどこかに記録しておく必要があります。

ヒント2を見る

ATAなら「ウォレットアドレス+Mintアドレス」という2つの既知の値から計算だけで求まるため、記録や問い合わせが不要です。