TareEngine¶
Collateralized debt position (CDP) engine for the TARE stablecoin. Users deposit registered collateral, mint TARE against it, and repay to withdraw. Liquidators close unsafe positions; stability fees accrue into the surplus buffer.
Source: TARE-Stablecoin/src/tare_engine.vy (Vyper 0.4.3)
Sepolia deployment: 0xC9A5e6eD510030fB84E9600440e7062D152489e0 — address book
Concept guide: TareEngine protocol page
Implementation overview¶
- Collateral registry — up to
MAX_COLLATERAL(16) assets viaadd_collateral; not hard-coded WETH/WBTC slots. - Oracles — primary + optional fallback Chainlink feeds per asset; pricing via
oracle_lib._get_price. - Health factor — risk-weighted: each asset contributes
value × liquidation_threshold / 100. Liquidatable whenHF < 1e18. - Surplus buffer — TARE held on-engine;
distribute_to_savingsskims abovesurplus_buffer_targetinto sTARE. - Circuit breakers —
pauseblocks new deposits/mints;emergency_shutdownpermanently blocks minting. Exits always open.
Events¶
| Event | When |
|---|---|
CollateralAdded |
New collateral listed |
CollateralDeposited |
User deposits collateral |
CollateralRedeemed |
User withdraws collateral |
SurplusDonated |
TARE donated to surplus |
BadDebtSettled |
Insolvent position wound down |
Paused / EmergencyShutdown |
Circuit breaker tripped |
FeesAccrued |
Stability fee minted to surplus |
SurplusDistributed |
Surplus sent to sTARE vault |
AuctionStarted / AuctionProceeds |
Dutch auction of seized collateral |
Admin¶
Owner-gated configuration. Owner cannot touch user collateral/debt directly or mint TARE arbitrarily.
add_collateral¶
TareEngine.add_collateral(token, price_feed, fallback_feed, max_oracle_deviation_bps, liquidation_threshold, token_decimals)
Lists a new collateral asset in the engine registry. Callable once per token; appends to collateral_tokens until MAX_COLLATERAL is reached.
| Param | Type | Description |
|---|---|---|
token |
address |
ERC-20 collateral token (plain transfer only — no hooks/rebasing) |
price_feed |
address |
Primary Chainlink USD price feed |
fallback_feed |
address |
Secondary feed; empty(address) if none |
max_oracle_deviation_bps |
uint256 |
Max disagreement between feeds before pricing halts |
liquidation_threshold |
uint256 |
Percent of this asset's USD value counted toward HF (e.g. 50 → 200% min CR) |
token_decimals |
uint256 |
Token decimals (1–30) for amount scaling |
Returns / state: Writes collateral_config[token] and appends token to collateral_tokens.
Access: owner only (ow._check_owner()).
Events: CollateralAdded(token, price_feed, fallback_feed, liquidation_threshold).
Source (TARE-Stablecoin/src/tare_engine.vy:284-336):
@external
def add_collateral(
token: address,
price_feed: address,
fallback_feed: address,
max_oracle_deviation_bps: uint256,
liquidation_threshold: uint256,
token_decimals: uint256,
):
ow._check_owner()
assert token != empty(address), "TareEngine: zero token"
assert price_feed != empty(address), "TareEngine: zero feed"
assert not self.collateral_config[token].allowed, "TareEngine: already listed"
# ... threshold + registry cap checks ...
self.collateral_config[token] = CollateralConfig(
allowed=True,
price_feed=price_feed,
fallback_feed=fallback_feed,
max_oracle_deviation_bps=max_oracle_deviation_bps,
liquidation_threshold=liquidation_threshold,
token_decimals=token_decimals,
)
self.collateral_tokens.append(token)
log CollateralAdded(...)
Example:
# Owner lists WETH with 50% liquidation threshold (200% min collateral ratio)
engine.add_collateral(
weth,
eth_usd_feed,
empty(address), # no fallback
200, # 2% max feed deviation
50, # 50% of value counts toward HF
18,
)
Cross-links: update_collateral_feed · deposit_collateral · oracle_lib _get_price · Collateral concept guide
update_collateral_feed¶
TareEngine.update_collateral_feed(token, new_price_feed, new_fallback_feed)
Replaces the primary and/or fallback Chainlink feed for an already-listed collateral. Use when a feed is deprecated or unreliable — no redeploy needed.
| Param | Type | Description |
|---|---|---|
token |
address |
Listed collateral token |
new_price_feed |
address |
New primary USD price feed (required, non-zero) |
new_fallback_feed |
address |
New fallback feed; empty(address) removes fallback |
Returns / state: Updates collateral_config[token].price_feed and .fallback_feed. Does not re-validate feed answers here — oracle_lib._get_price enforces on next pricing call.
Access: owner only.
Events: CollateralFeedUpdated(token, price_feed, fallback_feed).
Source (TARE-Stablecoin/src/tare_engine.vy:339-360):
@external
def update_collateral_feed(
token: address,
new_price_feed: address,
new_fallback_feed: address,
):
ow._check_owner()
assert self.collateral_config[token].allowed, "TareEngine: not listed"
assert new_price_feed != empty(address), "TareEngine: zero feed"
self.collateral_config[token].price_feed = new_price_feed
self.collateral_config[token].fallback_feed = new_fallback_feed
log CollateralFeedUpdated(
token=token,
price_feed=new_price_feed,
fallback_feed=new_fallback_feed,
)
Example:
# Swap WETH primary feed; drop fallback
engine.update_collateral_feed(weth, new_eth_usd_feed, empty(address))
Cross-links: add_collateral · get_usd_value · oracle_lib API
set_global_debt_ceiling¶
TareEngine.set_global_debt_ceiling(new_ceiling)
Sets the protocol-wide cap on total outstanding TARE debt. New mints revert once _total_debt() would exceed this ceiling.
| Param | Type | Description |
|---|---|---|
new_ceiling |
uint256 |
Maximum total TARE debt (wei) allowed across all positions |
Returns / state: Writes global_debt_ceiling.
Access: owner only.
Events: DebtCeilingSet(ceiling=new_ceiling).
Source (TARE-Stablecoin/src/tare_engine.vy:363-371):
@external
def set_global_debt_ceiling(new_ceiling: uint256):
ow._check_owner()
self.global_debt_ceiling = new_ceiling
log DebtCeilingSet(ceiling=new_ceiling)
Example:
Cross-links: mint_tare · total_tare_minted · deposit_and_mint
set_stability_fee¶
TareEngine.set_stability_fee(new_fee)
Sets the per-second stability fee rate (WAD-scaled). Accrued interest mints into the engine surplus buffer and eventually flows to sTARE via distribute_to_savings.
| Param | Type | Description |
|---|---|---|
new_fee |
uint256 |
Per-second fractional rate in WAD; max MAX_STABILITY_FEE (~315% APR cap) |
Returns / state: Calls _accrue() at the old rate first (non-retroactive), then writes stability_fee.
Access: owner only.
Events: StabilityFeeSet(old_fee, new_fee).
Source (TARE-Stablecoin/src/tare_engine.vy:374-387):
@external
def set_stability_fee(new_fee: uint256):
ow._check_owner()
assert new_fee <= MAX_STABILITY_FEE, "TareEngine: fee too high"
self._accrue()
old_fee: uint256 = self.stability_fee
self.stability_fee = new_fee
log StabilityFeeSet(old_fee=old_fee, new_fee=new_fee)
Example:
# ~5% APR ≈ 5e16 / SECONDS_PER_YEAR per-second WAD rate (verify against deploy params)
engine.set_stability_fee(1_584_000_000_000_000) # illustrative; use on-chain value
Cross-links: accrue · distribute_to_savings · set_savings_vault · sTARE API
set_savings_vault¶
TareEngine.set_savings_vault(vault)
Points distribute_to_savings at the sTARE ERC-4626 vault. Pass empty(address) to disable surplus skims to savers.
| Param | Type | Description |
|---|---|---|
vault |
address |
sTARE vault contract, or empty(address) to turn off distributions |
Returns / state: Writes savings_vault.
Access: owner only.
Events: SavingsVaultSet(vault=vault).
Source (TARE-Stablecoin/src/tare_engine.vy:390-398):
@external
def set_savings_vault(vault: address):
ow._check_owner()
self.savings_vault = vault
log SavingsVaultSet(vault=vault)
Example:
engine.set_savings_vault(stare_vault_address)
# Disable savings skim (surplus stays on-engine)
engine.set_savings_vault(empty(address))
Cross-links: distribute_to_savings · set_surplus_buffer_target · set_stability_fee · sTARE API
set_surplus_buffer_target¶
TareEngine.set_surplus_buffer_target(target)
Sets minimum TARE kept in engine surplus before distribute_to_savings skims the excess to sTARE. Acts as bad-debt backstop reserve.
| Param | Type | Description |
|---|---|---|
target |
uint256 |
Minimum surplus TARE (wei) retained on-engine before savings skim |
Returns / state: Writes surplus_buffer_target. Higher target → more self-insurance, less sTARE yield.
Access: owner only.
Events: SurplusBufferTargetSet(target=target).
Source (TARE-Stablecoin/src/tare_engine.vy:401-409):
@external
def set_surplus_buffer_target(target: uint256):
ow._check_owner()
self.surplus_buffer_target = target
log SurplusBufferTargetSet(target=target)
Example:
# Keep 500k TARE on-engine before skimming to sTARE
engine.set_surplus_buffer_target(500_000 * 10**18)
Cross-links: distribute_to_savings · donate_to_surplus · settle_bad_debt · set_savings_vault
set_auction_house¶
TareEngine.set_auction_house(house)
Wires the Dutch-auction contract that sells seized collateral for TARE. start_auction forwards collateral to this house via open_lot.
| Param | Type | Description |
|---|---|---|
house |
address |
AuctionHouse contract, or empty(address) to disable auctions |
Returns / state: Writes auction_house. With empty(address), start_auction reverts; owner can still recover collateral via withdraw_seized.
Access: owner only.
Events: AuctionHouseSet(auction_house=house).
Source (TARE-Stablecoin/src/tare_engine.vy:412-421):
@external
def set_auction_house(house: address):
ow._check_owner()
self.auction_house = house
log AuctionHouseSet(auction_house=house)
Example:
Cross-links: start_auction · absorb_auction_proceeds · withdraw_seized · AuctionHouse API
pause¶
TareEngine.pause()
Trips the reversible circuit breaker. Blocks new deposit_collateral and mint_tare paths while paused. Redeem, burn, liquidate, and settle stay open.
| Param | Type | Description |
|---|---|---|
| — | — | No inputs |
Returns / state: Sets paused = True.
Access: owner only.
Events: Paused(status=True).
Source (TARE-Stablecoin/src/tare_engine.vy:424-431):
Example:
Cross-links: unpause · emergency_shutdown · deposit_collateral · mint_tare
unpause¶
TareEngine.unpause()
Clears paused and resumes new deposits/mints. Reverts if emergency_shutdown already ran — shutdown is one-way.
| Param | Type | Description |
|---|---|---|
| — | — | No inputs |
Returns / state: Sets paused = False when shutdown is false.
Access: owner only.
Events: Paused(status=False).
Source (TARE-Stablecoin/src/tare_engine.vy:434-443):
@external
def unpause():
ow._check_owner()
assert not self.shutdown, "TareEngine: shutdown is final"
self.paused = False
log Paused(status=False)
Example:
Cross-links: pause · emergency_shutdown
emergency_shutdown¶
TareEngine.emergency_shutdown()
One-way kill switch. Permanently blocks new deposits and mints (shutdown = True, also sets paused). Redeem, burn, liquidate, and settle remain available so users can unwind. Cannot be undone — unpause reverts after shutdown.
| Param | Type | Description |
|---|---|---|
| — | — | No inputs |
Returns / state: Sets shutdown = True and paused = True.
Access: owner only.
Events: EmergencyShutdown(by=msg.sender).
Source (TARE-Stablecoin/src/tare_engine.vy:446-456):
@external
def emergency_shutdown():
ow._check_owner()
self.shutdown = True
self.paused = True
log EmergencyShutdown(by=msg.sender)
Example:
Cross-links: pause · unpause · deposit_collateral · redeem_collateral
pause_and_withdraw¶
TareEngine.pause_and_withdraw(token_collateral, amount)
Emergency user exit while engine is paused or shut down. Caller withdraws their own collateral even when normal redeem paths may be blocked. Burns TARE debt from caller's wallet first (best-effort, proportional).
| Param | Type | Description |
|---|---|---|
token_collateral |
address |
Collateral token to withdraw |
amount |
uint256 |
Collateral amount (token decimals) |
Returns / state: Decrements user_to_token_to_amount_deposited[msg.sender][token]; transfers collateral to msg.sender. If user has debt, burns up to min(debt, user_tare_balance) from caller via TARE.burn_from.
Access: anyone (@nonreentrant) — only own deposited collateral.
Events: CollateralRedeemed(token, _from=msg.sender, _to=msg.sender, amount).
Source (TARE-Stablecoin/src/tare_engine.vy:459-492):
@external
@nonreentrant
def pause_and_withdraw(token_collateral: address, amount: uint256):
assert amount > 0, "TareEngine: zero amount"
deposited: uint256 = self.user_to_token_to_amount_deposited[msg.sender][token_collateral]
assert deposited >= amount, "TareEngine: exceeds deposited"
self.user_to_token_to_amount_deposited[msg.sender][token_collateral] -= amount
# ... transfer collateral ...
if art > 0:
# burn up to min(debt, user_tare_balance) from msg.sender
extcall TARE.burn_from(msg.sender, burn_amount)
Example:
# User exits WETH collateral during pause (must hold TARE to cover debt burn)
engine.pause_and_withdraw(weth, 1 * 10**18)
Cross-links: pause · emergency_shutdown · redeem_collateral · burn_tare
migrate_tare_ownership¶
TareEngine.migrate_tare_ownership(new_engine)
One-way upgrade hook after emergency_shutdown. Grants successor engine TARE minter role and transfers TARE token ownership. Frozen engine keeps positions redeemable; new minting moves to successor on same TARE token.
| Param | Type | Description |
|---|---|---|
new_engine |
address |
Successor engine contract; must implement TARE() returning this engine's TARE |
Returns / state: TARE.set_minter(new_engine, True) then TARE.transfer_ownership(new_engine). This engine retains its minter flag (inert post-shutdown).
Access: owner only. Requires shutdown == True. Successor must be contract, non-zero, not self, and bound to same TARE.
Events: TareOwnershipMigrated(old_engine=self, new_engine=new_engine).
Source (TARE-Stablecoin/src/tare_engine.vy:495-525):
@external
def migrate_tare_ownership(new_engine: address):
ow._check_owner()
assert self.shutdown, "TareEngine: shutdown required"
assert new_engine != empty(address), "TareEngine: zero engine"
assert new_engine != self, "TareEngine: self migrate"
assert new_engine.is_contract, "TareEngine: not a contract"
assert staticcall IEngineMigratable(new_engine).TARE() == TARE.address, \
"TareEngine: TARE mismatch"
extcall TARE.set_minter(new_engine, True)
extcall TARE.transfer_ownership(new_engine)
log TareOwnershipMigrated(old_engine=self, new_engine=new_engine)
Example:
# After emergency_shutdown — hand mint rights to v2 engine
engine.migrate_tare_ownership(new_tare_engine_v2)
Cross-links: emergency_shutdown · set_peg_keeper · TareToken API
set_peg_keeper¶
TareEngine.set_peg_keeper(keeper)
Authorises a PegKeeper contract to mint/burn TARE within its own debt ceiling for peg defense. Revokes minter role from previous keeper.
| Param | Type | Description |
|---|---|---|
keeper |
address |
PegKeeper contract, or empty(address) to revoke |
Returns / state: Sets peg_keeper. Calls TARE.set_minter(old_keeper, False) then TARE.set_minter(keeper, True) when non-zero.
Access: owner only. Engine owns TARE token — only it can grant minter.
Events: PegKeeperSet(keeper=keeper).
Source (TARE-Stablecoin/src/tare_engine.vy:528-544):
@external
def set_peg_keeper(keeper: address):
ow._check_owner()
old_keeper: address = self.peg_keeper
if old_keeper != empty(address):
extcall TARE.set_minter(old_keeper, False)
self.peg_keeper = keeper
if keeper != empty(address):
extcall TARE.set_minter(keeper, True)
log PegKeeperSet(keeper=keeper)
Example:
Cross-links: PegKeeper API · mint_tare · TareToken API
User flows¶
Position entrypoints for CDP users. All mutating paths use @nonreentrant where noted.
deposit_collateral¶
TareEngine.deposit_collateral(token_collateral_address, amount_collateral)
Deposits ERC-20 collateral into caller's position. Pulls tokens via transferFrom. Does not mint TARE — pair with mint_tare or use deposit_and_mint.
| Param | Type | Description |
|---|---|---|
token_collateral_address |
address |
Listed collateral token |
amount_collateral |
uint256 |
Amount to deposit (token decimals) |
Returns / state: Increments user_to_token_to_amount_deposited[msg.sender][token]; pulls collateral to engine.
Access: anyone (@nonreentrant). Reverts if paused or token not in registry.
Events: CollateralDeposited(user=msg.sender, token, amount).
Source (TARE-Stablecoin/src/tare_engine.vy:556-562, internal 920-934):
@external
@nonreentrant
def deposit_collateral(token_collateral_address: address, amount_collateral: uint256):
self._deposit_collateral(token_collateral_address, amount_collateral)
@internal
def _deposit_collateral(token_collateral_address: address, amount_collateral: uint256):
assert not self.paused, "TareEngine: paused"
assert self.collateral_config[token_collateral_address].allowed, ...
self.user_to_token_to_amount_deposited[msg.sender][token_collateral_address] += amount_collateral
extcall IERC20(token_collateral_address).transferFrom(msg.sender, self, amount_collateral)
Example:
Cross-links: mint_tare · deposit_and_mint · add_collateral · health_factor
mint_tare¶
TareEngine.mint_tare(amount_tare_to_mint)
Mints TARE against caller's deposited collateral. Accrues stability fees first, updates normalized debt, checks health factor and global debt ceiling, then mints ERC-20 to msg.sender.
| Param | Type | Description |
|---|---|---|
amount_tare_to_mint |
uint256 |
TARE to mint (18 decimals) |
Returns / state: Increments user_debt_normalized and total_debt_normalized; mints TARE to caller via TARE.mint.
Access: anyone (@nonreentrant). Reverts if paused, zero amount, debt ceiling exceeded, or HF would break.
Events: None directly (debt change; fee accrual may emit FeesAccrued via _accrue).
Source (TARE-Stablecoin/src/tare_engine.vy:565-572, internal 937-955):
@external
@nonreentrant
def mint_tare(amount_tare_to_mint: uint256):
self._mint_tare(amount_tare_to_mint)
@internal
def _mint_tare(amount_tare_to_mint: uint256):
self._accrue()
assert not self.paused, "TareEngine: paused"
assert self._total_debt() + amount_tare_to_mint <= self.global_debt_ceiling, ...
dart: uint256 = amount_tare_to_mint * PRECISION // self.rate_index
self.user_debt_normalized[msg.sender] += dart
self.total_debt_normalized += dart
self._revert_if_health_factor_broken(msg.sender)
extcall TARE.mint(msg.sender, amount_tare_to_mint)
Example:
Cross-links: deposit_collateral · burn_tare · health_factor · set_global_debt_ceiling · accrue
accrue¶
TareEngine.accrue()
Permissionlessly advances stability-fee index and mints accrued interest into engine surplus. Keeper-callable during idle periods; all debt-changing entrypoints also call _accrue internally.
| Param | Type | Description |
|---|---|---|
| — | — | No inputs |
Returns / state: Bumps rate_index and last_accrual_time; mints minted TARE to engine and adds to surplus. No-op if stability_fee == 0, no debt, or block.timestamp <= last_accrual_time.
Access: anyone (no reentrancy guard — read-only on user positions).
Events: FeesAccrued(rate_index, minted_to_surplus, timestamp) when interest minted.
Source (TARE-Stablecoin/src/tare_engine.vy:575-583, internal 985-1024):
@external
def accrue():
self._accrue()
@internal
def _accrue():
# growth = rate_index * stability_fee * dt / PRECISION
# minted = total_debt_normalized * growth / PRECISION
self.rate_index = new_index
self.surplus += minted
extcall TARE.mint(self, minted)
log FeesAccrued(...)
Example:
Cross-links: set_stability_fee · distribute_to_savings · mint_tare · burn_tare
redeem_collateral¶
TareEngine.redeem_collateral(token_collateral_address, amount)
Withdraws collateral from caller's position to msg.sender. Reverts if withdrawal would break health factor (position must stay solvent).
| Param | Type | Description |
|---|---|---|
token_collateral_address |
address |
Collateral token to withdraw |
amount |
uint256 |
Amount to redeem (token decimals) |
Returns / state: Decrements user_to_token_to_amount_deposited[msg.sender][token]; transfers collateral to caller.
Access: anyone (@nonreentrant) — own position only. Works when paused/shutdown (exit path stays open).
Events: CollateralRedeemed(token, _from=msg.sender, _to=msg.sender, amount).
Source (TARE-Stablecoin/src/tare_engine.vy:586-590, internal 958-968):
@external
@nonreentrant
def redeem_collateral(token_collateral_address: address, amount: uint256):
self._redeem_collateral(token_collateral_address, amount, msg.sender, msg.sender)
self._revert_if_health_factor_broken(msg.sender)
@internal
def _redeem_collateral(...):
self.user_to_token_to_amount_deposited[_from][token] -= amount
extcall IERC20(token).transfer(_to, amount)
Example:
Cross-links: deposit_collateral · burn_tare · health_factor · pause_and_withdraw
deposit_and_mint¶
TareEngine.deposit_and_mint(token_collateral, amount_collateral, amount_tare)
Atomic open/increase position: deposit collateral then mint TARE in one tx. Same checks as separate calls; saves gas and avoids partial state.
| Param | Type | Description |
|---|---|---|
token_collateral |
address |
Collateral token to deposit |
amount_collateral |
uint256 |
Collateral amount (token decimals) |
amount_tare |
uint256 |
TARE to mint against new + existing collateral |
Returns / state: Runs _deposit_collateral then _mint_tare (accrue, debt update, HF + ceiling checks, mint).
Access: anyone (@nonreentrant). Reverts if paused on deposit/mint leg.
Events: CollateralDeposited + possible FeesAccrued from accrue inside mint.
Source (TARE-Stablecoin/src/tare_engine.vy:593-599):
@external
@nonreentrant
def deposit_and_mint(
token_collateral: address, amount_collateral: uint256, amount_tare: uint256
):
self._deposit_collateral(token_collateral, amount_collateral)
self._mint_tare(amount_tare)
Example:
Cross-links: deposit_collateral · mint_tare · redeem_for_tare
redeem_for_tare¶
TareEngine.redeem_for_tare(token_collateral, amount_collateral, amount_tare)
Atomic repay + withdraw: burns TARE debt from caller then withdraws collateral. Inverse of deposit_and_mint.
| Param | Type | Description |
|---|---|---|
token_collateral |
address |
Collateral token to withdraw |
amount_collateral |
uint256 |
Collateral amount to redeem |
amount_tare |
uint256 |
TARE to burn from caller (repay debt) |
Returns / state: _burn_tare (accrue, reduce normalized debt, TARE.burn_from) then _redeem_collateral transfer. HF checked after both legs.
Access: anyone (@nonreentrant). Caller must hold amount_tare and have approved engine for burn_from.
Events: CollateralRedeemed; possible FeesAccrued from accrue in burn path.
Source (TARE-Stablecoin/src/tare_engine.vy:602-609):
@external
@nonreentrant
def redeem_for_tare(
token_collateral: address, amount_collateral: uint256, amount_tare: uint256
):
self._burn_tare(amount_tare, msg.sender, msg.sender)
self._redeem_collateral(token_collateral, amount_collateral, msg.sender, msg.sender)
self._revert_if_health_factor_broken(msg.sender)
Example:
Cross-links: burn_tare · redeem_collateral · deposit_and_mint
burn_tare¶
TareEngine.burn_tare(amount)
Repays TARE debt by burning from caller's wallet. Does not withdraw collateral — use redeem_for_tare for combined repay+withdraw.
| Param | Type | Description |
|---|---|---|
amount |
uint256 |
TARE to burn (18 decimals) |
Returns / state: _accrue() then reduces user_debt_normalized and total_debt_normalized; TARE.burn_from(msg.sender, amount). HF checked after.
Access: anyone (@nonreentrant). Caller must hold TARE and have approved engine.
Events: Possible FeesAccrued from accrue.
Source (TARE-Stablecoin/src/tare_engine.vy:612-616, internal 971-977):
@external
@nonreentrant
def burn_tare(amount: uint256):
self._burn_tare(amount, msg.sender, msg.sender)
self._revert_if_health_factor_broken(msg.sender)
@internal
def _burn_tare(amount: uint256, on_behalf_of: address, tare_from: address):
self._accrue()
dart: uint256 = amount * PRECISION // self.rate_index
self.user_debt_normalized[on_behalf_of] -= dart
self.total_debt_normalized -= dart
extcall TARE.burn_from(tare_from, amount)
Example:
Cross-links: mint_tare · redeem_for_tare · redeem_collateral · health_factor
Liquidation & surplus¶
liquidate¶
TareEngine.liquidate(collateral, user, debt_to_cover)
Standard bonus liquidation for positions with HF < 1e18. Liquidator burns their TARE to cover victim debt and receives seized collateral plus LIQUIDATION_BONUS (10%).
| Param | Type | Description |
|---|---|---|
collateral |
address |
Collateral token to seize from victim |
user |
address |
Underwater position to liquidate |
debt_to_cover |
uint256 |
TARE debt amount liquidator repays (18 decimals) |
Returns / state: Accrues fees; seizes debt_to_cover worth of collateral + 10% bonus from victim to liquidator; burns debt_to_cover TARE from liquidator against victim's debt. Victim HF must improve; liquidator HF must stay valid.
Access: anyone (@nonreentrant) when victim HF < MIN_HEALTH_FACTOR. Reverts if victim lacks enough collateral for bonus — use settle_bad_debt for deep insolvency.
Events: CollateralRedeemed (seizure transfer); possible FeesAccrued.
Source (TARE-Stablecoin/src/tare_engine.vy:619-654):
@external
@nonreentrant
def liquidate(collateral: address, user: address, debt_to_cover: uint256):
assert starting_health_factor < MIN_HEALTH_FACTOR, "TareEngine: Health Factor is good"
token_amount_from_debt_covered: uint256 = self._get_token_amount_from_usd(collateral, debt_to_cover)
bonus_collateral: uint256 = token_amount_from_debt_covered * LIQUIDATION_BONUS // LIQUIDATION_PRECISION
self._redeem_collateral(collateral, token_amount_from_debt_covered + bonus_collateral, user, msg.sender)
self._burn_tare(debt_to_cover, user, msg.sender)
assert ending_health_factor > starting_health_factor, "TareEngine: Liquidation failed"
Example:
Cross-links: settle_bad_debt · health_factor · burn_tare · Liquidations concept guide
donate_to_surplus¶
TareEngine.donate_to_surplus(amount)
Permissionlessly funds engine surplus backstop with circulating TARE. Donated TARE later burned by settle_bad_debt to cover bad debt.
| Param | Type | Description |
|---|---|---|
amount |
uint256 |
TARE to donate (18 decimals) |
Returns / state: Increments surplus; pulls TARE from caller via transferFrom.
Access: anyone. Caller must approve engine for TARE.
Events: SurplusDonated(donor=msg.sender, amount=amount).
Source (TARE-Stablecoin/src/tare_engine.vy:661-672):
@external
def donate_to_surplus(amount: uint256):
assert amount > 0, "TareEngine: zero donation"
self.surplus += amount
success: bool = extcall IERC20(TARE.address).transferFrom(msg.sender, self, amount)
assert success, "TareEngine: donation transfer failed"
log SurplusDonated(donor=msg.sender, amount=amount)
Example:
Cross-links: settle_bad_debt · set_surplus_buffer_target · distribute_to_savings · CoilFeeRouter
settle_bad_debt¶
TareEngine.settle_bad_debt(user)
Winds down insolvent position where collateral value < debt (past profitable liquidation). Permissionless once underwater.
| Param | Type | Description |
|---|---|---|
user |
address |
Insolvent position owner |
Returns / state: Seizes all user collateral into seized_collateral; clears user debt from accounting; burns min(debt, surplus) TARE from surplus buffer; books remainder to total_bad_debt.
Access: anyone. Reverts if user has no debt or position is solvent (raw_collateral_value >= debt).
Events: BadDebtSettled(user, burned_from_surplus, booked_bad_debt).
Source (TARE-Stablecoin/src/tare_engine.vy:675-722):
@external
def settle_bad_debt(user: address):
self._accrue()
debt: uint256 = art * self.rate_index // PRECISION
assert raw_collateral_value < debt, "TareEngine: position solvent"
# seize collateral -> seized_collateral
self.user_debt_normalized[user] = 0
burn_amount: uint256 = min(debt, self.surplus)
if burn_amount > 0:
self.surplus -= burn_amount
extcall TARE.burn_from(self, burn_amount)
remainder: uint256 = debt - burn_amount
if remainder > 0:
self.total_bad_debt += remainder
log BadDebtSettled(...)
Example:
Cross-links: liquidate · donate_to_surplus · retire_bad_debt · withdraw_seized · start_auction
retire_bad_debt¶
TareEngine.retire_bad_debt(amount)
Burns surplus TARE to pay down previously booked total_bad_debt. Restores protocol backing once surplus buffer refilled.
| Param | Type | Description |
|---|---|---|
amount |
uint256 |
TARE to burn from surplus against bad-debt ledger (18 decimals) |
Returns / state: Decrements surplus and total_bad_debt; burns TARE from engine via burn_from.
Access: anyone. Reverts if amount exceeds total_bad_debt or surplus.
Events: BadDebtSettled(user=empty(address), burned_from_surplus=amount, booked_bad_debt=0).
Source (TARE-Stablecoin/src/tare_engine.vy:725-738):
@external
def retire_bad_debt(amount: uint256):
assert amount > 0, "TareEngine: zero amount"
assert amount <= self.total_bad_debt, "TareEngine: exceeds bad debt"
assert amount <= self.surplus, "TareEngine: insufficient surplus"
self.surplus -= amount
self.total_bad_debt -= amount
extcall TARE.burn_from(self, amount)
log BadDebtSettled(user=empty(address), burned_from_surplus=amount, booked_bad_debt=0)
Example:
Cross-links: settle_bad_debt · donate_to_surplus · absorb_auction_proceeds
distribute_to_savings¶
TareEngine.distribute_to_savings() -> uint256
Skims surplus above surplus_buffer_target into sTARE vault. Permissionless keeper call; raises sTARE share price.
| Param | Type | Description |
|---|---|---|
| — | — | No inputs |
Returns: uint256 — TARE sent to savings vault (0 if nothing above target).
State: _accrue() first; transfers excess = surplus - surplus_buffer_target to savings_vault; leaves surplus at target.
Access: anyone. Reverts if savings_vault unset.
Events: SurplusDistributed(vault, amount) when excess > 0.
Source (TARE-Stablecoin/src/tare_engine.vy:741-765):
@external
def distribute_to_savings() -> uint256:
self._accrue()
vault: address = self.savings_vault
assert vault != empty(address), "TareEngine: no savings vault"
if surplus_now <= target:
return 0
excess: uint256 = surplus_now - target
self.surplus = target
extcall IERC20(TARE.address).transfer(vault, excess)
log SurplusDistributed(vault=vault, amount=excess)
return excess
Example:
Cross-links: set_savings_vault · set_surplus_buffer_target · accrue · sTARE API
withdraw_seized¶
TareEngine.withdraw_seized(token, amount, to)
Owner withdraws collateral seized from insolvent positions via settle_bad_debt. Never touches live user deposits.
| Param | Type | Description |
|---|---|---|
token |
address |
Seized collateral token |
amount |
uint256 |
Amount to withdraw (token decimals) |
to |
address |
Recipient (non-zero) |
Returns / state: Decrements seized_collateral[token]; transfers ERC-20 to to.
Access: owner only.
Events: SeizedWithdrawn(token, to, amount).
Source (TARE-Stablecoin/src/tare_engine.vy:768-784):
@external
def withdraw_seized(token: address, amount: uint256, to: address):
ow._check_owner()
assert to != empty(address), "TareEngine: zero recipient"
assert self.seized_collateral[token] >= amount, "TareEngine: exceeds seized"
self.seized_collateral[token] -= amount
extcall IERC20(token).transfer(to, amount)
log SeizedWithdrawn(token=token, to=to, amount=amount)
Example:
Cross-links: settle_bad_debt · start_auction · set_auction_house
start_auction¶
TareEngine.start_auction(token, amount)
Owner sends seized collateral to AuctionHouse and opens Dutch auction lot. Proceeds return via absorb_auction_proceeds.
| Param | Type | Description |
|---|---|---|
token |
address |
Seized collateral token to auction |
amount |
uint256 |
Collateral amount (token decimals) |
Returns / state: Decrements seized_collateral[token]; transfers collateral to auction_house; calls open_lot(token, amount, token_decimals).
Access: owner only. Requires auction_house set.
Events: AuctionStarted(token, auction_house, amount).
Source (TARE-Stablecoin/src/tare_engine.vy:787-808):
@external
def start_auction(token: address, amount: uint256):
ow._check_owner()
house: address = self.auction_house
assert house != empty(address), "TareEngine: no auction house"
self.seized_collateral[token] -= amount
extcall IERC20(token).transfer(house, amount)
extcall IAuctionHouse(house).open_lot(
token, amount, self.collateral_config[token].token_decimals
)
log AuctionStarted(token=token, auction_house=house, amount=amount)
Example:
Cross-links: withdraw_seized · absorb_auction_proceeds · set_auction_house · AuctionHouse API
absorb_auction_proceeds¶
TareEngine.absorb_auction_proceeds(amount)
Auction-house callback: receives TARE from completed Dutch auction. Refills surplus to target, burns remainder against total_bad_debt, excess stays in surplus.
| Param | Type | Description |
|---|---|---|
amount |
uint256 |
TARE proceeds transferred from auction house (18 decimals) |
Returns / state: Pulls TARE via transferFrom; tops up surplus to surplus_buffer_target; burns min(leftover, total_bad_debt); any remainder adds to surplus.
Access: auction_house only (msg.sender == self.auction_house).
Events: AuctionProceeds(to_surplus=amount - burned, bad_debt_retired=burned).
Source (TARE-Stablecoin/src/tare_engine.vy:811-847):
@external
def absorb_auction_proceeds(amount: uint256):
assert msg.sender == self.auction_house, "TareEngine: only auction house"
extcall IERC20(TARE.address).transferFrom(msg.sender, self, amount)
# refill surplus to target, then burn against total_bad_debt
log AuctionProceeds(to_surplus=amount - burned, bad_debt_retired=burned)
Example:
# Called by AuctionHouse after lot settlement — not integrator-facing
auction_house.settle_and_forward_proceeds(...) # -> engine.absorb_auction_proceeds(amount)
Cross-links: start_auction · retire_bad_debt · distribute_to_savings · AuctionHouse API
Views¶
Read-only pricing and position queries. All USD values returned in 18-decimal wei (1e18 = $1).
get_usd_value¶
TareEngine.get_usd_value(collateral_address, amount) -> uint256
Returns USD value of a collateral amount via Chainlink feeds (primary + optional fallback through oracle_lib._get_price).
| Param | Type | Description |
|---|---|---|
collateral_address |
address |
Listed collateral token |
amount |
uint256 |
Token amount (token's native decimals) |
Returns: uint256 — USD value in 18-decimal wei.
State: None (view).
Access: anyone (@view). Reverts if token not allowed or oracle unhealthy.
Events: None.
Source (TARE-Stablecoin/src/tare_engine.vy:854-857, internal 1087-1099):
@external
@view
def get_usd_value(collateral_address: address, amount: uint256) -> uint256:
return self._get_usd_value(collateral_address, amount)
@internal
@view
def _get_usd_value(token: address, amount: uint256) -> uint256:
price: uint256 = oracle_lib._get_price(cfg.price_feed, cfg.fallback_feed, ...)
price_18: uint256 = price * ADDITIONAL_FEED_PRECISION # 8-dec feed -> 1e18
return (price_18 * amount) // (10 ** cfg.token_decimals)
Example:
Cross-links: get_token_amount_from_usd · health_factor · liquidate · oracle_lib API
get_collateral_value_of_user¶
TareEngine.get_collateral_value_of_user(user, token_collateral) -> uint256
Returns user's deposited balance of a specific collateral token (raw token units, not USD).
| Param | Type | Description |
|---|---|---|
user |
address |
Position owner |
token_collateral |
address |
Collateral token |
Returns: uint256 — deposited amount in token's native decimals.
State: None (view).
Access: anyone (@view).
Events: None.
Source (TARE-Stablecoin/src/tare_engine.vy:860-863):
@external
@view
def get_collateral_value_of_user(user: address, token_collateral: address) -> uint256:
return self.user_to_token_to_amount_deposited[user][token_collateral]
Example:
weth_deposited = engine.get_collateral_value_of_user(user, weth) # e.g. 2e18
usd = engine.get_usd_value(weth, weth_deposited) # convert to USD separately
Cross-links: get_account_collateral_value · get_usd_value · deposit_collateral
get_account_collateral_value¶
TareEngine.get_account_collateral_value(user) -> uint256
Returns total USD value of all collateral deposited by user across every listed token (unweighted — no liquidation threshold applied).
| Param | Type | Description |
|---|---|---|
user |
address |
Position owner |
Returns: uint256 — sum of _get_usd_value(token, amount) for each collateral with balance > 0, in 18-decimal USD wei.
State: None (view).
Access: anyone (@view).
Events: None.
Source (TARE-Stablecoin/src/tare_engine.vy:866-869, internal 1076-1082):
@external
@view
def get_account_collateral_value(user: address) -> uint256:
return self._raw_collateral_value(user)
@internal
@view
def _raw_collateral_value(user: address) -> uint256:
for token: address in self.collateral_tokens:
amount: uint256 = self.user_to_token_to_amount_deposited[user][token]
if amount > 0:
total_collateral_value_usd += self._get_usd_value(token, amount)
return total_collateral_value_usd
Example:
Cross-links: get_collateral_value_of_user · health_factor · get_usd_value
get_token_amount_from_usd¶
TareEngine.get_token_amount_from_usd(token, usd_amount_in_wei) -> uint256
Inverse of get_usd_value — converts USD amount to collateral token units at current oracle price.
| Param | Type | Description |
|---|---|---|
token |
address |
Listed collateral token |
usd_amount_in_wei |
uint256 |
USD value in 18-decimal wei |
Returns: uint256 — token amount in native decimals.
State: None (view).
Access: anyone (@view). Reverts if token not allowed or oracle unhealthy.
Events: None.
Source (TARE-Stablecoin/src/tare_engine.vy:872-875, internal 1104-1113):
@external
@view
def get_token_amount_from_usd(token: address, usd_amount_in_wei: uint256) -> uint256:
return self._get_token_amount_from_usd(token, usd_amount_in_wei)
@internal
@view
def _get_token_amount_from_usd(token: address, usd_amount_in_wei: uint256) -> uint256:
price_18: uint256 = price * ADDITIONAL_FEED_PRECISION
return (usd_amount_in_wei * (10 ** cfg.token_decimals)) // price_18
Example:
Cross-links: get_usd_value · liquidate · oracle_lib API
health_factor¶
TareEngine.health_factor(user) -> uint256
Risk-weighted position health. HF >= 1e18 = solvent; HF < 1e18 = liquidatable.
| Param | Type | Description |
|---|---|---|
user |
address |
Position owner |
Returns: uint256 — sum(value_i × threshold_i / 100) × 1e18 / debt. Returns max_uint256 if debt is zero.
State: None (view). Uses accrued debt via _current_debt (normalized debt × rate index).
Access: anyone (@view).
Events: None.
Source (TARE-Stablecoin/src/tare_engine.vy:878-881, internal 1052-1071):
@external
@view
def health_factor(user: address) -> uint256:
return self._health_factor(user)
@internal
@view
def _health_factor(user: address) -> uint256:
# HF = sum(value_i * threshold_i / 100) * 1e18 / debt
adjusted_collateral_usd += (value * threshold) // LIQUIDATION_PRECISION
return (adjusted_collateral_usd * PRECISION) // debt
Example:
Cross-links: liquidate · mint_tare · get_account_collateral_value · add_collateral
user_to_tare_minted¶
TareEngine.user_to_tare_minted(user) -> uint256
Returns user's live TARE debt including accrued stability fees (via rate index).
| Param | Type | Description |
|---|---|---|
user |
address |
Position owner |
Returns: uint256 — user_debt_normalized[user] × rate_index / PRECISION (18-decimal TARE wei).
State: None (view). May lag a few seconds of un-banked interest until next accrue() or debt op.
Access: anyone (@view).
Events: None.
Source (TARE-Stablecoin/src/tare_engine.vy:884-892, internal 1029-1030):
@external
@view
def user_to_tare_minted(user: address) -> uint256:
return self._current_debt(user)
@internal
@view
def _current_debt(user: address) -> uint256:
return self.user_debt_normalized[user] * self.rate_index // PRECISION
Example:
Cross-links: total_tare_minted · health_factor · accrue · burn_tare
total_tare_minted¶
TareEngine.total_tare_minted() -> uint256
Returns protocol-wide live TARE debt (sum of all positions, fee-accrued via rate index).
| Param | Type | Description |
|---|---|---|
| — | — | No inputs |
Returns: uint256 — total_debt_normalized × rate_index / PRECISION (18-decimal TARE wei).
State: None (view). Same accrual lag caveat as user_to_tare_minted.
Access: anyone (@view).
Events: None.
Source (TARE-Stablecoin/src/tare_engine.vy:895-901, internal 1035-1036):
@external
@view
def total_tare_minted() -> uint256:
return self._total_debt()
@internal
@view
def _total_debt() -> uint256:
return self.total_debt_normalized * self.rate_index // PRECISION
Example:
Cross-links: user_to_tare_minted · set_global_debt_ceiling · accrue
get_collateral_tokens_length¶
TareEngine.get_collateral_tokens_length() -> uint256
Returns count of collateral assets currently listed in the registry.
| Param | Type | Description |
|---|---|---|
| — | — | No inputs |
Returns: uint256 — len(collateral_tokens) (max MAX_COLLATERAL = 16).
State: None (view).
Access: anyone (@view).
Events: None.
Source (TARE-Stablecoin/src/tare_engine.vy:904-907):
Example:
n = engine.get_collateral_tokens_length()
for i in range(n):
# iterate collateral_tokens off-chain via events or indexer
...
Cross-links: add_collateral · is_collateral_allowed
is_collateral_allowed¶
TareEngine.is_collateral_allowed(token) -> bool
Checks whether a token is listed and allowed as collateral.
| Param | Type | Description |
|---|---|---|
token |
address |
Collateral token to query |
Returns: bool — collateral_config[token].allowed.
State: None (view).
Access: anyone (@view).
Events: None.
Source (TARE-Stablecoin/src/tare_engine.vy:909-912):
@external
@view
def is_collateral_allowed(token: address) -> bool:
return self.collateral_config[token].allowed
Example:
Cross-links: add_collateral · get_collateral_tokens_length · deposit_collateral