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

The AMM contract exposes several public variables for reading contract state and configuration.

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 (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

AddLiquidityRaw (Emitted when liquidity is added to the AMM)

  • provider (address) - Address that provided liquidity
  • token_amounts (uint256[2]) - Amounts of tokens added
  • fees (uint256[2]) - Fees charged for adding liquidity
  • invariant (uint256) - Pool invariant after adding liquidity
  • token_supply (uint256) - Total token supply after adding liquidity

RemoveLiquidityRaw (Emitted when liquidity is removed from the AMM)

  • provider (address) - Address that removed liquidity
  • token_amounts (uint256[2]) - Amounts of tokens removed
  • fees (uint256[2]) - Fees charged for removing liquidity
  • token_supply (uint256) - Total token supply after removing liquidity

SetRate (Emitted when the interest rate is updated)

  • rate (uint256) - New interest rate

CollectFees (Emitted when fees are collected from the AMM)

  • amount (uint256) - Amount of fees collected

SetFee (Emitted when the trading fee is updated)

  • fee (uint256) - New trading fee

SetKilled (Emitted when the emergency kill switch is toggled)

  • is_killed (bool) - New kill switch status

exchange()

Description:
Main trading function that swaps between stablecoins and LP tokens. Users can exchange stablecoins for LP tokens or vice versa, with the AMM maintaining constant 2x leverage throughout the process.

Returns:
uint256 - The actual amount of output tokens received after the swap.

Emits:
TokenExchange - Swap execution event with buyer address, token indices, amounts, fee, and oracle price.

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

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)

assert self.get_x0(p_o, collateral, debt, True) >= 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:
Calculates the expected output amount for a given input amount when swapping between stablecoins and LP tokens. This is a view function that applies fees and slippage calculations without executing the actual swap.

Returns:
uint256 - The expected output amount for the given input, accounting for fees and slippage.

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 including collateral, debt, and equilibrium point. This function provides a snapshot of the AMM's current position and is used by other contracts to understand the system state.

Returns:
AMMState - Struct containing collateral, debt, and equilibrium point.

📄 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:
Returns the current debt including accumulated interest. This function accounts for the time-based interest accrual since the last rate update.

Returns:
uint256 - Current debt amount including accumulated interest.

📄 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:
Returns the current rate multiplier which represents 1.0 + integral(rate, dt). This is used to calculate accumulated interest over time.

Returns:
uint256 - Rate multiplier in units where 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:
Sets the interest rate which affects the dependence of AMM base price over time. This function can only be called by the LT contract.

Returns:
uint256 - Rate multiplier (e.g. 1.0 + integral(rate, dt)).

Emits:
SetRate - Rate update event with new rate, rate multiplier, and timestamp.

InputTypeDescription
rateuint256New rate in units of 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()

Description:
Internal function for the LT contract to deposit LP tokens and borrow stablecoins. This function can only be called by the LT contract.

Returns:
OraclizedValue - Struct containing oracle price and USD value after deposit.

Emits:
AddLiquidityRaw - Liquidity addition event with token amounts, invariant, and oracle price.

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()

Description:
Internal function for the LT contract to withdraw LP tokens and repay stablecoins. This function can only be called by the LT contract.

Returns:
Pair - Struct containing LP tokens withdrawn and stablecoins repaid.

Emits:
RemoveLiquidityRaw - Liquidity removal event with collateral and debt changes.

InputTypeDescription
fracuint256Fraction of position to withdraw (in 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:
Collects the fees charged as interest. This function transfers accumulated fees to the LT contract.

Returns:
uint256 - Amount of fees collected.

Emits:
CollectFees - Fee collection event with amount collected and new supply.

📄 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()

Description:
Updates the trading fee. This function can only be called by the LT contract.

Returns:
No return value (void function).

Emits:
SetFee - Fee update event with new fee amount.

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()

Description:
Sets the emergency kill switch status. This function can only be called by the LT contract.

Returns:
No return value (void function).

Emits:
SetKilled - Kill status update event.

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:
Returns the current AMM price of LP tokens in stablecoins. This price represents the exchange rate between LP tokens and stablecoins.

Returns:
uint256 - Current LP token price in stablecoins.

📄 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:
Returns the current USD value of the AMM position based on the oracle price.

Returns:
OraclizedValue - Struct containing oracle price and USD value.

📄 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:
Returns the USD value for a hypothetical position with given collateral and debt amounts.

Returns:
OraclizedValue - Struct containing oracle price and USD value.

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:
Calculates the USD value change for a hypothetical deposit or withdrawal operation.

Returns:
OraclizedValue - Struct containing oracle price and USD value after the operation.

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:
Returns the maximum debt that can be borrowed, which is the current stablecoin balance plus the current debt.

Returns:
uint256 - Maximum borrowable debt amount.

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

accumulated_interest()

Description:
Calculates the amount of fees obtained from the interest. This represents the difference between what should be redeemed and what was originally minted.

Returns:
uint256 - Amount of accumulated interest fees.

📄 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:
Returns the token address for the given index. Index 0 returns the stablecoin address, index 1 returns the LP token address.

Returns:
IERC20 - Token contract address.

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

outdated_debt()

Description:
Returns the debt amount without applying the current rate multiplier. This represents the debt before interest accumulation.

Returns:
uint256 - Outdated debt amount without interest.

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

check_nonreentrant()

Description:
View function to check if the nonreentrant modifier is working correctly. This is used for testing purposes.

Returns:
No return value (void function).

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