Documentation
¶
Overview ¶
Package husdledger wires the chain-backed credit ledger (treasury mint service + husdindex projector, Steps 2-3) to commerce's production per-org SQLite stores, and exposes the ONE mint entrypoint (Service.MintCredit) the billing handlers call in place of a direct DB credit write (Step 4).
Decomplected from package treasury on purpose: treasury/husdindex stay free of the datastore (cgo) dependency so their logic is unit-tested + live-proved CGO-free; the datastore-touching adapters live here and are CI-tested.
Index ¶
- func SetDefault(s *Service)
- func ValidateConfig(cfg husd.Config, seed []byte) error
- type MigrateResult
- type Option
- type Service
- func (s *Service) Config() husd.Config
- func (s *Service) Enabled() bool
- func (s *Service) Migrate(ctx context.Context, dryRun bool) ([]MigrateResult, error)
- func (s *Service) MigrateOrg(ctx context.Context, org string, dryRun bool) (MigrateResult, error)
- func (s *Service) MintCredit(ctx context.Context, req treasury.MintRequest) (*treasury.Receipt, error)
- func (s *Service) Settle(ctx context.Context) ([]SettleResult, error)
- func (s *Service) SettleOrg(ctx context.Context, org string) (SettleResult, error)
- func (s *Service) SyncOnce(ctx context.Context) (int, error)
- type SettleResult
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func SetDefault ¶
func SetDefault(s *Service)
SetDefault installs the process-wide chain-backed ledger service. Called once at Bootstrap after the DB is wired.
func ValidateConfig ¶
ValidateConfig reports a FATAL chain-ledger misconfiguration that a caller must refuse to boot on: a derivation seed is supplied — the chain ledger is clearly intended — but the HUSD token address and/or treasury key are missing, so the ledger cannot run and would silently fall back to the DB credit path for a ledger the operator meant to enable. Fail closed.
The asymmetry is deliberate: HUSD token+key WITHOUT a seed is NOT an error — it is the inert state. The shared HUSD config (util/husd) is ALSO used by the OSS contributor payout path, which needs a token+key but no ledger seed; there the ledger stays disabled (Enabled()==false) and the existing DB credit path is used, exactly as before the chain ledger existed. The seed is the ledger's SOLE intent signal, so only "seed set but not Configured" is a partial/incoherent config worth refusing.
Types ¶
type MigrateResult ¶
type MigrateResult struct {
Org string `json:"org"`
Subjects int `json:"subjects"`
DBBalanceCents int64 `json:"dbBalanceCents"` // pre-migration DB balance (org total)
MintedCents int64 `json:"mintedCents"` // total minted on chain this run
ChainBalanceCents int64 `json:"chainBalanceCents"` // post-migration on-chain balanceOf(org)
PostDBCents int64 `json:"postDbCents"` // post-migration DB balance (must equal DBBalance)
Reconciled bool `json:"reconciled"` // chain == DB == postDB, exact to the cent
DryRun bool `json:"dryRun"`
Reason string `json:"reason,omitempty"`
}
MigrateResult is one org's migration outcome.
type Option ¶
type Option func(*Service)
Option configures a Service (chain-seam injection for tests).
func WithBalanceReader ¶
func WithBalanceReader(br husdindex.BalanceReader) Option
WithBalanceReader overrides the on-chain balance reader used by settlement + migration reconcile (tests inject a fake balanceOf so drift/reconcile is proven without a chain).
func WithIndexReader ¶
WithIndexReader overrides the indexer's chain reader (tests feed the projector synthetic Transfers so MintCredit's projection runs without a chain).
func WithMintTransfer ¶
func WithMintTransfer(fn blockchain.TokenTransferFn) Option
WithMintTransfer overrides the treasury mint transfer (tests fake it).
func WithSettleTransfer ¶
func WithSettleTransfer(fn blockchain.TokenTransferFn) Option
WithSettleTransfer overrides the org→treasury transfer function (tests inject a fake so the settlement flow is proven without a chain).
type Service ¶
type Service struct {
// contains filtered or unexported fields
}
Service is the chain-backed credit ledger, wired to production stores. It owns the treasury mint service (Steps 1-2), the on-chain indexer/projector (Step 3), and the settlement job (Step 5). MintCredit is the ONE way a credit is created once the chain path is enabled: a treasury-signed on-chain HUSD issuance whose value is projected back into the commerce ledger the balance endpoint reads.
When the chain path is NOT configured (no token/key/seed), Enabled() is false and the billing handlers keep their existing DB-mint behavior — so a deploy without HUSD configured is unchanged, and the migration (Step 6) flips reads over deliberately.
func Default ¶
func Default() *Service
Default returns the process-wide service (may be a disabled Service, never nil once SetDefault ran; callers must still guard with Enabled()).
func New ¶
New builds a Service from the HUSD config and the org-derivation master seed. If the chain path is not fully configured it returns a DISABLED service (no panic, no partial state) so callers can safely wire it unconditionally and let Enabled() gate the on-chain path.
func (*Service) Config ¶
Config returns the HUSD config (chain id, token, decimals) — read-only view for callers deciding Test partitioning / reconciliation.
func (*Service) Migrate ¶
Migrate migrates every org's ledger onto chain (dry-run reports the snapshot + current chain balance without minting). Platform/CronJob-driven, one-time.
func (*Service) MigrateOrg ¶
MigrateOrg migrates ONE org and reconciles chain==DB to the cent.
func (*Service) MintCredit ¶
func (s *Service) MintCredit(ctx context.Context, req treasury.MintRequest) (*treasury.Receipt, error)
MintCredit is the ONE mint entrypoint: a treasury-signed on-chain HUSD issuance for req, projected back into the commerce ledger. It authorizes via the passed ctx (mintauth) exactly like the DB path, then (best-effort, bounded) projects the minted tx so the balance reflects it on return. Idempotent by req.IdemKey.
func (*Service) Settle ¶
func (s *Service) Settle(ctx context.Context) ([]SettleResult, error)
Settle sweeps every org whose on-chain balance has drifted above its ledger balance by at least the threshold. Idempotent: a second pass finds ~zero drift. Driven by an external CronJob (mirrors the OSS/contributor payout endpoints).
func (*Service) SettleOrg ¶
SettleOrg computes one org's drift and, if it clears the threshold, sweeps it org→treasury (signed by the org's derived key) and records an audit line.
type SettleResult ¶
type SettleResult struct {
Org string `json:"org"`
OrgAddress string `json:"orgAddress"`
OnChainCents int64 `json:"onChainCents"`
SpendableCents int64 `json:"spendableCents"`
DriftCents int64 `json:"driftCents"`
Settled bool `json:"settled"`
TxHash string `json:"txHash,omitempty"`
Reason string `json:"reason,omitempty"`
}
SettleResult is the per-org outcome of a settlement pass.