Deployer API
A new crvUSD market can be added by the CurveOwnershipAgent. Therefore, adding a new market requires a successfully passed DAO vote.
add_market
¶
ControllerFactory.add_market(token: address, A: uint256, fee: uint256, admin_fee: uint256, _price_oracle_contract: address, monetary_policy: address, loan_discount: uint256, liquidation_discount: uint256, debt_ceiling: uint256) -> address[2]:
Guarded Method
This function is only callable by the admin
of the contract.
Function to add a new market and automatically deploy a new AMM and a Controller from the implementation contracts (see Implementations). Additionally, when initializing a new market, rate_write()
from the MonetaryPolicy contract is called to check if it has a correct ABI.
Returns: AMM and Controller contracts (address
).
Emits: AddNewMarket
Input | Type | Description |
---|---|---|
token | address | Collateral token address |
A | uint256 | Amplification coefficient. One band size is 1/n |
fee | uint256 | AMM fee in the market's AMM |
admin_fee | uint256 | AMM admin fee |
_price_oracle_contract | address | Address of the price oracle contract for the market |
monetary_policy | address | Monetary policy for the market |
loan_discount | uint256 | Loan Discount: allowed to borrow only up to x_down * (1 - loan_discount) |
liquidation_discount | uint256 | Discount defining a bad liquidation threshold |
debt_ceiling | uint256 | Debt ceiling for the market |
Warning
There are some limitation values for adding new markets regarding fee
, A
and liquidation_discount
.
Source code
# Limits
MIN_A: constant(uint256) = 2
MAX_A: constant(uint256) = 10000
MIN_FEE: constant(uint256) = 10**6 # 1e-12, still needs to be above 0
MAX_FEE: constant(uint256) = 10**17 # 10%
MAX_ADMIN_FEE: constant(uint256) = 10**18 # 100%
MAX_LOAN_DISCOUNT: constant(uint256) = 5 * 10**17
MIN_LIQUIDATION_DISCOUNT: constant(uint256) = 10**16
@external
@nonreentrant('lock')
def add_market(token: address, A: uint256, fee: uint256, admin_fee: uint256,
_price_oracle_contract: address,
monetary_policy: address, loan_discount: uint256, liquidation_discount: uint256,
debt_ceiling: uint256) -> address[2]:
"""
@notice Add a new market, creating an AMM and a Controller from a blueprint
@param token Collateral token address
@param A Amplification coefficient; one band size is 1/A
@param fee AMM fee in the market's AMM
@param admin_fee AMM admin fee
@param _price_oracle_contract Address of price oracle contract for this market
@param monetary_policy Monetary policy for this market
@param loan_discount Loan discount: allowed to borrow only up to x_down * (1 - loan_discount)
@param liquidation_discount Discount which defines a bad liquidation threshold
@param debt_ceiling Debt ceiling for this market
@return (Controller, AMM)
"""
assert msg.sender == self.admin, "Only admin"
assert A >= MIN_A and A <= MAX_A, "Wrong A"
assert fee <= MAX_FEE, "Fee too high"
assert fee >= MIN_FEE, "Fee too low"
assert admin_fee < MAX_ADMIN_FEE, "Admin fee too high"
assert liquidation_discount >= MIN_LIQUIDATION_DISCOUNT, "Liquidation discount too low"
assert loan_discount <= MAX_LOAN_DISCOUNT, "Loan discount too high"
assert loan_discount > liquidation_discount, "need loan_discount>liquidation_discount"
MonetaryPolicy(monetary_policy).rate_write() # Test that MonetaryPolicy has correct ABI
p: uint256 = PriceOracle(_price_oracle_contract).price() # This also validates price oracle ABI
assert p > 0
assert PriceOracle(_price_oracle_contract).price_w() == p
A_ratio: uint256 = 10**18 * A / (A - 1)
amm: address = create_from_blueprint(
self.amm_implementation,
STABLECOIN.address, 10**(18 - STABLECOIN.decimals()),
token, 10**(18 - ERC20(token).decimals()), # <- This validates ERC20 ABI
A, isqrt(A_ratio * 10**18), self.ln_int(A_ratio),
p, fee, admin_fee, _price_oracle_contract,
code_offset=3)
controller: address = create_from_blueprint(
self.controller_implementation,
token, monetary_policy, loan_discount, liquidation_discount, amm,
code_offset=3)
AMM(amm).set_admin(controller)
self._set_debt_ceiling(controller, debt_ceiling, True)
N: uint256 = self.n_collaterals
self.collaterals[N] = token
for i in range(1000):
if self.collaterals_index[token][i] == 0:
self.collaterals_index[token][i] = 2**128 + N
break
assert i != 999, "Too many controllers for same collateral"
self.controllers[N] = controller
self.amms[N] = amm
self.n_collaterals = N + 1
log AddMarket(token, controller, amm, monetary_policy, N)
return [controller, amm]
@internal
@view
def calculate_rate() -> uint256:
sigma: int256 = self.sigma
target_debt_fraction: uint256 = self.target_debt_fraction
p: int256 = convert(PRICE_ORACLE.price(), int256)
pk_debt: uint256 = 0
for pk in self.peg_keepers:
if pk.address == empty(address):
break
pk_debt += pk.debt()
power: int256 = (10**18 - p) * 10**18 / sigma # high price -> negative pow -> low rate
if pk_debt > 0:
total_debt: uint256 = CONTROLLER_FACTORY.total_debt()
if total_debt == 0:
return 0
else:
power -= convert(pk_debt * 10**18 / total_debt * 10**18 / target_debt_fraction, int256)
return self.rate0 * min(self.exp(power), MAX_EXP) / 10**18
@external
def rate_write() -> uint256:
# Not needed here but useful for more automated policies
# which change rate0 - for example rate0 targeting some fraction pl_debt/total_debt
return self.calculate_rate()
@external
@view
def price() -> uint256:
n: uint256 = self.n_price_pairs
prices: uint256[MAX_PAIRS] = empty(uint256[MAX_PAIRS])
D: uint256[MAX_PAIRS] = empty(uint256[MAX_PAIRS])
Dsum: uint256 = 0
DPsum: uint256 = 0
for i in range(MAX_PAIRS):
if i == n:
break
price_pair: PricePair = self.price_pairs[i]
pool_supply: uint256 = price_pair.pool.totalSupply()
if pool_supply >= MIN_LIQUIDITY:
p: uint256 = price_pair.pool.price_oracle()
if price_pair.is_inverse:
p = 10**36 / p
prices[i] = p
_D: uint256 = price_pair.pool.get_virtual_price() * pool_supply / 10**18
D[i] = _D
Dsum += _D
DPsum += _D * p
if Dsum == 0:
return 10**18 # Placeholder for no active pools
p_avg: uint256 = DPsum / Dsum
e: uint256[MAX_PAIRS] = empty(uint256[MAX_PAIRS])
e_min: uint256 = max_value(uint256)
for i in range(MAX_PAIRS):
if i == n:
break
p: uint256 = prices[i]
e[i] = (max(p, p_avg) - min(p, p_avg))**2 / (SIGMA**2 / 10**18)
e_min = min(e[i], e_min)
wp_sum: uint256 = 0
w_sum: uint256 = 0
for i in range(MAX_PAIRS):
if i == n:
break
w: uint256 = D[i] * self.exp(-convert(e[i] - e_min, int256)) / 10**18
w_sum += w
wp_sum += w * prices[i]
return wp_sum / w_sum