Skip to content

exapsy/chainkit-go-sdk

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

40 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

chainkit

chainkit

A chain-agnostic, multi-provider blockchain client library for Go.

CI Go reference Go report card MIT license

chainkit gives you a single interface to multiple blockchain data providers with automatic fallback, circuit breaking, rate limiting, and provider selection strategies — so your application keeps working when any individual provider is down or rate-limited.

Bitcoin is the first supported chain. The architecture is designed to add Ethereum and other chains without breaking existing code.

Want managed observability + remote configuration over your provider fleet? Drop in the cloudagent sub-package and point it at chainkit cloud — live provider scoreboard, per-chain strategy switching from a dashboard, no-rebuild retry / breaker tuning. The SDK stays the same; the cloud is opt-in and never sits in your blockchain request path. See the chainkit cloud section below.

Supported providers

Eight network providers + one local primitive, all drop-in. Most are free or have a generous free tier; the fallback chain lets you mix free and paid so the cheap providers serve most traffic and the paid ones step in only when the free tier rate-limits or 5xx's.

mempool.space Blockstream Esplora BlockCypher Tatum Blockchain.com Bitref.com CoinGecko Binance

Plus chainkit's local Metal primitives for key derivation, address generation/validation, and transaction assembly/signing/sizing — no network required for the crypto-only operations.

Full per-provider capability matrix is in the Bitcoin providers section below.

Features

  • Multi-provider fallback — register N providers per role; chainkit tries them in priority order until one succeeds
  • Circuit breaker — opens automatically after repeated failures, re-checks on a configurable timeout
  • Rate limiting — per-chain request rate limiting with burst support
  • Retry with backoff — exponential backoff with optional jitter
  • Provider selection strategies — priority-only, round-robin, random, least-loaded
  • Metrics hook — inject your own MetricsRecorder (Prometheus, Datadog, etc.)
  • Health checks — optional periodic health probing per provider chain
  • Zero required roles — register only the capabilities you use; unregistered roles return ErrProviderNotConfigured

chainkit cloud (optional)

The SDK is fully usable on its own — your blockchain RPC calls go direct to providers, and chainkit is never in the request path.

When you want observability + remote configuration over your provider fleet without standing up Prometheus + Grafana + a config service yourself, chainkit cloud is the official hosted complement:

  • Live provider scoreboard — per-operation latency p50/p95/p99, success rate, rate-limit hits, error class breakdown, recent failures.
  • Per-chain selection strategy at runtime — flip a chain from priorityround-robinweighted from a dashboard; the SDK picks the change up within ~30 s, no redeploy.
  • Retry + circuit-breaker + scoring-weight tuning from the dashboard — change behaviour in production without a rebuild.
  • Cloud is observability + config only — it never proxies blockchain RPC. If chainkit cloud goes dark, your customer-facing calls keep working. The SDK buffers telemetry locally (drop-oldest on overflow) until the cloud is reachable again.

Add it to an existing chainkit setup with two extra calls:

import "github.com/exapsy/chainkit/cloudagent"

opts := cloudagent.Options{
    Endpoint: "https://api.chainkit.dev",
    APIKey:   "ck_live_...",
}

// 1. Pipe per-request + scoring telemetry to the cloud.
rec := cloudagent.NewMetricsRecorder(opts)

// 2. Poll the cloud for per-chain config changes every ~30s and apply
//    them via the SDK's ConfigUpdater interface — no client restart.
poller := cloudagent.NewConfigPoller(client, opts)
poller.Start(ctx)
defer poller.Stop()

Pass rec via chainkit.WithMetricsRecorder when building your MixedProviders. See cloudagent/doc.go for the full surface. Sign up + pricing at https://chainkit.dev/pricing.

Installation

go get github.com/exapsy/chainkit@latest

Requires Go 1.22+.

Quick start

import (
    "github.com/exapsy/chainkit"
    "github.com/exapsy/chainkit/bitcoin/providers"
    "github.com/exapsy/chainkit/bitcoin/types"
)

metal := providers.NewMetal(providers.MetalProviderOptions{
    Network: types.BitcoinNetworkMainnet,
})

mempool := providers.NewMempool(providers.MempoolOptions{
    Network: types.BitcoinNetworkMainnet,
    BaseURL: "https://mempool.space/api",
})

