導入
「あるウォレットが、あるトークンをいくら持っているか」を調べたいとき、その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を作成しながら送金できる(
createとtransferを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つの既知の値から計算だけで求まるため、記録や問い合わせが不要です。