From 4fe706d7a7e3efd39bb19d0e7ad1c8e29385a098 Mon Sep 17 00:00:00 2001 From: Charles Cooper Date: Sun, 21 Aug 2022 15:43:03 -0700 Subject: [PATCH] make admin immutable (saving an SLOAD) breaks dependency cycle between amm and controller by moving calculation of the created address off chain --- boa_tests/lendborrow/conftest.py | 24 +++++++++++++++++++++++- contracts/AMM.vy | 25 +++++++++++-------------- contracts/ControllerFactory.vy | 15 +++++++++++---- 3 files changed, 45 insertions(+), 19 deletions(-) diff --git a/boa_tests/lendborrow/conftest.py b/boa_tests/lendborrow/conftest.py index 63de0418..e041917c 100644 --- a/boa_tests/lendborrow/conftest.py +++ b/boa_tests/lendborrow/conftest.py @@ -1,5 +1,8 @@ import boa import pytest +from eth_hash.auto import keccak +from eth_utils import to_canonical_address +import rlp @pytest.fixture(scope="session") @@ -53,15 +56,34 @@ def monetary_policy(admin): return policy +def calculate_create_address(deployer, nonce=None): + deployer_address = to_canonical_address(deployer.address) + if nonce is None: + nonce = get_nonce(deployer) + 1 + preimage = rlp.encode([deployer_address, nonce]) + return keccak(preimage)[12:].rjust(20, b"\x00") + + +def get_nonce(contract): + return contract.env.vm.state.get_nonce(to_canonical_address(contract.address)) + + @pytest.fixture(scope="session") def market(controller_factory, collateral_token, monetary_policy, price_oracle, admin): with boa.env.prank(admin): + if controller_factory.n_collaterals() == 0: + nonce = get_nonce(controller_factory) + amm_address = calculate_create_address(controller_factory, nonce) + controller_address = calculate_create_address(controller_factory, nonce + 1) + controller_factory.add_market( collateral_token.address, 100, 10**16, 0, price_oracle.address, monetary_policy.address, 5 * 10**16, 2 * 10**16, - 10**6 * 10**18) + 10**6 * 10**18, + amm_address, controller_address) + return controller_factory diff --git a/contracts/AMM.vy b/contracts/AMM.vy index 45040deb..ede40e54 100644 --- a/contracts/AMM.vy +++ b/contracts/AMM.vy @@ -67,7 +67,7 @@ BORROWED_PRECISION: immutable(uint256) COLLATERAL_TOKEN: immutable(ERC20) # y COLLATERAL_PRECISION: immutable(uint256) BASE_PRICE: immutable(uint256) -admin: public(address) +ADMIN: immutable(address) A: immutable(uint256) Aminus1: immutable(uint256) @@ -109,6 +109,7 @@ def __init__( _log_A_ratio: int256, _base_price: uint256, fee: uint256, + admin: address, admin_fee: uint256, _price_oracle_contract: address, ): @@ -134,13 +135,9 @@ def __init__( # log(A / (A - 1)) - needs to be pre-calculated externally LOG_A_RATIO = _log_A_ratio - -@external -def set_admin(_admin: address): - assert self.admin == empty(address) - self.admin = _admin - BORROWED_TOKEN.approve(_admin, max_value(uint256)) - COLLATERAL_TOKEN.approve(_admin, max_value(uint256)) + ADMIN = admin + BORROWED_TOKEN.approve(ADMIN, max_value(uint256)) + COLLATERAL_TOKEN.approve(ADMIN, max_value(uint256)) # Low-level math @@ -453,7 +450,7 @@ def can_skip_bands(n_end: int256) -> bool: @external @nonreentrant('lock') def deposit_range(user: address, amount: uint256, n1: int256, n2: int256, move_coins: bool): - assert msg.sender == self.admin + assert msg.sender == ADMIN n0: int256 = self.active_band band: int256 = max(n1, n2) # Fill from high N to low N @@ -542,7 +539,7 @@ def deposit_range(user: address, amount: uint256, n1: int256, n2: int256, move_c @external @nonreentrant('lock') def withdraw(user: address, move_to: address) -> uint256[2]: - assert msg.sender == self.admin + assert msg.sender == ADMIN ns: int256[2] = self._read_user_tick_numbers(user) user_shares: uint256[MAX_TICKS] = self._read_user_ticks(user, ns[1] - ns[0] + 1) @@ -1066,7 +1063,7 @@ def get_amount_for_price(p: uint256) -> (uint256, bool): @external def set_rate(rate: uint256) -> uint256: - assert msg.sender == self.admin + assert msg.sender == ADMIN rate_mul: uint256 = self._rate_mul() self.rate_mul = rate_mul self.rate_time = block.timestamp @@ -1077,7 +1074,7 @@ def set_rate(rate: uint256) -> uint256: @external def set_fee(fee: uint256): - assert msg.sender == self.admin + assert msg.sender == ADMIN assert fee < MAX_FEE, "High fee" self.fee = fee log SetFee(fee) @@ -1085,14 +1082,14 @@ def set_fee(fee: uint256): @external def set_admin_fee(fee: uint256): - assert msg.sender == self.admin + assert msg.sender == ADMIN assert fee < MAX_ADMIN_FEE, "High fee" self.admin_fee = fee log SetAdminFee(fee) @external def set_price_oracle(price_oracle: address): - assert msg.sender == self.admin + assert msg.sender == ADMIN assert PriceOracle(price_oracle).price_w() > 0 assert PriceOracle(price_oracle).price() > 0 self.price_oracle_contract = PriceOracle(price_oracle) diff --git a/contracts/ControllerFactory.vy b/contracts/ControllerFactory.vy index 53f4be26..f24f4529 100644 --- a/contracts/ControllerFactory.vy +++ b/contracts/ControllerFactory.vy @@ -145,8 +145,10 @@ def _set_debt_ceiling(addr: address, debt_ceiling: uint256, update: bool): @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]: + monetary_policy: address, loan_discount: uint256, + liquidation_discount: uint256, debt_ceiling: uint256, + calculated_amm_address: address, calculated_controller_address: address, + ) -> address[2]: assert msg.sender == self.admin, "Only admin" assert self.controllers[token] == empty(address) and self.amms[token] == empty(address), "Already exists" assert A >= MIN_A and A <= MAX_A, "Wrong A" @@ -164,13 +166,18 @@ def add_market(token: address, A: uint256, fee: uint256, admin_fee: uint256, STABLECOIN.address, 10**(18 - STABLECOIN.decimals()), token, 10**(18 - ERC20(token).decimals()), A, self.sqrt_int(A_ratio), self.ln_int(A_ratio), - p, fee, admin_fee, _price_oracle_contract, + p, fee, calculated_controller_address, admin_fee, _price_oracle_contract, code_offset=3) + + assert amm == calculated_amm_address # debug: amm create fail + controller: address = create_from_blueprint( self.controller_implementation, token, monetary_policy, loan_discount, liquidation_discount, amm, code_offset=3) - AMM(amm).set_admin(controller) + + assert controller == calculated_controller_address # debug: controller create fail + self._set_debt_ceiling(controller, debt_ceiling, True) N: uint256 = self.n_collaterals