client, err := chainkit.NewMixedProvidersBuilder().
    WithAddressGeneratorChain(chainkit.AddressGeneratorConfig{Generator: metal, Priority: 1}).
    WithTxAssemblerChain(chainkit.TxAssemblerConfig{Assembler: metal, Priority: 1}).
    WithTxSignerChain(chainkit.TxSignerConfig{Signer: metal, Priority: 1}).
    WithTxSizerChain(chainkit.TxSizerConfig{Sizer: metal, Priority: 1}).
    WithFeeEstimatorChain(chainkit.FeeEstimatorConfig{Estimator: metal, Priority: 1}).
    WithFeeRecommenderChain(chainkit.FeeRecommenderConfig{Recommender: mempool, Priority: 1}).
    WithBalanceFetcherChain(chainkit.BalanceFetcherConfig{Fetcher: mempool, Priority: 1}).
    WithUTXOFetcherChain(chainkit.UTXOFetcherConfig{Fetcher: mempool, Priority: 1}).
    WithRateFetcherChain(chainkit.RateFetcherConfig{Fetcher: mempool, Priority: 1}).
    WithTxBroadcasterChain(chainkit.TxBroadcasterConfig{Broadcaster: mempool, Priority: 1}).
    WithTxStatusFetcher(mempool).
    Build()
if err != nil {
    return err
}

// Get UTXOs
utxos, err := client.GetUTXOs(ctx, "bc1q...")

// Get balance
balance, err := client.GetBalance(ctx, "bc1q...", nil)

// Get fee recommendations
fees, err := client.GetTxFees(ctx)

Multi-provider fallback example

Register multiple providers for the same role. chainkit tries them in priority order (lower number = higher priority) and falls back automatically.

client, err := chainkit.NewMixedProvidersBuilder().
    WithBalanceFetcherChain(
        chainkit.BalanceFetcherConfig{Fetcher: mempool,     Priority: 1},
        chainkit.BalanceFetcherConfig{Fetcher: blockcypher, Priority: 2},
        chainkit.BalanceFetcherConfig{Fetcher: blockstream, Priority: 3},
    ).
    Build()

Custom chain configuration

Override circuit breaker, retry, rate limit, and selection strategy per role:

import "time"

chainCfg := chainkit.ChainConfig{
    RetryPolicy: chainkit.RetryPolicy{
        Enabled:           true,
        MaxAttempts:       3,
        InitialDelay:      200 * time.Millisecond,
        MaxDelay:          10 * time.Second,
        BackoffMultiplier: 2.0,
        Jitter:            true,
    },
    CircuitBreaker: chainkit.CircuitBreakerConfig{
        Enabled:          true,
        FailureThreshold: 5,
        SuccessThreshold: 2,
        Timeout:          30 * time.Second,
    },
    SelectionStrategy: chainkit.SelectionStrategyRoundRobin,
    Timeout:           15 * time.Second,
}

client, err := chainkit.NewMixedProvidersBuilder().
    WithBalanceFetcherChain(
        chainkit.BalanceFetcherConfig{
            Fetcher:     mempool,
            Priority:    1,
            ChainConfig: &chainCfg,
        },
    ).
    Build()

Provider selection strategies

Strategy Behaviour
SelectionStrategyPriorityOnly Always try providers in priority order (default)
SelectionStrategyRoundRobin Round-robin within same-priority providers
SelectionStrategyRandom Random ordering within same-priority providers
SelectionStrategyLeastLoaded Prefer providers with fewer recent failures

Pinning a specific provider

Use ProviderSelector to pin all calls to a named provider (useful for debugging or A/B testing):

selector := chainkit.NewProviderSelector(client, "Mempool")
balance, err := selector.GetBalance(ctx, address, nil)

Metrics

Implement MetricsRecorder and pass it to the builder:

type prometheusRecorder struct{ ... }

func (r *prometheusRecorder) RecordBlockchainRequest(
    ctx context.Context,
    provider, operation string,
    success bool,
    duration time.Duration,
) {
    // record to your metrics system
}

client, err := chainkit.NewMixedProvidersBuilder().
    WithMetricsRecorder(&prometheusRecorder{}).
    // ...
    Build()

The default is NoOpMetricsRecorder which discards all metrics.

Bitcoin providers

All nine providers wired into the SDK today. Auth column is what chainkit needs to construct the provider; chains lists the BTC networks each backend serves; capabilities is the canonical set of operations the provider implements.

Provider Auth Chains Capabilities
Metal (chainkit-internal) none, local mainnet · testnet3 · testnet4 address generation · address validation · tx assembly · tx signing · tx sizing · fee estimation
mempool.space none mainnet · testnet3 balance · UTXOs · tx status · broadcast · fee tiers · BTC/USDT rate
Blockstream Esplora Enterprise OAuth (ClientID + ClientSecret) mainnet · testnet3 · testnet4 balance · UTXOs · tx status · broadcast · fee tiers · address validation
BlockCypher API key mainnet · testnet3 balance · UTXOs · tx status · broadcast · address validation · api-key validation
Tatum API key mainnet · testnet3 tx status · broadcast · fee tiers · api-key validation
Blockchain.com none mainnet balance · broadcast
Bitref.com API key mainnet balance · broadcast · fee tiers · fee estimation
CoinGecko optional API key mainnet (rate only) BTC/USD rate · api-key validation
Binance none mainnet (rate only) BTC/USDT rate

