Raxol.Payments.RebalanceAdvisor (Raxol Payments v0.2.0)

Copy Markdown View Source

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_underfunded alert.

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

balances()

@type balances() :: %{
  gas: %{required(pos_integer()) => non_neg_integer()},
  inventory: %{
    required(pos_integer()) => %{required(String.t()) => Decimal.t()}
  }
}

recommendation()

@type recommendation() ::
  {:refuel_gas, map()} | {:rebalance_inventory, map()} | {:alert, map()}

Functions

advise(policy, balances, drain, opts \\ [])

Like recommend/4 but emits telemetry per recommendation and a summary.

gather_gas_balances(reader, solver_address, chains)

@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).

gather_inventory(reader, solver_address, chains, symbols)

@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.

recommend(policy, balances, drain, opts \\ [])

@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.