From 5b16ede29b7bde80a69e4e7b553a9b79af0ae225 Mon Sep 17 00:00:00 2001 From: aazhou1 Date: Fri, 25 Oct 2024 23:35:42 -0700 Subject: [PATCH] add governor role for access to setters for term strategy parameters --- .../workflows/deploy-sepolia-strategy.yaml | 1 + script/Strategy.s.sol | 4 +- src/Strategy.sol | 35 ++++++++------ src/test/TestUSDCIntegration.t.sol | 22 ++++----- src/test/TestUSDCOffers.t.sol | 10 ++-- src/test/TestUSDCSellRepoToken.t.sol | 48 +++++++++---------- src/test/TestUSDCSubmitOffer.t.sol | 4 +- src/test/utils/ExtendedTest.sol | 10 ++++ src/test/utils/Setup.sol | 7 ++- 9 files changed, 84 insertions(+), 57 deletions(-) diff --git a/.github/workflows/deploy-sepolia-strategy.yaml b/.github/workflows/deploy-sepolia-strategy.yaml index 46d83e77..267d8d12 100644 --- a/.github/workflows/deploy-sepolia-strategy.yaml +++ b/.github/workflows/deploy-sepolia-strategy.yaml @@ -64,4 +64,5 @@ jobs: REPOTOKEN_CONCENTRATION_LIMIT: ${{ github.event.inputs.repoTokenConcentrationLimit }} ADMIN_ADDRESS: ${{ vars.ADMIN_ADDRESS }} DEVOPS_ADDRESS: ${{ vars.DEVOPS_ADDRESS }} + GOVERNOR_ROLE_ADDRESS: ${{ vars.GOVERNOR_ROLE_ADDRESS }} \ No newline at end of file diff --git a/script/Strategy.s.sol b/script/Strategy.s.sol index 11b8c458..d64e52ab 100644 --- a/script/Strategy.s.sol +++ b/script/Strategy.s.sol @@ -22,6 +22,7 @@ contract DeployStrategy is Script { address discountRateAdapterAddress = vm.envAddress("DISCOUNT_RATE_ADAPTER_ADDRESS"); address admin = vm.envAddress("ADMIN_ADDRESS"); address devops = vm.envAddress("DEVOPS_ADDRESS"); + address governorRoleAddress = vm.envAddress("GOVERNOR_ROLE_ADDRESS"); address termController = vm.envOr("TERM_CONTROLLER_ADDRESS", address(0)); uint256 discountRateMarkup = vm.envOr("DISCOUNT_RATE_MARKUP", uint256(0)); address collateralTokenAddr = vm.envOr("COLLATERAL_TOKEN_ADDR", address(0)); @@ -36,7 +37,8 @@ contract DeployStrategy is Script { name, yearnVaultAddress, discountRateAdapterAddress, - address(eventEmitter) + address(eventEmitter), + governorRoleAddress ); console.log("deployed strateghy contract to"); diff --git a/src/Strategy.sol b/src/Strategy.sol index 7bced9fe..97b5735d 100644 --- a/src/Strategy.sol +++ b/src/Strategy.sol @@ -16,6 +16,7 @@ import {ITermAuction} from "./interfaces/term/ITermAuction.sol"; import {RepoTokenList, RepoTokenListData} from "./RepoTokenList.sol"; import {TermAuctionList, TermAuctionListData, PendingOffer} from "./TermAuctionList.sol"; import {RepoTokenUtils} from "./RepoTokenUtils.sol"; +import {AccessControl} from "@openzeppelin/contracts/access/AccessControl.sol"; // Import interfaces for many popular DeFi projects, or add your own! //import "../interfaces//.sol"; @@ -33,7 +34,7 @@ import {RepoTokenUtils} from "./RepoTokenUtils.sol"; // NOTE: To implement permissioned functions you can use the onlyManagement, onlyEmergencyAuthorized and onlyKeepers modifiers -contract Strategy is BaseStrategy, Pausable, ReentrancyGuard { +contract Strategy is BaseStrategy, Pausable, AccessControl, ReentrancyGuard { using SafeERC20 for IERC20; using RepoTokenList for RepoTokenListData; using TermAuctionList for TermAuctionListData; @@ -47,6 +48,8 @@ contract Strategy is BaseStrategy, Pausable, ReentrancyGuard { error RepoTokenBlacklisted(address repoToken); error DepositPaused(); + bytes32 public constant GOVERNOR_ROLE = keccak256("GOVERNOR_ROLE"); + // Immutable state variables ITermVaultEvents public immutable TERM_VAULT_EVENT_EMITTER; uint256 public immutable PURCHASE_TOKEN_PRECISION; @@ -81,7 +84,7 @@ contract Strategy is BaseStrategy, Pausable, ReentrancyGuard { /** * @notice Pause the contract */ - function pauseDeposit() external onlyManagement { + function pauseDeposit() external onlyRole(GOVERNOR_ROLE) { depositLock = true; TERM_VAULT_EVENT_EMITTER.emitDepositPaused(); } @@ -89,7 +92,7 @@ contract Strategy is BaseStrategy, Pausable, ReentrancyGuard { /** * @notice Unpause the contract */ - function unpauseDeposit() external onlyManagement { + function unpauseDeposit() external onlyRole(GOVERNOR_ROLE) { depositLock = false; TERM_VAULT_EVENT_EMITTER.emitDepositUnpaused(); } @@ -97,7 +100,7 @@ contract Strategy is BaseStrategy, Pausable, ReentrancyGuard { /** * @notice Pause the contract */ - function pauseStrategy() external onlyManagement { + function pauseStrategy() external onlyRole(GOVERNOR_ROLE) { _pause(); depositLock = true; TERM_VAULT_EVENT_EMITTER.emitStrategyPaused(); @@ -106,7 +109,7 @@ contract Strategy is BaseStrategy, Pausable, ReentrancyGuard { /** * @notice Unpause the contract */ - function unpauseStrategy() external onlyManagement { + function unpauseStrategy() external onlyRole(GOVERNOR_ROLE) { _unpause(); depositLock = false; TERM_VAULT_EVENT_EMITTER.emitStrategyUnpaused(); @@ -118,7 +121,7 @@ contract Strategy is BaseStrategy, Pausable, ReentrancyGuard { */ function setTermController( address newTermController - ) external onlyManagement { + ) external onlyRole(GOVERNOR_ROLE) { require(newTermController != address(0)); require(ITermController(newTermController).getProtocolReserveAddress() != address(0)); address current = address(currTermController); @@ -136,7 +139,7 @@ contract Strategy is BaseStrategy, Pausable, ReentrancyGuard { */ function setDiscountRateAdapter( address newAdapter - ) external onlyManagement { + ) external onlyRole(GOVERNOR_ROLE) { ITermDiscountRateAdapter newDiscountRateAdapter = ITermDiscountRateAdapter(newAdapter); require(address(newDiscountRateAdapter.TERM_CONTROLLER()) != address(0)); TERM_VAULT_EVENT_EMITTER.emitDiscountRateAdapterUpdated( @@ -152,7 +155,7 @@ contract Strategy is BaseStrategy, Pausable, ReentrancyGuard { */ function setTimeToMaturityThreshold( uint256 newTimeToMaturityThreshold - ) external onlyManagement { + ) external onlyRole(GOVERNOR_ROLE) { TERM_VAULT_EVENT_EMITTER.emitTimeToMaturityThresholdUpdated( timeToMaturityThreshold, newTimeToMaturityThreshold @@ -167,7 +170,7 @@ contract Strategy is BaseStrategy, Pausable, ReentrancyGuard { */ function setRequiredReserveRatio( uint256 newRequiredReserveRatio - ) external onlyManagement { + ) external onlyRole(GOVERNOR_ROLE) { TERM_VAULT_EVENT_EMITTER.emitRequiredReserveRatioUpdated( requiredReserveRatio, newRequiredReserveRatio @@ -181,7 +184,7 @@ contract Strategy is BaseStrategy, Pausable, ReentrancyGuard { */ function setRepoTokenConcentrationLimit( uint256 newRepoTokenConcentrationLimit - ) external onlyManagement { + ) external onlyRole(GOVERNOR_ROLE) { TERM_VAULT_EVENT_EMITTER.emitRepoTokenConcentrationLimitUpdated( repoTokenConcentrationLimit, newRepoTokenConcentrationLimit @@ -195,7 +198,7 @@ contract Strategy is BaseStrategy, Pausable, ReentrancyGuard { */ function setDiscountRateMarkup( uint256 newDiscountRateMarkup - ) external onlyManagement { + ) external onlyRole(GOVERNOR_ROLE) { TERM_VAULT_EVENT_EMITTER.emitDiscountRateMarkupUpdated( discountRateMarkup, newDiscountRateMarkup @@ -210,7 +213,7 @@ contract Strategy is BaseStrategy, Pausable, ReentrancyGuard { function setCollateralTokenParams( address tokenAddr, uint256 minCollateralRatio - ) external onlyManagement { + ) external onlyRole(GOVERNOR_ROLE) { TERM_VAULT_EVENT_EMITTER.emitMinCollateralRatioUpdated( tokenAddr, minCollateralRatio @@ -218,7 +221,7 @@ contract Strategy is BaseStrategy, Pausable, ReentrancyGuard { repoTokenListData.collateralTokenParams[tokenAddr] = minCollateralRatio; } - function setRepoTokenBlacklist(address repoToken, bool blacklisted) external onlyManagement { + function setRepoTokenBlacklist(address repoToken, bool blacklisted) external onlyRole(GOVERNOR_ROLE) { TERM_VAULT_EVENT_EMITTER.emitRepoTokenBlacklistUpdated(repoToken, blacklisted); repoTokenBlacklist[repoToken] = blacklisted; } @@ -1091,13 +1094,15 @@ contract Strategy is BaseStrategy, Pausable, ReentrancyGuard { * @param _yearnVault The address of the Yearn vault * @param _discountRateAdapter The address of the discount rate adapter * @param _eventEmitter The address of the event emitter + * @param _governorAddress The address of the governor */ constructor( address _asset, string memory _name, address _yearnVault, address _discountRateAdapter, - address _eventEmitter + address _eventEmitter, + address _governorAddress ) BaseStrategy(_asset, _name) { YEARN_VAULT = IERC4626(_yearnVault); TERM_VAULT_EVENT_EMITTER = ITermVaultEvents(_eventEmitter); @@ -1111,6 +1116,8 @@ contract Strategy is BaseStrategy, Pausable, ReentrancyGuard { requiredReserveRatio = 0.2e18; discountRateMarkup = 0.005e18; repoTokenConcentrationLimit = 0.1e18; + + _grantRole(GOVERNOR_ROLE, _governorAddress); } /*////////////////////////////////////////////////////////////// diff --git a/src/test/TestUSDCIntegration.t.sol b/src/test/TestUSDCIntegration.t.sol index 7c167f8d..4bfa2bdf 100644 --- a/src/test/TestUSDCIntegration.t.sol +++ b/src/test/TestUSDCIntegration.t.sol @@ -61,7 +61,7 @@ contract TestUSDCIntegration is Setup { repoToken1MonthAuction = new MockTermAuction(repoToken1Month); repoToken1YearAuction = new MockTermAuction(repoToken1Year); - vm.startPrank(management); + vm.startPrank(governor); termStrategy.setCollateralTokenParams(address(mockCollateral), 0.5e18); termStrategy.setTimeToMaturityThreshold(3 weeks); termStrategy.setRepoTokenConcentrationLimit(1e18); @@ -248,7 +248,7 @@ contract TestUSDCIntegration is Setup { termController.setOracleRate(repoToken1Month.termRepoId(), 0.05e6); - vm.startPrank(management); + vm.startPrank(governor); termStrategy.setCollateralTokenParams(address(mockCollateral), 0.5e18); termStrategy.setTimeToMaturityThreshold(3 weeks); vm.stopPrank(); @@ -303,11 +303,11 @@ contract TestUSDCIntegration is Setup { function testRepoTokenBlacklist() public { address testUser = vm.addr(0x11111); vm.prank(testUser); - vm.expectRevert("!management"); + vm.expectRevert(); termStrategy.setRepoTokenBlacklist(address(repoToken1Week), true); vm.stopPrank(); - vm.prank(management); + vm.prank(governor); termStrategy.setRepoTokenBlacklist(address(repoToken1Week), true); vm.stopPrank(); @@ -320,13 +320,13 @@ contract TestUSDCIntegration is Setup { address testUser = vm.addr(0x11111); mockUSDC.mint(testUser, 1e18); vm.prank(testUser); - vm.expectRevert("!management"); + vm.expectRevert(); termStrategy.pauseDeposit(); - vm.expectRevert("!management"); + vm.expectRevert(); termStrategy.unpauseDeposit(); vm.stopPrank(); - vm.prank(management); + vm.prank(governor); termStrategy.pauseDeposit(); vm.stopPrank(); @@ -338,7 +338,7 @@ contract TestUSDCIntegration is Setup { IERC4626(address(termStrategy)).deposit(1e6, testUser); vm.stopPrank(); - vm.prank(management); + vm.prank(governor); termStrategy.unpauseDeposit(); vm.stopPrank(); @@ -354,14 +354,14 @@ contract TestUSDCIntegration is Setup { TermDiscountRateAdapter valid = new TermDiscountRateAdapter(address(termController), adminWallet); vm.prank(testUser); - vm.expectRevert("!management"); + vm.expectRevert(); termStrategy.setDiscountRateAdapter(address(valid)); - vm.prank(management); + vm.prank(governor); vm.expectRevert(); termStrategy.setDiscountRateAdapter(address(invalid)); - vm.prank(management); + vm.prank(governor); termStrategy.setDiscountRateAdapter(address(valid)); vm.stopPrank(); diff --git a/src/test/TestUSDCOffers.t.sol b/src/test/TestUSDCOffers.t.sol index 8fe2e775..e717d2a3 100644 --- a/src/test/TestUSDCOffers.t.sol +++ b/src/test/TestUSDCOffers.t.sol @@ -40,7 +40,7 @@ contract TestUSDCSubmitOffer is Setup { repoToken1WeekAuction = new MockTermAuction(repoToken1Week); repoToken100WeekAuction = new MockTermAuction(repoToken100Week); - vm.startPrank(management); + vm.startPrank(governor); termStrategy.setCollateralTokenParams(address(mockCollateral), 0.5e18); termStrategy.setTimeToMaturityThreshold(3 weeks); termStrategy.setRepoTokenConcentrationLimit(1e18); @@ -88,9 +88,11 @@ contract TestUSDCSubmitOffer is Setup { } function testSubmitOfferBelowLiquidReserveRatio() public { - vm.startPrank(management); + vm.startPrank(governor); termStrategy.setRequiredReserveRatio(0.5e18); + vm.stopPrank(); + vm.startPrank(management); vm.expectRevert(abi.encodeWithSelector(Strategy.BalanceBelowRequiredReserveRatio.selector)); termStrategy.submitAuctionOffer( repoToken1WeekAuction, address(repoToken1Week), bytes32("offer id hash 1"), bytes32("test price"), 51e6 @@ -132,9 +134,11 @@ contract TestUSDCSubmitOffer is Setup { } function testSubmitOfferFailsIfMinCollatRatioisZero() public { - vm.startPrank(management); + vm.startPrank(governor); termStrategy.setCollateralTokenParams(address(mockCollateral), 0); + vm.stopPrank(); + vm.startPrank(management); vm.expectRevert(abi.encodeWithSelector(RepoTokenList.InvalidRepoToken.selector, address(repoToken1Week))); termStrategy.submitAuctionOffer( repoToken1WeekAuction, address(repoToken1Week), bytes32("offer id hash 1"), bytes32("test price"), 1e6 diff --git a/src/test/TestUSDCSellRepoToken.t.sol b/src/test/TestUSDCSellRepoToken.t.sol index 44576d16..f6017d11 100644 --- a/src/test/TestUSDCSellRepoToken.t.sol +++ b/src/test/TestUSDCSellRepoToken.t.sol @@ -45,7 +45,7 @@ contract TestUSDCSellRepoToken is Setup { termStrategy = Strategy(address(strategy)); - vm.startPrank(management); + vm.startPrank(governor); termStrategy.setCollateralTokenParams(address(mockCollateral), 0.5e18); termStrategy.setTimeToMaturityThreshold(10 weeks); termStrategy.setRepoTokenConcentrationLimit(1e18); @@ -77,7 +77,7 @@ contract TestUSDCSellRepoToken is Setup { termController.setOracleRate(repoToken1Week.termRepoId(), 0.05e18); - vm.startPrank(management); + vm.startPrank(governor); termStrategy.setCollateralTokenParams(address(mockCollateral), 0.5e18); termStrategy.setTimeToMaturityThreshold(3 weeks); vm.stopPrank(); @@ -129,7 +129,7 @@ contract TestUSDCSellRepoToken is Setup { termController.setOracleRate(repoToken1Week.termRepoId(), 0.05e18); termController.markNotTermDeployed(address(repoToken1Week)); - vm.startPrank(management); + vm.startPrank(governor); termStrategy.setCollateralTokenParams(address(mockCollateral), 0.5e18); termStrategy.setTimeToMaturityThreshold(3 weeks); vm.stopPrank(); @@ -155,7 +155,7 @@ contract TestUSDCSellRepoToken is Setup { termController.setOracleRate(repoToken1Week.termRepoId(), 0.00001e18); - vm.startPrank(management); + vm.startPrank(governor); termStrategy.setCollateralTokenParams(address(mockCollateral), 0.5e18); termStrategy.setTimeToMaturityThreshold(3 weeks); vm.stopPrank(); @@ -181,7 +181,7 @@ contract TestUSDCSellRepoToken is Setup { termController.setOracleRate(repoToken1Week.termRepoId(), 0.05e18); - vm.startPrank(management); + vm.startPrank(governor); termStrategy.setRequiredReserveRatio(0.5e18); termStrategy.setCollateralTokenParams(address(mockCollateral), 0.5e18); termStrategy.setTimeToMaturityThreshold(3 weeks); @@ -406,44 +406,44 @@ contract TestUSDCSellRepoToken is Setup { function testSetGovernanceParameters() public { MockTermController newController = new MockTermController(); - vm.expectRevert("!management"); + vm.expectRevert(); termStrategy.setTermController(address(newController)); vm.expectRevert(); - vm.prank(management); + vm.prank(governor); termStrategy.setTermController(address(0)); address currentController = address(termStrategy.currTermController()); - vm.prank(management); + vm.prank(governor); termStrategy.setTermController(address(newController)); assertEq(address(termStrategy.currTermController()), address(newController)); assertEq(address(termStrategy.prevTermController()), currentController); - vm.expectRevert("!management"); + vm.expectRevert(); termStrategy.setTimeToMaturityThreshold(12345); - vm.prank(management); + vm.prank(governor); termStrategy.setTimeToMaturityThreshold(12345); assertEq(termStrategy.timeToMaturityThreshold(), 12345); - vm.expectRevert("!management"); + vm.expectRevert(); termStrategy.setRequiredReserveRatio(12345); - vm.prank(management); + vm.prank(governor); termStrategy.setRequiredReserveRatio(12345); assertEq(termStrategy.requiredReserveRatio(), 12345); - vm.expectRevert("!management"); + vm.expectRevert(); termStrategy.setDiscountRateMarkup(12345); - vm.prank(management); + vm.prank(governor); termStrategy.setDiscountRateMarkup(12345); assertEq(termStrategy.discountRateMarkup(), 12345); - vm.expectRevert("!management"); + vm.expectRevert(); termStrategy.setCollateralTokenParams(address(mockCollateral), 12345); - vm.prank(management); + vm.prank(governor); termStrategy.setCollateralTokenParams(address(mockCollateral), 12345); assertEq(termStrategy.discountRateMarkup(), 12345); } @@ -466,7 +466,7 @@ contract TestUSDCSellRepoToken is Setup { termController.setOracleRate(repoToken1Week.termRepoId(), 0.05e18); termController.setOracleRate(repoTokenMatured.termRepoId(), 0.05e18); - vm.prank(management); + vm.prank(governor); termStrategy.setCollateralTokenParams(address(mockCollateral), 0); // test: min collateral ratio not set @@ -474,7 +474,7 @@ contract TestUSDCSellRepoToken is Setup { vm.prank(testUser); termStrategy.sellRepoToken(address(repoToken1Week), 1e18); - vm.startPrank(management); + vm.startPrank(governor); termStrategy.setCollateralTokenParams(address(mockCollateral), 0.5e18); termStrategy.setTimeToMaturityThreshold(3 weeks); vm.stopPrank(); @@ -490,7 +490,7 @@ contract TestUSDCSellRepoToken is Setup { (uint256 timeToMat, ,) = termStrategy.simulateTransaction(address(0), 0); - vm.prank(management); + vm.prank(governor); termStrategy.setTimeToMaturityThreshold(timeToMat); // test: can't sell 4 week repo token because of time to maturity threshold @@ -627,11 +627,11 @@ contract TestUSDCSellRepoToken is Setup { IERC4626(address(termStrategy)).deposit(depositAmount, testDepositor); vm.stopPrank(); - vm.expectRevert("!management"); + vm.expectRevert(); termStrategy.setRepoTokenConcentrationLimit(0.4e18); // Set to 40% - vm.prank(management); + vm.prank(governor); termStrategy.setRepoTokenConcentrationLimit(0.4e18); termController.setOracleRate(repoToken2Week.termRepoId(), 0.05e18); @@ -655,10 +655,10 @@ contract TestUSDCSellRepoToken is Setup { mockUSDC.mint(testDepositor, depositAmount); - vm.expectRevert("!management"); + vm.expectRevert(); termStrategy.pauseStrategy(); - vm.prank(management); + vm.prank(governor); termStrategy.pauseStrategy(); vm.startPrank(testDepositor); @@ -673,7 +673,7 @@ contract TestUSDCSellRepoToken is Setup { "Pausable: paused" ); - vm.prank(management); + vm.prank(governor); termStrategy.unpauseStrategy(); vm.prank(testDepositor); IERC4626(address(termStrategy)).deposit(depositAmount, testDepositor); diff --git a/src/test/TestUSDCSubmitOffer.t.sol b/src/test/TestUSDCSubmitOffer.t.sol index 2ce1da0b..64733de1 100644 --- a/src/test/TestUSDCSubmitOffer.t.sol +++ b/src/test/TestUSDCSubmitOffer.t.sol @@ -33,7 +33,7 @@ contract TestUSDCSubmitOffer is Setup { repoToken1WeekAuction = new MockTermAuction(repoToken1Week); - vm.startPrank(management); + vm.startPrank(governor); termStrategy.setCollateralTokenParams(address(mockCollateral), 0.5e18); termStrategy.setTimeToMaturityThreshold(3 weeks); termStrategy.setRepoTokenConcentrationLimit(1e18); @@ -93,7 +93,7 @@ contract TestUSDCSubmitOffer is Setup { function testEditOfferWithConcentrationLimit() public { bytes32 idHash1 = bytes32("offer id hash 1"); - vm.prank(management); + vm.prank(governor); termStrategy.setRepoTokenConcentrationLimit(0.5e18); // 50% concentration diff --git a/src/test/utils/ExtendedTest.sol b/src/test/utils/ExtendedTest.sol index 4dfd4f1f..e840e4b9 100644 --- a/src/test/utils/ExtendedTest.sol +++ b/src/test/utils/ExtendedTest.sol @@ -83,4 +83,14 @@ contract ExtendedTest is Test { } } } + + function expectRoleRevert(bytes32 role) internal { + string memory revertMessage = string(abi.encodePacked( + "AccessControl: account ", + address(this), + " is missing role ", + role + )); + vm.expectRevert(bytes(revertMessage)); + } } diff --git a/src/test/utils/Setup.sol b/src/test/utils/Setup.sol index d2ad37c6..88bd61d6 100644 --- a/src/test/utils/Setup.sol +++ b/src/test/utils/Setup.sol @@ -41,6 +41,7 @@ contract Setup is ExtendedTest, IEvents { address public user = address(10); address public keeper = address(4); address public management = address(1); + address public governor = address(2); address public performanceFeeRecipient = address(3); address public adminWallet = address(111); address public devopsWallet = address(222); @@ -104,6 +105,7 @@ contract Setup is ExtendedTest, IEvents { vm.label(address(mockFactory), "mockFactory"); vm.label(address(asset), "asset"); vm.label(management, "management"); + vm.label(governor, "governor"); vm.label(address(strategy), "strategy"); vm.label(performanceFeeRecipient, "performanceFeeRecipient"); } @@ -117,7 +119,8 @@ contract Setup is ExtendedTest, IEvents { "Tokenized Strategy", address(mockYearnVault), address(discountRateAdapter), - address(termVaultEventEmitter) + address(termVaultEventEmitter), + governor ) ) ); @@ -135,7 +138,7 @@ contract Setup is ExtendedTest, IEvents { vm.prank(management); _strategy.acceptManagement(); - vm.prank(management); + vm.prank(governor); _strategy.setTermController(address(termController)); return address(_strategy);