アドレスの検証

誤ったアドレスにトークンを送信すると、資金が永久に失われる可能性があります。アドレス検証により、適切に受信してアクセスできるアドレスにのみトークンを送信できるようになります。

支払いの基本概念については、Solanaでの支払いの仕組みを参照してください。

Solanaアドレスの理解

Solanaアカウントには、オンカーブとオフカーブの2種類のアドレスがあります。

オンカーブアドレス

標準アドレスは、Ed25519鍵ペアの公開鍵です。これらのアドレスは:

  • トランザクションに署名できる対応する秘密鍵を持つ
  • ウォレットアドレスとして使用される

オフカーブアドレス(PDA)

プログラム派生アドレスは、プログラムIDとシードから決定論的に派生されます。これらのアドレスは:

  • 対応する秘密鍵を持たない
  • アドレスが派生されたプログラムによってのみ署名できる

支払いにおけるアカウントタイプ

アドレスを使用してネットワークからアカウントを取得し、そのプログラム所有者とアカウントタイプを確認して、アドレスの処理方法を決定します。

アドレスがオンカーブかオフカーブかを知るだけでは、それがどのタイプのアカウントか、どのプログラムが所有しているか、またはそのアドレスにアカウントが存在するかはわかりません。これらの詳細を判断するには、ネットワークからアカウントを取得する必要があります。

System Programアカウント(ウォレット)

System Programが所有するアカウントは標準的なウォレットです。ウォレットにSPLトークンを送信するには、その関連トークンアカウント(ATA)を派生して使用します。

ATAアドレスを派生した後、トークンアカウントがオンチェーンに存在するかどうかを確認します。ATAが存在しない場合、転送と同じトランザクション内に受信者のトークンアカウントを作成する命令を含めることができます。ただし、これには新しいトークンアカウントのrentの支払いが必要です。受信者がATAを所有しているため、rentに支払われたSOLは送信者が回収することはできません。

セーフガードがない場合、ATAの作成を補助することは悪用される可能性があります。悪意のあるユーザーは転送をリクエストし、あなたの費用でATAを作成させ、ATAを閉じてレントSOLを回収し、これを繰り返すことができます。

トークンアカウント

トークンアカウントはToken ProgramまたはToken-2022 Programによって所有され、トークン残高を保持します。受け取ったアドレスがトークンプログラムによって所有されている場合、送信する前にそのアカウントがトークンアカウント(mint accountではない)であり、期待されるトークンmint accountと一致することを確認する必要があります。

Token Programsは、転送における両方のトークンアカウントが同じmintのトークンを保持していることを自動的に検証します。検証が失敗した場合、トランザクションは拒否され、資金は失われません。

Mint account

Mint accountは、特定のトークンのトークン供給量とメタデータを追跡します。Mint accountもToken Programsによって所有されていますが、トークン転送の有効な受取先ではありません。Mintアドレスにトークンを送信しようとすると、トランザクションは失敗しますが、資金は失われません。

その他のアカウント

他のプログラムによって所有されているアカウントには、ポリシー決定が必要です。一部のアカウント(例:マルチシグウォレット)は有効なトークンアカウント所有者である可能性がありますが、他のアカウントは拒否されるべきです。

検証フロー

次の図は、アドレスを検証するための参照決定木を示しています:

Address Verification Flow

アカウントを取得

アドレスを使用して、ネットワークからアカウントの詳細を取得します。

アカウントが存在しない

このアドレスにアカウントが存在しない場合、アドレスがオンカーブかオフカーブかを確認します:

  • オフカーブ(PDA): アクセス不可能なATAへの送信を避けるため、アドレスを保守的に拒否します。既存のアカウントがない場合、アドレスだけからこのPDAを導出したプログラムや、アドレスがATAのものかどうかを判断できません。このアドレスのATAを導出してトークンを送信すると、アクセス不可能なトークンアカウントに資金がロックされる可能性があります。

  • オンカーブ: これはまだ資金が供給されていない有効なウォレットアドレス(公開鍵)です。ATAを導出し、存在するか確認してトークンを送信します。ATAが存在しない場合、その作成に資金を供給するかどうかのポリシー決定を行う必要があります。

アカウントが存在する場合

アカウントが存在する場合、どのプログラムが所有しているか確認します:

  • システムプログラム: これは標準的なウォレットです。ATAを導出し、存在するか確認してトークンを送信します。ATAが存在しない場合、その作成に資金を供給するかどうかのポリシー決定を行う必要があります。

  • トークンプログラム / Token-2022: アカウントがトークンアカウント(ミントアカウントではない)であり、送信しようとしているトークン(ミント)を保持していることを確認します。有効な場合、このアドレスに直接トークンを送信します。ミントアカウントまたは別のミントのトークンアカウントの場合、アドレスを拒否します。

  • その他のプログラム: これはポリシー決定が必要です。マルチシグウォレットなどの一部のプログラムは、トークンアカウントの所有者として許容される場合があります。ポリシーで許可されている場合、ATAを導出して送信します。それ以外の場合、アドレスを拒否します。

デモ

次の例は、アドレス検証ロジックのみを示しています。これは説明目的の参考コードです。

このデモでは、ATAの導出方法やトークン送信のトランザクション構築方法は示していません。サンプルコードについては、トークンアカウントおよびトークン転送のドキュメントを参照してください。

以下のデモでは、3つの可能な結果を使用します。

結果意味アクション
IS_WALLET有効なウォレットアドレスassociated token accountを導出して送信
IS_TOKEN_ACCOUNT有効なトークンアカウントこのアドレスに直接トークンを送信
REJECT無効なアドレス送信しない
Demo
/**
* Validates an input address and classifies it as a wallet, token account, or invalid.
*
* @param inputAddress - The address to validate
* @param rpc - Optional RPC client (defaults to mainnet)
* @returns Classification result:
* - IS_WALLET: Valid wallet address
* - IS_TOKEN_ACCOUNT: Valid token account
* - REJECT: Invalid address for transfers
*/
export async function validateAddress(
inputAddress: Address,
rpc: Rpc<GetAccountInfoApi> = defaultRpc
): Promise<ValidationResult> {
const account = await fetchJsonParsedAccount(rpc, inputAddress);
// Log the account data for demo
console.log("\nAccount:", account);
// Account doesn't exist on-chain
if (!account.exists) {
// Off-curve = PDA that doesn't exist as an account
// Reject conservatively to avoid sending to an address that may be inaccessible.
if (isOffCurveAddress(inputAddress)) {
return { type: "REJECT", reason: "PDA doesn't exist as an account" };
}
// On-curve = valid keypair address, treat as unfunded wallet
return { type: "IS_WALLET" };
}
// Account exists, check program owner
const owner = account.programAddress;
// System Program = wallet
if (owner === SYSTEM_PROGRAM) {
return { type: "IS_WALLET" };
}
// Token Program or Token-2022, check if token account
if (owner === TOKEN_PROGRAM || owner === TOKEN_2022_PROGRAM) {
const accountType = (
account.data as { parsedAccountMeta?: { type?: string } }
).parsedAccountMeta?.type;
if (accountType === "account") {
return { type: "IS_TOKEN_ACCOUNT" };
}
// Reject if not a token account (mint account)
return {
type: "REJECT",
reason: "Not a token account"
};
}
// Unknown program owner
return { type: "REJECT", reason: "Unknown program owner" };
}
// =============================================================================
// Examples
// =============================================================================
Console
Click to execute the code.

Is this page helpful?

管理運営

© 2026 Solana Foundation.
無断転載を禁じます。
つながろう