Skip to main content

Leverage-AMM

The source code of the AMM.vy contract can be found on GitHub. The contract is written with Vyper version 0.4.3.

Overview

The LEVAMM is a two-asset AMM that maintains a constant leverage between coins(0) = STABLECOIN (18 decimals) and coins(1) = COLLATERAL (the Curve pool's LP token). It supports any leverage > 1; in YieldBasis we use . The AMM tracks LP collateral and debt, prices trades along a leveraged curve anchored to an external price oracle, enforces a safe post-trade region, and charges a configurable trading fee.

Debt grows over time at an interest rate set by the linked LT.vy contract; accrued interest is periodically realized and sent to LT as fees.

Used by VirtualPool. VirtualPool routes swaps through this AMM so users can trade between the market's tokens without ever handling LP tokens. Flash loans cover transient balances so any LP exposure stays internal to VirtualPool during the call.


Function Documentation

Public Variables

Constants

  • MAX_FEE (uint256) - Maximum allowed trading fee (10^17 = 10%)
  • MAX_RATE (uint256) - Maximum allowed interest rate (10^18 / (365 * 86400) = 100% APR)

Immutable Variables

  • LEVERAGE (uint256) - Leverage ratio set at deployment (must be > 1e18)
  • LT_CONTRACT (address) - LT contract address (immutable)
  • COLLATERAL (IERC20) - LP token contract address (immutable)
  • STABLECOIN (IERC20) - Stablecoin contract address (immutable)
  • PRICE_ORACLE_CONTRACT (PriceOracle) - Price oracle contract address (immutable)

State Variables

  • fee (uint256) - Current trading fee rate
  • collateral_amount (uint256) - Current LP token balance
  • rate (uint256) - Current interest rate
  • rate_mul (uint256) - Rate multiplier for interest calculation
  • rate_time (uint256) - Last rate update timestamp
  • minted (uint256) - Total stablecoins minted
  • redeemed (uint256) - Total stablecoins redeemed
  • is_killed (bool) - Emergency kill switch status

Data Structs

AMMState

  • collateral (uint256) - LP token amount held as collateral
  • debt (uint256) - Interest-accrued stablecoin debt
  • x0 (uint256) - Virtual balance for maintaining leverage

Pair

  • collateral (uint256) - LP token amount
  • debt (uint256) - Stablecoin debt amount

OraclizedValue

  • p_o (uint256) - Oracle price used for calculation
  • value (uint256) - Calculated value in stablecoin terms

Events

TokenExchange (Emitted when tokens are exchanged through the AMM)

  • buyer (indexed address) - Address that initiated the exchange
  • sold_id (uint256) - Index of token sold (0=stablecoin, 1=collateral)
  • tokens_sold (uint256) - Amount of tokens sold
  • bought_id (uint256) - Index of token bought (0=stablecoin, 1=collateral)
  • tokens_bought (uint256) - Amount of tokens bought
  • fee (uint256) - Fee rate applied to this swap (1e18-based)
  • price_oracle (uint256) - Oracle price observed for the swap

AddLiquidityRaw

  • token_amounts (uint256[2]) - Amounts of tokens added
  • invariant (uint256) - Pool invariant after adding liquidity
  • price_oracle (uint256) - Oracle price at time of addition

RemoveLiquidityRaw

  • collateral_change (uint256) - Amount of collateral removed
  • debt_change (uint256) - Amount of debt removed

SetRate

  • rate (uint256) - New interest rate
  • rate_mul (uint256) - Rate multiplier
  • time (uint256) - Timestamp

CollectFees

  • amount (uint256) - Amount of fees collected
  • new_supply (uint256) - New supply

SetFeefee: uint256

SetKilledis_killed: bool


exchange()

Description: Main trading function — swaps between stablecoin and LP token. Maintains constant leverage through the state-update invariant.

Returns: uint256 - Output amount received after the swap.

Emits: TokenExchange - Swap execution event.

InputTypeDescription
iuint256Input token index (0=stablecoin, 1=LP token)
juint256Output token index (must be opposite of i)
in_amountuint256Amount of input tokens to swap
min_outuint256Minimum acceptable output amount (slippage protection)
_foraddressRecipient address for output tokens (defaults to msg.sender)
📄 View Source Code
@external
@nonreentrant
def exchange(i: uint256, j: uint256, in_amount: uint256, min_out: uint256, _for: address = msg.sender) -> uint256:
assert (i == 0 and j == 1) or (i == 1 and j == 0)
assert not self.is_killed

collateral: uint256 = self.collateral_amount
assert collateral > 0, "Empty AMM"
debt: uint256 = self._debt_w()
p_o: uint256 = extcall PRICE_ORACLE_CONTRACT.price_w()
x0: uint256 = self.get_x0(p_o, collateral, debt, False)
x_initial: uint256 = x0 - debt

out_amount: uint256 = 0
fee: uint256 = self.fee

# Asymmetric safety check: capture coll/debt ratio BEFORE the trade and
# AFTER, then only enforce the strict `safe_limits` branch when the trade
# made the state worse. If the trade moves the AMM toward L=2 equilibrium
# we relax the check so rebalances are not blocked.
coll_vs_debt_before: uint256 = unsafe_div(p_o * collateral * COLLATERAL_PRECISION, debt)
if debt == 0:
coll_vs_debt_before = max_value(uint256)

if i == 0: # Trader buys collateral from us
x: uint256 = x_initial + in_amount
y: uint256 = math._ceil_div(x_initial * collateral, x)
out_amount = (collateral - y) * (10**18 - fee) // 10**18
assert out_amount >= min_out, "Slippage"
debt -= in_amount
collateral -= out_amount
self.redeemed += in_amount
assert extcall STABLECOIN.transferFrom(msg.sender, self, in_amount, default_return_value=True)
assert extcall COLLATERAL.transfer(_for, out_amount, default_return_value=True)

else: # Trader sells collateral to us
y: uint256 = collateral + in_amount
x: uint256 = math._ceil_div(x_initial * collateral, y)
out_amount = (x_initial - x) * (10**18 - fee) // 10**18
assert out_amount >= min_out, "Slippage"
debt += out_amount
self.minted += out_amount
collateral = y
assert extcall COLLATERAL.transferFrom(msg.sender, self, in_amount, default_return_value=True)
assert extcall STABLECOIN.transfer(_for, out_amount, default_return_value=True)

coll_vs_debt_after: uint256 = unsafe_div(p_o * collateral * COLLATERAL_PRECISION, debt)
if debt == 0:
coll_vs_debt_after = max_value(uint256)

check_state: bool = True
if coll_vs_debt_after > 2 * 10**18:
if coll_vs_debt_before > coll_vs_debt_after:
check_state = False # state improved on the "too-much-collateral" side
else:
if coll_vs_debt_before < coll_vs_debt_after:
check_state = False # state improved on the "too-much-debt" side

assert self.get_x0(p_o, collateral, debt, check_state) >= x0, "Bad final state"
self.collateral_amount = collateral
self.debt = debt

log TokenExchange(buyer=msg.sender, sold_id=i, tokens_sold=in_amount,
bought_id=j, tokens_bought=out_amount, fee=fee, price_oracle=p_o)

if LT_CONTRACT != empty(address) and LT_CONTRACT.is_contract:
self._collect_fees()
extcall LT(LT_CONTRACT).distribute_borrower_fees()

return out_amount

get_dy()

Description: Quote expected output for a given input. Applies fee. View function.

Returns: uint256 - Expected output amount.

InputTypeDescription
iuint256Input token index (0=stablecoin, 1=LP token)
juint256Output token index (must be opposite of i)
in_amountuint256Amount of input tokens to swap
📄 View Source Code
@external
@view
def get_dy(i: uint256, j: uint256, in_amount: uint256) -> uint256:
assert (i == 0 and j == 1) or (i == 1 and j == 0)

p_o: uint256 = staticcall PRICE_ORACLE_CONTRACT.price()
collateral: uint256 = self.collateral_amount
debt: uint256 = self._debt()
x_initial: uint256 = self.get_x0(p_o, collateral, debt, False) - debt

if i == 0: # Buy collateral
assert in_amount <= debt, "Amount too large"
x: uint256 = x_initial + in_amount
y: uint256 = math._ceil_div(x_initial * collateral, x)
return (collateral - y) * (10**18 - self.fee) // 10**18

else: # Sell collateral
y: uint256 = collateral + in_amount
x: uint256 = math._ceil_div(x_initial * collateral, y)
return (x_initial - x) * (10**18 - self.fee) // 10**18

get_state()

Description: Returns the current AMM state (collateral, debt, x0).

Returns: AMMState.

📄 View Source Code
@external
@view
def get_state() -> AMMState:
p_o: uint256 = staticcall PRICE_ORACLE_CONTRACT.price()
state: AMMState = empty(AMMState)
state.collateral = self.collateral_amount
state.debt = self._debt()
state.x0 = self.get_x0(p_o, state.collateral, state.debt, False)
return state

get_debt()

Description: Current debt including accumulated interest.

Returns: uint256.

📄 View Source Code
@external
@view
def get_debt() -> uint256:
return self._debt()

@internal
@view
def _debt() -> uint256:
return self.debt * self._rate_mul() // self.rate_mul

get_rate_mul()

Description: Rate multiplier 1.0 + integral(rate, dt).

Returns: uint256 - Rate multiplier (1.0 == 1e18).

📄 View Source Code
@external
@view
def get_rate_mul() -> uint256:
return self._rate_mul()

@internal
@view
def _rate_mul() -> uint256:
return unsafe_div(self.rate_mul * (10**18 + self.rate * (block.timestamp - self.rate_time)), 10**18)

set_rate()

Description: Set interest rate. Only callable by LT.

Returns: uint256 - Rate multiplier.

Emits: SetRate.

InputTypeDescription
rateuint256New rate (int(fraction * 1e18) per second)
📄 View Source Code
@external
@nonreentrant
def set_rate(rate: uint256) -> uint256:
assert msg.sender == LT_CONTRACT, "Access"
assert rate <= MAX_RATE, "Rate too high"
rate_mul: uint256 = self._rate_mul()
self.debt = self.debt * rate_mul // self.rate_mul
self.rate_mul = rate_mul
self.rate_time = block.timestamp
self.rate = rate
log SetRate(rate=rate, rate_mul=rate_mul, time=block.timestamp)
return rate_mul

_deposit() (LT-only)

Description: Deposit LP tokens and borrow stablecoins. Only callable by LT.

Returns: OraclizedValue.

Emits: AddLiquidityRaw.

InputTypeDescription
d_collateraluint256LP tokens to add
d_debtuint256Stablecoins to borrow
📄 View Source Code
@external
def _deposit(d_collateral: uint256, d_debt: uint256) -> OraclizedValue:
assert msg.sender == LT_CONTRACT, "Access violation"
assert not self.is_killed

p_o: uint256 = extcall PRICE_ORACLE_CONTRACT.price_w()
collateral: uint256 = self.collateral_amount
debt: uint256 = self._debt_w()

debt += d_debt
collateral += d_collateral
self.minted += d_debt

self.debt = debt
self.collateral_amount = collateral

value_after: uint256 = self.get_x0(p_o, collateral, debt, True) * 10**18 // (2 * LEVERAGE - 10**18)

log AddLiquidityRaw(token_amounts=[d_collateral, d_debt], invariant=value_after, price_oracle=p_o)
return OraclizedValue(p_o=p_o, value=value_after)

_withdraw() (LT-only)

Description: Withdraw LP tokens and repay stablecoins proportionally. Only callable by LT.

Returns: Pair - (collateral_withdrawn, debt_repaid).

Emits: RemoveLiquidityRaw.

InputTypeDescription
fracuint256Fraction of position to withdraw (1e18 units)
📄 View Source Code
@external
def _withdraw(frac: uint256) -> Pair:
assert msg.sender == LT_CONTRACT, "Access violation"

collateral: uint256 = self.collateral_amount
debt: uint256 = self._debt_w()

d_collateral: uint256 = collateral * frac // 10**18
d_debt: uint256 = math._ceil_div(debt * frac, 10**18)

self.collateral_amount -= d_collateral
self.debt = debt - d_debt
self.redeemed += d_debt

log RemoveLiquidityRaw(collateral_change=d_collateral, debt_change=d_debt)

return Pair(collateral=d_collateral, debt=d_debt)

collect_fees()

Description: Realize accrued interest. Transfers available stablecoins to LT.

Returns: uint256 - Amount of fees collected.

Emits: CollectFees.

📄 View Source Code
@external
@nonreentrant
def collect_fees() -> uint256:
return self._collect_fees()

@internal
def _collect_fees() -> uint256:
assert not self.is_killed

debt: uint256 = self._debt_w()
self.debt = debt
minted: uint256 = self.minted
to_be_redeemed: uint256 = debt + self.redeemed

if to_be_redeemed > minted:
self.minted = to_be_redeemed
to_be_redeemed = unsafe_sub(to_be_redeemed, minted)
stables_in_amm: uint256 = staticcall STABLECOIN.balanceOf(self)
if stables_in_amm < to_be_redeemed:
self.minted -= (to_be_redeemed - stables_in_amm)
to_be_redeemed = stables_in_amm
assert extcall STABLECOIN.transfer(LT_CONTRACT, to_be_redeemed, default_return_value=True)
log CollectFees(amount=to_be_redeemed, new_supply=debt)
return to_be_redeemed
else:
log CollectFees(amount=0, new_supply=debt)
return 0

set_fee() (LT-only)

Description: Update trading fee. Only callable by LT.

Emits: SetFee.

InputTypeDescription
feeuint256New trading fee (must be ≤ MAX_FEE)
📄 View Source Code
@external
def set_fee(fee: uint256):
assert msg.sender == LT_CONTRACT, "Access"
assert fee <= MAX_FEE
self.fee = fee
log SetFee(fee=fee)

set_killed() (LT-only)

Description: Emergency kill switch. Only callable by LT.

Emits: SetKilled.

InputTypeDescription
is_killedboolNew kill switch status
📄 View Source Code
@external
def set_killed(is_killed: bool):
assert msg.sender == LT_CONTRACT, "Access"
self.is_killed = is_killed
log SetKilled(is_killed=is_killed)

get_p()

Description: AMM price of LP tokens in stablecoins.

Returns: uint256.

📄 View Source Code
@external
@view
def get_p() -> uint256:
p_o: uint256 = staticcall PRICE_ORACLE_CONTRACT.price()
collateral: uint256 = self.collateral_amount
debt: uint256 = self._debt()
return (self.get_x0(p_o, collateral, debt, False) - debt) * (10**18 // COLLATERAL_PRECISION) // collateral

value_oracle()

Description: USD value of the AMM position at oracle price (sandwich-resistant).

Returns: OraclizedValue.

📄 View Source Code
@external
@view
def value_oracle() -> OraclizedValue:
p_o: uint256 = staticcall PRICE_ORACLE_CONTRACT.price()
collateral: uint256 = self.collateral_amount
debt: uint256 = self._debt()
return OraclizedValue(p_o=p_o, value=self.get_x0(p_o, collateral, debt, False) * 10**18 // (2 * LEVERAGE - 10**18))

value_oracle_for()

Description: USD value for a hypothetical position with given collateral and debt.

Returns: OraclizedValue.

InputTypeDescription
collateraluint256Hypothetical collateral amount
debtuint256Hypothetical debt amount
📄 View Source Code
@external
@view
def value_oracle_for(collateral: uint256, debt: uint256) -> OraclizedValue:
p_o: uint256 = staticcall PRICE_ORACLE_CONTRACT.price()
return OraclizedValue(p_o=p_o, value=self.get_x0(p_o, collateral, debt, False) * 10**18 // (2 * LEVERAGE - 10**18))

value_change()

Description: USD value after a hypothetical deposit or withdrawal.

Returns: OraclizedValue - (oracle_price, value_after).

InputTypeDescription
collateral_amountuint256Amount of collateral to add/remove
borrowed_amountuint256Amount of debt to add/remove
is_depositboolTrue for deposit, false for withdrawal
📄 View Source Code
@external
@view
def value_change(collateral_amount: uint256, borrowed_amount: uint256, is_deposit: bool) -> OraclizedValue:
p_o: uint256 = staticcall PRICE_ORACLE_CONTRACT.price()
collateral: uint256 = self.collateral_amount
debt: uint256 = self._debt()

if is_deposit:
collateral += collateral_amount
debt += borrowed_amount
else:
collateral -= collateral_amount
debt -= borrowed_amount

x0_after: uint256 = self.get_x0(p_o, collateral, debt, is_deposit)

return OraclizedValue(
p_o = p_o,
value = x0_after * 10**18 // (2 * LEVERAGE - 10**18))

max_debt()

Description: Maximum debt the AMM can take (current stable balance + current debt).

Returns: uint256.

📄 View Source Code
@external
@view
def max_debt() -> uint256:
return staticcall STABLECOIN.balanceOf(self) + self._debt()

accumulated_interest()

Description: Amount of interest fees available for collection.

Returns: uint256.

📄 View Source Code
@external
@view
def accumulated_interest() -> uint256:
minted: uint256 = self.minted
return unsafe_sub(max(self._debt() + self.redeemed, minted), minted)

coins()

Description: Token address at index (0 = stablecoin, 1 = LP token).

InputTypeDescription
iuint256Token index
📄 View Source Code
@external
@view
def coins(i: uint256) -> IERC20:
return [STABLECOIN, COLLATERAL][i]

outdated_debt()

Description: Debt amount without applying the rate multiplier (pre-interest-accrual).

Returns: uint256.

📄 View Source Code
@external
@view
def outdated_debt() -> uint256:
return self.debt

check_nonreentrant()

Description: View function to check that the nonreentrant modifier is in place.

📄 View Source Code
@external
@nonreentrant
@view
def check_nonreentrant():
pass