Інтеграція кошелька з Phantom (Solana)
Phantom це де-факто стандартний кошелек в екосистемі Solana. Його API інжектується в window.solana та слідує інтерфейсу SolanaProvider зі спеціфікації wallet-standard. Інтеграція несмартна, але є кілька місць, де легко вистрелити собі в ногу.
Підключення та виявлення провайдера
Перша помилка — перевіряти window.solana одразу при завантаженні сторінки. Розширення інжектується асинхронно, та на швидких машинах воно встигає раніше, ніж виконається ваш JS, а на повільних — ні. Надійний паттерн:
const getProvider = (): PhantomProvider | undefined => {
if ('phantom' in window) {
const provider = (window as any).phantom?.solana;
if (provider?.isPhantom) return provider;
}
return undefined;
};
window.phantom.solana — краще за window.solana, тому що останній може бути перехоплений іншими кошельками (Backpack, Solflare). Якщо потрібна підтримка кількох кошельків, використовуємо wallet-adapter від Solana Labs — @solana/wallet-adapter-react, який абстрагує всі провайдери через єдиний інтерфейс.
Підключення, підпис та транзакції
// Підключення
const response = await provider.connect();
const publicKey = response.publicKey.toString();
// Підпис повідомлення (для аутентифікації)
const message = new TextEncoder().encode("Sign in to MyApp");
const { signature } = await provider.signMessage(message, "utf8");
// Відправка транзакції
const transaction = new Transaction().add(/* instruction */);
transaction.feePayer = provider.publicKey;
transaction.recentBlockhash = (
await connection.getLatestBlockhash()
).blockhash;
const { signature: txSig } = await provider.signAndSendTransaction(transaction);
Важливий момент: signAndSendTransaction відправляє транзакцію через власний RPC Phantom'а. Якщо потрібно контролювати RPC endpoint (наприклад, використовувати Helius або QuickNode з пріоритетними fee), використовуйте signTransaction + connection.sendRawTransaction вручну.
Обробка стану та подій
Phantom еміцирует подіїї connect, disconnect та accountChanged. Обов'язково підписуватися на accountChanged — користувач може переключити аккаунт всередині кошелька без переподключення, та ваш додаток про це не дізнається без явного listener'а.
provider.on('accountChanged', (publicKey: PublicKey | null) => {
if (publicKey) {
// Оновити стан додатку
} else {
// Кошелек заблокований — розлогінити користувача
provider.connect().catch(() => {});
}
});
Для React-додатків весь цей шар краще вивести в @solana/wallet-adapter-react — він управляє життєвим циклом, мемоізацією та реконнектом автоматично.







