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 2×. 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 ratecollateral_amount(uint256) - Current LP token balancerate(uint256) - Current interest raterate_mul(uint256) - Rate multiplier for interest calculationrate_time(uint256) - Last rate update timestampminted(uint256) - Total stablecoins mintedredeemed(uint256) - Total stablecoins redeemedis_killed(bool) - Emergency kill switch status
Data Structs
AMMState
collateral(uint256) - LP token amount held as collateraldebt(uint256) - Interest-accrued stablecoin debtx0(uint256) - Virtual balance for maintaining leverage
Pair
collateral(uint256) - LP token amountdebt(uint256) - Stablecoin debt amount
OraclizedValue
p_o(uint256) - Oracle price used for calculationvalue(uint256) - Calculated value in stablecoin terms
Events
TokenExchange (Emitted when tokens are exchanged through the AMM)
buyer(indexed address) - Address that initiated the exchangesold_id(uint256) - Index of token sold (0=stablecoin, 1=collateral)tokens_sold(uint256) - Amount of tokens soldbought_id(uint256) - Index of token bought (0=stablecoin, 1=collateral)tokens_bought(uint256) - Amount of tokens boughtfee(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 addedinvariant(uint256) - Pool invariant after adding liquidityprice_oracle(uint256) - Oracle price at time of addition
RemoveLiquidityRaw
collateral_change(uint256) - Amount of collateral removeddebt_change(uint256) - Amount of debt removed
SetRate
rate(uint256) - New interest raterate_mul(uint256) - Rate multipliertime(uint256) - Timestamp
CollectFees
amount(uint256) - Amount of fees collectednew_supply(uint256) - New supply
SetFee — fee: uint256
SetKilled — is_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.
| Input | Type | Description |
|---|---|---|
i | uint256 | Input token index (0=stablecoin, 1=LP token) |
j | uint256 | Output token index (must be opposite of i) |
in_amount | uint256 | Amount of input tokens to swap |
min_out | uint256 | Minimum acceptable output amount (slippage protection) |
_for | address | Recipient 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.
| Input | Type | Description |
|---|---|---|
i | uint256 | Input token index (0=stablecoin, 1=LP token) |
j | uint256 | Output token index (must be opposite of i) |
in_amount | uint256 | Amount 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.
| Input | Type | Description |
|---|---|---|
rate | uint256 | New 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.
| Input | Type | Description |
|---|---|---|
d_collateral | uint256 | LP tokens to add |
d_debt | uint256 | Stablecoins 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.
| Input | Type | Description |
|---|---|---|
frac | uint256 | Fraction 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.
| Input | Type | Description |
|---|---|---|
fee | uint256 | New 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.
| Input | Type | Description |
|---|---|---|
is_killed | bool | New 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.
| Input | Type | Description |
|---|---|---|
collateral | uint256 | Hypothetical collateral amount |
debt | uint256 | Hypothetical 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).
| Input | Type | Description |
|---|---|---|
collateral_amount | uint256 | Amount of collateral to add/remove |
borrowed_amount | uint256 | Amount of debt to add/remove |
is_deposit | bool | True 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).
| Input | Type | Description |
|---|---|---|
i | uint256 | Token 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
Related
- Core: LT — vault that owns this AMM.
- Core: VirtualPool — flash-loan wrapper that routes arb trades through
exchange. - Core: CryptopoolLPOracle — oracle source used by
PRICE_ORACLE_CONTRACT. - Action Flow: Rebalance — end-to-end arb call sequence.
- User: Math Primer §3 — full invariant derivation.