From c757b9cb0186b753057fcacad09868ca729adeff Mon Sep 17 00:00:00 2001 From: felix2feng Date: Fri, 31 May 2019 18:03:33 -0700 Subject: [PATCH 1/2] Add initialize function --- .../managers/ETHTwentyDayMACOManager.sol | 42 +++-- .../managers/ethTwentyDayMACOManager.spec.ts | 160 +++++++++++++----- 2 files changed, 150 insertions(+), 52 deletions(-) diff --git a/contracts/managers/ETHTwentyDayMACOManager.sol b/contracts/managers/ETHTwentyDayMACOManager.sol index 0bd5ca6..4f1526b 100644 --- a/contracts/managers/ETHTwentyDayMACOManager.sol +++ b/contracts/managers/ETHTwentyDayMACOManager.sol @@ -58,6 +58,7 @@ contract ETHTwentyDayMACOManager { uint256 constant ETH_DECIMALS = 18; /* ============ State Variables ============ */ + address public rebalancingSetTokenAddress; address public coreAddress; address public movingAveragePriceFeed; address public setTokenFactory; @@ -124,25 +125,43 @@ contract ETHTwentyDayMACOManager { riskOn = _riskOn; } - /* ============ External ============ */ - /* - * When allowed on RebalancingSetToken, anyone can call for a new rebalance proposal. The Sets off a six - * hour period where the signal con be confirmed before moving ahead with rebalance. + * This function sets the Rebalancing Set Token address that the manager is associated with. + * Since, the rebalancing set token must first specify the address of the manager before deployment, + * we cannot know what the rebalancing set token is in advance. This function is only meant to be called + * once during initialization. * - * @param _rebalancingSetTokenAddress The address of Rebalancing Set Token to propose new allocation + * @param _rebalancingSetTokenAddress The address of the rebalancing Set token */ - function initialPropose( + function initialize( address _rebalancingSetTokenAddress ) external { + require( + rebalancingSetTokenAddress == address(0), + "ETHTwentyDayMACOManager.initialize: Rebalancing SetToken Address must be empty" + ); + // Make sure the rebalancingSetToken is tracked by Core require( ICore(coreAddress).validSets(_rebalancingSetTokenAddress), - "ETHTwentyDayMACOManager.initialPropose: Invalid or disabled SetToken address" + "ETHTwentyDayMACOManager.initialize: Invalid or disabled SetToken address" ); + rebalancingSetTokenAddress = _rebalancingSetTokenAddress; + } + + /* ============ External ============ */ + + /* + * When allowed on RebalancingSetToken, anyone can call for a new rebalance proposal. The Sets off a six + * hour period where the signal con be confirmed before moving ahead with rebalance. + * + */ + function initialPropose() + external + { // Make sure propose in manager hasn't already been initiated require( block.timestamp > proposalTimestamp.add(TWELVE_HOURS_IN_SECONDS), @@ -150,7 +169,7 @@ contract ETHTwentyDayMACOManager { ); // Create interface to interact with RebalancingSetToken and check enough time has passed for proposal - FlexibleTimingManagerLibrary.validateManagerPropose(IRebalancingSetToken(_rebalancingSetTokenAddress)); + FlexibleTimingManagerLibrary.validateManagerPropose(IRebalancingSetToken(rebalancingSetTokenAddress)); // Get price data from oracles ( @@ -167,11 +186,8 @@ contract ETHTwentyDayMACOManager { /* * After initial propose is called, confirm the signal has been met and determine parameters for the rebalance * - * @param _rebalancingSetTokenAddress The address of Rebalancing Set Token to propose new allocation */ - function confirmPropose( - address _rebalancingSetTokenAddress - ) + function confirmPropose() external { // Make sure enough time has passed to initiate proposal on Rebalancing Set Token @@ -214,7 +230,7 @@ contract ETHTwentyDayMACOManager { ); // Propose new allocation to Rebalancing Set Token - IRebalancingSetToken(_rebalancingSetTokenAddress).propose( + IRebalancingSetToken(rebalancingSetTokenAddress).propose( nextSetAddress, auctionLibrary, auctionTimeToPivot, diff --git a/test/contracts/managers/ethTwentyDayMACOManager.spec.ts b/test/contracts/managers/ethTwentyDayMACOManager.spec.ts index 8eaa88a..5a42079 100644 --- a/test/contracts/managers/ethTwentyDayMACOManager.spec.ts +++ b/test/contracts/managers/ethTwentyDayMACOManager.spec.ts @@ -265,8 +265,114 @@ contract('ETHTwentyDayMACOManager', accounts => { }); }); - describe('#initialPropose', async () => { + describe('#initialize', async () => { let subjectRebalancingSetToken: Address; + let subjectCaller: Address; + + let updatedValues: BigNumber[]; + let proposalPeriod: BigNumber; + let auctionTimeToPivot: BigNumber; + + before(async () => { + updatedValues = _.map(new Array(19), function(el, i) {return ether(150 + i); }); + }); + + beforeEach(async () => { + await oracleWrapper.batchUpdateDailyPriceFeedAsync( + dailyPriceFeed, + ethMedianizer, + 20, + updatedValues + ); + + auctionTimeToPivot = ONE_DAY_IN_SECONDS.div(4); + const [riskOn, initialAllocationAddress] = await managerWrapper.getMACOInitialAllocationAsync( + stableCollateral, + riskCollateral, + ethMedianizer, + movingAverageOracle, + new BigNumber(20) + ); + + ethTwentyDayMACOManager = await managerWrapper.deployETHTwentyDayMACOManagerAsync( + core.address, + movingAverageOracle.address, + daiMock.address, + wrappedETH.address, + stableCollateral.address, + riskCollateral.address, + factory.address, + linearAuctionPriceCurve.address, + auctionTimeToPivot, + riskOn, + ); + + proposalPeriod = ONE_DAY_IN_SECONDS; + rebalancingSetToken = await protocolWrapper.createDefaultRebalancingSetTokenAsync( + core, + rebalancingFactory.address, + ethTwentyDayMACOManager.address, + initialAllocationAddress, + proposalPeriod + ); + + subjectRebalancingSetToken = rebalancingSetToken.address; + subjectCaller = deployerAccount; + }); + + async function subject(): Promise { + return ethTwentyDayMACOManager.initialize.sendTransactionAsync( + subjectRebalancingSetToken, + { from: subjectCaller, gas: DEFAULT_GAS} + ); + } + + it('sets the rebalancing set token address', async () => { + await subject(); + + const rebalancingSetTokenAddress = await ethTwentyDayMACOManager.rebalancingSetTokenAddress.callAsync(); + + expect(rebalancingSetTokenAddress).to.equal(subjectRebalancingSetToken); + }); + + describe('when the rebalancing set address has already been set', async () => { + beforeEach(async () => { + await ethTwentyDayMACOManager.initialize.sendTransactionAsync( + subjectRebalancingSetToken, + { from: subjectCaller, gas: DEFAULT_GAS} + ); + }); + + it('should revert', async () => { + await expectRevertError(subject()); + }); + }); + + describe('but the passed rebalancing set address was not created by Core', async () => { + beforeEach(async () => { + const unTrackedSetToken = await protocolWrapper.createDefaultRebalancingSetTokenAsync( + core, + rebalancingFactory.address, + ethTwentyDayMACOManager.address, + riskCollateral.address, + proposalPeriod, + ); + + await core.disableSet.sendTransactionAsync( + unTrackedSetToken.address, + { from: deployerAccount, gas: DEFAULT_GAS }, + ); + + subjectRebalancingSetToken = unTrackedSetToken.address; + }); + + it('should revert', async () => { + await expectRevertError(subject()); + }); + }); + }); + + describe('#initialPropose', async () => { let subjectTimeFastForward: BigNumber; let subjectCaller: Address; @@ -319,6 +425,11 @@ contract('ETHTwentyDayMACOManager', accounts => { proposalPeriod ); + await ethTwentyDayMACOManager.initialize.sendTransactionAsync( + rebalancingSetToken.address, + { from: subjectCaller, gas: DEFAULT_GAS} + ); + const blockInfo = await web3.eth.getBlock('latest'); await oracleWrapper.updateMedianizerPriceAsync( ethMedianizer, @@ -326,7 +437,6 @@ contract('ETHTwentyDayMACOManager', accounts => { new BigNumber(blockInfo.timestamp + 1), ); - subjectRebalancingSetToken = rebalancingSetToken.address; subjectTimeFastForward = ONE_DAY_IN_SECONDS.add(1); subjectCaller = deployerAccount; }); @@ -334,7 +444,6 @@ contract('ETHTwentyDayMACOManager', accounts => { async function subject(): Promise { await blockchain.increaseTimeAsync(subjectTimeFastForward); return ethTwentyDayMACOManager.initialPropose.sendTransactionAsync( - subjectRebalancingSetToken, { from: subjectCaller, gas: DEFAULT_GAS} ); } @@ -365,36 +474,11 @@ contract('ETHTwentyDayMACOManager', accounts => { }); }); - describe('but the passed rebalancing set address was not created by Core', async () => { - beforeEach(async () => { - const unTrackedSetToken = await protocolWrapper.createDefaultRebalancingSetTokenAsync( - core, - rebalancingFactory.address, - ethTwentyDayMACOManager.address, - riskCollateral.address, - proposalPeriod, - ); - - await core.disableSet.sendTransactionAsync( - unTrackedSetToken.address, - { from: deployerAccount, gas: DEFAULT_GAS }, - ); - - subjectRebalancingSetToken = unTrackedSetToken.address; - }); - - it('should revert', async () => { - await expectRevertError(subject()); - }); - }); - describe('when 12 hours has not passed from an initial proposal', async () => { beforeEach(async () => { const timeFastForward = ONE_DAY_IN_SECONDS; await blockchain.increaseTimeAsync(timeFastForward); - await ethTwentyDayMACOManager.initialPropose.sendTransactionAsync( - subjectRebalancingSetToken, - ); + await ethTwentyDayMACOManager.initialPropose.sendTransactionAsync(); subjectTimeFastForward = ONE_DAY_IN_SECONDS.div(4); }); @@ -449,14 +533,10 @@ contract('ETHTwentyDayMACOManager', accounts => { describe('when propose is called and rebalancing set token is in Proposal state', async () => { beforeEach(async () => { await blockchain.increaseTimeAsync(subjectTimeFastForward); - await ethTwentyDayMACOManager.initialPropose.sendTransactionAsync( - subjectRebalancingSetToken, - ); + await ethTwentyDayMACOManager.initialPropose.sendTransactionAsync(); await blockchain.increaseTimeAsync(ONE_DAY_IN_SECONDS.div(4)); - await ethTwentyDayMACOManager.confirmPropose.sendTransactionAsync( - subjectRebalancingSetToken, - ); + await ethTwentyDayMACOManager.confirmPropose.sendTransactionAsync(); }); it('should revert', async () => { @@ -466,7 +546,6 @@ contract('ETHTwentyDayMACOManager', accounts => { }); describe('#confirmPropose', async () => { - let subjectRebalancingSetToken: Address; let subjectTimeFastForward: BigNumber; let subjectCaller: Address; @@ -521,6 +600,11 @@ contract('ETHTwentyDayMACOManager', accounts => { proposalPeriod ); + await ethTwentyDayMACOManager.initialize.sendTransactionAsync( + rebalancingSetToken.address, + { from: subjectCaller, gas: DEFAULT_GAS} + ); + const triggerBlockInfo = await web3.eth.getBlock('latest'); await oracleWrapper.updateMedianizerPriceAsync( ethMedianizer, @@ -528,9 +612,8 @@ contract('ETHTwentyDayMACOManager', accounts => { new BigNumber(triggerBlockInfo.timestamp + 1), ); - subjectRebalancingSetToken = rebalancingSetToken.address; await blockchain.increaseTimeAsync(ONE_DAY_IN_SECONDS.add(1)); - await ethTwentyDayMACOManager.initialPropose.sendTransactionAsync(subjectRebalancingSetToken); + await ethTwentyDayMACOManager.initialPropose.sendTransactionAsync(); const lastBlockInfo = await web3.eth.getBlock('latest'); await oracleWrapper.updateMedianizerPriceAsync( @@ -546,7 +629,6 @@ contract('ETHTwentyDayMACOManager', accounts => { async function subject(): Promise { await blockchain.increaseTimeAsync(subjectTimeFastForward); return ethTwentyDayMACOManager.confirmPropose.sendTransactionAsync( - subjectRebalancingSetToken, { from: subjectCaller, gas: DEFAULT_GAS} ); } From 4eb5f49213ade18832e8a6987fca281cba760257 Mon Sep 17 00:00:00 2001 From: felix2feng Date: Fri, 31 May 2019 18:10:20 -0700 Subject: [PATCH 2/2] Restrict initialize caller to deployer --- contracts/managers/ETHTwentyDayMACOManager.sol | 9 ++++++++- .../managers/ethTwentyDayMACOManager.spec.ts | 11 +++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/contracts/managers/ETHTwentyDayMACOManager.sol b/contracts/managers/ETHTwentyDayMACOManager.sol index 4f1526b..26aeacd 100644 --- a/contracts/managers/ETHTwentyDayMACOManager.sol +++ b/contracts/managers/ETHTwentyDayMACOManager.sol @@ -58,6 +58,7 @@ contract ETHTwentyDayMACOManager { uint256 constant ETH_DECIMALS = 18; /* ============ State Variables ============ */ + address public contractDeployer; address public rebalancingSetTokenAddress; address public coreAddress; address public movingAveragePriceFeed; @@ -111,6 +112,7 @@ contract ETHTwentyDayMACOManager { ) public { + contractDeployer = msg.sender; coreAddress = _coreAddress; movingAveragePriceFeed = _movingAveragePriceFeed; setTokenFactory = _setTokenFactory; @@ -129,7 +131,7 @@ contract ETHTwentyDayMACOManager { * This function sets the Rebalancing Set Token address that the manager is associated with. * Since, the rebalancing set token must first specify the address of the manager before deployment, * we cannot know what the rebalancing set token is in advance. This function is only meant to be called - * once during initialization. + * once during initialization by the contract deployer. * * @param _rebalancingSetTokenAddress The address of the rebalancing Set token */ @@ -138,6 +140,11 @@ contract ETHTwentyDayMACOManager { ) external { + require( + msg.sender == contractDeployer, + "ETHTwentyDayMACOManager.initialize: Only the contract deployer can initialize" + ); + require( rebalancingSetTokenAddress == address(0), "ETHTwentyDayMACOManager.initialize: Rebalancing SetToken Address must be empty" diff --git a/test/contracts/managers/ethTwentyDayMACOManager.spec.ts b/test/contracts/managers/ethTwentyDayMACOManager.spec.ts index 5a42079..5827af7 100644 --- a/test/contracts/managers/ethTwentyDayMACOManager.spec.ts +++ b/test/contracts/managers/ethTwentyDayMACOManager.spec.ts @@ -53,6 +53,7 @@ const setTestUtils = new SetTestUtils(web3); contract('ETHTwentyDayMACOManager', accounts => { const [ deployerAccount, + notDeployerAccount, ] = accounts; let rebalancingSetToken: RebalancingSetTokenContract; @@ -348,6 +349,16 @@ contract('ETHTwentyDayMACOManager', accounts => { }); }); + describe('but caller is not the contract deployer', async () => { + beforeEach(async () => { + subjectCaller = notDeployerAccount; + }); + + it('should revert', async () => { + await expectRevertError(subject()); + }); + }); + describe('but the passed rebalancing set address was not created by Core', async () => { beforeEach(async () => { const unTrackedSetToken = await protocolWrapper.createDefaultRebalancingSetTokenAsync(