wallets

package
v1.801.475 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 5, 2026 License: Apache-2.0 Imports: 28 Imported by: 0

Documentation

Overview

Package wallets is blockchain key custody: create wallets, rotate their keys, and sign with them.

It serves /v1/wallets/* over one custody seam with FOUR interchangeable signing backends, selected PER WALLET by its Kind.

TOPOLOGY (HIP-0106). Custody composes the canonical Hanzo key services without fusing either into the hot binary:

  • KMS single-sig (KindKMS) is IN-PROCESS via the embedded luxfi/kms client (deps.KMS, a cloud.KMSClient). This is the fully-exercised spine: a real secp256k1 key is generated, its private bytes sealed under the KMS envelope, and every Sign recovers to the wallet address. No network hop.

  • MPC m-of-n (KindMPC), treasury named-signer governance (KindTreasury) and the Safe smart wallet owned by an MPC EOA (KindSafe) DELEGATE over HTTP to the DEPLOYED luxfi/mpc cluster (mpcclient.go, safeclient.go), thin typed REST clients. cloud is a faithful CLIENT of the real service — it never imports github.com/luxfi/mpc (that drags chi/Postgres/HSM/webauthn into the binary). Exactly the apps/mpc precedent. When the cluster is not configured these backends fail CLOSED (ErrMPCNotConfigured); a signature is NEVER fabricated.

It is the recipient side of x402: a priced resource's payee resolves through ResolvePaymentTarget here, and x402 settles against that wallet's ledger account.

The seam is the whole point: swapping a wallet's custody is a config value on one row, not a code path. custody.go owns the interface + the four backends; mpcclient.go + safeclient.go own the wire; store.go owns persistence; wallets.go owns HTTP.

Index

Constants

View Source
const DefaultTier = TierHot

DefaultTier is the tier a wallet gets when the request omits one.

Variables

View Source
var (
	ErrMPCNotConfigured = errors.New("mpc cluster not configured; set CLOUD_WALLETS_MPC_ADDR")
	ErrUnknownCustody   = errors.New("unknown custody kind")
)

Fail-closed sentinels. ErrMPCNotConfigured is returned whenever an MPC/treasury wallet is provisioned or signed without a configured ring + internal API key — the operator must wire CLOUD_WALLETS_MPC_ADDR (and the KMS API-key ref).

Functions

func Mount

func Mount(app cloud.Router, deps cloud.Deps) error

Mount wires the wallets surface onto app per HIP-0106. Complex flavour: it holds a package-global (mounted, the finance seam singleton) so it constructs the Service value directly rather than via cloud.Mount.

func Mounted added in v1.801.350

func Mounted() bool

Mounted reports whether this process holds the wallet store.

It exists because ResolvePaymentTarget folds two facts into one false — "there is no such wallet" and "the wallets subsystem is not in this binary" — and a caller that must ask elsewhere when it is absent has to tell them apart. Answering the first over a socket would be a second lookup of a question already answered NO; answering the second in memory is impossible.

func Shutdown

func Shutdown() error

Shutdown closes the store. Idempotent.

func TreasuryAnchorSigner

func TreasuryAnchorSigner(ctx context.Context, org, chain string) (address string, sign func(context.Context, []byte) ([]byte, error), ok bool)

TreasuryAnchorSigner resolves-or-provisions the org's stable treasury MPC wallet and returns its EVM address plus a sign closure that produces an EVM-recoverable (r‖s‖v) signature delegating to the ring's threshold custody. ok=false when wallets is unmounted or treasury (ring) custody is not configured — the anchor then keeps its KMS-key signer (fail-safe, never a fabricated signer).

The wallet is a KindTreasury wallet under a reserved account+name, so repeated calls resolve the SAME wallet (idempotent) — its address is stable, so funding it for gas once is durable.

func WalletForLedgerAccount

func WalletForLedgerAccount(ctx context.Context, org, ledgerAccount string) (address string, ok bool)

WalletForLedgerAccount resolves the on-chain wallet bound to a finance ledger account — the seam by which the treasury reserve signer BECOMES an MPC treasury wallet later. Pure lookup; ("",false) when unmounted/unbound. Does NOT modify treasury.

Types

type Custody

type Custody interface {
	Kind() Kind
	Provision(ctx context.Context, w *Wallet) (address string, err error)       // create signing material, set w.KeyRef, return address
	Sign(ctx context.Context, w *Wallet, digest []byte) (sig []byte, err error) // sign a 32-byte digest
	Rotate(ctx context.Context, w *Wallet) (address string, err error)          // roll key material, set w.KeyRef, return address
}

Custody is the ONE seam. Each backend creates signing material (Provision), signs a 32-byte digest (Sign), and rolls the material (Rotate). Provision and Rotate return the resulting pubkey ADDRESS and set w.KeyRef to the backend's handle for that material.

type Kind

type Kind string

Kind selects a wallet's custody backend. One interface, four backends.

const (
	KindKMS      Kind = "kms"      // in-process single-sig via the embedded luxfi/kms
	KindMPC      Kind = "mpc"      // m-of-n threshold via the deployed luxfi/mpc cluster (HTTP)
	KindTreasury Kind = "treasury" // named-signer governance via luxfi/mpc's treasury surface (HTTP)
	KindSafe     Kind = "safe"     // Safe (Gnosis-Safe-style) smart wallet on the ring, owned by an MPC EOA (HTTP: :9800 keygen + :8081 deploy/propose)
)

func (Kind) Valid

func (k Kind) Valid() bool

Valid reports whether k is one of the three supported custody kinds.

type PaymentTarget

type PaymentTarget struct {
	Address string
	Org     string
	Subject string // ledger subject for the earnings credit (the wallet id)
}

PaymentTarget is a wallet resolved for RECEIVING a payment: its on-chain address (the x402 payee) plus the org + ledger subject whose books the earnings credit.

func ResolvePaymentTarget

func ResolvePaymentTarget(ctx context.Context, org, walletID string) (PaymentTarget, bool)

ResolvePaymentTarget resolves a payout wallet {org, walletID} to its address + ledger subject — the seam the x402 settlement uses to route payment to a recipient wallet. The lookup is org-scoped (getWallet), so a resource can only ever name a wallet WITHIN the org it declared: no cross-org payee spoofing. ("", false) when wallets is unmounted or the wallet is not found in that org.

type SafeTx

type SafeTx struct {
	To      string `json:"to"`
	Value   string `json:"value"`
	Data    string `json:"data"`
	ChainID int64  `json:"chainId"`
	Nonce   int    `json:"nonce"`
}

SafeTx is the Safe transaction to propose (and MPC-sign the EIP-712 hash of).

type SafeTxResult

type SafeTxResult struct {
	SafeTxHash string `json:"safeTxHash"`
	R          string `json:"r"`
	S          string `json:"s"`
}

SafeTxResult is the outcome of a propose: the EIP-712 Safe-tx hash the ring computed and the threshold ECDSA signature (r,s) its MPC produced over it.

type Scope

type Scope struct {
	Org       string `json:"org"`
	Project   string `json:"project,omitempty"`
	Agent     string `json:"agent,omitempty"`
	AccountID string `json:"accountId"`
}

Scope is the ownership+addressing scope of custodied key material and the ONE key both the KMS secret ref (keyRef) and the store lookup derive from. Org is the tenant isolation boundary — always required, never crossed. Project, Agent, and AccountID are optional NARROWINGS within the org: a wallet may belong to the whole org (all narrowings empty), to an org project, to a specific agent, or to a named account grouping. One scope type, one derivation/lookup path — so a wallet's key material and its row are addressed identically at every layer.

type Tier

type Tier string

Tier mirrors luxfi/mpc's 9-tier wallet model (pkg/wallet/tier.go) as string constants so cloud never imports the mpc package. The values are the wire contract the cluster keys its TierPolicy on; a local mirror keeps them in ONE place here and refuses an unknown tier at the boundary.

const (
	TierHot           Tier = "hot"
	TierWarm          Tier = "warm"
	TierCold          Tier = "cold"
	TierGas           Tier = "gas"
	TierBridge        Tier = "bridge"
	TierContractAdmin Tier = "contract_admin"
	TierValidator     Tier = "validator"
	TierQuarantine    Tier = "quarantine"
	TierDR            Tier = "disaster_recovery"
)

type Wallet

type Wallet struct {
	ID             string `json:"id"`
	Scope                 // Org, Project, Agent, AccountID — the addressing scope
	Name           string `json:"name"`
	Custody        Kind   `json:"custody"`
	Tier           Tier   `json:"tier"`
	Chain          string `json:"chain"`
	Address        string `json:"address"`
	KeyRef         string `json:"-"` // custody-internal handle; never serialized
	FinanceAccount string `json:"financeAccount,omitempty"`
	CreatedAt      int64  `json:"createdAt"`
}

Wallet is one signing identity. Custody selects the backend; KeyRef is the custody-internal HANDLE to the signing material (a KMS secret ref, or the mpc wallet id) — set by Provision, never a private-key VALUE, and never returned over the API. It embeds Scope, so Org/Project/Agent/AccountID promote (and stay flat in JSON) and the wallet is addressed through the ONE scope type.

type WalletAccount added in v1.801.350

type WalletAccount struct {
	ID        string `json:"id"`
	Org       string `json:"org"`
	Name      string `json:"name"`
	CreatedAt int64  `json:"createdAt"`
}

WalletAccount is a named grouping of wallets owned by exactly one org.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL