diff --git a/config/badger_config.py b/config/badger_config.py index 4ed0cfba..f31f45fa 100644 --- a/config/badger_config.py +++ b/config/badger_config.py @@ -30,6 +30,14 @@ withdrawalFee=0, ), ), + digg=DotMap( + strategyName="StrategyDiggRewards", + params=DotMap( + performanceFeeStrategist=0, + performanceFeeGovernance=0, + withdrawalFee=0, + ), + ), uniBadgerWbtc=DotMap( strategyName="StrategyBadgerLpMetaFarm", params=DotMap( diff --git a/contracts/badger-sett/DiggSett.sol b/contracts/badger-sett/DiggSett.sol index e8561ef6..c62a2951 100644 --- a/contracts/badger-sett/DiggSett.sol +++ b/contracts/badger-sett/DiggSett.sol @@ -94,6 +94,7 @@ contract DiggSett is Sett { // Check balance uint256 _sharesInSett = digg.sharesOf(address(this)); + uint256 _fee; // If we don't have sufficient idle want in Sett, withdraw from Strategy if (_sharesInSett < _sharesToRedeem) { @@ -109,9 +110,12 @@ contract DiggSett is Sett { if (_diff < _toWithdraw) { _sharesToRedeem = _sharesInSett.add(_diff); } + _fee = _processWithdrawalFee(digg.sharesToFragments(_sharesToRedeem.sub(_sharesInSett))); + } else { + _fee = _processWithdrawalFee(digg.sharesToFragments(_sharesToRedeem)); } // Transfer the corresponding number of shares, scaled to DIGG fragments, to recipient - digg.transfer(msg.sender, digg.sharesToFragments(_sharesToRedeem)); + digg.transfer(msg.sender, digg.sharesToFragments(_sharesToRedeem).sub(_fee)); } } diff --git a/contracts/badger-sett/Sett.sol b/contracts/badger-sett/Sett.sol index 21de1001..682bf6df 100644 --- a/contracts/badger-sett/Sett.sol +++ b/contracts/badger-sett/Sett.sol @@ -29,9 +29,12 @@ import "./SettAccessControlDefended.sol"; V1.2 * Transfer functions are now pausable along with all other non-permissioned write functions * All permissioned write functions, with the exception of pause() & unpause(), are pausable as well + + V1.3 + * Withdrawals are processed from idle want in sett. */ -contract Sett is ERC20Upgradeable, SettAccessControlDefended, PausableUpgradeable { +contract Sett is ERC20Upgradeable, PausableUpgradeable, SettAccessControlDefended { using SafeERC20Upgradeable for IERC20Upgradeable; using AddressUpgradeable for address; using SafeMathUpgradeable for uint256; @@ -50,6 +53,9 @@ contract Sett is ERC20Upgradeable, SettAccessControlDefended, PausableUpgradeabl address public guardian; + uint256 public withdrawalFee; + uint256 public constant MAX_FEE = 10000; + event FullPricePerShareUpdated(uint256 value, uint256 indexed timestamp, uint256 indexed blockNumber); function initialize( @@ -111,10 +117,10 @@ contract Sett is ERC20Upgradeable, SettAccessControlDefended, PausableUpgradeabl /// ===== View Functions ===== function version() public view returns (string memory) { - return "1.2"; + return "1.3"; } - function getPricePerFullShare() public view virtual returns (uint256) { + function getPricePerFullShare() public virtual view returns (uint256) { if (totalSupply() == 0) { return 1e18; } @@ -130,7 +136,7 @@ contract Sett is ERC20Upgradeable, SettAccessControlDefended, PausableUpgradeabl /// @notice Defines how much of the Setts' underlying can be borrowed by the Strategy for use /// @notice Custom logic in here for how much the vault allows to be borrowed /// @notice Sets minimum required on-hand to keep small withdrawals cheap - function available() public view virtual returns (uint256) { + function available() public virtual view returns (uint256) { return token.balanceOf(address(this)).mul(min).div(max); } @@ -197,6 +203,12 @@ contract Sett is ERC20Upgradeable, SettAccessControlDefended, PausableUpgradeabl guardian = _guardian; } + function setWithdrawalFee(uint256 _withdrawalFee) external { + _onlyGovernance(); + require(_withdrawalFee <= MAX_FEE, "sett/excessive-withdrawal-fee"); + withdrawalFee = _withdrawalFee; + } + /// ===== Permissioned Actions: Controller ===== /// @notice Used to swap any borrowed reserve over the debt limit to liquidate to 'token' @@ -262,6 +274,7 @@ contract Sett is ERC20Upgradeable, SettAccessControlDefended, PausableUpgradeabl _burn(msg.sender, _shares); // Check balance + uint256 _fee; uint256 b = token.balanceOf(address(this)); if (b < r) { uint256 _toWithdraw = r.sub(b); @@ -271,9 +284,12 @@ contract Sett is ERC20Upgradeable, SettAccessControlDefended, PausableUpgradeabl if (_diff < _toWithdraw) { r = b.add(_diff); } + _fee = _processWithdrawalFee(r.sub(b)); + } else { + _fee = _processWithdrawalFee(r); } - token.safeTransfer(msg.sender, r); + token.safeTransfer(msg.sender, r.sub(_fee)); } function _lockForBlock(address account) internal { @@ -296,4 +312,18 @@ contract Sett is ERC20Upgradeable, SettAccessControlDefended, PausableUpgradeabl _blockLocked(); return super.transferFrom(sender, recipient, amount); } + + /// ===== Internal Helper Functions ===== + + /// @notice If withdrawal fee is active, take the appropriate amount from the given value and transfer to rewards recipient + /// @return The withdrawal fee that was taken + function _processWithdrawalFee(uint256 _amount) internal returns (uint256) { + if (withdrawalFee == 0) { + return 0; + } + + uint256 fee = _amount.mul(withdrawalFee).div(MAX_FEE); + token.safeTransfer(IController(controller).rewards(), fee); + return fee; + } } diff --git a/helpers/sett/strategy_registry.py b/helpers/contract_registry.py similarity index 97% rename from helpers/sett/strategy_registry.py rename to helpers/contract_registry.py index 134f8023..57130281 100644 --- a/helpers/sett/strategy_registry.py +++ b/helpers/contract_registry.py @@ -41,5 +41,5 @@ } -def strategy_name_to_artifact(name): +def contract_name_to_artifact(name): return name_to_artifact[name] diff --git a/helpers/multicall/functions.py b/helpers/multicall/functions.py index 96a7c313..10674741 100644 --- a/helpers/multicall/functions.py +++ b/helpers/multicall/functions.py @@ -34,6 +34,7 @@ def as_original(value): strategist="strategist()(address)", keeper="keeper()(address)", shares="shares()(uint256)", + withdrawalFee="withdrawalFee()(uint256)", ) strategy = DotMap( balanceOfPool="balanceOfPool()(uint256)", diff --git a/helpers/registry.py b/helpers/registry.py index c3502fcf..74f44228 100644 --- a/helpers/registry.py +++ b/helpers/registry.py @@ -138,10 +138,10 @@ router="0xd9e1cE17f2641f24aE83637ab66a2cca9C378B9F", factory="0xC0AEe478e3658e2610c5F7A4A2E1777cE9e4f2Ac", lpTokens=DotMap( - sushiBadgerWBtc="0x110492b31c59716AC47337E616804E3E3AdC0b4a", - sushiWbtcWeth="0xCEfF51756c56CeFFCA006cD410B03FFC46dd3a58" + sushiBadgerWbtc="0x110492b31c59716AC47337E616804E3E3AdC0b4a", + sushiWbtcEth="0xCEfF51756c56CeFFCA006cD410B03FFC46dd3a58" ), - pids=DotMap(sushiBadgerWBtc=73, sushiEthWBtc=21), + pids=DotMap(sushiBadgerWbtc=73, sushiEthWBtc=21), ) curve_registry = DotMap( diff --git a/helpers/sett/resolvers/StrategyCoreResolver.py b/helpers/sett/resolvers/StrategyCoreResolver.py index e3a0efdf..0466ebfb 100644 --- a/helpers/sett/resolvers/StrategyCoreResolver.py +++ b/helpers/sett/resolvers/StrategyCoreResolver.py @@ -70,6 +70,13 @@ def add_sett_snap(self, calls): calls.append( Call(sett.address, [func.erc20.totalSupply], [["sett.totalSupply", as_wei]]) ) + calls.append( + Call( + sett.address, + [func.sett.withdrawalFee], + [["sett.withdrawalFee", as_wei]], + ) + ) return calls @@ -239,13 +246,29 @@ def confirm_withdraw(self, before, after, params, tx): # Controller rewards should earn if ( - before.get("strategy.withdrawalFee") > 0 and - # Fees are only processed when withdrawing from the strategy. - before.balances("want", "strategy") > after.balances("want", "strategy") + ( + before.get("strategy.withdrawalFee") > 0 and + # Strategy fees are only processed when withdrawing from the strategy. + before.balances("want", "strategy") > after.balances("want", "strategy") + ) or ( + before.get("sett.withdrawalFee") > 0 and + # Sett fees are always processed. + before.balances("want", "sett") > after.balances("want", "sett") + ) ): - assert after.balances("want", "governanceRewards") > before.balances( + # Fees can be split between sett and strategy withdrawals. + rewardsDiff = after.balances("want", "governanceRewards") - before.balances( "want", "governanceRewards" ) + userDiff = after.balances("want", "user") - before.balances("want", "user") + strategyDiff = before.balances("want", "strategy") - after.balances("want", "strategy") + settDiff = before.balances("want", "sett") - after.balances("want", "sett") + # Want diff in user/rewards should be equal to diff in strategy/sett + assert approx( + rewardsDiff + userDiff, + strategyDiff + settDiff, + 1, + ) def confirm_deposit(self, before, after, params): """ diff --git a/helpers/sett/simulation/actors/UserActor.py b/helpers/sett/simulation/actors/UserActor.py index 79e89002..66362083 100644 --- a/helpers/sett/simulation/actors/UserActor.py +++ b/helpers/sett/simulation/actors/UserActor.py @@ -1,7 +1,8 @@ import random -from brownie import Sett +from brownie import Sett, Controller from typing import Any +from helpers.utils import approx from helpers.constants import MaxUint256 from helpers.sett.SnapshotManager import SnapshotManager from .BaseAction import BaseAction @@ -24,6 +25,7 @@ def run(self): user = self.user want = self.want sett = self.sett + rewards = Controller.at(sett.controller()).rewards() beforeSettBalance = sett.balanceOf(user) startingBalance = want.balanceOf(user) @@ -46,12 +48,17 @@ def run(self): afterSettBalance = sett.balanceOf(user) settDeposited = afterSettBalance - beforeSettBalance + beforeRewardsBalance = want.balanceOf(rewards) # Confirm that before and after balance does not exceed # max precision loss. self.snap.settWithdraw(settDeposited, {"from": self.user}) - + rewardsDiff = want.balanceOf(rewards) - beforeRewardsBalance endingBalance = want.balanceOf(user) - assert startingBalance - endingBalance <= 2 + assert approx( + startingBalance, + endingBalance + rewardsDiff, + 1, + ) class DepositAction(BaseAction): diff --git a/interfaces/digg/IDigg.sol b/interfaces/digg/IDigg.sol index 429a551f..fe78da49 100644 --- a/interfaces/digg/IDigg.sol +++ b/interfaces/digg/IDigg.sol @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: MIT + pragma solidity >=0.5.0 <0.8.0; interface IDigg { @@ -41,6 +43,7 @@ interface IDigg { function sharesOf(address who) external view returns (uint256); function _sharesPerFragment() external view returns (uint256); + function _initialSharesPerFragment() external view returns (uint256); /** @@ -56,6 +59,7 @@ interface IDigg { function sharesToFragments(uint256 shares) external view returns (uint256); function scaledSharesToShares(uint256 fragments) external view returns (uint256); + function sharesToScaledShares(uint256 shares) external view returns (uint256); /** diff --git a/interfaces/keeper/ILiquidityPoolV2.sol b/interfaces/keeper/ILiquidityPoolV2.sol new file mode 100644 index 00000000..726df229 --- /dev/null +++ b/interfaces/keeper/ILiquidityPoolV2.sol @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.6.0; + +interface ILiquidityPoolV2 { + // ===== Write ===== + function deposit(address _token, uint256 _amount) external; +} diff --git a/scripts/deploy/upgrade.py b/scripts/deploy/upgrade.py new file mode 100644 index 00000000..8a8fa7ac --- /dev/null +++ b/scripts/deploy/upgrade.py @@ -0,0 +1,58 @@ +from brownie.network.contract import ProjectContract + +from scripts.systems.badger_system import BadgerSystem +from helpers.gnosis_safe import GnosisSafe, MultisigTxMetadata + + +# Upgrades versioned proxy contract if not latest version. +def upgrade_versioned_proxy( + badger: BadgerSystem, + # upgradeable proxy contract + proxy: ProjectContract, + # latest logic contract + logic: ProjectContract, +) -> None: + # Do nothing if the proxy and logic contracts have the same version + # or if the logic contract has an older version. + proxyVersion = _get_version(proxy) + logicVersion = _get_version(logic) + if ( + proxyVersion == logicVersion or + float(logicVersion) < float(proxyVersion) + ): + return + + multi = GnosisSafe(badger.devMultisig) + + multi.execute( + MultisigTxMetadata(description="Upgrade versioned proxy contract with new logic version",), + { + "to": badger.devProxyAdmin.address, + "data": badger.devProxyAdmin.upgrade.encode_input( + proxy, logic + ), + }, + ) + + +def _get_version(contract: ProjectContract) -> float: + # try all the methods in priority order + methods = [ + "version", + "baseStrategyVersion", + ] + + for method in methods: + version, ok = _try_get_version(contract, method) + if ok: + return version + + # NB: Prior to V1.1, Setts do not have a version function. + return 0.0 + + +def _try_get_version(contract: ProjectContract, method: str) -> (float, bool): + try: + return getattr(contract, method)(), True + except Exception: + return 0.0, False diff --git a/scripts/systems/badger_system.py b/scripts/systems/badger_system.py index ca450423..8d29fb21 100644 --- a/scripts/systems/badger_system.py +++ b/scripts/systems/badger_system.py @@ -8,19 +8,16 @@ badger_config, sett_config, ) -from scripts.systems.sett_system import ( - deploy_controller, - deploy_strategy, -) from helpers.registry import registry from helpers.time_utils import days -from helpers.sett.strategy_registry import name_to_artifact, strategy_name_to_artifact +from helpers.sett.strategy_registry import name_to_artifact, contract_name_to_artifact from helpers.proxy_utils import deploy_proxy, deploy_proxy_admin from helpers.gnosis_safe import GnosisSafe, MultisigTxMetadata -from helpers.sett.strategy_registry import name_to_artifact -from scripts.systems.constants import SettType -from scripts.systems.digg_system import connect_digg -from scripts.systems.constants import SettType +from scripts.systems.constants import ( + SettType, + SyncFeeType, + WITHDRAWAL_FEE, +) from scripts.systems.digg_system import DiggSystem, connect_digg from scripts.systems.claw_system import ClawSystem from scripts.systems.swap_system import SwapSystem @@ -387,8 +384,10 @@ def deploy_sett_strategy_logic(self): def deploy_sett_strategy_logic_for(self, name): deployer = self.deployer - artifact = strategy_name_to_artifact(name) - self.logic[name] = artifact.deploy({"from": deployer}) + artifact = contract_name_to_artifact(name) + self.logic[name] = artifact.deploy( + {"from": deployer} + ) # TODO: Initialize to remove that function @@ -602,7 +601,7 @@ def deploy_strategy( guardian, ) - Artifact = strategy_name_to_artifact(strategyName) + Artifact = contract_name_to_artifact(strategyName) self.sett_system.strategies[id] = strategy self.set_strategy_artifact(id, strategyName, Artifact) @@ -897,7 +896,7 @@ def connect_sett_system(self, sett_system, geysers): self.connect_geyser(key, address) def connect_strategy(self, id, address, strategyArtifactName): - Artifact = strategy_name_to_artifact(strategyArtifactName) + Artifact = contract_name_to_artifact(strategyArtifactName) strategy = Artifact.at(address) self.sett_system.strategies[id] = strategy self.set_strategy_artifact(id, strategyArtifactName, Artifact) @@ -941,7 +940,7 @@ def connect_community_pool(self, address): def connect_logic(self, logic): for name, address in logic.items(): - Artifact = strategy_name_to_artifact(name) + Artifact = contract_name_to_artifact(name) self.logic[name] = Artifact.at(address) def connect_dao_badger_timelock(self, address): @@ -1048,6 +1047,11 @@ def getSett(self, id): return self.sett_system.vaults[id] + def getSettArtifact(self, id): + if id == "native.digg": + return DiggSett + return Sett + def getSettRewards(self, id): return self.sett_system.rewards[id] @@ -1062,7 +1066,29 @@ def getStrategyWant(self, id): return interface.IERC20(self.sett_system.strategies[id].want()) def getStrategyArtifact(self, id): - return self.strategy_artifacts[id].artifact + return self.strategy_artifacts[id]["artifact"] def getStrategyArtifactName(self, id): return self.strategy_artifacts[id]["artifactName"] + + # ===== Configure ===== + + def syncWithdrawalFees(self, id, syncFeeType=SyncFeeType.CONFIG): + sett = self.getSett(id) + strategy = self.getStrategy(id) + # By default, we grab the fee from config. + fee = self._getWithdrawalFeesFromConfig(strategy._name) + if syncFeeType == SyncFeeType.ZERO: + fee = 0 + if syncFeeType == SyncFeeType.CONSTANT: + fee = WITHDRAWAL_FEE + strategy.setWithdrawalFee(fee, {"from": strategy.governance()}) + sett.setWithdrawalFee(fee, {"from": sett.governance()}) + + def _getWithdrawalFeesFromConfig(self, strategyName): + for setts in sett_config.values(): + for opts in setts.values(): + if opts.strategyName == strategyName: + return opts.params.withdrawalFee + return 0 + diff --git a/scripts/systems/constants.py b/scripts/systems/constants.py index 62bc630b..4e181e21 100644 --- a/scripts/systems/constants.py +++ b/scripts/systems/constants.py @@ -5,3 +5,18 @@ class SettType(Enum): DEFAULT = 1 DIGG = 2 + + +# Constant withdrawal fee (in bps) for testing. +WITHDRAWAL_FEE = 75 + + +# Types of sync fee strategies. +class SyncFeeType(Enum): + # CONFIG fees are synced from config. + CONFIG = 1 + # ZERO fees are applied to all setts/strategies. + ZERO = 2 + # CONSTANT fees are hard coded and applied to all setts/strategies. + CONSTANT = 2 + diff --git a/scripts/systems/digg_system.py b/scripts/systems/digg_system.py index a22d4057..66aff79e 100644 --- a/scripts/systems/digg_system.py +++ b/scripts/systems/digg_system.py @@ -1,4 +1,4 @@ -from helpers.sett.strategy_registry import strategy_name_to_artifact +from helpers.contract_registry import contract_name_to_artifact import json from brownie import * from dotmap import DotMap @@ -197,7 +197,7 @@ def connect_logic(self, logic): for name, address in logic.items(): if env_config.debug: print("ConnectLogic:", name, address) - Artifact = strategy_name_to_artifact(name) + Artifact = contract_name_to_artifact(name) self.logic[name] = Artifact.at(address) # ===== Deployers ===== diff --git a/tests/conftest.py b/tests/conftest.py index a1241122..15367d54 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -91,7 +91,7 @@ def isolate(fn_isolation): # @pytest.fixture() -def badger_single_sett(settConfig, deploy=True): +def badger_single_sett(settConfig, deploy=True, upgrade=False): if deploy: deployer = accounts[0] guardian = accounts[1] @@ -120,7 +120,7 @@ def badger_single_sett(settConfig, deploy=True): guardian=guardian, keeper=keeper, governance=governance, - ).deploy(deploy=deploy) + ).deploy(deploy=deploy, upgrade=upgrade) if settId == "native.renCrv": return CurveGaugeRenBtcMiniDeploy( "native.renCrv", @@ -130,7 +130,7 @@ def badger_single_sett(settConfig, deploy=True): guardian=guardian, keeper=keeper, governance=governance, - ).deploy(deploy=deploy) + ).deploy(deploy=deploy, upgrade=upgrade) if settId == "native.sbtcCrv": return CurveGaugeSBtcMiniDeploy( "native.sbtcCrv", @@ -140,7 +140,7 @@ def badger_single_sett(settConfig, deploy=True): guardian=guardian, keeper=keeper, governance=governance, - ).deploy(deploy=deploy) + ).deploy(deploy=deploy, upgrade=upgrade) if settId == "native.tbtcCrv": return CurveGaugeTBtcMiniDeploy( "native.tbtcCrv", @@ -150,7 +150,7 @@ def badger_single_sett(settConfig, deploy=True): guardian=guardian, keeper=keeper, governance=governance, - ).deploy(deploy=deploy) + ).deploy(deploy=deploy, upgrade=upgrade) if settId == "native.uniBadgerWbtc": return BadgerLpMetaFarmMiniDeploy( "native.uniBadgerWbtc", @@ -160,7 +160,7 @@ def badger_single_sett(settConfig, deploy=True): guardian=guardian, keeper=keeper, governance=governance, - ).deploy(deploy=deploy) + ).deploy(deploy=deploy, upgrade=upgrade) if settId == "harvest.renCrv": return HarvestMetaFarmMiniDeploy( "harvest.renCrv", @@ -170,8 +170,8 @@ def badger_single_sett(settConfig, deploy=True): guardian=guardian, keeper=keeper, governance=governance, - ).deploy(deploy=deploy) - if settId == "native.sushiBadgerWbtc": + ).deploy(deploy=deploy, upgrade=upgrade) + if settId == "sushi.sushiBadgerWbtc": return SushiBadgerWBtcMiniDeploy( "native.sushiBadgerWbtc", "StrategySushiBadgerWbtc", @@ -180,7 +180,7 @@ def badger_single_sett(settConfig, deploy=True): guardian=guardian, keeper=keeper, governance=governance, - ).deploy(deploy=deploy) + ).deploy(deploy=deploy, upgrade=upgrade) if settId == "native.sushiWbtcEth": return SushiBadgerLpOptimizerMiniDeploy( "native.sushiWbtcEth", @@ -190,7 +190,7 @@ def badger_single_sett(settConfig, deploy=True): guardian=guardian, keeper=keeper, governance=governance, - ).deploy(deploy=deploy) + ).deploy(deploy=deploy, upgrade=upgrade) if settId == "native.digg": return DiggRewardsMiniDeploy( "native.digg", @@ -200,7 +200,7 @@ def badger_single_sett(settConfig, deploy=True): guardian=guardian, keeper=keeper, governance=governance, - ).deploy(sett_type=SettType.DIGG, deploy=deploy) + ).deploy(sett_type=SettType.DIGG, deploy=deploy, upgrade=upgrade) if settId == "native.uniDiggWbtc": return UniDiggWbtcLpMiniDeploy( "native.uniDiggWbtc", @@ -210,7 +210,7 @@ def badger_single_sett(settConfig, deploy=True): guardian=guardian, keeper=keeper, governance=governance, - ).deploy(deploy=deploy) + ).deploy(deploy=deploy, upgrade=upgrade) if settId == "native.sushiDiggWbtc": return SushiDiggWbtcLpOptimizerMiniDeploy( "native.sushiDiggWbtc", @@ -220,7 +220,7 @@ def badger_single_sett(settConfig, deploy=True): guardian=guardian, keeper=keeper, governance=governance, - ).deploy(deploy=deploy) + ).deploy(deploy=deploy, upgrade=upgrade) if settId == "native.sushiSClawUSDC": # Claw/USDC mini deploy can be used for any CLAW synthetic token. return SushiClawUSDCMiniDeploy( @@ -232,7 +232,7 @@ def badger_single_sett(settConfig, deploy=True): guardian=guardian, keeper=keeper, governance=governance, - ).deploy(deploy=deploy) + ).deploy(deploy=deploy, upgrade=upgrade) if settId == "native.sushiBClawUSDC": # Claw/USDC mini deploy can be used for any CLAW synthetic token. return SushiClawUSDCMiniDeploy( @@ -244,7 +244,7 @@ def badger_single_sett(settConfig, deploy=True): guardian=guardian, keeper=keeper, governance=governance, - ).deploy(deploy=deploy) + ).deploy(deploy=deploy, upgrade=upgrade) if settConfig['mode'] == 'prod': """ Run vs prod contracts, transferring assets to the test user diff --git a/tests/sett/fixtures/SettMiniDeployBase.py b/tests/sett/fixtures/SettMiniDeployBase.py index 6eaee5c7..c81756c3 100644 --- a/tests/sett/fixtures/SettMiniDeployBase.py +++ b/tests/sett/fixtures/SettMiniDeployBase.py @@ -4,6 +4,7 @@ from scripts.systems.badger_system import BadgerSystem, connect_badger from scripts.systems.badger_minimal import deploy_badger_minimal from scripts.systems.constants import SettType +from scripts.deploy.upgrade import upgrade_versioned_proxy from config.badger_config import badger_config from rich.console import Console @@ -39,7 +40,7 @@ def __init__( self.guardian = guardian self.deployer = deployer - def deploy(self, sett_type=SettType.DEFAULT, deploy=True) -> BadgerSystem: + def deploy(self, sett_type=SettType.DEFAULT, deploy=True, upgrade=False) -> BadgerSystem: if not deploy: self.badger = connect_badger(badger_config.prod_json) @@ -64,6 +65,9 @@ def deploy(self, sett_type=SettType.DEFAULT, deploy=True) -> BadgerSystem: except exceptions.VirtualMachineError: pass + if upgrade: + self.upgrade_versioned() + return self.badger self.badger = deploy_badger_minimal(self.deployer, self.keeper, self.guardian) @@ -120,6 +124,38 @@ def deploy_required_logic(self): self.badger.deploy_sett_core_logic() self.badger.deploy_sett_strategy_logic_for(self.strategyName) + # Upgrade versioned contracts if not up to date. + # Currently the only versioned contracts are strategy/sett contracts. + # NB: This must be run AFTER connecting to the badger system. + def upgrade_versioned(self): + badger = self.badger + deployer = self.deployer + for key, contract in badger.contracts_upgradeable.items(): + if key.removesuffix(".strategy").removesuffix(".sett") != self.key: + continue + + if key.endswith(".strategy"): + Artifact = badger.getStrategyArtifact( + key.removesuffix(".strategy") + ) + latest = Artifact.deploy({"from": deployer}) + upgrade_versioned_proxy( + badger, + contract, + latest, + ) + + if key.endswith(".sett"): + Artifact = badger.getSettArtifact( + key.removesuffix(".sett") + ) + latest = Artifact.deploy({"from": deployer}) + upgrade_versioned_proxy( + badger, + contract, + latest, + ) + # ===== Specific instance must implement ===== def fetch_params(self): return False diff --git a/tests/sett/test_digg_simulation.py b/tests/sett/test_digg_simulation.py index 87651806..76a17cd7 100644 --- a/tests/sett/test_digg_simulation.py +++ b/tests/sett/test_digg_simulation.py @@ -3,19 +3,39 @@ from helpers.sett.DiggSnapshotManager import DiggSnapshotManager from helpers.sett.simulation.SimulationManager import SimulationManager from tests.conftest import badger_single_sett, diggSettTestConfig +from scripts.systems.constants import SyncFeeType -# @pytest.mark.skip() @pytest.mark.parametrize( "settConfig", diggSettTestConfig, ) -def test_digg_simulation(settConfig): +def test_digg_simulation_constant_fee(settConfig): # connect to prod deploy and run simulation - badger = badger_single_sett(settConfig, deploy=False) - snap = DiggSnapshotManager(badger, settConfig["id"]) - simulation = SimulationManager(badger, snap, settConfig["id"]) + settID = settConfig["id"] + badger = badger_single_sett(settConfig, deploy=False, upgrade=True) + badger.syncWithdrawalFees(settID, syncFeeType=SyncFeeType.CONSTANT) + snap = DiggSnapshotManager(badger, settID) + simulation = SimulationManager(badger, snap, settID, seed=1611893572) simulation.provision() - # Randomize 100 actions. - simulation.randomize(100) + # Randomize 30 actions. + simulation.randomize(30) + simulation.run() + + +@pytest.mark.skip() +# @pytest.mark.parametrize( +# "settConfig", diggSettTestConfig, +# ) +def test_digg_simulation_zero_fee(settConfig): + # connect to prod deploy and run simulation + settID = settConfig["id"] + badger = badger_single_sett(settConfig, deploy=False, upgrade=True) + badger.syncWithdrawalFees(settID, syncFeeType=SyncFeeType.ZERO) + snap = DiggSnapshotManager(badger, settID) + simulation = SimulationManager(badger, snap, settID) + + simulation.provision() + # Randomize 30 actions. + simulation.randomize(30) simulation.run() diff --git a/tests/sett/test_simulation.py b/tests/sett/test_simulation.py index a26ba690..e809c033 100644 --- a/tests/sett/test_simulation.py +++ b/tests/sett/test_simulation.py @@ -3,6 +3,7 @@ from helpers.sett.SnapshotManager import SnapshotManager from helpers.sett.simulation.SimulationManager import SimulationManager from tests.conftest import badger_single_sett, settTestConfig +from scripts.systems.constants import SyncFeeType # @pytest.mark.skip() @@ -11,9 +12,11 @@ ) def test_simulation(settConfig): # connect to prod deploy and run simulation - badger = badger_single_sett(settConfig, deploy=False) - snap = SnapshotManager(badger, settConfig["id"]) - simulation = SimulationManager(badger, snap, settConfig["id"]) + settID = settConfig["id"] + badger = badger_single_sett(settConfig, deploy=False, upgrade=True) + badger.syncWithdrawalFees(settID, syncFeeType=SyncFeeType.CONSTANT) + snap = SnapshotManager(badger, settID) + simulation = SimulationManager(badger, snap, settID) simulation.provision() # Randomize 30 actions.