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
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 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
(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 bought
AddLiquidityRaw (Emitted when liquidity is added to the AMM)
provider
(address
) - Address that provided liquiditytoken_amounts
(uint256[2]
) - Amounts of tokens addedfees
(uint256[2]
) - Fees charged for adding liquidityinvariant
(uint256
) - Pool invariant after adding liquiditytoken_supply
(uint256
) - Total token supply after adding liquidity
RemoveLiquidityRaw (Emitted when liquidity is removed from the AMM)
provider
(address
) - Address that removed liquiditytoken_amounts
(uint256[2]
) - Amounts of tokens removedfees
(uint256[2]
) - Fees charged for removing liquiditytoken_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.
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
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.
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 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.
Input | Type | Description |
---|---|---|
rate | uint256 | New 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.
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()
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.
Input | Type | Description |
---|---|---|
frac | uint256 | Fraction 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.
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()
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.
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:
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.
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:
Calculates the USD value change for a hypothetical deposit or withdrawal operation.
Returns:
OraclizedValue
- Struct containing oracle price and USD value after the operation.
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:
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.
Input | Type | Description |
---|---|---|
i | uint256 | Token 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