mirror of
https://github.com/hanzoai/bootnode.git
synced 2026-10-25 02:31:30 +00:00
Adds a Go subsystem alongside the existing Python FastAPI service so
bootnode can fold into `superbase` per HIP-0106. The Python service in
`api/bootnode/` stays untouched.
What landed:
- `schema/bootnode.zap` — public typed surface (chains, RPC, tokens,
NFTs, transfers, smart wallets, ERC-4337, gas, webhooks, fleets,
nodes, usage). No `.capnp` anywhere — ZAP only, per brand policy.
- `zap/gen/` — hand-rolled Go bindings that match what `zapc generate`
emits, so `go build` works without zapc installed locally.
- `api/mount.go` — the HIP-0106 contract:
func Mount(r chi.Router, deps Deps) error
mounts every FastAPI-parity route group through `api/handlers/`.
- `api/deps.go` — cross-subsystem ZAP client interfaces (IAM, KMS,
Base, O11y, Commerce). No business logic, no hard imports — keeps
the Bootnode module independent of sibling subsystems until they
ship their typed clients.
- `api/handlers/` — 149 chi routes mounted (Python has 150). Each
handler body is a 501 stub with a `TODO(bootnode-port)` pointing at
the Python file + symbol to port.
- `api/middleware/` — IAM-backed auth, OTEL-friendly telemetry,
per-org rate limit (stub).
- `api/store/` — per-tenant SQLite access via `deps.Base.Open(orgID,
"bootnode")` per HIP-0302.
- `api/web3/` — chain registry + RPC pool surface (production version
prefers luxfi/* over upstream go-ethereum).
- `cmd/bootnode/main.go` — standalone shim; same `api.Mount` superbase
calls in production.
- `docs/PYTHON_TO_GO_PORT.md` — endpoint-by-endpoint port map.
Build: `go build ./...` and `go vet ./...` clean. Standalone shim
smokes 200 on /health, /ready, /v1/chains; 401 on auth-required
endpoints with no JWT; 501 on every TODO route.
Out of scope (not in this commit):
- Handler bodies (Phase 2 — porting in RPC → tokens → NFTs → ...
priority order).
- superbase wiring (lands when the superbase repo opens its main).
- Python service removal (stays until handlers reach prod parity).
372 lines
13 KiB
Go
372 lines
13 KiB
Go
// Package bootnodezap is the generated typed surface for the Bootnode
|
|
// subsystem. The contents here are normally regenerated by zapc from
|
|
// ../../schema/bootnode.zap; the hand-rolled stubs below let `go build`
|
|
// succeed when zapc is not installed and define the canonical type
|
|
// shape that the generator emits.
|
|
//
|
|
// Brand policy: ZAP only. No Cap'n Proto references anywhere in the
|
|
// public surface (HIP-0106).
|
|
package bootnodezap
|
|
|
|
import "context"
|
|
|
|
// Chain is the cross-cutting blockchain identifier. Values are the
|
|
// kebab-cased subset of HIP-0106 chains Bootnode speaks today.
|
|
type Chain string
|
|
|
|
const (
|
|
ChainEthereum Chain = "ethereum"
|
|
ChainPolygon Chain = "polygon"
|
|
ChainArbitrum Chain = "arbitrum"
|
|
ChainOptimism Chain = "optimism"
|
|
ChainBase Chain = "base"
|
|
ChainBSC Chain = "bsc"
|
|
ChainAvalanche Chain = "avalanche"
|
|
ChainGnosis Chain = "gnosis"
|
|
ChainCelo Chain = "celo"
|
|
ChainLinea Chain = "linea"
|
|
ChainScroll Chain = "scroll"
|
|
ChainZkSync Chain = "zksync"
|
|
ChainBlast Chain = "blast"
|
|
ChainMantle Chain = "mantle"
|
|
ChainMetis Chain = "metis"
|
|
ChainZora Chain = "zora"
|
|
ChainOpBNB Chain = "opbnb"
|
|
ChainBitcoin Chain = "bitcoin"
|
|
ChainSolana Chain = "solana"
|
|
ChainAptos Chain = "aptos"
|
|
ChainFlow Chain = "flow"
|
|
ChainBerachain Chain = "berachain"
|
|
ChainSei Chain = "sei"
|
|
ChainSonic Chain = "sonic"
|
|
ChainTron Chain = "tron"
|
|
ChainTON Chain = "ton"
|
|
ChainLux Chain = "lux"
|
|
)
|
|
|
|
// Network identifies a chain's deployment environment.
|
|
type Network string
|
|
|
|
const (
|
|
NetworkMainnet Network = "mainnet"
|
|
NetworkSepolia Network = "sepolia"
|
|
NetworkHolesky Network = "holesky"
|
|
NetworkFuji Network = "fuji"
|
|
NetworkDevnet Network = "devnet"
|
|
NetworkTestnet Network = "testnet"
|
|
NetworkLocalnet Network = "localnet"
|
|
)
|
|
|
|
// WebhookEvent enumerates the supported webhook subscription types.
|
|
type WebhookEvent string
|
|
|
|
const (
|
|
WebhookEventAddressActivity WebhookEvent = "addressActivity"
|
|
WebhookEventMinedTransaction WebhookEvent = "minedTransaction"
|
|
WebhookEventNFTActivity WebhookEvent = "nftActivity"
|
|
WebhookEventTokenTransfer WebhookEvent = "tokenTransfer"
|
|
WebhookEventInternalTransfer WebhookEvent = "internalTransfer"
|
|
WebhookEventGraphQL WebhookEvent = "graphql"
|
|
)
|
|
|
|
// NodeProvider identifies how a single-instance blockchain node is run.
|
|
type NodeProvider string
|
|
|
|
const (
|
|
NodeProviderDocker NodeProvider = "docker"
|
|
NodeProviderKubernetes NodeProvider = "kubernetes"
|
|
NodeProviderProcess NodeProvider = "process"
|
|
NodeProviderHelm NodeProvider = "helm"
|
|
)
|
|
|
|
// RPCRequest is a JSON-RPC envelope sent to the multi-chain proxy.
|
|
type RPCRequest struct {
|
|
Chain Chain `json:"chain"`
|
|
Network Network `json:"network"`
|
|
Method string `json:"method"`
|
|
Params string `json:"params"`
|
|
ID int64 `json:"id"`
|
|
}
|
|
|
|
// RPCResponse is the upstream's JSON-RPC reply, optionally annotated
|
|
// with the block at which it was served and whether it was cached.
|
|
type RPCResponse struct {
|
|
JSONRPC string `json:"jsonrpc"`
|
|
ID int64 `json:"id"`
|
|
Result string `json:"result"`
|
|
Error string `json:"error"`
|
|
BlockNumber uint64 `json:"blockNumber"`
|
|
Cached bool `json:"cached"`
|
|
}
|
|
|
|
// TokenBalance is one ERC-20 / native-coin entry for an address.
|
|
type TokenBalance struct {
|
|
Contract string `json:"contract"`
|
|
Symbol string `json:"symbol"`
|
|
Name string `json:"name"`
|
|
Decimals int32 `json:"decimals"`
|
|
Balance string `json:"balance"`
|
|
BalanceUSD float64 `json:"balanceUsd"`
|
|
}
|
|
|
|
// TokenMetadata describes a fungible token contract.
|
|
type TokenMetadata struct {
|
|
Contract string `json:"contract"`
|
|
Chain Chain `json:"chain"`
|
|
Symbol string `json:"symbol"`
|
|
Name string `json:"name"`
|
|
Decimals int32 `json:"decimals"`
|
|
Logo string `json:"logo"`
|
|
TotalSupply string `json:"totalSupply"`
|
|
}
|
|
|
|
// NFTMetadata describes a single token within an NFT contract.
|
|
type NFTMetadata struct {
|
|
Contract string `json:"contract"`
|
|
TokenID string `json:"tokenId"`
|
|
Name string `json:"name"`
|
|
Description string `json:"description"`
|
|
Image string `json:"image"`
|
|
Attributes string `json:"attributes"`
|
|
TokenURI string `json:"tokenUri"`
|
|
Chain Chain `json:"chain"`
|
|
}
|
|
|
|
// NFTCollection summarizes an ERC-721 / ERC-1155 contract.
|
|
type NFTCollection struct {
|
|
Contract string `json:"contract"`
|
|
Chain Chain `json:"chain"`
|
|
Name string `json:"name"`
|
|
Symbol string `json:"symbol"`
|
|
TotalSupply string `json:"totalSupply"`
|
|
OwnerCount int64 `json:"ownerCount"`
|
|
}
|
|
|
|
// Transfer is one historical asset movement.
|
|
type Transfer struct {
|
|
Chain Chain `json:"chain"`
|
|
TxHash string `json:"txHash"`
|
|
BlockNumber uint64 `json:"blockNumber"`
|
|
From string `json:"fromAddr"`
|
|
To string `json:"toAddr"`
|
|
Value string `json:"value"`
|
|
Asset string `json:"asset"`
|
|
Category string `json:"category"`
|
|
Timestamp int64 `json:"timestamp"`
|
|
}
|
|
|
|
// SmartWallet is one ERC-4337 account record.
|
|
type SmartWallet struct {
|
|
Address string `json:"address"`
|
|
Chain Chain `json:"chain"`
|
|
Owner string `json:"owner"`
|
|
Factory string `json:"factory"`
|
|
Salt string `json:"salt"`
|
|
Nonce uint64 `json:"nonce"`
|
|
Deployed bool `json:"deployed"`
|
|
CreatedAt int64 `json:"createdAt"`
|
|
}
|
|
|
|
// UserOp is the canonical ERC-4337 v0.6 user-operation envelope.
|
|
type UserOp struct {
|
|
Sender string `json:"sender"`
|
|
Nonce string `json:"nonce"`
|
|
InitCode string `json:"initCode"`
|
|
CallData string `json:"callData"`
|
|
CallGasLimit string `json:"callGasLimit"`
|
|
VerificationGasLimit string `json:"verificationGasLimit"`
|
|
PreVerificationGas string `json:"preVerificationGas"`
|
|
MaxFeePerGas string `json:"maxFeePerGas"`
|
|
MaxPriorityFeePerGas string `json:"maxPriorityFeePerGas"`
|
|
PaymasterAndData string `json:"paymasterAndData"`
|
|
Signature string `json:"signature"`
|
|
}
|
|
|
|
// UserOpReceipt is what the bundler returns for a successful userOp.
|
|
type UserOpReceipt struct {
|
|
UserOpHash string `json:"userOpHash"`
|
|
Sender string `json:"sender"`
|
|
Nonce string `json:"nonce"`
|
|
Success bool `json:"success"`
|
|
TxHash string `json:"txHash"`
|
|
BlockNumber uint64 `json:"blockNumber"`
|
|
GasUsed string `json:"gasUsed"`
|
|
}
|
|
|
|
// GasPrices is the current gas-price surface for a chain.
|
|
type GasPrices struct {
|
|
Chain Chain `json:"chain"`
|
|
Slow string `json:"slow"`
|
|
Standard string `json:"standard"`
|
|
Fast string `json:"fast"`
|
|
BaseFee string `json:"baseFee"`
|
|
MaxPriorityFeePerGas string `json:"maxPriorityFeePerGas"`
|
|
UpdatedAt int64 `json:"updatedAt"`
|
|
}
|
|
|
|
// GasPolicy controls per-org paymaster sponsorship caps.
|
|
type GasPolicy struct {
|
|
ID string `json:"id"`
|
|
OrgID string `json:"orgId"`
|
|
Name string `json:"name"`
|
|
Chain Chain `json:"chain"`
|
|
MaxGwei string `json:"maxGwei"`
|
|
DailyLimitUSD float64 `json:"dailyLimitUsd"`
|
|
Enabled bool `json:"enabled"`
|
|
}
|
|
|
|
// Webhook is a customer subscription to chain events.
|
|
type Webhook struct {
|
|
ID string `json:"id"`
|
|
OrgID string `json:"orgId"`
|
|
URL string `json:"url"`
|
|
Chain Chain `json:"chain"`
|
|
Event WebhookEvent `json:"event"`
|
|
Filters string `json:"filters"`
|
|
Secret string `json:"secret"`
|
|
Active bool `json:"active"`
|
|
CreatedAt int64 `json:"createdAt"`
|
|
}
|
|
|
|
// WebhookDelivery is one attempt to POST a webhook payload.
|
|
type WebhookDelivery struct {
|
|
ID string `json:"id"`
|
|
WebhookID string `json:"webhookId"`
|
|
Status string `json:"status"`
|
|
ResponseCode int32 `json:"responseCode"`
|
|
Attempts int32 `json:"attempts"`
|
|
Payload string `json:"payload"`
|
|
CreatedAt int64 `json:"createdAt"`
|
|
}
|
|
|
|
// Project groups a customer's API keys + usage under one envelope.
|
|
type Project struct {
|
|
ID string `json:"id"`
|
|
OrgID string `json:"orgId"`
|
|
Name string `json:"name"`
|
|
CreatedAt int64 `json:"createdAt"`
|
|
}
|
|
|
|
// APIKey is the durable record. The plaintext is only returned once on
|
|
// creation; everywhere else KeyHash is the bcrypt'd identifier.
|
|
type APIKey struct {
|
|
ID string `json:"id"`
|
|
ProjectID string `json:"projectId"`
|
|
OrgID string `json:"orgId"`
|
|
KeyHash string `json:"keyHash"`
|
|
Name string `json:"name"`
|
|
Scopes []string `json:"scopes"`
|
|
RateLimit int32 `json:"rateLimit"`
|
|
CreatedAt int64 `json:"createdAt"`
|
|
}
|
|
|
|
// CreateAPIKeyResult bundles the durable record with the
|
|
// return-it-once plaintext.
|
|
type CreateAPIKeyResult struct {
|
|
APIKey APIKey `json:"apiKey"`
|
|
Plaintext string `json:"plaintext"`
|
|
}
|
|
|
|
// Fleet is a multi-replica blockchain node deployment.
|
|
type Fleet struct {
|
|
ID string `json:"id"`
|
|
OrgID string `json:"orgId"`
|
|
Chain Chain `json:"chain"`
|
|
Network Network `json:"network"`
|
|
ClusterID string `json:"clusterId"`
|
|
Replicas int32 `json:"replicas"`
|
|
Status string `json:"status"`
|
|
CreatedAt int64 `json:"createdAt"`
|
|
}
|
|
|
|
// Node is a single-instance blockchain node managed by Bootnode.
|
|
type Node struct {
|
|
ID string `json:"id"`
|
|
OrgID string `json:"orgId"`
|
|
Name string `json:"name"`
|
|
Chain Chain `json:"chain"`
|
|
Network Network `json:"network"`
|
|
Provider NodeProvider `json:"provider"`
|
|
RPCPort int32 `json:"rpcPort"`
|
|
WSPort int32 `json:"wsPort"`
|
|
Status string `json:"status"`
|
|
ContainerID string `json:"containerId"`
|
|
CreatedAt int64 `json:"createdAt"`
|
|
}
|
|
|
|
// Cluster is a Kubernetes cluster Bootnode manages on the customer's
|
|
// behalf (DOKS / EKS / GKE / AKS).
|
|
type Cluster struct {
|
|
ID string `json:"id"`
|
|
OrgID string `json:"orgId"`
|
|
Name string `json:"name"`
|
|
Region string `json:"region"`
|
|
NodeCount int32 `json:"nodeCount"`
|
|
Status string `json:"status"`
|
|
}
|
|
|
|
// UsageRecord is one billable API call.
|
|
type UsageRecord struct {
|
|
OrgID string `json:"orgId"`
|
|
ProjectID string `json:"projectId"`
|
|
Chain Chain `json:"chain"`
|
|
Method string `json:"method"`
|
|
ComputeUnits int32 `json:"computeUnits"`
|
|
Timestamp int64 `json:"timestamp"`
|
|
}
|
|
|
|
// UsageSummary is a roll-up over a billing period.
|
|
type UsageSummary struct {
|
|
OrgID string `json:"orgId"`
|
|
Total int64 `json:"total"`
|
|
PeriodStart int64 `json:"periodStart"`
|
|
PeriodEnd int64 `json:"periodEnd"`
|
|
}
|
|
|
|
// Service is the typed surface every BootnodeService implementation
|
|
// must satisfy. Both the in-process Go client and the ZAP RPC client
|
|
// resolve through this interface.
|
|
type Service interface {
|
|
RPC(ctx context.Context, req RPCRequest) (RPCResponse, error)
|
|
|
|
TokenBalances(ctx context.Context, chain Chain, address string) ([]TokenBalance, error)
|
|
TokenMetadata(ctx context.Context, chain Chain, contract string) (TokenMetadata, error)
|
|
|
|
NFTMetadata(ctx context.Context, chain Chain, contract, tokenID string) (NFTMetadata, error)
|
|
NFTCollection(ctx context.Context, chain Chain, contract string) (NFTCollection, error)
|
|
NFTsOwned(ctx context.Context, chain Chain, address string) ([]NFTMetadata, error)
|
|
|
|
ListTransfers(ctx context.Context, chain Chain, address string) ([]Transfer, error)
|
|
|
|
CreateSmartWallet(ctx context.Context, chain Chain, owner string) (SmartWallet, error)
|
|
GetSmartWallet(ctx context.Context, address string) (SmartWallet, error)
|
|
|
|
SendUserOp(ctx context.Context, chain Chain, op UserOp) (string, error)
|
|
GetUserOpReceipt(ctx context.Context, chain Chain, hash string) (UserOpReceipt, error)
|
|
|
|
GasPrices(ctx context.Context, chain Chain) (GasPrices, error)
|
|
SponsorUserOp(ctx context.Context, chain Chain, op UserOp, policyID string) (string, error)
|
|
|
|
CreateWebhook(ctx context.Context, w Webhook) (Webhook, error)
|
|
ListWebhooks(ctx context.Context, orgID string) ([]Webhook, error)
|
|
DeleteWebhook(ctx context.Context, id string) (bool, error)
|
|
|
|
CreateAPIKey(ctx context.Context, orgID, projectID, name string, scopes []string) (CreateAPIKeyResult, error)
|
|
ListAPIKeys(ctx context.Context, orgID string) ([]APIKey, error)
|
|
RevokeAPIKey(ctx context.Context, orgID, id string) (bool, error)
|
|
|
|
CreateFleet(ctx context.Context, f Fleet) (Fleet, error)
|
|
ListFleets(ctx context.Context, orgID string) ([]Fleet, error)
|
|
ScaleFleet(ctx context.Context, id string, replicas int32) (Fleet, error)
|
|
DeleteFleet(ctx context.Context, id string) (bool, error)
|
|
|
|
DeployNode(ctx context.Context, n Node) (Node, error)
|
|
ListNodes(ctx context.Context, orgID string) ([]Node, error)
|
|
StartNode(ctx context.Context, id string) (bool, error)
|
|
StopNode(ctx context.Context, id string) (bool, error)
|
|
DeleteNode(ctx context.Context, id string) (bool, error)
|
|
|
|
RecordUsage(ctx context.Context, r UsageRecord) (bool, error)
|
|
GetUsage(ctx context.Context, orgID string, periodStart, periodEnd int64) (UsageSummary, error)
|
|
}
|