diff --git a/contracts/core/tokens/RebalancingSetToken.sol b/contracts/core/tokens/RebalancingSetToken.sol index 7197cb86c..bbc7a3f41 100644 --- a/contracts/core/tokens/RebalancingSetToken.sol +++ b/contracts/core/tokens/RebalancingSetToken.sol @@ -341,10 +341,21 @@ contract RebalancingSetToken is function endFailedAuction() external { + ( + , + uint256 calculatedUnitShares + ) = StandardSettleRebalanceLibrary.calculateNextSetIssueQuantity( + totalSupply(), + naturalUnit, + nextSet, + vault + ); + // Fail auction and either reset to Default state or kill Rebalancing Set Token and enter Drawdown // state uint8 integerRebalanceState = StandardFailAuctionLibrary.endFailedAuction( startingCurrentSetAmount, + calculatedUnitShares, currentSet, core, auctionParameters, diff --git a/contracts/core/tokens/rebalancing-libraries/StandardFailAuctionLibrary.sol b/contracts/core/tokens/rebalancing-libraries/StandardFailAuctionLibrary.sol index 3b2eb1a31..cdd2d7247 100644 --- a/contracts/core/tokens/rebalancing-libraries/StandardFailAuctionLibrary.sol +++ b/contracts/core/tokens/rebalancing-libraries/StandardFailAuctionLibrary.sol @@ -42,6 +42,7 @@ library StandardFailAuctionLibrary { * if bids have been placed. Reset to Default state if no bids placed. * * @param _startingCurrentSetAmount Amount of current set at beginning or rebalance + * @param _calculatedUnitShares Calculated unitShares amount if rebalance were to be settled * @param _currentSet The Set that failed to rebalance * @param _coreAddress Core address * @param _auctionParameters Struct containing auction price curve parameters @@ -51,6 +52,7 @@ library StandardFailAuctionLibrary { */ function endFailedAuction( uint256 _startingCurrentSetAmount, + uint256 _calculatedUnitShares, address _currentSet, address _coreAddress, RebalancingHelperLibrary.AuctionPriceParameters _auctionParameters, @@ -77,30 +79,41 @@ library StandardFailAuctionLibrary { "RebalanceAuctionModule.endFailedAuction: Can only be called after auction reaches pivot" ); - // If settleRebalance can be called than endFailedAuction can't be - require( - _biddingParameters.remainingCurrentSets >= _biddingParameters.minimumBid, - "RebalancingSetToken.endFailedAuction: Cannot be called if rebalance is completed" - ); - - // Declare rebalance state variable - RebalancingHelperLibrary.State _newRebalanceState; - - // Check if any bids have been placed - if (_startingCurrentSetAmount == _biddingParameters.remainingCurrentSets) { - // If bid not placed, reissue current Set - ICore(_coreAddress).issueInVault( - _currentSet, - _startingCurrentSetAmount - ); - - // Set Rebalance Set Token state to Default - _newRebalanceState = RebalancingHelperLibrary.State.Default; + uint8 newRebalanceState; + /** + * If not enough sets have been bid on then allow auction to fail where no bids being registered + * returns the rebalancing set token to pre-auction state and some bids being registered puts the + * rebalancing set token in Drawdown mode. + * + * However, if enough sets have been bid on. Then allow auction to fail and enter Drawdown state if + * and only if the calculated post-auction unitShares is equal to 0. + */ + if (_biddingParameters.remainingCurrentSets >= _biddingParameters.minimumBid) { + // Check if any bids have been placed + if (_startingCurrentSetAmount == _biddingParameters.remainingCurrentSets) { + // If bid not placed, reissue current Set + ICore(_coreAddress).issueInVault( + _currentSet, + _startingCurrentSetAmount + ); + + // Set Rebalance Set Token state to Default + newRebalanceState = uint8(RebalancingHelperLibrary.State.Default); + } else { + // Set Rebalancing Set Token to Drawdown state + newRebalanceState = uint8(RebalancingHelperLibrary.State.Drawdown); + } } else { - // Set Rebalancing Set Token to Drawdown state - _newRebalanceState = RebalancingHelperLibrary.State.Drawdown; + // If settleRebalance can be called then endFailedAuction can't be + require( + _calculatedUnitShares == 0, + "RebalancingSetToken.endFailedAuction: Cannot be called if rebalance is viably completed" + ); + + // If calculated unitShares equals 0 set to Drawdown state + newRebalanceState = uint8(RebalancingHelperLibrary.State.Drawdown); } - return uint8(_newRebalanceState); + return newRebalanceState; } } \ No newline at end of file diff --git a/contracts/core/tokens/rebalancing-libraries/StandardSettleRebalanceLibrary.sol b/contracts/core/tokens/rebalancing-libraries/StandardSettleRebalanceLibrary.sol index 000918826..87469c76b 100644 --- a/contracts/core/tokens/rebalancing-libraries/StandardSettleRebalanceLibrary.sol +++ b/contracts/core/tokens/rebalancing-libraries/StandardSettleRebalanceLibrary.sol @@ -97,6 +97,11 @@ library StandardSettleRebalanceLibrary { _vaultAddress ); + require( + nextUnitShares > 0, + "RebalancingSetToken.settleRebalance: Failed rebalance, unitshares equals 0. Call endFailedAuction." + ); + // Issue nextSet to RebalancingSetToken ICore(_coreAddress).issueInVault( _nextSet, @@ -132,7 +137,7 @@ library StandardSettleRebalanceLibrary { _vaultAddress, setDetails ); - + // Calculate the amount of naturalUnits worth of rebalancingSetToken outstanding uint256 naturalUnitsOutstanding = _totalSupply.div(_naturalUnit); @@ -201,6 +206,6 @@ library StandardSettleRebalanceLibrary { } } - return maxIssueAmount; + return maxIssueAmount; } } diff --git a/contracts/core/tokens/rebalancing-libraries/StandardStartRebalanceLibrary.sol b/contracts/core/tokens/rebalancing-libraries/StandardStartRebalanceLibrary.sol index a507b0652..9fe69ba42 100644 --- a/contracts/core/tokens/rebalancing-libraries/StandardStartRebalanceLibrary.sol +++ b/contracts/core/tokens/rebalancing-libraries/StandardStartRebalanceLibrary.sol @@ -88,13 +88,13 @@ library StandardStartRebalanceLibrary { // Must be in "Proposal" state before going into "Rebalance" state require( _rebalanceState == uint8(RebalancingHelperLibrary.State.Proposal), - "RebalancingSetToken.rebalance: State must be Proposal" + "RebalancingSetToken.startRebalance: State must be Proposal" ); // Be sure the full proposal period has elapsed require( block.timestamp >= _proposalStartTime.add(_proposalPeriod), - "RebalancingSetToken.rebalance: Proposal period not elapsed" + "RebalancingSetToken.startRebalance: Proposal period not elapsed" ); // Create combined array data structures and calculate minimum bid needed for auction @@ -111,6 +111,13 @@ library StandardStartRebalanceLibrary { _vaultAddress ); + // Require remainingCurrentSets to be greater than minimumBid otherwise no bidding would + // be allowed + require( + biddingParameters.remainingCurrentSets >= biddingParameters.minimumBid, + "RebalancingSetToken.startRebalance: Not enough collateral to rebalance" + ); + return biddingParameters; } diff --git a/contracts/mocks/core/lib/UpdatableConstantAuctionPriceCurve.sol b/contracts/mocks/core/lib/UpdatableConstantAuctionPriceCurve.sol new file mode 100644 index 000000000..79ae8d515 --- /dev/null +++ b/contracts/mocks/core/lib/UpdatableConstantAuctionPriceCurve.sol @@ -0,0 +1,58 @@ +/* + Copyright 2018 Set Labs Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +pragma solidity 0.4.25; +pragma experimental "ABIEncoderV2"; + +import { SafeMath } from "openzeppelin-solidity/contracts/math/SafeMath.sol"; +import { ConstantAuctionPriceCurve } from "./ConstantAuctionPriceCurve.sol"; +import { RebalancingHelperLibrary } from "../../../core/lib/RebalancingHelperLibrary.sol"; + +/** + * @title UpdatableConstantAuctionPriceCurve + * @author Set Protocol + * + * Contract used in rebalancing auction testing to return consistent price. + * !!!!!!!!!!!!! DO NOT DEPLOY !!!!!!!!!!!!! + * + */ + +contract UpdatableConstantAuctionPriceCurve is ConstantAuctionPriceCurve { + + constructor( + uint256 _priceNumerator, + uint256 _priceDenominator + ) + public + ConstantAuctionPriceCurve( + _priceNumerator, + _priceDenominator + ) + {} + + /* + * Update constant auction price + * + * @param _newPrice Price to update to + */ + function updatePrice( + uint256 _newPrice + ) + public + { + priceNumerator = _newPrice; + } +} \ No newline at end of file diff --git a/test/contracts/core/modules/rebalanceAuctionModule.spec.ts b/test/contracts/core/modules/rebalanceAuctionModule.spec.ts index 51d6c7d29..b3060173a 100644 --- a/test/contracts/core/modules/rebalanceAuctionModule.spec.ts +++ b/test/contracts/core/modules/rebalanceAuctionModule.spec.ts @@ -551,6 +551,13 @@ contract('RebalanceAuctionModule', accounts => { describe('when getBidPrice is called from Rebalance State', async () => { beforeEach(async () => { + // Issue currentSetToken + await coreMock.issue.sendTransactionAsync(currentSetToken.address, ether(9), {from: deployerAccount}); + await erc20Wrapper.approveTransfersAsync([currentSetToken], transferProxy.address); + + // Use issued currentSetToken to issue rebalancingSetToken + await coreMock.issue.sendTransactionAsync(rebalancingSetToken.address, ether(7)); + await rebalancingWrapper.defaultTransitionToRebalanceAsync( coreMock, rebalancingComponentWhiteList, @@ -762,6 +769,13 @@ contract('RebalanceAuctionModule', accounts => { describe('when placeBid is called from Rebalance State', async () => { beforeEach(async () => { + // Issue currentSetToken + await coreMock.issue.sendTransactionAsync(currentSetToken.address, ether(9), {from: deployerAccount}); + await erc20Wrapper.approveTransfersAsync([currentSetToken], transferProxy.address); + + // Use issued currentSetToken to issue rebalancingSetToken + await coreMock.issue.sendTransactionAsync(rebalancingSetToken.address, ether(7)); + await rebalancingWrapper.defaultTransitionToRebalanceAsync( coreMock, rebalancingComponentWhiteList, diff --git a/test/contracts/core/tokens/rebalancingSetToken.spec.ts b/test/contracts/core/tokens/rebalancingSetToken.spec.ts index 5cb661dce..b5109fa03 100644 --- a/test/contracts/core/tokens/rebalancingSetToken.spec.ts +++ b/test/contracts/core/tokens/rebalancingSetToken.spec.ts @@ -11,7 +11,6 @@ import ChaiSetup from '@utils/chaiSetup'; import { BigNumberSetup } from '@utils/bigNumberSetup'; import { CoreMockContract, - ConstantAuctionPriceCurveContract, SetTokenContract, RebalanceAuctionModuleContract, RebalancingSetTokenContract, @@ -19,6 +18,7 @@ import { SetTokenFactoryContract, StandardTokenMockContract, TransferProxyContract, + UpdatableConstantAuctionPriceCurveContract, VaultContract, WhiteListContract, } from '@utils/contracts'; @@ -31,6 +31,7 @@ import { ZERO, DEFAULT_AUCTION_PRICE_NUMERATOR, DEFAULT_AUCTION_PRICE_DENOMINATOR, + DEFAULT_REBALANCING_NATURAL_UNIT, } from '@utils/constants'; import { getExpectedTransferLog, @@ -77,7 +78,7 @@ contract('RebalancingSetToken', accounts => { let factory: SetTokenFactoryContract; let rebalancingFactory: RebalancingSetTokenFactoryContract; let rebalancingComponentWhiteList: WhiteListContract; - let constantAuctionPriceCurve: ConstantAuctionPriceCurveContract; + let constantAuctionPriceCurve: UpdatableConstantAuctionPriceCurveContract; const coreWrapper = new CoreWrapper(deployerAccount, deployerAccount); const erc20Wrapper = new ERC20Wrapper(deployerAccount); @@ -113,7 +114,7 @@ contract('RebalancingSetToken', accounts => { coreMock.address, rebalancingComponentWhiteList.address, ); - constantAuctionPriceCurve = await rebalancingWrapper.deployConstantAuctionPriceCurveAsync( + constantAuctionPriceCurve = await rebalancingWrapper.deployUpdatableConstantAuctionPriceCurveAsync( DEFAULT_AUCTION_PRICE_NUMERATOR, DEFAULT_AUCTION_PRICE_DENOMINATOR, ); @@ -540,6 +541,14 @@ contract('RebalancingSetToken', accounts => { describe('when mint is called from Rebalance state', async () => { beforeEach(async () => { + // Issue currentSetToken + await coreMock.issue.sendTransactionAsync(currentSetToken.address, ether(8), {from: deployerAccount}); + await erc20Wrapper.approveTransfersAsync([currentSetToken], transferProxy.address); + + // Use issued currentSetToken to issue rebalancingSetToken + const rebalancingSetQuantityToIssue = ether(7); + await coreMock.issue.sendTransactionAsync(rebalancingSetToken.address, rebalancingSetQuantityToIssue); + await rebalancingWrapper.defaultTransitionToRebalanceAsync( coreMock, rebalancingComponentWhiteList, @@ -1287,6 +1296,14 @@ contract('RebalancingSetToken', accounts => { describe('when propose is called from Rebalance state', async () => { beforeEach(async () => { + // Issue currentSetToken + await coreMock.issue.sendTransactionAsync(currentSetToken.address, ether(8), {from: deployerAccount}); + await erc20Wrapper.approveTransfersAsync([currentSetToken], transferProxy.address); + + // Use issued currentSetToken to issue rebalancingSetToken + const rebalancingSetQuantityToIssue = ether(7); + await coreMock.issue.sendTransactionAsync(rebalancingSetToken.address, rebalancingSetQuantityToIssue); + const auctionTimeToPivot = new BigNumber(100000); const auctionStartPrice = new BigNumber(500); const auctionPivotPrice = DEFAULT_AUCTION_PRICE_NUMERATOR; @@ -1360,6 +1377,7 @@ contract('RebalancingSetToken', accounts => { let currentSetToken: SetTokenContract; let nextSetToken: SetTokenContract; let rebalancingSetQuantityToIssue: BigNumber; + let setTokenNaturalUnits: BigNumber[]; beforeEach(async () => { const setTokensToDeploy = 2; @@ -1368,6 +1386,7 @@ contract('RebalancingSetToken', accounts => { factory.address, transferProxy.address, setTokensToDeploy, + undefined || setTokenNaturalUnits ); currentSetToken = setTokens[0]; @@ -1589,6 +1608,23 @@ contract('RebalancingSetToken', accounts => { await expectRevertError(subject()); }); }); + + describe('when currentRemainingSets does not exceed the minimumBid amount', async () => { + before(async () => { + setTokenNaturalUnits = [new BigNumber(10 ** 14), new BigNumber(10 ** 14)]; + }); + + beforeEach(async () => { + const minimumBid = new BigNumber(10 ** 14).mul(1000); + + const redeemAmount = rebalancingSetQuantityToIssue.sub(minimumBid).add(DEFAULT_REBALANCING_NATURAL_UNIT); + await coreMock.redeem.sendTransactionAsync(rebalancingSetToken.address, redeemAmount); + }); + + it('should revert', async () => { + await expectRevertError(subject()); + }); + }); }); describe('when startRebalance is called from Rebalance State', async () => { @@ -1643,7 +1679,11 @@ contract('RebalancingSetToken', accounts => { let proposalPeriod: BigNumber; let nextSetToken: SetTokenContract; - let rebalancingSetQuantityToIssue: BigNumber; + let currentSetToken: SetTokenContract; + + let rebalancingSetQuantityToIssue: BigNumber = ether(7); + let setTokenNaturalUnits: BigNumber[]; + let rebalancingSetUnitShares: BigNumber; beforeEach(async () => { const setTokensToDeploy = 2; @@ -1652,8 +1692,9 @@ contract('RebalancingSetToken', accounts => { factory.address, transferProxy.address, setTokensToDeploy, + undefined || setTokenNaturalUnits, ); - const currentSetToken = setTokens[0]; + currentSetToken = setTokens[0]; nextSetToken = setTokens[1]; proposalPeriod = ONE_DAY_IN_SECONDS; @@ -1663,6 +1704,7 @@ contract('RebalancingSetToken', accounts => { managerAccount, currentSetToken.address, proposalPeriod, + undefined || rebalancingSetUnitShares, ); // Issue currentSetToken @@ -1670,7 +1712,6 @@ contract('RebalancingSetToken', accounts => { await erc20Wrapper.approveTransfersAsync([currentSetToken], transferProxy.address); // Use issued currentSetToken to issue rebalancingSetToken - rebalancingSetQuantityToIssue = ether(7); await coreMock.issue.sendTransactionAsync(rebalancingSetToken.address, rebalancingSetQuantityToIssue); subjectCaller = managerAccount; @@ -1822,6 +1863,77 @@ contract('RebalancingSetToken', accounts => { }); }); + describe("when settleRebalance is called but issuable amount is less than nextSet's natural unit", async () => { + before(async () => { + setTokenNaturalUnits = [new BigNumber(10 ** 14), new BigNumber(10 ** 14)]; + }); + + after(async () => { + setTokenNaturalUnits = undefined; + }); + + beforeEach(async () => { + await rebalancingWrapper.defaultTransitionToRebalanceAsync( + coreMock, + rebalancingComponentWhiteList, + rebalancingSetToken, + nextSetToken, + constantAuctionPriceCurve.address, + managerAccount + ); + + const newPrice = new BigNumber(8 * 10 ** 7); + await constantAuctionPriceCurve.updatePrice.sendTransactionAsync(newPrice); + + await rebalanceAuctionModule.bid.sendTransactionAsync( + rebalancingSetToken.address, + rebalancingSetQuantityToIssue + ); + }); + + it('should revert', async () => { + await expectRevertError(subject()); + }); + }); + + describe('when settleRebalance is called but unitShares is 0', async () => { + before(async () => { + rebalancingSetUnitShares = new BigNumber(1); + setTokenNaturalUnits = [new BigNumber(10 ** 14), new BigNumber(10 ** 14)]; + rebalancingSetQuantityToIssue = new BigNumber(10 ** 27); + }); + + after(async () => { + rebalancingSetUnitShares = undefined; + setTokenNaturalUnits = undefined; + rebalancingSetQuantityToIssue = ether(7); + }); + + beforeEach(async () => { + await rebalancingWrapper.defaultTransitionToRebalanceAsync( + coreMock, + rebalancingComponentWhiteList, + rebalancingSetToken, + nextSetToken, + constantAuctionPriceCurve.address, + managerAccount + ); + + const newPrice = new BigNumber(1001); + await constantAuctionPriceCurve.updatePrice.sendTransactionAsync(newPrice); + + const bidQuantity = rebalancingSetQuantityToIssue.div(10 ** 10); + await rebalanceAuctionModule.bid.sendTransactionAsync( + rebalancingSetToken.address, + bidQuantity + ); + }); + + it('should revert', async () => { + await expectRevertError(subject()); + }); + }); + describe('when settleRebalance is called from Drawdown State', async () => { beforeEach(async () => { await rebalancingWrapper.defaultTransitionToRebalanceAsync( @@ -1858,7 +1970,9 @@ contract('RebalancingSetToken', accounts => { let nextSetToken: SetTokenContract; let currentSetToken: SetTokenContract; - let rebalancingSetQuantityToIssue: BigNumber; + let rebalancingSetQuantityToIssue: BigNumber = ether(7); + let setTokenNaturalUnits: BigNumber[]; + let rebalancingSetUnitShares: BigNumber; beforeEach(async () => { const setTokensToDeploy = 2; @@ -1867,6 +1981,7 @@ contract('RebalancingSetToken', accounts => { factory.address, transferProxy.address, setTokensToDeploy, + undefined || setTokenNaturalUnits, ); currentSetToken = setTokens[0]; nextSetToken = setTokens[1]; @@ -1878,6 +1993,7 @@ contract('RebalancingSetToken', accounts => { managerAccount, currentSetToken.address, proposalPeriod, + undefined || rebalancingSetUnitShares ); // Issue currentSetToken @@ -1885,7 +2001,6 @@ contract('RebalancingSetToken', accounts => { await erc20Wrapper.approveTransfersAsync([currentSetToken], transferProxy.address); // Use issued currentSetToken to issue rebalancingSetToken - rebalancingSetQuantityToIssue = ether(7); await coreMock.issue.sendTransactionAsync(rebalancingSetToken.address, rebalancingSetQuantityToIssue); subjectCaller = managerAccount; @@ -1920,7 +2035,7 @@ contract('RebalancingSetToken', accounts => { }); }); - describe('when endFailedAuction is called from Rebalance State and no bids have been placed', async () => { + describe('when endFailedAuction is called from Rebalance State', async () => { beforeEach(async () => { await rebalancingWrapper.defaultTransitionToRebalanceAsync( coreMock, @@ -1989,6 +2104,71 @@ contract('RebalancingSetToken', accounts => { }); }); + describe('and issueAmount is insufficient', async () => { + before(async () => { + setTokenNaturalUnits = [new BigNumber(10 ** 14), new BigNumber(10 ** 14)]; + }); + + after(async () => { + setTokenNaturalUnits = undefined; + }); + + beforeEach(async () => { + const newPrice = new BigNumber(8 * 10 ** 7); + await constantAuctionPriceCurve.updatePrice.sendTransactionAsync(newPrice); + + await rebalanceAuctionModule.bid.sendTransactionAsync( + rebalancingSetToken.address, + rebalancingSetQuantityToIssue + ); + + const defaultTimeToPivot = new BigNumber(100000); + await blockchain.increaseTimeAsync(defaultTimeToPivot.add(1)); + }); + + it('updates the rebalanceState to Drawdown', async () => { + await subject(); + + const newRebalanceState = await rebalancingSetToken.rebalanceState.callAsync(); + expect(newRebalanceState).to.be.bignumber.equal(SetUtils.REBALANCING_STATE.DRAWDOWN); + }); + }); + + describe('but unitShares is 0', async () => { + before(async () => { + rebalancingSetUnitShares = new BigNumber(1); + setTokenNaturalUnits = [new BigNumber(10 ** 14), new BigNumber(10 ** 14)]; + rebalancingSetQuantityToIssue = new BigNumber(10 ** 27); + }); + + after(async () => { + rebalancingSetUnitShares = undefined; + setTokenNaturalUnits = undefined; + rebalancingSetQuantityToIssue = ether(7); + }); + + beforeEach(async () => { + const newPrice = new BigNumber(1001); + await constantAuctionPriceCurve.updatePrice.sendTransactionAsync(newPrice); + + const bidQuantity = rebalancingSetQuantityToIssue.div(10 ** 10); + await rebalanceAuctionModule.bid.sendTransactionAsync( + rebalancingSetToken.address, + bidQuantity + ); + + const defaultTimeToPivot = new BigNumber(100000); + await blockchain.increaseTimeAsync(defaultTimeToPivot.add(1)); + }); + + it('updates the rebalanceState to Drawdown', async () => { + await subject(); + + const newRebalanceState = await rebalancingSetToken.rebalanceState.callAsync(); + expect(newRebalanceState).to.be.bignumber.equal(SetUtils.REBALANCING_STATE.DRAWDOWN); + }); + }); + describe('when pivot point has not been reached', async () => { it('should revert', async () => { await expectRevertError(subject()); diff --git a/test/contracts/supplementary/btcEthRebalancingManager.spec.ts b/test/contracts/supplementary/btcEthRebalancingManager.spec.ts index 087b930af..b1d8ff3a8 100644 --- a/test/contracts/supplementary/btcEthRebalancingManager.spec.ts +++ b/test/contracts/supplementary/btcEthRebalancingManager.spec.ts @@ -328,6 +328,17 @@ contract('BTCETHRebalancingManager', accounts => { ethPrice, SetTestUtils.generateTimestamp(1000), ); + + // Issue currentSetToken + await coreMock.issue.sendTransactionAsync( + initialAllocationToken.address, + ether(9), + {from: deployerAccount, gas: DEFAULT_GAS}, + ); + await erc20Wrapper.approveTransfersAsync([initialAllocationToken], transferProxy.address); + + // Use issued currentSetToken to issue rebalancingSetToken + await coreMock.issue.sendTransactionAsync(rebalancingSetToken.address, ether(7), { gas: DEFAULT_GAS }); }); async function subject(): Promise { @@ -671,7 +682,9 @@ contract('BTCETHRebalancingManager', accounts => { // Transition to rebalance await blockchain.increaseTimeAsync(ONE_DAY_IN_SECONDS.add(1)); - await rebalancingSetToken.startRebalance.sendTransactionAsync(); + await rebalancingSetToken.startRebalance.sendTransactionAsync( + { from: otherAccount, gas: DEFAULT_GAS } + ); }); it('should revert', async () => { @@ -681,14 +694,6 @@ contract('BTCETHRebalancingManager', accounts => { describe('when proposeNewRebalance is called from Drawdown State', async () => { beforeEach(async () => { - // Issue currentSetToken - await coreMock.issue.sendTransactionAsync(initialAllocationToken.address, ether(9), {from: deployerAccount}); - await erc20Wrapper.approveTransfersAsync([initialAllocationToken], transferProxy.address); - - // Use issued currentSetToken to issue rebalancingSetToken - const rebalancingSetQuantityToIssue = ether(7); - await coreMock.issue.sendTransactionAsync(rebalancingSetToken.address, rebalancingSetQuantityToIssue); - // propose rebalance await blockchain.increaseTimeAsync(subjectTimeFastForward); await btcethRebalancingManager.propose.sendTransactionAsync( @@ -699,7 +704,7 @@ contract('BTCETHRebalancingManager', accounts => { await blockchain.increaseTimeAsync(ONE_DAY_IN_SECONDS.add(1)); await rebalancingSetToken.startRebalance.sendTransactionAsync( - { from: otherAccount, gas: DEFAULT_GAS} + { from: otherAccount, gas: DEFAULT_GAS } ); const defaultTimeToPivot = new BigNumber(100000); diff --git a/utils/contracts.ts b/utils/contracts.ts index e5b8bc440..5babd277f 100644 --- a/utils/contracts.ts +++ b/utils/contracts.ts @@ -40,6 +40,7 @@ import { TimeLockUpgradeMockContract } from '../types/generated/time_lock_upgrad import { TimeLockUpgradeContract } from '../types/generated/time_lock_upgrade'; import { TransferProxyContract } from '../types/generated/transfer_proxy'; import { VaultContract } from '../types/generated/vault'; +import { UpdatableConstantAuctionPriceCurveContract } from '../types/generated/updatable_constant_auction_price_curve'; import { WethMockContract } from '../types/generated/weth_mock'; import { WhiteListContract } from '../types/generated/white_list'; import { ZeroExExchangeWrapperContract } from '../types/generated/zero_ex_exchange_wrapper'; @@ -86,6 +87,7 @@ export { TimeLockUpgradeContract, TimeLockUpgradeMockContract, TransferProxyContract, + UpdatableConstantAuctionPriceCurveContract, VaultContract, WethMockContract, WhiteListContract, diff --git a/utils/wrappers/rebalancingWrapper.ts b/utils/wrappers/rebalancingWrapper.ts index db7cb44ad..0dea19502 100644 --- a/utils/wrappers/rebalancingWrapper.ts +++ b/utils/wrappers/rebalancingWrapper.ts @@ -10,6 +10,7 @@ import { LinearAuctionPriceCurveContract, SetTokenContract, RebalancingSetTokenContract, + UpdatableConstantAuctionPriceCurveContract, VaultContract, WhiteListContract, } from '../contracts'; @@ -38,6 +39,7 @@ const ConstantAuctionPriceCurve = artifacts.require('ConstantAuctionPriceCurve') const LinearAuctionPriceCurve = artifacts.require('LinearAuctionPriceCurve'); const RebalancingSetToken = artifacts.require('RebalancingSetToken'); const SetToken = artifacts.require('SetToken'); +const UpdatableConstantAuctionPriceCurve = artifacts.require('UpdatableConstantAuctionPriceCurve'); declare type CoreLikeContract = CoreMockContract | CoreContract; const { SetProtocolTestUtils: SetTestUtils, SetProtocolUtils: SetUtils } = setProtocolUtils; @@ -225,6 +227,26 @@ export class RebalancingWrapper { ); } + public async deployUpdatableConstantAuctionPriceCurveAsync( + priceNumerator: BigNumber, + priceDenominator: BigNumber, + from: Address = this._tokenOwnerAddress + ): Promise { + const truffleUpdatableConstantAuctionPriceCurve = await UpdatableConstantAuctionPriceCurve.new( + priceNumerator, + priceDenominator, + { from }, + ); + + return new UpdatableConstantAuctionPriceCurveContract( + new web3.eth.Contract( + truffleUpdatableConstantAuctionPriceCurve.abi, + truffleUpdatableConstantAuctionPriceCurve.address + ), + { from, gas: DEFAULT_GAS }, + ); + } + public async addPriceLibraryAsync( core: CoreLikeContract, priceLibrary: ConstantAuctionPriceCurveContract | LinearAuctionPriceCurveContract,