Documentation
¶
Overview ¶
Package fee is the native fee/gas SETTLEMENT primitive for Lux service chains (K-Chain keyvm today; M-Chain and F-Chain next). It is the half the 2026-05 fee audit found missing: node/vms/types/fee declares an ADMISSION policy (is a submitted fee acceptable at the gate?), but nothing could actually METER, DEBIT, and BURN a fee during block execution the way the C-Chain EVM does (evm/core/state_transition.go buyGas: balance check -> ErrInsufficientFunds -> SubBalance). Service chains charged "fees" that were unbacked integers a caller wrote into a JSON request — never settled against real on-chain balance.
This package supplies the three pillars those chains lacked, modelled on the EVM's buyGas but for the native account model (P/X-Chain style direct usage, not EVM-gas yet — that is a later dual-metering layer that composes on top):
Balances (balance.go) — a debitable balance surface the VM can Burn from. Burn(acct, amount) is the debit: it removes funds from the payer AND reduces circulating supply (no coinbase credit) — i.e. a native burn. Credit funds an account (genesis / future treasury inflows). Ledger (ledger.go) is the canonical KV-backed implementation; any chain whose state is a luxfi/database.Database gets a working ledger with no bespoke code.
GasMeter (meter.go) — per-operation gas metering with a hard limit, mirroring the EVM gas pool (SubGas / ErrOutOfGas). A VM meters each operation's real cost against the payer's GasLimit before pricing it.
Settlement (settle.go) — Cost converts metered gas to nLUX at a price; CanPay is the read-only affordability check a block runs in Verify (so an unpayable block is never accepted — fail closed); Charge is the authoritative debit+burn a block runs in Accept. Settlement happens INSIDE consensus block processing, atomically with the operation's state effect via the VM's versiondb commit — never in a synchronous RPC.
Orthogonality. This package is deliberately separate from, and complementary to, node/vms/types/fee: that package is ADMISSION POLICY (the boot-time floor declaration Manager validates); this package is SETTLEMENT MECHANISM (the per-block debit+burn). A VM declares a Policy AND settles through a Ledger; the two compose, they do not overlap. The schedule of "which operation costs how much gas" is supplied BY THE VM (keyvm prices per cryptographic algorithm) — this package is the pure mechanism, the VM owns the values.
It lives in the chains module (not node) on purpose: a service VM must be able to build and settle fees under the reproducible GOWORK=off build, where the chains module resolves a pinned node from the module cache and therefore cannot see VM-local additions made to the node tree. Keeping the settlement primitive beside the VMs that use it is what makes it buildable and reusable by K/M/F under that build.
Index ¶
Constants ¶
This section is empty.
Variables ¶
var ( // ErrInsufficientFunds mirrors the EVM's error of the same intent // (core/state_transition.go buyGas): the payer cannot cover the fee. ErrInsufficientFunds = errors.New("fee: insufficient funds") // ErrBalanceOverflow is returned by Credit when an account balance or the // burned-supply counter would exceed 2^64-1 nLUX. ErrBalanceOverflow = errors.New("fee: balance overflow") )
Sentinel errors. Settlement is fail-secure: every error path denies the operation (the block fails Verify or Accept) — none silently proceeds.
var ErrOutOfGas = errors.New("fee: out of gas")
ErrOutOfGas is returned by GasMeter.Consume when an operation's gas exceeds the remaining limit. It mirrors the EVM gas pool's exhaustion error and is fail-secure: the operation does not proceed.
Functions ¶
func CanPay ¶
CanPay is the read-only affordability check a block runs in Verify, for every fee-bearing transaction, BEFORE the block can be accepted. It never mutates state, so verifying a block cannot move funds; it only proves the payer could cover the fee. A block containing any unaffordable transaction fails Verify and is never accepted — fail closed.
func Charge ¶
Charge is the authoritative settlement a block runs in Accept: it debits the fee from the payer and burns it (reduces circulating supply). It is the native analogue of the EVM's buyGas SubBalance, but burning rather than crediting a coinbase. Because it writes through the VM's versiondb, the debit commits atomically with the operation it pays for. If the payer cannot cover the fee (which Verify should already have prevented), Charge returns ErrInsufficientFunds and the caller MUST abort block acceptance — a key operation never takes effect unpaid.
Types ¶
type Account ¶
Account is a fee payer identity. It is the canonical 20-byte Lux address (ids.ShortID) — the same type P/X-Chain use for UTXO owners — so balances here interoperate with existing address tooling. It is PUBLIC: an account identifier never carries secret material.
type Balances ¶
type Balances interface {
Balance(acct Account) (uint64, error)
Credit(acct Account, amount uint64) error
Burn(acct Account, amount uint64) error
Burned() (uint64, error)
}
Balances is the debitable balance surface a fee-charging VM exposes to the settler. It is the minimal contract the EVM expresses as GetBalance / SubBalance, adapted to the native account model and to BURNING (no coinbase):
- Balance reports an account's spendable nLUX.
- Credit adds nLUX (genesis seeding; future treasury/bridge inflows).
- Burn removes nLUX from the payer AND reduces circulating supply — the fee debit. It is the only spend path here; there is intentionally no account->account transfer, because service-chain fees are burned, not paid to a validator. (A treasury split, if ever wanted, is a new method, not a reinterpretation of Burn.)
- Burned reports cumulative burned supply, for audit.
Implementations MUST be atomic with respect to a single call and MUST be driven inside a transaction/versiondb whose commit is the block's commit, so a fee debit and the operation it pays for either both land or neither does.
type Gas ¶
type Gas uint64
Gas is a unit of metered work. A VM's gas schedule assigns a Gas cost to each operation (keyvm prices per cryptographic algorithm); Cost converts Gas to nLUX at a per-unit price.
type GasMeter ¶
type GasMeter struct {
// contains filtered or unexported fields
}
GasMeter meters gas consumption against a hard limit, exactly like the EVM gas pool (SubGas / out-of-gas). A VM constructs one per fee-bearing operation with the payer's declared GasLimit, then Consumes the operation's metered cost; Consume past the limit denies the operation rather than overdraw.
func NewGasMeter ¶
NewGasMeter returns a meter with the given limit, fully unconsumed.
type KV ¶
type KV interface {
Has(key []byte) (bool, error)
Get(key []byte) ([]byte, error)
Put(key []byte, value []byte) error
}
KV is the minimal key/value surface Ledger needs. It is the read/write subset of luxfi/database.Database (satisfied by versiondb, memdb, and any backing store), declared locally so the settlement primitive does not pin a database module version — keeping it buildable beside any VM under GOWORK=off.
type Ledger ¶
type Ledger struct {
// contains filtered or unexported fields
}
Ledger is the canonical KV-backed Balances implementation. It writes to the VM's state KV (a versiondb in production), so every Credit/Burn participates in the block's atomic commit: a fee burn and the operation it pays for land together or not at all. Balances are nLUX (1e-6 LUX), matching the node/vms/types/fee floor units.
func NewLedger ¶
NewLedger returns a Ledger over kv. kv is the VM's state database; in a block Accept it is the versiondb whose Commit the block performs.
func (*Ledger) Burn ¶
Burn debits amount nLUX from acct and reduces circulating supply by the same amount (the burned counter rises). It is the fee settlement op: it returns ErrInsufficientFunds if acct cannot cover amount, leaving state untouched.