Recommends -- never executes -- solver treasury moves from a
Raxol.Payments.RebalancePolicy, current balances, and per-chain native drain
(from Raxol.Payments.SettlementLedger). raxol cannot move the solver's funds
(Riddler owns the wallet), so this produces a recommendation list an operator or
the Riddler rebalancer acts on.
Handles the multi-asset settlement set (USDC, USDT, USDG, WETH):
- Gas refuel -- a chain whose native balance fell below its floor gets a
:refuel_gas. On an ETH-native chain the cheapest source is unwrapping WETH (1:1, no DEX/slippage); otherwise it recommends swapping a stable. A chain that cannot pay gas is dead, so refuels come first. - Inventory rebalance -- per stable asset, surplus chains are paired against
deficit chains (USDC via CCTP; USDT/USDG via a bridge). A deficit with no
surplus source becomes an
:inventory_underfundedalert.
recommend/4 is pure and testable; gather_gas_balances/3 and
gather_inventory/4 are the only IO; advise/4 wraps recommend/4 with
telemetry.
Summary
Functions
Like recommend/4 but emits telemetry per recommendation and a summary.
Read native gas balances (wei) for solver_address on each chain. A chain that
errors reads as 0 (surfaces as below-floor).
Read per-(chain, symbol) ERC-20 balances (human Decimal) for the solver. A
symbol absent on a chain, or an errored read, is 0.
Compute recommendations. balances is %{gas: %{chain => wei}, inventory: %{chain => %{symbol => Decimal}}}; drain is %{chain => Decimal (wei)} from
SettlementLedger.native_drain_by_chain/2 and only affects refuel ordering.
opts[:price_fn] (native_symbol -> Decimal | nil) sizes a stable-swap refuel.
Types
@type balances() :: %{ gas: %{required(pos_integer()) => non_neg_integer()}, inventory: %{ required(pos_integer()) => %{required(String.t()) => Decimal.t()} } }
Functions
@spec advise(Raxol.Payments.RebalancePolicy.t(), balances(), map(), keyword()) :: [ recommendation() ]
Like recommend/4 but emits telemetry per recommendation and a summary.
@spec gather_gas_balances(Raxol.Payments.ChainReader.reader(), String.t(), [ pos_integer() ]) :: %{ required(pos_integer()) => non_neg_integer() }
Read native gas balances (wei) for solver_address on each chain. A chain that
errors reads as 0 (surfaces as below-floor).
@spec gather_inventory( Raxol.Payments.ChainReader.reader(), String.t(), [pos_integer()], [String.t()] ) :: %{required(pos_integer()) => %{required(String.t()) => Decimal.t()}}
Read per-(chain, symbol) ERC-20 balances (human Decimal) for the solver. A
symbol absent on a chain, or an errored read, is 0.
@spec recommend(Raxol.Payments.RebalancePolicy.t(), balances(), map(), keyword()) :: [ recommendation() ]
Compute recommendations. balances is %{gas: %{chain => wei}, inventory: %{chain => %{symbol => Decimal}}}; drain is %{chain => Decimal (wei)} from
SettlementLedger.native_drain_by_chain/2 and only affects refuel ordering.
opts[:price_fn] (native_symbol -> Decimal | nil) sizes a stable-swap refuel.