Creating providers

import (
    "github.com/exapsy/chainkit/bitcoin/providers"
    "github.com/exapsy/chainkit/bitcoin/types"
)

// Metal — local crypto primitives; no network
metal := providers.NewMetal(providers.MetalProviderOptions{
    Network: types.BitcoinNetworkMainnet,
})

// mempool.space — public API, no auth
mempool := providers.NewMempool(providers.MempoolOptions{
    Network: types.BitcoinNetworkMainnet,
    BaseURL: "https://mempool.space/api",
})

// Blockstream Esplora — Enterprise OAuth required
blockstream, err := providers.NewBlockstream(providers.BlockstreamOptions{
    ClientID:     "your-client-id",
    ClientSecret: "your-client-secret",
    BaseURL:      "https://enterprise.blockstream.com",
    LoginURL:     "https://login.blockstream.com",
})

// BlockCypher — API key required
blockcypher := providers.NewBlockcypher(providers.BlockcypherProviderOptions{
    Network: types.BitcoinNetworkMainnet,
    APIKey:  "your-token",
})

// Tatum — API key required (positional API; pending migration to options)
tatum := providers.NewTatum(
    types.BitcoinNetworkMainnet,
    "https://api.tatum.io/v3",
    "your-api-key",
)

// Blockchain.com — no auth
blockchain := providers.NewBlockchainCom()

// Bitref.com — API key required
bitref := providers.NewBitrefcom(providers.BitrefcomOptions{
    APIKey:  "your-api-key",
    BaseURL: "https://bitref.com/api",
})

// CoinGecko — public API; pass an API key for higher rate limits
coingecko := providers.NewCoingecko(providers.CoingeckoOptions{})

// Binance — no auth (public spot endpoints)
binance := providers.NewBinance()

Payment utilities

import "github.com/exapsy/chainkit/payment"

// Build a BIP-21 bitcoin: payment URI
link := payment.BuildPaymentLink(payment.BuildPaymentLinkOptions{
    Address: "bc1q...",
    Amount:  1500000, // satoshis
    Coin:    payment.CoinBTC,
})
// "bitcoin:bc1q...?amount=0.015"

// Convert satoshis to BTC
btc := payment.ConvertSatoshisToBitcoin(1500000) // 0.015

HD wallet utilities

import "github.com/exapsy/chainkit/bitcoin"

// Derive BIP32 index and child-index from an arbitrary string key
index, childIndex, err := bitcoin.DeriveHDIndices("some-session-id42")

Error handling

When no provider is registered for a role:

balance, err := client.GetBalance(ctx, address, nil)
if errors.Is(err, chainkit.ErrProviderNotConfigured) {
    // register a BalanceFetcher with WithBalanceFetcherChain
}

Package structure

chainkit/
├── interfaces.go          # Core provider interfaces
├── config.go              # RetryPolicy, CircuitBreakerConfig, ChainConfig, etc.
├── builder.go             # MixedProvidersBuilder
├── providers.go           # MixedProviders composite implementation
├── provider_selector.go   # ProviderSelector — pins calls to a named provider
├── manager.go             # ProviderManager — circuit breaker, retry, rate limit
├── metrics.go             # MetricsRecorder interface + NoOpMetricsRecorder
├── provider_types.go      # ProviderChainType, SelectionStrategy constants
├── selection_strategy.go  # Priority, round-robin, random, least-loaded selectors
├── debug.go               # DebugInfo, ExtractDebugInfo
│
├── bitcoin/
│   ├── types/types.go     # UTXO, Tx, SignedTx, FeeTier, CoinRate, etc.
│   ├── providers/         # Bitcoin provider implementations
│   ├── derive.go          # DeriveHDIndices (BIP32)
│   ├── address.go         # ValidatePublicAddress
│   └── keys.go            # GenerateKeys (secp256k1)
│
└── payment/
    └── bitcoin.go         # BuildPaymentLink, ConvertSatoshisToBitcoin

Versioning

  • v0.x.y — unstable; interfaces may change between minors
  • v1.0.0 — stable API, once the interface is confirmed in production

License

MIT

About

Your payment crypto framework

Resources

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages