Skip to content

SurplusSplitter

Routes skimmed TARE surplus from the engine to sTARE (senior) and CoilMakerStrategy (emission slice). Installed as engine savings_vault.

Source: TARE-Stablecoin/src/SurplusSplitter.vy (Vyper 0.4.3)

Concept guide: SurplusSplitter protocol page

Wired by: TareEngine set_savings_vault · distribute_to_savings

Implementation overview

  • Engine skim receiver — engine calls distribute_to_savings when surplus exceeds surplus_buffer_target; TARE lands here when this contract is savings_vault.
  • Senior-firstsplit() sends majority to immutable SAVINGS (sTARE); at most emission_bps (≤30%) to emission_sink.
  • No-op until configured — sink unset or emission_bps == 0 → 100% to sTARE (same as wiring sTARE directly).
  • FLYWHEEL 2.0emission_sink should be CoilMakerStrategy (not bare MultiStrategyVault).
flowchart LR
    Engine[TareEngine] -->|skim TARE| SS[SurplusSplitter]
    SS -->|senior slice| sTARE[sTARE vault]
    SS -->|emission_bps| CMS[CoilMakerStrategy]

Immutables

Name Role
TARE Stablecoin routed
SAVINGS sTARE vault — senior claim, never redirectable

Constants

Name Value Role
MAX_BPS 10_000 Basis-point denominator
MAX_EMISSION_BPS 3_000 Hard cap — savings always ≥70% of skim

Events

Event When
EmissionSinkSet Owner sets/clears emission sink
EmissionBpsSet Owner updates emission rate
SurplusSplit split() routes balance

Admin

set_emission_sink

SurplusSplitter.set_emission_sink(_sink)

Set address receiving emission slice from split(). Pass empty(address) to disable emissions (100% → sTARE).

Param Type Description
_sink address CoilMakerStrategy in FLYWHEEL 2.0; empty = all to savings

Returns / state: Updates emission_sink storage.

Access: owner only.

Events: EmissionSinkSet(sink).

Source (TARE-Stablecoin/src/SurplusSplitter.vy:111-120):

@external
def set_emission_sink(_sink: address):
    ow._check_owner()
    self.emission_sink = _sink
    log EmissionSinkSet(sink=_sink)

Example:

splitter.set_emission_sink(coil_maker_strategy)
splitter.set_emission_sink("0x0000000000000000000000000000000000000000")  # disable

Cross-links: set_emission_bps · split · sTARE API

set_emission_bps

SurplusSplitter.set_emission_bps(_bps)

Set emission slice in basis points. Capped at MAX_EMISSION_BPS (3000 = 30%) so savings always get ≥70%.

Param Type Description
_bps uint256 Emission share of skim (03000 bps)

Returns / state: Updates emission_bps storage. No effect until emission_sink is set and split() runs.

Access: owner only. Reverts if _bps > MAX_EMISSION_BPS.

Events: EmissionBpsSet(bps).

Source (TARE-Stablecoin/src/SurplusSplitter.vy:123-132):

@external
def set_emission_bps(_bps: uint256):
    ow._check_owner()
    assert _bps <= MAX_EMISSION_BPS, "SurplusSplitter: bps > cap"
    self.emission_bps = _bps
    log EmissionBpsSet(bps=_bps)

Example:

splitter.set_emission_bps(1500)  # 15% to emission sink, 85% to sTARE

Cross-links: set_emission_sink · split

Split

split

SurplusSplitter.split() -> (uint256, uint256)

Permissionless. Forward contract TARE balance: emission slice → emission_sink, remainder → SAVINGS (sTARE). No-op on zero balance.

Returns: (to_savings, to_emissions) — amounts transferred this call.

Access: Any caller (@nonreentrant). Senior slice transferred first.

Events: SurplusSplit(caller, to_savings, to_emissions).

Source (TARE-Stablecoin/src/SurplusSplitter.vy:139-171):

@external
@nonreentrant
def split() -> (uint256, uint256):
    bal: uint256 = staticcall TARE.balanceOf(self)
    if bal == 0:
        return (0, 0)
    sink: address = self.emission_sink
    to_emissions: uint256 = 0
    if sink != empty(address):
        to_emissions = bal * self.emission_bps // MAX_BPS
    to_savings: uint256 = bal - to_emissions
    extcall TARE.transfer(SAVINGS, to_savings)
    extcall TARE.transfer(sink, to_emissions)
    return (to_savings, to_emissions)

Example:

to_savings, to_emissions = splitter.split()

Cross-links: set_emission_sink · set_emission_bps · TareEngine distribute_to_savings · sTARE API