Розробка бекенду dApp на Go

Проєктуємо та розробляємо блокчейн-рішення повного циклу: від архітектури смарт-контрактів до запуску DeFi-протоколів, NFT-маркетплейсів та криптобірж. Аудит безпеки, токеноміка, інтеграція з наявною інфраструктурою.
Показано 1 з 1Усі 1306 послуг
Розробка бекенду dApp на Go
Середній
~1-2 тижні
Часті запитання

Напрямки блокчейн-розробки

Етапи блокчейн-розробки

Останні роботи

  • image_website-b2b-advance_0.webp
    Розробка сайту компанії B2B ADVANCE
    1306
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1218
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    920
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1147
  • image_logo-advance_0.webp
    Розробка логотипу компанії B2B Advance
    610
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    885

Розробка DApp бекенду на Go

Go в контексті блокчейну — не очевидний вибір на перший погляд: більшість туторіалів про dApp використовують Node.js/TypeScript. Але на практиці Go виграє там, де важлива надійність під навантаженням: індексація подій, обробка webhook'ів від нод, off-chain компоненти keeper'ів та bot'ів. Geth написаний на Go, а go-ethereum — найзрілісша low-level бібліотека для роботи з EVM.

go-ethereum: ключові паттерни

Підключення та читання

client, err := ethclient.Dial("wss://eth-mainnet.g.alchemy.com/v2/KEY")
// Для production — fallback між кількома провайдерами

// Читання даних контракту через сгенерований binding
tokenABI, _ := abi.JSON(strings.NewReader(ERC20ABI))
contract := bind.NewBoundContract(tokenAddress, tokenABI, client, client, client)

var balanceResult []interface{}
contract.Call(nil, &balanceResult, "balanceOf", userAddress)
balance := balanceResult[0].(*big.Int)

На практиці — використовуйте abigen для генерації Go-біндингів з ABI. Це дає типізовані методи замість interface{}:

abigen --abi=./abi/Token.json --pkg=token --out=./contracts/token.go
token, _ := token.NewToken(tokenAddress, client)
balance, _ := token.BalanceOf(nil, userAddress)  // типізовано

Event підписки

WebSocket підписка на події — основа індексерів:

query := ethereum.FilterQuery{
    Addresses: []common.Address{contractAddress},
    Topics: [][]common.Hash{{
        crypto.Keccak256Hash([]byte("Transfer(address,address,uint256)")),
    }},
}

logs := make(chan types.Log)
sub, err := client.SubscribeFilterLogs(ctx, query, logs)

for {
    select {
    case err := <-sub.Err():
        // reconnect логіка
    case log := <-logs:
        processTransferEvent(log)
    }
}

Критично: WebSocket з'єднання падають. Вам потрібна reconnect логіка з exponential backoff. Для production — окрема goroutine, що слідкує за станом підписки та пересоздає її при розриві.

Архітектура indexer-сервісу

Типовий use case: зібрати події смарт-контракту, зберегти у PostgreSQL, надати REST/GraphQL API для фронтенду.

Структура сервісу

cmd/
  indexer/main.go      — точка входу
  api/main.go          — HTTP сервер
internal/
  indexer/             — логіка обробки подій
  repository/          — шар даних (PostgreSQL)
  blockchain/          — go-ethereum клієнт
  api/handlers/        — HTTP handler'и

Обробка реорганізацій (reorgs)

Це найменш очевидне для розробників без blockchain-досвіду. Блоки можуть бути реорганізовані — транзакція, яка була в блоці 100, може зникнути, якщо відбудеться reorg. Наївний індексер, що не враховує reorg, накопить невірні дані.

Розв'язання: не помічайте блоки як «фіналізовані» відразу. Чекайте N підтверджень (12 для Ethereum, 3 для Polygon, 1 для Arbitrum з його фіналізацією). Зберігайте block_hash разом з даними подій. При виявленні reorg — відкатіть всі записи із змінених block_hash.

type IndexedEvent struct {
    ID          int64
    BlockNumber uint64
    BlockHash   common.Hash
    TxHash      common.Hash
    LogIndex    uint
    Data        []byte
    Finalized   bool
}

Періодично опитуйте eth_getBlockByNumber для останніх N блоків та порівнюйте block_hash зі збереженими.

Backfill історичних даних

При першому запуску або після пропусків — потрібно пройти по історичних блоках. FilterLogs з діапазоном блоків, але з обмеженням на розмір діапазону (ноди зазвичай лімітують до 2000-10000 блоків). Паралельний backfill з worker pool:

func backfill(ctx context.Context, from, to uint64, workers int) {
    chunks := make(chan [2]uint64, workers)
    var wg sync.WaitGroup

    for i := 0; i < workers; i++ {
        wg.Add(1)
        go func() {
            defer wg.Done()
            for chunk := range chunks {
                logs, _ := client.FilterLogs(ctx, ethereum.FilterQuery{
                    FromBlock: new(big.Int).SetUint64(chunk[0]),
                    ToBlock:   new(big.Int).SetUint64(chunk[1]),
                    Addresses: []common.Address{contractAddress},
                })
                processLogs(logs)
            }
        }()
    }
    // Розділити [from, to] на chunks та відправити в канал
}

Підписання транзакцій та відправка

Для off-chain компонентів (keeper'и, автоматичні транзакції) — управління приватним ключем у бекенді:

privateKey, _ := crypto.HexToECDSA(os.Getenv("PRIVATE_KEY"))
auth, _ := bind.NewKeyedTransactorWithChainID(privateKey, chainID)

// EIP-1559 ціноутворення
tip, _ := client.SuggestGasTipCap(ctx)
auth.GasTipCap = tip
auth.GasFeeCap = new(big.Int).Add(baseFee, tip) // baseFee з останнього блоку

tx, err := contract.SomeMethod(auth, arg1, arg2)

Для production: AWS KMS або HashiCorp Vault замість env-змінної для зберігання ключа. Управління nonce — окрема тема: при паралельній відправці транзакцій потрібен nonce manager, який атомарно видає наступний nonce та обробляє dropped/stuck транзакції.

API шар

// Стандартний стек: chi або gin для роутингу, pgx/v5 для PostgreSQL
r := chi.NewRouter()
r.Use(middleware.Logger)
r.Use(middleware.RealIP)
r.Use(cors.Handler(cors.Options{
    AllowedOrigins: []string{"https://app.example.com"},
    AllowedMethods: []string{"GET", "POST"},
}))

r.Get("/api/v1/events", handlers.GetEvents)
r.Get("/api/v1/user/{address}/positions", handlers.GetUserPositions)

WebSocket endpoint для real-time оновлень — gorilla/websocket або nhooyr.io/websocket. Одна goroutine на з'єднання, channel-based broadcast від індексера до WebSocket клієнтів.

Графік розробки

Простий індексер + REST API (один контракт, 3-5 endpoint'ів): 3-4 дні. Повноцінний сервіс з backfill, reorg handling, WebSocket, nonce manager: 1,5-2 тижні.