Skip to content

jmwalletd.fee_policy

jmwalletd.fee_policy

Resolve reference-style [POLICY] fee overrides set through configset.

JAM (and other clients written against the reference jmwalletd API) do not send fee parameters with each request. Instead they write them to the [POLICY] config section via POST /configset and expect the daemon to honor those values for subsequent operations (direct sends, coinjoins, and tumbles). The daemon keeps them in DaemonState.config_overrides; this module translates them into the override arguments understood by our config builders (issue #566: the values used to be stored and echoed back by configget but never applied, so a sat/vB fee set in JAM was silently ignored and the taker fell back to block-target estimation, which fails on the neutrino backend).

Reference semantics for tx_fees:

  • 1 <= tx_fees <= 1000: block confirmation target for backend fee estimation.
  • tx_fees > 1000: manual fee rate in satoshis per kilo-vbyte (sat/vB = tx_fees / 1000).

Values that fail to parse or are out of range are ignored with a warning so a bad override degrades to the configured settings instead of breaking the operation.

Attributes

TX_FEES_BLOCK_TARGET_MAX = 1000 module-attribute

Classes

PolicyFeeOverrides

Bases: NamedTuple

Fee-related overrides parsed from the [POLICY] config section.

None fields mean "no override; use the configured settings value". fee_rate (sat/vB) and block_target are mutually exclusive by construction (both derive from tx_fees).

Source code in jmwalletd/src/jmwalletd/fee_policy.py
39
40
41
42
43
44
45
46
47
48
49
50
51
class PolicyFeeOverrides(NamedTuple):
    """Fee-related overrides parsed from the ``[POLICY]`` config section.

    ``None`` fields mean "no override; use the configured settings value".
    ``fee_rate`` (sat/vB) and ``block_target`` are mutually exclusive by
    construction (both derive from ``tx_fees``).
    """

    fee_rate: float | None = None
    block_target: int | None = None
    tx_fee_factor: float | None = None
    max_cj_fee_abs: int | None = None
    max_cj_fee_rel: str | None = None
Attributes
block_target: int | None = None class-attribute instance-attribute
fee_rate: float | None = None class-attribute instance-attribute
max_cj_fee_abs: int | None = None class-attribute instance-attribute
max_cj_fee_rel: str | None = None class-attribute instance-attribute
tx_fee_factor: float | None = None class-attribute instance-attribute

Functions:

resolve_policy_fee_overrides(config_overrides: Mapping[str, Mapping[str, str]] | None) -> PolicyFeeOverrides

Parse fee overrides from the in-memory configset store.

Accepts the DaemonState.config_overrides mapping (section -> field -> raw string value) and returns the subset of fee policy knobs that our taker/spend config builders understand.

Source code in jmwalletd/src/jmwalletd/fee_policy.py
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
def resolve_policy_fee_overrides(
    config_overrides: Mapping[str, Mapping[str, str]] | None,
) -> PolicyFeeOverrides:
    """Parse fee overrides from the in-memory ``configset`` store.

    Accepts the ``DaemonState.config_overrides`` mapping (section -> field ->
    raw string value) and returns the subset of fee policy knobs that our
    taker/spend config builders understand.
    """
    if not config_overrides:
        return PolicyFeeOverrides()
    policy = config_overrides.get("POLICY")
    if not policy:
        return PolicyFeeOverrides()

    fee_rate, block_target = _parse_tx_fees(policy.get("tx_fees"))
    return PolicyFeeOverrides(
        fee_rate=fee_rate,
        block_target=block_target,
        tx_fee_factor=_parse_positive_float(policy.get("tx_fees_factor"), "tx_fees_factor"),
        max_cj_fee_abs=_parse_positive_int(policy.get("max_cj_fee_abs"), "max_cj_fee_abs"),
        max_cj_fee_rel=_parse_rel_fee(policy.get("max_cj_fee_rel")),
    )