Скануйте, щоб завантажити додаток Gate
qrCode
Більше варіантів завантаження
Не нагадувати сьогодні

Розробка системи гаманця для біржі — інтеграція мережі Solana

Попередній раз ми доповнили систему управління ризиками біржі, а в цій статті розглянемо підключення гаманців до мережі Solana. Модель облікових записів, збереження логів та механізм підтвердження в Solana значно відрізняються від Ethereum-ланцюгів. Якщо використовувати підхід, характерний для Ethereum, можна легко наткнутися на помилки. Нижче наведено загальний огляд підходу до роботи з Solana.

Знайомство з унікальними особливостями Solana

Модель облікових записів Solana

Solana використовує модель, де програми та дані розділені: програми є спільними, а дані зберігаються окремо у PDA (Program Derived Address) облікових записах. Оскільки програми спільні, для розрізнення різних токенів застосовують Token Mint — глобальні метадані токена, що зберігають інформацію, таку як mint authority (право на емісію), загальний обсяг (supply), кількість десяткових знаків (decimals) тощо.
Кожен токен має унікальну адресу Mint, наприклад, USDC у мережі Solana має адресу EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v.

В мережі Solana існує дві системи програм для токенів: SPL Token та SPL Token-2022. Для зберігання балансу користувача кожен токен використовує ATA (Associated Token Account). При переказі токенів викликаються відповідні програми для переміщення активів між ATA.

Обмеження логів у Solana

На відміну від Ethereum, де історичні логи транзакцій використовуються для визначення переказів токенів, у Solana логи виконання за замовчуванням не зберігаються довго. Логи не є частиною стану блокчейну і не мають Bloom-фільтрів, а їх вивід може бути обрізаний під час виконання.
Тому для відновлення інформації про депозити/зняття потрібно використовувати getBlock або getSignaturesForAddress для аналізу інструкцій.

Підтвердження та реорганізація блоків у Solana

Час створення блоку — приблизно 400 мс. Після 32 підтверджень (близько 12 секунд) блок вважається finalised. Якщо вимоги до швидкості не дуже високі, можна довіряти лише finalised-блокам.
Для більш високої швидкості потрібно враховувати можливі реорганізації блоків, хоча вони трапляються рідко. Оскільки консенсус Solana не базується на parentBlockHash для формування ланцюга, його не можна використовувати для визначення фрікцій через різницю між parentBlockHash та blockHash, як у Ethereum.
Як визначити реорганізацію?
На локальному рівні слід зберігати blockhash для кожного slot. Якщо для одного slot з’являється різний blockhash — це сигнал про відкат.

Розуміння особливостей Solana дозволяє перейти до реалізації. Спершу розглянемо зміни у базі даних:

Модель бази даних

Оскільки у Solana існує два типи токенів, у таблиці tokens потрібно додати поле token_type для розрізнення SPL Token та SPL Token-2022.

Адреси Solana, хоча й відрізняються від Ethereum, можна отримати шляхом деривації за BIP32/BIP44, але з іншими шляхами. Тому достатньо використовувати існуючу таблицю wallets, доповнивши її для підтримки ATA та слідкування за блоками:

Назва таблиці Ключові поля Опис
solana_slots slot, block_hash, status, parent_slot Зберігає інформацію про слоти для виявлення фрікцій і відкатів
solana_transactions tx_hash, slot, to_addr, token_mint, amount, type Деталі транзакцій, включаючи депозити та зняття, з унікальним tx_hash для відслідковування
solana_token_accounts wallet_id, wallet_address, token_mint, ata_address Мапінг ATA до гаманця, що дозволяє швидко знаходити внутрішні облікові записи

Детальніше про структуру таблиць можна знайти у db_gateway/database.md.

Обробка депозитів користувачів

Для обробки депозитів потрібно постійно сканувати мережу Solana. Є два підходи:

  1. Сканування за підписами (getSignaturesForAddress)
  2. Сканування за блоками (getBlock)

Перший метод: отримання підписів для ATA або programID, що належать користувачу, з використанням getSignaturesForAddress. Потім за допомогою getTransaction отримати деталі транзакцій. Цей спосіб підходить при невеликій кількості облікових записів.

Другий метод: постійне отримання останнього slot і виклик getBlock для аналізу всіх транзакцій у ньому. Це більш ефективно при великій кількості облікових записів.

Примітка: через високий TPS у Solana, у виробничому середовищі може виникнути затримка у обробці логів. Тому рекомендується використовувати черги (Kafka/RabbitMQ) для фільтрації потенційних депозитів і збереження популярних даних у Redis для швидкого доступу.

Альтернативно, можна використовувати сторонні індексатори або RPC-сервіси, що пропонують Webhook або підписки на облікові записи.

Процес сканування

Ми використовуємо метод 2 (сканування за блоками). Основний код реалізовано у модулях scan/solana-scan/blockScanner.ts та txParser.ts. Основні етапи:

  1. Початкове синхронізування (performInitialSync):

    • Починаючи з останнього сканованого slot, послідовно скануємо до актуального.
    • Перевіряємо кожні 100 слотів на появу нових.
    • Використовуємо confirmed commitment для балансу між швидкістю та надійністю.
  2. Постійне сканування (scanNewSlots):

    • Перевіряємо нові слоти, що з’являються.
    • Перевіряємо підтверджені слоти на можливий відкат.
  3. Аналіз блоку (txParser.parseBlock):

    • Викликаємо getBlock з параметрами commitment: “confirmed” та encoding: “jsonParsed”.
    • Проходимо по кожній транзакції, аналізуючи instructions та innerInstructions.
    • Обробляємо лише успішні транзакції (tx.meta.err === null).
  4. Аналіз інструкцій (txParser.parseInstruction):

    • SOL переказ: шукаємо інструкції системної програми з типом ‘transfer’ та порівнюємо destination з моніторинговим списком.
    • Token переказ: шукаємо інструкції Token Program або Token-2022, з типом ‘transfer’ або ‘transferChecked’, порівнюємо destination з ATA, потім через базу даних отримуємо відповідний гаманець та TokenMint.

