From f42b3ac227436ed38b035467924e4268683944fa Mon Sep 17 00:00:00 2001 From: bweick Date: Thu, 31 Jan 2019 16:53:11 -0800 Subject: [PATCH 01/12] Added UpdatableConstantAuctionPriceCurve. --- .../UpdatableConstantAuctionPriceCurve.sol | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 contracts/mocks/core/lib/UpdatableConstantAuctionPriceCurve.sol 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 From c8aa63db1b2ee57ed4d62d627c372c6fafbb20de Mon Sep 17 00:00:00 2001 From: bweick Date: Fri, 1 Feb 2019 17:52:37 -0800 Subject: [PATCH 02/12] SettleRebalance check that unitShares isn't 0. EndFailedAuction unitShares equals 0 drawdown. --- contracts/core/tokens/RebalancingSetToken.sol | 11 + .../StandardFailAuctionLibrary.sol | 45 ++-- .../StandardSettleRebalanceLibrary.sol | 9 +- .../StandardStartRebalanceLibrary.sol | 11 +- .../core/tokens/rebalancingSetToken.spec.ts | 198 +++++++++++++++++- utils/contracts.ts | 2 + utils/wrappers/rebalancingWrapper.ts | 22 ++ 7 files changed, 272 insertions(+), 26 deletions(-) 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..b34e9f95b 100644 --- a/contracts/core/tokens/rebalancing-libraries/StandardFailAuctionLibrary.sol +++ b/contracts/core/tokens/rebalancing-libraries/StandardFailAuctionLibrary.sol @@ -51,6 +51,7 @@ library StandardFailAuctionLibrary { */ function endFailedAuction( uint256 _startingCurrentSetAmount, + uint256 _calculatedUnitShares, address _currentSet, address _coreAddress, RebalancingHelperLibrary.AuctionPriceParameters _auctionParameters, @@ -80,25 +81,43 @@ library StandardFailAuctionLibrary { // If settleRebalance can be called than endFailedAuction can't be require( _biddingParameters.remainingCurrentSets >= _biddingParameters.minimumBid, - "RebalancingSetToken.endFailedAuction: Cannot be called if rebalance is completed" + "RebalancingSetToken.endFailedAuction: Cannot be called if rebalance is viably 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; - } else { - // Set Rebalancing Set Token to Drawdown state + /** + * 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 = RebalancingHelperLibrary.State.Default; + } else { + // Set Rebalancing Set Token to Drawdown state + _newRebalanceState = RebalancingHelperLibrary.State.Drawdown; + } + } else if (true) { _newRebalanceState = RebalancingHelperLibrary.State.Drawdown; + } else { + // If settleRebalance can be called than endFailedAuction can't be + require( + _biddingParameters.remainingCurrentSets >= _biddingParameters.minimumBid, + "RebalancingSetToken.endFailedAuction: Cannot be called if rebalance is viably completed" + ); } return uint8(_newRebalanceState); 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/test/contracts/core/tokens/rebalancingSetToken.spec.ts b/test/contracts/core/tokens/rebalancingSetToken.spec.ts index 5cb661dce..be05e6714 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( @@ -1851,14 +1963,16 @@ contract('RebalancingSetToken', accounts => { }); }); - describe('#endFailedAuction', async () => { + describe.only('#endFailedAuction', async () => { let subjectCaller: Address; let proposalPeriod: BigNumber; 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; @@ -1989,6 +2104,71 @@ contract('RebalancingSetToken', accounts => { }); }); + describe('when endFailedAuction is called 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('when settleRebalance is called with an issuable amount 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/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, From a32f20b45720280c38b5babf02d8c95af289ca1f Mon Sep 17 00:00:00 2001 From: bweick Date: Sun, 3 Feb 2019 10:34:57 -0800 Subject: [PATCH 03/12] Removed unnecessary require statement. --- .../StandardFailAuctionLibrary.sol | 23 ++++++------------- .../core/tokens/rebalancingSetToken.spec.ts | 4 ++-- 2 files changed, 9 insertions(+), 18 deletions(-) diff --git a/contracts/core/tokens/rebalancing-libraries/StandardFailAuctionLibrary.sol b/contracts/core/tokens/rebalancing-libraries/StandardFailAuctionLibrary.sol index b34e9f95b..a38bb4910 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 @@ -78,15 +79,6 @@ 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 viably completed" - ); - - // Declare rebalance state variable - RebalancingHelperLibrary.State _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 @@ -105,21 +97,20 @@ library StandardFailAuctionLibrary { ); // Set Rebalance Set Token state to Default - _newRebalanceState = RebalancingHelperLibrary.State.Default; + return uint8(RebalancingHelperLibrary.State.Default); } else { // Set Rebalancing Set Token to Drawdown state - _newRebalanceState = RebalancingHelperLibrary.State.Drawdown; + return uint8(RebalancingHelperLibrary.State.Drawdown); } - } else if (true) { - _newRebalanceState = RebalancingHelperLibrary.State.Drawdown; - } else { + } else if (_calculatedUnitShares == 0) { + // If calculated unitShares equals 0 set to Drawdown state + return uint8(RebalancingHelperLibrary.State.Drawdown); + } else { // If settleRebalance can be called than endFailedAuction can't be require( _biddingParameters.remainingCurrentSets >= _biddingParameters.minimumBid, "RebalancingSetToken.endFailedAuction: Cannot be called if rebalance is viably completed" ); } - - return uint8(_newRebalanceState); } } \ No newline at end of file diff --git a/test/contracts/core/tokens/rebalancingSetToken.spec.ts b/test/contracts/core/tokens/rebalancingSetToken.spec.ts index be05e6714..2957eab4b 100644 --- a/test/contracts/core/tokens/rebalancingSetToken.spec.ts +++ b/test/contracts/core/tokens/rebalancingSetToken.spec.ts @@ -2104,7 +2104,7 @@ contract('RebalancingSetToken', accounts => { }); }); - describe('when endFailedAuction is called and issueAmount is insufficient', async () => { + describe.only('when endFailedAuction is called and issueAmount is insufficient', async () => { before(async () => { setTokenNaturalUnits = [new BigNumber(10 ** 14), new BigNumber(10 ** 14)]; }); @@ -2134,7 +2134,7 @@ contract('RebalancingSetToken', accounts => { }); }); - describe('when settleRebalance is called with an issuable amount but unitShares is 0', async () => { + describe.only('when settleRebalance is called with an issuable amount but unitShares is 0', async () => { before(async () => { rebalancingSetUnitShares = new BigNumber(1); setTokenNaturalUnits = [new BigNumber(10 ** 14), new BigNumber(10 ** 14)]; From 561e5296f7e99584171e4c81519004221f4382e2 Mon Sep 17 00:00:00 2001 From: bweick Date: Sun, 3 Feb 2019 11:24:14 -0800 Subject: [PATCH 04/12] Fixed tests. --- .../core/tokens/rebalancingSetToken.spec.ts | 6 +++--- .../btcEthRebalancingManager.spec.ts | 15 +++++++-------- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/test/contracts/core/tokens/rebalancingSetToken.spec.ts b/test/contracts/core/tokens/rebalancingSetToken.spec.ts index 2957eab4b..3abb725e1 100644 --- a/test/contracts/core/tokens/rebalancingSetToken.spec.ts +++ b/test/contracts/core/tokens/rebalancingSetToken.spec.ts @@ -1963,7 +1963,7 @@ contract('RebalancingSetToken', accounts => { }); }); - describe.only('#endFailedAuction', async () => { + describe('#endFailedAuction', async () => { let subjectCaller: Address; let proposalPeriod: BigNumber; @@ -2104,7 +2104,7 @@ contract('RebalancingSetToken', accounts => { }); }); - describe.only('when endFailedAuction is called and issueAmount is insufficient', async () => { + describe('when endFailedAuction is called and issueAmount is insufficient', async () => { before(async () => { setTokenNaturalUnits = [new BigNumber(10 ** 14), new BigNumber(10 ** 14)]; }); @@ -2134,7 +2134,7 @@ contract('RebalancingSetToken', accounts => { }); }); - describe.only('when settleRebalance is called with an issuable amount but unitShares is 0', async () => { + describe('when settleRebalance is called with an issuable amount but unitShares is 0', async () => { before(async () => { rebalancingSetUnitShares = new BigNumber(1); setTokenNaturalUnits = [new BigNumber(10 ** 14), new BigNumber(10 ** 14)]; diff --git a/test/contracts/supplementary/btcEthRebalancingManager.spec.ts b/test/contracts/supplementary/btcEthRebalancingManager.spec.ts index 087b930af..3f66747e0 100644 --- a/test/contracts/supplementary/btcEthRebalancingManager.spec.ts +++ b/test/contracts/supplementary/btcEthRebalancingManager.spec.ts @@ -328,6 +328,13 @@ contract('BTCETHRebalancingManager', accounts => { ethPrice, SetTestUtils.generateTimestamp(1000), ); + + // Issue currentSetToken + await coreMock.issue.sendTransactionAsync(initialAllocationToken.address, ether(9), {from: deployerAccount}); + await erc20Wrapper.approveTransfersAsync([initialAllocationToken], transferProxy.address); + + // Use issued currentSetToken to issue rebalancingSetToken + await coreMock.issue.sendTransactionAsync(rebalancingSetToken.address, ether(7)); }); async function subject(): Promise { @@ -681,14 +688,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( From a96f0f6c010fb5c2130a61b2bd5d3b2637441f11 Mon Sep 17 00:00:00 2001 From: bweick Date: Sun, 3 Feb 2019 11:40:19 -0800 Subject: [PATCH 05/12] Fix rebalanceAuctionModule tests. --- .../core/modules/rebalanceAuctionModule.spec.ts | 14 ++++++++++++++ 1 file changed, 14 insertions(+) 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, From b7d60155bbbae9d68fe1e94b7c600cc34c26474f Mon Sep 17 00:00:00 2001 From: bweick Date: Sun, 3 Feb 2019 12:22:10 -0800 Subject: [PATCH 06/12] Add DEFAULT_GAS to fix tests. --- .../supplementary/btcEthRebalancingManager.spec.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/test/contracts/supplementary/btcEthRebalancingManager.spec.ts b/test/contracts/supplementary/btcEthRebalancingManager.spec.ts index 3f66747e0..01576892b 100644 --- a/test/contracts/supplementary/btcEthRebalancingManager.spec.ts +++ b/test/contracts/supplementary/btcEthRebalancingManager.spec.ts @@ -330,11 +330,15 @@ contract('BTCETHRebalancingManager', accounts => { ); // Issue currentSetToken - await coreMock.issue.sendTransactionAsync(initialAllocationToken.address, ether(9), {from: deployerAccount}); + 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)); + await coreMock.issue.sendTransactionAsync(rebalancingSetToken.address, ether(7), { gas: DEFAULT_GAS }); }); async function subject(): Promise { From 0a4e665df27578895c2d096673be38d1d65968dd Mon Sep 17 00:00:00 2001 From: bweick Date: Sun, 3 Feb 2019 12:51:16 -0800 Subject: [PATCH 07/12] Add DEFAULT_GAS to fix tests. --- .../supplementary/btcEthRebalancingManager.spec.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/test/contracts/supplementary/btcEthRebalancingManager.spec.ts b/test/contracts/supplementary/btcEthRebalancingManager.spec.ts index 01576892b..b1d8ff3a8 100644 --- a/test/contracts/supplementary/btcEthRebalancingManager.spec.ts +++ b/test/contracts/supplementary/btcEthRebalancingManager.spec.ts @@ -333,7 +333,7 @@ contract('BTCETHRebalancingManager', accounts => { await coreMock.issue.sendTransactionAsync( initialAllocationToken.address, ether(9), - {from: deployerAccount, gas: DEFAULT_GAS} + {from: deployerAccount, gas: DEFAULT_GAS}, ); await erc20Wrapper.approveTransfersAsync([initialAllocationToken], transferProxy.address); @@ -682,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 () => { @@ -702,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); From 688e8cd8273ebf3a995e54d8cbe298f9f961ab9b Mon Sep 17 00:00:00 2001 From: bweick Date: Mon, 4 Feb 2019 10:07:21 -0800 Subject: [PATCH 08/12] Updated some commments. --- .../rebalancing-libraries/StandardFailAuctionLibrary.sol | 2 +- test/contracts/core/tokens/rebalancingSetToken.spec.ts | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/contracts/core/tokens/rebalancing-libraries/StandardFailAuctionLibrary.sol b/contracts/core/tokens/rebalancing-libraries/StandardFailAuctionLibrary.sol index a38bb4910..90837f93e 100644 --- a/contracts/core/tokens/rebalancing-libraries/StandardFailAuctionLibrary.sol +++ b/contracts/core/tokens/rebalancing-libraries/StandardFailAuctionLibrary.sol @@ -106,7 +106,7 @@ library StandardFailAuctionLibrary { // If calculated unitShares equals 0 set to Drawdown state return uint8(RebalancingHelperLibrary.State.Drawdown); } else { - // If settleRebalance can be called than endFailedAuction can't be + // If settleRebalance can be called then endFailedAuction can't be require( _biddingParameters.remainingCurrentSets >= _biddingParameters.minimumBid, "RebalancingSetToken.endFailedAuction: Cannot be called if rebalance is viably completed" diff --git a/test/contracts/core/tokens/rebalancingSetToken.spec.ts b/test/contracts/core/tokens/rebalancingSetToken.spec.ts index 3abb725e1..b5109fa03 100644 --- a/test/contracts/core/tokens/rebalancingSetToken.spec.ts +++ b/test/contracts/core/tokens/rebalancingSetToken.spec.ts @@ -2035,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, @@ -2104,7 +2104,7 @@ contract('RebalancingSetToken', accounts => { }); }); - describe('when endFailedAuction is called and issueAmount is insufficient', async () => { + describe('and issueAmount is insufficient', async () => { before(async () => { setTokenNaturalUnits = [new BigNumber(10 ** 14), new BigNumber(10 ** 14)]; }); @@ -2134,7 +2134,7 @@ contract('RebalancingSetToken', accounts => { }); }); - describe('when settleRebalance is called with an issuable amount but unitShares is 0', async () => { + describe('but unitShares is 0', async () => { before(async () => { rebalancingSetUnitShares = new BigNumber(1); setTokenNaturalUnits = [new BigNumber(10 ** 14), new BigNumber(10 ** 14)]; From abeb397d8f108264dcc205a37a013d3224598ffd Mon Sep 17 00:00:00 2001 From: bweick Date: Mon, 4 Feb 2019 11:04:33 -0800 Subject: [PATCH 09/12] Change control logic to meet coverage requirements. --- .../rebalancing-libraries/StandardFailAuctionLibrary.sol | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/contracts/core/tokens/rebalancing-libraries/StandardFailAuctionLibrary.sol b/contracts/core/tokens/rebalancing-libraries/StandardFailAuctionLibrary.sol index 90837f93e..a1bccd536 100644 --- a/contracts/core/tokens/rebalancing-libraries/StandardFailAuctionLibrary.sol +++ b/contracts/core/tokens/rebalancing-libraries/StandardFailAuctionLibrary.sol @@ -79,6 +79,7 @@ library StandardFailAuctionLibrary { "RebalanceAuctionModule.endFailedAuction: Can only be called after auction reaches pivot" ); + 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 @@ -97,14 +98,14 @@ library StandardFailAuctionLibrary { ); // Set Rebalance Set Token state to Default - return uint8(RebalancingHelperLibrary.State.Default); + newRebalanceState = uint8(RebalancingHelperLibrary.State.Default); } else { // Set Rebalancing Set Token to Drawdown state - return uint8(RebalancingHelperLibrary.State.Drawdown); + newRebalanceState = uint8(RebalancingHelperLibrary.State.Drawdown); } } else if (_calculatedUnitShares == 0) { // If calculated unitShares equals 0 set to Drawdown state - return uint8(RebalancingHelperLibrary.State.Drawdown); + newRebalanceState = uint8(RebalancingHelperLibrary.State.Drawdown); } else { // If settleRebalance can be called then endFailedAuction can't be require( @@ -112,5 +113,7 @@ library StandardFailAuctionLibrary { "RebalancingSetToken.endFailedAuction: Cannot be called if rebalance is viably completed" ); } + + return newRebalanceState; } } \ No newline at end of file From 0947ad4ca1931c0903582890b05ea236be31ca87 Mon Sep 17 00:00:00 2001 From: bweick Date: Mon, 4 Feb 2019 11:50:44 -0800 Subject: [PATCH 10/12] Consolidate unitShares checking logic. --- .../StandardFailAuctionLibrary.sol | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/contracts/core/tokens/rebalancing-libraries/StandardFailAuctionLibrary.sol b/contracts/core/tokens/rebalancing-libraries/StandardFailAuctionLibrary.sol index a1bccd536..cdd2d7247 100644 --- a/contracts/core/tokens/rebalancing-libraries/StandardFailAuctionLibrary.sol +++ b/contracts/core/tokens/rebalancing-libraries/StandardFailAuctionLibrary.sol @@ -103,15 +103,15 @@ library StandardFailAuctionLibrary { // Set Rebalancing Set Token to Drawdown state newRebalanceState = uint8(RebalancingHelperLibrary.State.Drawdown); } - } else if (_calculatedUnitShares == 0) { - // If calculated unitShares equals 0 set to Drawdown state - newRebalanceState = uint8(RebalancingHelperLibrary.State.Drawdown); - } else { + } else { // If settleRebalance can be called then endFailedAuction can't be require( - _biddingParameters.remainingCurrentSets >= _biddingParameters.minimumBid, + _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 newRebalanceState; From 6e4560e12e4044262e71048c461482c0babf5611 Mon Sep 17 00:00:00 2001 From: felix2feng Date: Tue, 5 Feb 2019 18:24:25 -0800 Subject: [PATCH 11/12] Fix extraneous space --- contracts/supplementary/PayableExchangeIssue.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contracts/supplementary/PayableExchangeIssue.sol b/contracts/supplementary/PayableExchangeIssue.sol index 0fe7b10a3..66e6d7036 100644 --- a/contracts/supplementary/PayableExchangeIssue.sol +++ b/contracts/supplementary/PayableExchangeIssue.sol @@ -129,10 +129,10 @@ contract PayableExchangeIssue is */ function issueRebalancingSetWithEther( address _rebalancingSetAddress, - ExchangeIssueLibrary.ExchangeIssueParams memory _exchangeIssueData, + ExchangeIssueLibrary.ExchangeIssueParams _exchangeIssueData, bytes _orderData ) - public + external payable nonReentrant { From 16ed6fdea0b90fcce00e4f9d8b62cb3c86bd0cd7 Mon Sep 17 00:00:00 2001 From: felix2feng Date: Tue, 5 Feb 2019 18:26:38 -0800 Subject: [PATCH 12/12] Undo commit --- contracts/supplementary/PayableExchangeIssue.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contracts/supplementary/PayableExchangeIssue.sol b/contracts/supplementary/PayableExchangeIssue.sol index 66e6d7036..0fe7b10a3 100644 --- a/contracts/supplementary/PayableExchangeIssue.sol +++ b/contracts/supplementary/PayableExchangeIssue.sol @@ -129,10 +129,10 @@ contract PayableExchangeIssue is */ function issueRebalancingSetWithEther( address _rebalancingSetAddress, - ExchangeIssueLibrary.ExchangeIssueParams _exchangeIssueData, + ExchangeIssueLibrary.ExchangeIssueParams memory _exchangeIssueData, bytes _orderData ) - external + public payable nonReentrant {