Agenda
現象
Token Accountを作成すると以下のエラーが発生。
ターミナル
/Users/user/Documents/Programming/Blockchain/solana-anchor-react-minimal-example/scripts/solana/spl-token-v0.3.x/node_modules/@solana/web3.js/src/connection.ts:5791
throw new SendTransactionError(
^
SendTransactionError: failed to send transaction: Transaction simulation failed: Error processing Instruction 0: incorrect program id for instruction
at Connection.sendEncodedTransaction (/Users/user/Documents/Programming/Blockchain/solana-anchor-react-minimal-example/scripts/solana/spl-token-v0.3.x/node_modules/@solana/web3.js/src/connection.ts:5791:13)
at processTicksAndRejections (node:internal/process/task_queues:96:5)
at async Connection.sendRawTransaction (/Users/user/Documents/Programming/Blockchain/solana-anchor-react-minimal-example/scripts/solana/spl-token-v0.3.x/node_modules/@solana/web3.js/src/connection.ts:5750:20)
at async Connection.sendTransaction (/Users/user/Documents/Programming/Blockchain/solana-anchor-react-minimal-example/scripts/solana/spl-token-v0.3.x/node_modules/@solana/web3.js/src/connection.ts:5738:12)
at async sendAndConfirmTransaction (/Users/user/Documents/Programming/Blockchain/solana-anchor-react-minimal-example/scripts/solana/spl-token-v0.3.x/node_modules/@solana/web3.js/src/utils/send-and-confirm-transaction.ts:35:21) {
logs: [
'Program ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL invoke [1]',
'Program log: Create',
'Program ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL consumed 3749 of 200000 compute units',
'Program ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL failed: incorrect program id for instruction'
]
}
ts
import {
createMint,
createAccount,
TOKEN_PROGRAM_ID,
} from '@solana/spl-token';
const mint = await createMint(
connection, // connection
payer, // payer
payer.publicKey, // mintAuthority
null, // freezeAuthority
9 // decimals
);
const tokenAccount = await createAccount(
connection, // connection,
payer, // payer
Keypair.generate().publicKey, // mint
payer.publicKey, // owner
);
原因
Mintアドレスが間違っている
mintアドレスが正しくないため。
今回、わかりやすく失敗させるため、keypairで適当にアドレスを発行した。
Keypair.generate().publicKey, // mint
Mint Accountが存在していない
これもよくあるケースとして、createMint(Mint Accountの作成)してから、すぐにそのMint Accountを使おうとすると、トランザクションが完了していない場合が多々ある。その場合、「そのMintアドレスが間違っている(=まだ存在していない)」というエラーになる。
以下のようにデバッグするとMint Accountがnullになっていることがわかる。
console.log(await connection.getAccountInfo(mint));
// => null
対応
ケース1:mintアドレスを正しいものにする
正しいmintアドレスを指定する。
const mint = await createMint(
connection, // connection
payer, // payer
payer.publicKey, // mintAuthority
null, // freezeAuthority
9 // decimals
);
const tokenAccount = await createAccount(
connection, // connection,
payer, // payer
mint, // mint ← 正しいmintアドレスを指定
payer.publicKey, // owner
);
ケース2:カスタムRPCにする
もしDevnet(https://api.devnet.solana.com/)で試している場合は、カスタムRPCを使う。
Solana公式のDevnetだと、バリデーター側の処理が遅いだけのため、カスタムRPCを使うだけでも解決することがある。
ケース3:commitmentOrConfigをセットする
Connectionで「confirmed」をセットする。
型:
new Connection(endpoint: string, commitmentOrConfig?: Commitment | ConnectionConfig): Connection
実装例:
const connection = new web3.Connection('http://127.0.0.1:8899', 'confirmed');
API:constructor
ケース4:sleepさせる
トランザクションが終わっていない場合は、終わるまでsleep&retryして待つ。
mainnet-betaで厳密に運用するならこの方法が確実。
Mint Accountのトランザクションチェック
import {
Connection,
SendTransactionError,
AccountInfo,
} from '@solana/web3.js';
import {
createAccount,
createMint,
getAccount,
TokenAccountNotFoundError,
} from '@solana/spl-token';
...
// Create mint
const mint = await createMint(
connection,
payer,
payer.publicKey,
payer.publicKey,
0,
);
// Check transaction
{
const retry = 60; // Max retry
let accountInfo: null | AccountInfo<Buffer>;
for (let i = 0; i < retry; i++) {
console.count('Checking for mint account transaction...');
accountInfo = await connection.getAccountInfo(mint);
if(accountInfo) {
console.log("Mint address:", mint.toBase58());
break;
} else {
sleep(1000); // Wait for 1s(=1000ms)
}
}
}
ATA(Associated Token Account)のトランザクションチェック
import {
Connection,
SendTransactionError,
AccountInfo,
} from '@solana/web3.js';
import {
createAccount,
createMint,
getAccount,
TokenAccountNotFoundError,
} from '@solana/spl-token';
...
// Create associated token account
const tokenAccount = await createAccount(
connection,
payer,
mint,
payer.publicKey,
);
// Check transaction
{
const retry = 60; // Max retry
for (let i = 0; i < retry; i++) {
console.count('Checking for token account transaction...');
try {
const tokenAccountInfo = await getAccount(connection, tokenAccount);
console.log("Token account:", tokenAccountInfo.address.toBase58());
break;
} catch (error: any) {
if (!(error instanceof SendTransactionError || TokenAccountNotFoundError)) {
console.error(error);
}
sleep(1000); // Wait for 1s(=1000ms)
}
}
}