Обробка відкатів:
Постійно отримуємо найновий finalizedSlot. Якщо slot ≤ finalizedSlot, він вважається підтвердженим. Для confirmed-слотів перевіряємо, чи не змінився blockhash — це сигнал про відкат.

Приклад основного коду:

// blockScanner.ts - сканування одного слоту
async function scanSingleSlot(slot: number) {
  const block = await solanaClient.getBlock(slot);
  if (!block) {
    await insertSlot({ slot, status: 'skipped' });
    return;
  }
  const finalizedSlot = await getCachedFinalizedSlot();
  const status = slot <= finalizedSlot ? 'finalized' : 'confirmed';
  await processBlock(slot, block, status);
}

// txParser.ts - парсинг транзакцій
for (const tx of block.transactions) {
  if (tx.meta?.err) continue; // пропускаємо неуспішні
  const instructions = [
    ...tx.transaction.message.instructions,
    ...(tx.meta.innerInstructions ?? []).flatMap(i => i.instructions)
  ];
  for (const ix of instructions) {
    // SOL переказ
    if (ix.programId === SYSTEM_PROGRAM_ID && ix.parsed?.type === 'transfer') {
      if (monitoredAddresses.has(ix.parsed.info.destination)) {
        // обробка депозиту
      }
    }
    // Token переказ
    if (ix.programId === TOKEN_PROGRAM_ID || ix.programId === TOKEN_2022_PROGRAM_ID) {
      if (ix.parsed?.type === 'transfer' || ix.parsed?.type === 'transferChecked') {
        const ataAddress = ix.parsed.info.destination;
        const walletAddress = ataToWalletMap.get(ataAddress);
        if (walletAddress && monitoredAddresses.has(walletAddress)) {
          // обробка депозиту
        }
      }
    }
  }
}

Після виявлення депозиту, у поєднанні з системою двофакторного контролю (DB Gateway + ризик-менеджмент), дані записуються у таблицю транзакцій.

Зняття

Процес зняття у Solana схожий на Ethereum, але з особливостями:

  1. Існує два типи токенів: SPL Token та SPL Token-2022, з різними programID. При формуванні транзакції потрібно враховувати цей факт.
  2. Транзакція складається з двох частин: підписів (signatures) та повідомлення (message).
  3. recentBlockhash використовується для обмеження терміну дії транзакції (близько 1 хвилини). Його потрібно отримувати безпосередньо з мережі перед підписанням.

Процес зняття:

  • Створюємо інструкції для переказу SOL або токенів.
  • Формуємо повідомлення транзакції, встановлюємо платника (fee payer).
  • Підписуємо транзакцію за допомогою приватних ключів.
  • Надсилаємо через RPC.

Приклад коду підписання:

// Інструкція для SOL
const instruction = getTransferSolInstruction({
  source: hotWalletSigner,
  destination: solanaAddress.to,
  amount: BigInt(amount)
});

// Інструкція для токенів
const instruction = getTransferInstruction({
  source: sourceAta,
  destination: destAta,
  authority: hotWalletSigner,
  amount: BigInt(amount)
});

// Створення повідомлення
const transactionMessage = createTransactionMessage({
  feePayer: hotWalletSigner.publicKey,
  recentBlockhash: blockhash,
  instructions: [instruction]
});

// Підписання
const signedTx = await signTransaction(transactionMessage, [hotWalletSigner]);
// Отримання закодованого для відправки
const signedTransaction = getBase64EncodedWireTransaction(signedTx);

Відправка транзакції:

const solanaRpc = chainConfigManager.getSolanaRpc();
const txSignature = await solanaRpc.sendTransaction(signedTransaction);

Розміщення коду з реалізацією у відповідних модулях:

  • Wallet: walletBusinessService.ts (405-754)
  • Signer: solanaSigner.ts (29-122)
  • Тестовий скрипт: requestWithdrawOnSolana.ts

Додаткові оптимізації:

  • Перевірка існування ATA перед зняттям (для уникнення додаткових витрат).
  • Встановлення пріоритету через computeUnitPrice при високій навантаженості.

Підсумки

Інтеграція Solana у біржу вимагає адаптації до її унікальної моделі облікових записів, структури транзакцій та механізмів підтвердження.
Для депозитів важливо підтримувати актуальні мапінги ATA до гаманця, слідкувати за зміною blockhash для виявлення реорганізацій, а також динамічно оновлювати статуси транзакцій.
При зняттях — отримувати актуальний recentBlockhash, формувати транзакції для SOL, SPL Token або Token-2022 відповідно до типу активу.

SOL3.82%
ETH4.12%
USDC-0.01%
Переглянути оригінал
Ця сторінка може містити контент третіх осіб, який надається виключно в інформаційних цілях (не в якості запевнень/гарантій) і не повинен розглядатися як схвалення його поглядів компанією Gate, а також як фінансова або професійна консультація. Див. Застереження для отримання детальної інформації.
  • Нагородити
  • Прокоментувати
  • Репост
  • Поділіться
Прокоментувати
0/400
Немає коментарів
  • Закріпити