From 6fcfb866f4d2df4403f53d1e660a97cbb9cb9ecf Mon Sep 17 00:00:00 2001 From: Brian Weickmann Date: Mon, 20 Aug 2018 15:33:24 -0700 Subject: [PATCH 01/11] Added logic to create token and unit arrays for bidding calcs. Added code to redeem currentSets when rebalance called. Waiting on RebalancingTokenFactory to complete tests. --- contracts/core/RebalancingSetToken.sol | 80 ++++++- contracts/core/extensions/CoreIssuance.sol | 4 +- contracts/core/interfaces/ICore.sol | 12 + package.json | 2 +- test/core/rebalancingSetToken.spec.ts | 256 +++++++++++++++++---- 5 files changed, 308 insertions(+), 46 deletions(-) diff --git a/contracts/core/RebalancingSetToken.sol b/contracts/core/RebalancingSetToken.sol index bfb826aab..087ae052e 100644 --- a/contracts/core/RebalancingSetToken.sol +++ b/contracts/core/RebalancingSetToken.sol @@ -19,9 +19,11 @@ pragma solidity 0.4.24; import { DetailedERC20 } from "zeppelin-solidity/contracts/token/ERC20/DetailedERC20.sol"; import { SafeMath } from "zeppelin-solidity/contracts/math/SafeMath.sol"; import { StandardToken } from "zeppelin-solidity/contracts/token/ERC20/StandardToken.sol"; -import { Bytes32 } from "../lib/Bytes32.sol"; +import { ICore } from "./interfaces/ICore.sol"; import { ISetFactory } from "./interfaces/ISetFactory.sol"; - +import { Bytes32 } from "../lib/Bytes32.sol"; +import { ISetToken } from "./interfaces/ISetToken.sol"; +import { AddressArrayUtils } from "../external/cryptofin/AddressArrayUtils.sol"; /** * @title SetToken @@ -35,6 +37,7 @@ contract RebalancingSetToken is { using SafeMath for uint256; using Bytes32 for bytes32; + using AddressArrayUtils for address[]; /* ============ Enums ============ */ @@ -162,6 +165,7 @@ contract RebalancingSetToken is ) external { + // Make sure it is manager that is proposing the rebalance require(msg.sender == manager); @@ -178,6 +182,9 @@ contract RebalancingSetToken is auctionStartPrice = _auctionStartPrice; auctionPriceDivisor = _auctionPriceDivisor; + // Create token arrays needed for auction + parseUnitArrays(); + // Update state parameters proposalStartTime = block.timestamp; rebalanceState = State.Proposal; @@ -202,6 +209,12 @@ contract RebalancingSetToken is // Be sure the full proposal period has elapsed require(block.timestamp >= proposalStartTime.add(proposalPeriod)); + // Get core address + address core = ISetFactory(factory).core(); + + // Redeem current set held by rebalancing token in vault + ICore(core).redeemInVault(currentSet, unitShares.mul(totalSupply_)); + // Update state parameters auctionStartTime = block.timestamp; rebalanceState = State.Rebalance; @@ -376,4 +389,67 @@ contract RebalancingSetToken is // Use inherited transferFrom function return super.transferFrom(_from, _to, _value); } + + /* ============ Internal Functions ============ */ + function parseUnitArrays() + internal + { + // Create interfaces for interacting with sets + ISetToken currentSetInterface = ISetToken(currentSet); + ISetToken rebalancingSetInterface = ISetToken(rebalancingSet); + + // Create combined token Array + address[] memory oldComponents = currentSetInterface.getComponents(); + address[] memory newComponents = rebalancingSetInterface.getComponents(); + combinedTokenArray = oldComponents.union(newComponents); + + // Get naturalUnit of both sets + uint256 currentSetNaturalUnit = currentSetInterface.naturalUnit(); + uint256 rebalancingSetNaturalUnit = rebalancingSetInterface.naturalUnit(); + + // Get units arrays for both sets + uint256[] memory currentSetUnits = currentSetInterface.getUnits(); + uint256[] memory rebalancingSetUnits = rebalancingSetInterface.getUnits(); + + for (uint16 i=0; i < combinedTokenArray.length; i++) { + // Check if component in arrays and get index if it is + (uint256 indexCurrent, bool isInCurrent) = oldComponents.indexOf(combinedTokenArray[i]); + (uint256 indexRebalance, bool isInRebalance) = newComponents.indexOf(combinedTokenArray[i]); + + // Compute and push unit amounts of token in currentSet, push 0 if not in set + if (isInCurrent) { + combinedCurrentUnits.push( + computeUnits(currentSetUnits[indexCurrent], currentSetNaturalUnit) + ); + } else { + combinedCurrentUnits.push(uint256(0)); + } + + // Compute and push unit amounts of token in rebalancingSet, push 0 if not in set + if (isInRebalance) { + combinedRebalanceUnits.push( + computeUnits(rebalancingSetUnits[indexRebalance], rebalancingSetNaturalUnit) + ); + } else { + combinedRebalanceUnits.push(uint256(0)); + } + } + } + + /** + * Function to calculate the transfer value of a component given quantity of Set + * + * @param _unit The units of the component token + * @param _naturalUnit The natural unit of the Set token + */ + function computeUnits( + uint256 _unit, + uint256 _naturalUnit + ) + internal + returns (uint256) + { + uint256 coefficient = uint256(10) ** uint256(18); + return coefficient.mul(_unit).div(_naturalUnit); + } } diff --git a/contracts/core/extensions/CoreIssuance.sol b/contracts/core/extensions/CoreIssuance.sol index 936bed03b..c78dc1495 100644 --- a/contracts/core/extensions/CoreIssuance.sol +++ b/contracts/core/extensions/CoreIssuance.sol @@ -100,7 +100,7 @@ contract CoreIssuance is * * Normally, you should expect to be able to withdraw all of the tokens. * However, some have central abilities to freeze transfers (e.g. EOS). _toExclude - * allows you to optionally specify which component tokens to exclude when + * allows you to optionally specify which component tokens to exclude when * redeeming. They will remain in the vault under the users' addresses. * * @param _set The address of the Set token @@ -365,6 +365,6 @@ contract CoreIssuance is internal returns (uint256) { - return _quantity.div(_naturalUnit).mul(_componentUnits); + return _quantity.mul(_componentUnits).div(_naturalUnit); } } diff --git a/contracts/core/interfaces/ICore.sol b/contracts/core/interfaces/ICore.sol index e25b4f1db..fb877ead3 100644 --- a/contracts/core/interfaces/ICore.sol +++ b/contracts/core/interfaces/ICore.sol @@ -109,6 +109,18 @@ interface ICore { ) external; + /** + * Function to convert Set Tokens held in vault into underlying components + * + * @param _set The address of the Set token + * @param _quantity The number of tokens to redeem. Should be multiple of natural unit. + */ + function redeemInVault( + address _set, + uint256 _quantity + ) + external; + /** * Deposit multiple tokens to the vault. Quantities should be in the * order of the addresses of the tokens being deposited. diff --git a/package.json b/package.json index b60c1e2fb..67136a23d 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,7 @@ "clean": "rm -rf build; rm -rf transpiled; rm -rf types/generated", "compile": "truffle compile", "prepare-test": "yarn clean && truffle compile --all && yarn run generate-typings && yarn run transpile", - "test": "yarn prepare-test && truffle test `find transpiled/test -name '*.spec.js'`", + "test": "yarn prepare-test && truffle test `find transpiled/test -name 'rebalancingSetToken.spec.js'`", "test-continuous": "truffle test", "transpile": "tsc", "generate-typings": "abi-gen --abis './build/contracts/*.json' --out './types/generated' --template './types/contract_templates/contract.mustache' --partials './types/contract_templates/partials/*.mustache'", diff --git a/test/core/rebalancingSetToken.spec.ts b/test/core/rebalancingSetToken.spec.ts index 18af6700c..2fb11239d 100644 --- a/test/core/rebalancingSetToken.spec.ts +++ b/test/core/rebalancingSetToken.spec.ts @@ -1,3 +1,4 @@ +import * as _ from 'lodash'; import * as ABIDecoder from 'abi-decoder'; import * as chai from 'chai'; import { BigNumber } from 'bignumber.js'; @@ -6,9 +7,13 @@ import { Address } from 'set-protocol-utils'; import ChaiSetup from '../../utils/chaiSetup'; import { BigNumberSetup } from '../../utils/bigNumberSetup'; import { + CoreContract, + SetTokenContract, RebalancingSetTokenContract, - StandardTokenMockContract, SetTokenFactoryContract, + StandardTokenMockContract, + TransferProxyContract, + VaultContract, } from '../../utils/contracts'; import { Blockchain } from '../../utils/blockchain'; import { ether } from '../../utils/units'; @@ -24,10 +29,13 @@ import { expectRevertError, assertTokenBalance } from '../../utils/tokenAssertio import { CoreWrapper } from '../../utils/coreWrapper'; import { ERC20Wrapper } from '../../utils/erc20Wrapper'; + + BigNumberSetup.configure(); ChaiSetup.configure(); const { expect } = chai; const RebalancingSetToken = artifacts.require('RebalancingSetToken'); +const Core = artifacts.require('Core'); contract('RebalancingSetToken', accounts => { const [ @@ -41,6 +49,10 @@ contract('RebalancingSetToken', accounts => { let rebalancingSetToken: RebalancingSetTokenContract; let components: StandardTokenMockContract[] = []; + + let core: CoreContract; + let transferProxy: TransferProxyContract; + let vault: VaultContract; let factory: SetTokenFactoryContract; const coreWrapper = new CoreWrapper(deployerAccount, deployerAccount); @@ -49,14 +61,24 @@ contract('RebalancingSetToken', accounts => { before(async () => { await blockchain.saveSnapshotAsync(); + ABIDecoder.addABI(Core.abi); ABIDecoder.addABI(RebalancingSetToken.abi); }); after(async () => { + ABIDecoder.removeABI(Core.abi); ABIDecoder.removeABI(RebalancingSetToken.abi); await blockchain.revertAsync(); }); + beforeEach(async () => { + transferProxy = await coreWrapper.deployTransferProxyAsync(); + vault = await coreWrapper.deployVaultAsync(); + core = await coreWrapper.deployCoreAsync(transferProxy, vault); + factory = await coreWrapper.deploySetTokenFactoryAsync(core.address); + await coreWrapper.setDefaultStateAndAuthorizationsAsync(core, vault, transferProxy, factory); + }); + describe('#constructor', async () => { let subjectFactory: Address; let subjectManager: Address; @@ -253,21 +275,47 @@ contract('RebalancingSetToken', accounts => { }); describe('#mint', async () => { + let rebalancingSetToken: RebalancingSetTokenContract; let subjectIssuer: Address; let subjectQuantity: BigNumber; let subjectCaller: Address; + let proposalPeriod: BigNumber; - let newRebalancingToken: Address; + let currentSetToken: SetTokenContract; + let newRebalancingSetToken: SetTokenContract; + + const naturalUnit: BigNumber = ether(2); beforeEach(async () => { components = await erc20Wrapper.deployTokensAsync(2, deployerAccount); factory = await coreWrapper.deploySetTokenFactoryAsync(coreAccount); + await erc20Wrapper.approveTransfersAsync(components, transferProxy.address); + + const currentComponentAddresses = _.map(components.slice(0, 2), token => token.address); + const currentComponentUnits = _.map(components.slice(0, 2), () => naturalUnit.mul(2)); // Multiple of naturalUnit + currentSetToken = await coreWrapper.createSetTokenAsync( + core, + factory.address, + currentComponentAddresses, + currentComponentUnits, + naturalUnit, + ); + + const newComponentAddresses = _.map(components.slice(1, 3), token => token.address); + const newComponentUnits = _.map(components.slice(1, 3), () => naturalUnit.mul(1)); // Multiple of naturalUnit + newRebalancingSetToken = await coreWrapper.createSetTokenAsync( + core, + factory.address, + newComponentAddresses, + newComponentUnits, + naturalUnit, + ); const manager = managerAccount; - const initialSet = components[0].address; + const initialSet = currentSetToken.address; const initialUnitShares = ether(1); - const proposalPeriod = new BigNumber(100000); - const rebalanceInterval = new BigNumber(100000); + const rebalanceInterval = new BigNumber(90000); + proposalPeriod = new BigNumber(90000); rebalancingSetToken = await coreWrapper.deployRebalancingSetTokenAsync( factory.address, @@ -278,8 +326,6 @@ contract('RebalancingSetToken', accounts => { rebalanceInterval, ); - newRebalancingToken = components[1].address; - subjectIssuer = deployerAccount, subjectQuantity = ether(5); subjectCaller = coreAccount; @@ -347,7 +393,7 @@ contract('RebalancingSetToken', accounts => { blockchain.increaseTimeAsync(timeFastForward); await rebalancingSetToken.propose.sendTransactionAsync( - newRebalancingToken, + newRebalancingSetToken.address, auctionLibrary, curveCoefficient, auctionStartPrice, @@ -368,21 +414,49 @@ contract('RebalancingSetToken', accounts => { }); describe('#burn', async () => { + let rebalancingSetToken: RebalancingSetTokenContract; let subjectBurner: Address; let subjectQuantity: BigNumber; let subjectCaller: Address; + let proposalPeriod: BigNumber; + + let currentSetToken: SetTokenContract; + let newRebalancingSetToken: SetTokenContract; let newRebalancingToken: Address; + const naturalUnit: BigNumber = ether(2); + beforeEach(async () => { components = await erc20Wrapper.deployTokensAsync(2, deployerAccount); factory = await coreWrapper.deploySetTokenFactoryAsync(coreAccount); + await erc20Wrapper.approveTransfersAsync(components, transferProxy.address); + + const currentComponentAddresses = _.map(components.slice(0, 2), token => token.address); + const currentComponentUnits = _.map(components.slice(0, 2), () => naturalUnit.mul(2)); // Multiple of naturalUnit + currentSetToken = await coreWrapper.createSetTokenAsync( + core, + factory.address, + currentComponentAddresses, + currentComponentUnits, + naturalUnit, + ); + + const newComponentAddresses = _.map(components.slice(1, 3), token => token.address); + const newComponentUnits = _.map(components.slice(1, 3), () => naturalUnit.mul(1)); // Multiple of naturalUnit + newRebalancingSetToken = await coreWrapper.createSetTokenAsync( + core, + factory.address, + newComponentAddresses, + newComponentUnits, + naturalUnit, + ); const manager = managerAccount; - const initialSet = components[0].address; + const initialSet = currentSetToken.address; const initialUnitShares = ether(1); - const proposalPeriod = new BigNumber(100000); - const rebalanceInterval = new BigNumber(100000); + const rebalanceInterval = new BigNumber(90000); + proposalPeriod = new BigNumber(90000); rebalancingSetToken = await coreWrapper.deployRebalancingSetTokenAsync( factory.address, @@ -477,16 +551,16 @@ contract('RebalancingSetToken', accounts => { const caller = managerAccount; const timeFastForward = 100000; + console.log('here'); blockchain.increaseTimeAsync(timeFastForward); await rebalancingSetToken.propose.sendTransactionAsync( - newRebalancingToken, + newRebalancingSetToken.address, auctionLibrary, curveCoefficient, auctionStartPrice, auctionPriceDivisor, { from: caller, gas: DEFAULT_GAS} ); - blockchain.increaseTimeAsync(timeFastForward); await rebalancingSetToken.rebalance.sendTransactionAsync( { from: caller, gas: DEFAULT_GAS } @@ -729,14 +803,38 @@ contract('RebalancingSetToken', accounts => { let subjectTimeFastForward: number; let proposalPeriod: BigNumber; - let newRebalancingToken: Address; + let currentSetToken: SetTokenContract; + let newRebalancingSetToken: SetTokenContract; + + const naturalUnit: BigNumber = ether(2); beforeEach(async () => { components = await erc20Wrapper.deployTokensAsync(2, deployerAccount); factory = await coreWrapper.deploySetTokenFactoryAsync(coreAccount); + await erc20Wrapper.approveTransfersAsync(components, transferProxy.address); + + const currentComponentAddresses = _.map(components.slice(0, 2), token => token.address); + const currentComponentUnits = _.map(components.slice(0, 2), () => naturalUnit.mul(2)); // Multiple of naturalUnit + currentSetToken = await coreWrapper.createSetTokenAsync( + core, + factory.address, + currentComponentAddresses, + currentComponentUnits, + naturalUnit, + ); + + const newComponentAddresses = _.map(components.slice(1, 3), token => token.address); + const newComponentUnits = _.map(components.slice(1, 3), () => naturalUnit.mul(1)); // Multiple of naturalUnit + newRebalancingSetToken = await coreWrapper.createSetTokenAsync( + core, + factory.address, + newComponentAddresses, + newComponentUnits, + naturalUnit, + ); const manager = managerAccount; - const initialSet = components[0].address; + const initialSet = currentSetToken.address; const initialUnitShares = ether(1); const rebalanceInterval = new BigNumber(90000); proposalPeriod = new BigNumber(90000); @@ -750,9 +848,7 @@ contract('RebalancingSetToken', accounts => { rebalanceInterval, ); - newRebalancingToken = components[1].address; - - subjectRebalancingToken = components[1].address; + subjectRebalancingToken = newRebalancingSetToken.address; subjectAuctionLibrary = libraryAccount; subjectCurveCoefficient = ether(1); subjectAuctionStartPrice = ether(5); @@ -816,6 +912,39 @@ contract('RebalancingSetToken', accounts => { expect(newRebalanceState).to.be.bignumber.equal(REBALANCING_STATE.PROPOSAL); }); + it('creates the correct combinedTokenArray', async () => { + const oldSet = await currentSetToken.getComponents.callAsync(); + const newSet = await newRebalancingSetToken.getComponents.callAsync(); + + await subject(); + + const expectedCombinedTokenArray = _.union(oldSet, newSet); + expectedCombinedTokenArray.forEach(async (expectAddress, index) => { + const actualAddress = await rebalancingSetToken.combinedTokenArray.callAsync(new BigNumber(index)); + expect(actualAddress).to.be.bignumber.equal(expectAddress); + }); + }); + + it('creates the correct combinedCurrentUnits', async () => { + await subject(); + + const expectedCombinedCurrentUnits = [ether(2), ether(2), ether(0)]; + expectedCombinedCurrentUnits.forEach(async (expectUnit, index) => { + const actualUnit = await rebalancingSetToken.combinedCurrentUnits.callAsync(new BigNumber(index)); + expect(actualUnit).to.be.bignumber.equal(expectUnit); + }); + }); + + it('creates the correct combinedRebalanceUnits', async () => { + await subject(); + + const expectedCombinedRebalanceUnits = [ether(0), ether(1), ether(1)]; + expectedCombinedRebalanceUnits.forEach(async (expectUnit, index) => { + const actualUnit = await rebalancingSetToken.combinedRebalanceUnits.callAsync(new BigNumber(index)); + expect(actualUnit).to.be.bignumber.equal(expectUnit); + }); + }); + it('emits the correct RebalanceProposed event', async () => { const txHash = await subject(); @@ -865,7 +994,7 @@ contract('RebalancingSetToken', accounts => { blockchain.increaseTimeAsync(timeFastForward); await rebalancingSetToken.propose.sendTransactionAsync( - newRebalancingToken, + newRebalancingSetToken.address, auctionLibrary, curveCoefficient, auctionStartPrice, @@ -890,7 +1019,7 @@ contract('RebalancingSetToken', accounts => { blockchain.increaseTimeAsync(timeFastForward); await rebalancingSetToken.propose.sendTransactionAsync( - newRebalancingToken, + newRebalancingSetToken.address, auctionLibrary, curveCoefficient, auctionStartPrice, @@ -915,20 +1044,43 @@ contract('RebalancingSetToken', accounts => { let subjectTimeFastForward: number; let proposalPeriod: BigNumber; - let initialSet: Address; - let newRebalancingToken: Address; + let currentSetToken: SetTokenContract; + let newRebalancingSetToken: SetTokenContract; + + const naturalUnit: BigNumber = ether(2); beforeEach(async () => { - components = await erc20Wrapper.deployTokensAsync(2, deployerAccount); + components = await erc20Wrapper.deployTokensAsync(3, deployerAccount); + await erc20Wrapper.approveTransfersAsync(components, transferProxy.address); + + const currentComponentAddresses = _.map(components.slice(0, 2), token => token.address); + const currentComponentUnits = _.map(components.slice(0, 2), () => naturalUnit.mul(2)); // Multiple of naturalUnit + currentSetToken = await coreWrapper.createSetTokenAsync( + core, + factory.address, + currentComponentAddresses, + currentComponentUnits, + naturalUnit, + ); + + const newComponentAddresses = _.map(components.slice(1, 3), token => token.address); + const newComponentUnits = _.map(components.slice(1, 3), () => naturalUnit.mul(1)); // Multiple of naturalUnit + newRebalancingSetToken = await coreWrapper.createSetTokenAsync( + core, + factory.address, + newComponentAddresses, + newComponentUnits, + naturalUnit, + ); const manager = managerAccount; + const initialSet = currentSetToken.address; const initialUnitShares = ether(1); const rebalanceInterval = new BigNumber(90000); proposalPeriod = new BigNumber(90000); - initialSet = components[0].address; rebalancingSetToken = await coreWrapper.deployRebalancingSetTokenAsync( - factoryAccount, + factory.address, manager, initialSet, initialUnitShares, @@ -936,7 +1088,6 @@ contract('RebalancingSetToken', accounts => { rebalanceInterval, ); - newRebalancingToken = components[1].address; subjectCaller = managerAccount; subjectTimeFastForward = 100000; }); @@ -965,7 +1116,7 @@ contract('RebalancingSetToken', accounts => { blockchain.increaseTimeAsync(timeFastForward); await rebalancingSetToken.propose.sendTransactionAsync( - newRebalancingToken, + newRebalancingSetToken.address, auctionLibrary, curveCoefficient, auctionStartPrice, @@ -986,8 +1137,8 @@ contract('RebalancingSetToken', accounts => { const formattedLogs = await getFormattedLogsFromTxHash(txHash); const expectedLogs = getExpectedRebalanceStartedLog( - initialSet, - newRebalancingToken, + currentSetToken.address, + newRebalancingSetToken.address, rebalancingSetToken.address, ); @@ -1016,7 +1167,7 @@ contract('RebalancingSetToken', accounts => { blockchain.increaseTimeAsync(timeFastForward); await rebalancingSetToken.propose.sendTransactionAsync( - newRebalancingToken, + newRebalancingSetToken.address, auctionLibrary, curveCoefficient, auctionStartPrice, @@ -1040,29 +1191,51 @@ contract('RebalancingSetToken', accounts => { let subjectCaller: Address; let proposalPeriod: BigNumber; - let initialSet: Address; - let newRebalancingToken: Address; + let currentSetToken: SetTokenContract; + let newRebalancingSetToken: SetTokenContract; + + const naturalUnit: BigNumber = ether(2); beforeEach(async () => { - components = await erc20Wrapper.deployTokensAsync(2, deployerAccount); + components = await erc20Wrapper.deployTokensAsync(3, deployerAccount); + await erc20Wrapper.approveTransfersAsync(components, transferProxy.address); + + const currentComponentAddresses = _.map(components.slice(0, 2), token => token.address); + const currentComponentUnits = _.map(components.slice(0, 2), () => naturalUnit.mul(2)); // Multiple of naturalUnit + currentSetToken = await coreWrapper.createSetTokenAsync( + core, + factory.address, + currentComponentAddresses, + currentComponentUnits, + naturalUnit, + ); + + const newComponentAddresses = _.map(components.slice(1, 3), token => token.address); + const newComponentUnits = _.map(components.slice(1, 3), () => naturalUnit.mul(1)); // Multiple of naturalUnit + newRebalancingSetToken = await coreWrapper.createSetTokenAsync( + core, + factory.address, + newComponentAddresses, + newComponentUnits, + naturalUnit, + ); const manager = managerAccount; + const initialSet = currentSetToken.address; const initialUnitShares = ether(1); const rebalanceInterval = new BigNumber(90000); proposalPeriod = new BigNumber(90000); - initialSet = components[0].address; - - newRebalancingToken = components[1].address; - subjectCaller = managerAccount; rebalancingSetToken = await coreWrapper.deployRebalancingSetTokenAsync( - factoryAccount, + factory.address, manager, initialSet, initialUnitShares, proposalPeriod, rebalanceInterval, ); + + subjectCaller = managerAccount; }); async function subject(): Promise { @@ -1088,7 +1261,7 @@ contract('RebalancingSetToken', accounts => { blockchain.increaseTimeAsync(timeFastForward); await rebalancingSetToken.propose.sendTransactionAsync( - newRebalancingToken, + newRebalancingSetToken.address, auctionLibrary, curveCoefficient, auctionStartPrice, @@ -1112,8 +1285,9 @@ contract('RebalancingSetToken', accounts => { const timeFastForward = 100000; blockchain.increaseTimeAsync(timeFastForward); + await rebalancingSetToken.propose.sendTransactionAsync( - newRebalancingToken, + newRebalancingSetToken.address, auctionLibrary, curveCoefficient, auctionStartPrice, @@ -1138,7 +1312,7 @@ contract('RebalancingSetToken', accounts => { await subject(); const newCurrentSet = await rebalancingSetToken.currentSet.callAsync(); - expect(newCurrentSet).to.equal(newRebalancingToken); + expect(newCurrentSet).to.equal(newRebalancingSetToken.address); }); }); }); From 572281f983348f19788eb52dc09a610e1aeb5474 Mon Sep 17 00:00:00 2001 From: Brian Weickmann Date: Tue, 21 Aug 2018 09:48:12 -0700 Subject: [PATCH 02/11] Integrating RebalancingTokenFactory to tests. --- test/core/rebalancingSetToken.spec.ts | 35 +++++++++++++++++++++++---- utils/coreWrapper.ts | 35 +++++++++++++++++++++++++++ 2 files changed, 65 insertions(+), 5 deletions(-) diff --git a/test/core/rebalancingSetToken.spec.ts b/test/core/rebalancingSetToken.spec.ts index 2fb11239d..ca3af67bf 100644 --- a/test/core/rebalancingSetToken.spec.ts +++ b/test/core/rebalancingSetToken.spec.ts @@ -3,6 +3,7 @@ import * as ABIDecoder from 'abi-decoder'; import * as chai from 'chai'; import { BigNumber } from 'bignumber.js'; import { Address } from 'set-protocol-utils'; +import { SetProtocolUtils as Utils } from 'set-protocol-utils'; import ChaiSetup from '../../utils/chaiSetup'; import { BigNumberSetup } from '../../utils/bigNumberSetup'; @@ -10,6 +11,7 @@ import { CoreContract, SetTokenContract, RebalancingSetTokenContract, + RebalancingSetTokenFactoryContract, SetTokenFactoryContract, StandardTokenMockContract, TransferProxyContract, @@ -54,6 +56,7 @@ contract('RebalancingSetToken', accounts => { let transferProxy: TransferProxyContract; let vault: VaultContract; let factory: SetTokenFactoryContract; + let rebalancingFactory: RebalancingSetTokenFactoryContract; const coreWrapper = new CoreWrapper(deployerAccount, deployerAccount); const erc20Wrapper = new ERC20Wrapper(deployerAccount); @@ -279,7 +282,6 @@ contract('RebalancingSetToken', accounts => { let subjectIssuer: Address; let subjectQuantity: BigNumber; let subjectCaller: Address; - let proposalPeriod: BigNumber; let currentSetToken: SetTokenContract; let newRebalancingSetToken: SetTokenContract; @@ -314,8 +316,11 @@ contract('RebalancingSetToken', accounts => { const manager = managerAccount; const initialSet = currentSetToken.address; const initialUnitShares = ether(1); - const rebalanceInterval = new BigNumber(90000); - proposalPeriod = new BigNumber(90000); + const proposalPeriod = new BigNumber(100000); + const rebalanceInterval = new BigNumber(100000); + + rebalancingFactory = await coreWrapper.deployRebalancingSetTokenFactoryAsync(coreAccount); + await coreWrapper.enableFactoryAsync(core, rebalancingFactory); rebalancingSetToken = await coreWrapper.deployRebalancingSetTokenAsync( factory.address, @@ -382,7 +387,7 @@ contract('RebalancingSetToken', accounts => { }); }); - describe('when mint is called from Rebalance state', async () => { + describe.only('when mint is called from Rebalance state', async () => { beforeEach(async () => { const auctionLibrary = libraryAccount; const curveCoefficient = ether(1); @@ -457,6 +462,11 @@ contract('RebalancingSetToken', accounts => { const initialUnitShares = ether(1); const rebalanceInterval = new BigNumber(90000); proposalPeriod = new BigNumber(90000); + const callData = Utils.bufferArrayToHex([ + Utils.paddedBufferForPrimitive(manager), + Utils.paddedBufferForBigNumber(proposalPeriod), + Utils.paddedBufferForBigNumber(rebalanceInterval), + ]); rebalancingSetToken = await coreWrapper.deployRebalancingSetTokenAsync( factory.address, @@ -551,7 +561,6 @@ contract('RebalancingSetToken', accounts => { const caller = managerAccount; const timeFastForward = 100000; - console.log('here'); blockchain.increaseTimeAsync(timeFastForward); await rebalancingSetToken.propose.sendTransactionAsync( newRebalancingSetToken.address, @@ -561,6 +570,7 @@ contract('RebalancingSetToken', accounts => { auctionPriceDivisor, { from: caller, gas: DEFAULT_GAS} ); + blockchain.increaseTimeAsync(timeFastForward); await rebalancingSetToken.rebalance.sendTransactionAsync( { from: caller, gas: DEFAULT_GAS } @@ -838,6 +848,11 @@ contract('RebalancingSetToken', accounts => { const initialUnitShares = ether(1); const rebalanceInterval = new BigNumber(90000); proposalPeriod = new BigNumber(90000); + const callData = Utils.bufferArrayToHex([ + Utils.paddedBufferForPrimitive(manager), + Utils.paddedBufferForBigNumber(proposalPeriod), + Utils.paddedBufferForBigNumber(rebalanceInterval), + ]); rebalancingSetToken = await coreWrapper.deployRebalancingSetTokenAsync( factory.address, @@ -1078,6 +1093,11 @@ contract('RebalancingSetToken', accounts => { const initialUnitShares = ether(1); const rebalanceInterval = new BigNumber(90000); proposalPeriod = new BigNumber(90000); + const callData = Utils.bufferArrayToHex([ + Utils.paddedBufferForPrimitive(manager), + Utils.paddedBufferForBigNumber(proposalPeriod), + Utils.paddedBufferForBigNumber(rebalanceInterval), + ]); rebalancingSetToken = await coreWrapper.deployRebalancingSetTokenAsync( factory.address, @@ -1225,6 +1245,11 @@ contract('RebalancingSetToken', accounts => { const initialUnitShares = ether(1); const rebalanceInterval = new BigNumber(90000); proposalPeriod = new BigNumber(90000); + const callData = Utils.bufferArrayToHex([ + Utils.paddedBufferForPrimitive(manager), + Utils.paddedBufferForBigNumber(proposalPeriod), + Utils.paddedBufferForBigNumber(rebalanceInterval), + ]); rebalancingSetToken = await coreWrapper.deployRebalancingSetTokenAsync( factory.address, diff --git a/utils/coreWrapper.ts b/utils/coreWrapper.ts index 971c4ca0b..1627b679a 100644 --- a/utils/coreWrapper.ts +++ b/utils/coreWrapper.ts @@ -377,6 +377,41 @@ export class CoreWrapper { ); } + public async createRebalancingTokenAsync( + core: CoreContract, + factory: Address, + componentAddresses: Address[], + units: BigNumber[], + naturalUnit: BigNumber, + name: string = 'Set Token', + symbol: string = 'SET', + callData: string = '', + from: Address = this._tokenOwnerAddress, + ): Promise { + const encodedName = stringToBytes32(name); + const encodedSymbol = stringToBytes32(symbol); + + const txHash = await core.create.sendTransactionAsync( + factory, + componentAddresses, + units, + naturalUnit, + encodedName, + encodedSymbol, + callData, + { from }, + ); + + const logs = await getFormattedLogsFromTxHash(txHash); + const setAddress = extractNewSetTokenAddressFromLogs(logs); + + return await RebalancingTokenContract.at( + setAddress, + web3, + { from } + ); + } + /* ============ CoreAccounting Extension ============ */ public async depositFromUser( From f34d327475457d130a34b05545792a372f6a0887 Mon Sep 17 00:00:00 2001 From: Felix Feng Date: Wed, 22 Aug 2018 10:39:37 -0700 Subject: [PATCH 03/11] Pre-rebase commit (#185) --- contracts/mocks/core/CoreMock.sol | 64 ++++++++++++++ test/core/rebalancingSetToken.spec.ts | 116 +++++++++++++++++++++----- utils/contracts.ts | 2 + utils/coreWrapper.ts | 46 ++++++++-- 4 files changed, 202 insertions(+), 26 deletions(-) create mode 100644 contracts/mocks/core/CoreMock.sol diff --git a/contracts/mocks/core/CoreMock.sol b/contracts/mocks/core/CoreMock.sol new file mode 100644 index 000000000..360b05064 --- /dev/null +++ b/contracts/mocks/core/CoreMock.sol @@ -0,0 +1,64 @@ +pragma solidity 0.4.24; + +import { Core } from "../../core/Core.sol"; +import { ISetToken } from "../../core/interfaces/ISetToken.sol"; + +// Mock contract implementation of Core with extra functions for testing +contract CoreMock is Core { + constructor( + address _transferProxy, + address _vault + ) + public + Core(_transferProxy, _vault) + {} + + /* + * Mint set token for given address. + * Can only be called by authorized contracts. + * + * @param _set The address of the Set to mint + * @param _issuer The address of the issuing account + * @param _quantity The number of sets to attribute to issuer + */ + function mint( + address _set, + address _issuer, + uint256 _quantity + ) + external + { + ISetToken setToken = ISetToken(_set); + + // Issue set token + setToken.mint( + _issuer, + _quantity + ); + } + + /* + * Burn set token for given address. + * Can only be called by authorized contracts. + * + * @param _set The address of the Set to burn + * @param _from The address of the redeeming account + * @param _quantity The number of sets to burn from redeemer + */ + function burn( + address _set, + address _from, + uint256 _quantity + ) + external + { + ISetToken setToken = ISetToken(_set); + + // Issue set token + setToken.burn( + _from, + _quantity + ); + } +} + diff --git a/test/core/rebalancingSetToken.spec.ts b/test/core/rebalancingSetToken.spec.ts index ca3af67bf..c50a9a09a 100644 --- a/test/core/rebalancingSetToken.spec.ts +++ b/test/core/rebalancingSetToken.spec.ts @@ -8,7 +8,7 @@ import { SetProtocolUtils as Utils } from 'set-protocol-utils'; import ChaiSetup from '../../utils/chaiSetup'; import { BigNumberSetup } from '../../utils/bigNumberSetup'; import { - CoreContract, + CoreMockContract, SetTokenContract, RebalancingSetTokenContract, RebalancingSetTokenFactoryContract, @@ -31,8 +31,6 @@ import { expectRevertError, assertTokenBalance } from '../../utils/tokenAssertio import { CoreWrapper } from '../../utils/coreWrapper'; import { ERC20Wrapper } from '../../utils/erc20Wrapper'; - - BigNumberSetup.configure(); ChaiSetup.configure(); const { expect } = chai; @@ -52,7 +50,7 @@ contract('RebalancingSetToken', accounts => { let rebalancingSetToken: RebalancingSetTokenContract; let components: StandardTokenMockContract[] = []; - let core: CoreContract; + let core: CoreMockContract; let transferProxy: TransferProxyContract; let vault: VaultContract; let factory: SetTokenFactoryContract; @@ -77,7 +75,7 @@ contract('RebalancingSetToken', accounts => { beforeEach(async () => { transferProxy = await coreWrapper.deployTransferProxyAsync(); vault = await coreWrapper.deployVaultAsync(); - core = await coreWrapper.deployCoreAsync(transferProxy, vault); + core = await coreWrapper.deployCoreMockAsync(transferProxy, vault); factory = await coreWrapper.deploySetTokenFactoryAsync(core.address); await coreWrapper.setDefaultStateAndAuthorizationsAsync(core, vault, transferProxy, factory); }); @@ -277,14 +275,13 @@ contract('RebalancingSetToken', accounts => { }); }); - describe('#mint', async () => { - let rebalancingSetToken: RebalancingSetTokenContract; + describe('#mint called directly', async () => { + let rebalancingToken: RebalancingSetTokenContract; let subjectIssuer: Address; let subjectQuantity: BigNumber; let subjectCaller: Address; let currentSetToken: SetTokenContract; - let newRebalancingSetToken: SetTokenContract; const naturalUnit: BigNumber = ether(2); @@ -303,16 +300,6 @@ contract('RebalancingSetToken', accounts => { naturalUnit, ); - const newComponentAddresses = _.map(components.slice(1, 3), token => token.address); - const newComponentUnits = _.map(components.slice(1, 3), () => naturalUnit.mul(1)); // Multiple of naturalUnit - newRebalancingSetToken = await coreWrapper.createSetTokenAsync( - core, - factory.address, - newComponentAddresses, - newComponentUnits, - naturalUnit, - ); - const manager = managerAccount; const initialSet = currentSetToken.address; const initialUnitShares = ether(1); @@ -386,8 +373,99 @@ contract('RebalancingSetToken', accounts => { await expectRevertError(subject()); }); }); + }); + + describe('#mint called from Core', async () => { + let rebalancingToken: RebalancingSetTokenContract; + let subjectIssuer: Address; + let subjectQuantity: BigNumber; + let subjectCaller: Address; + + let currentSetToken: SetTokenContract; + let newRebalancingSetToken: SetTokenContract; + + const naturalUnit: BigNumber = ether(2); + + const setName: string = 'Rebalancing Set'; + const setSymbol: string = 'RBSET'; + + beforeEach(async () => { + components = await erc20Wrapper.deployTokensAsync(3, deployerAccount); + await erc20Wrapper.approveTransfersAsync(components, transferProxy.address); + + const currentComponentAddresses = _.map(components.slice(0, 2), token => token.address); + const currentComponentUnits = _.map(components.slice(0, 2), () => naturalUnit.mul(2)); // Multiple of naturalUnit + currentSetToken = await coreWrapper.createSetTokenAsync( + core, + factory.address, + currentComponentAddresses, + currentComponentUnits, + naturalUnit, + ); + + const newComponentAddresses = _.map(components.slice(1, 3), token => token.address); + const newComponentUnits = _.map(components.slice(1, 3), () => naturalUnit.mul(1)); // Multiple of naturalUnit + newRebalancingSetToken = await coreWrapper.createSetTokenAsync( + core, + factory.address, + newComponentAddresses, + newComponentUnits, + naturalUnit, + ); + + const manager = managerAccount; + const initialSet = currentSetToken.address; + const initialUnitShares = ether(1); + const proposalPeriod = new BigNumber(100000); + const rebalanceInterval = new BigNumber(100000); + + rebalancingFactory = await coreWrapper.deployRebalancingTokenFactoryAsync(coreAccount); + await coreWrapper.enableFactoryAsync(core, rebalancingFactory); + + rebalancingToken = await coreWrapper.deployRebalancingTokenAsync( + rebalancingFactory.address, + manager, + initialSet, + initialUnitShares, + proposalPeriod, + rebalanceInterval, + setName, + setSymbol, + ); + + subjectIssuer = deployerAccount, + subjectQuantity = ether(5); + subjectCaller = coreAccount; + }); + + async function subject(): Promise { + console.log( + 'Args', + rebalancingToken.address, + subjectIssuer, + subjectQuantity + ); + + console.log('Subject caller', subjectCaller); + + return core.mint.sendTransactionAsync( + rebalancingToken.address, + subjectIssuer, + subjectQuantity, + { from: subjectCaller, gas: DEFAULT_GAS} + ); + } + + it.only('updates the balances of the user correctly', async () => { + const existingBalance = await rebalancingToken.balanceOf.callAsync(subjectIssuer); + + await subject(); + + const expectedNewBalance = existingBalance.add(subjectQuantity); + assertTokenBalance(rebalancingToken, expectedNewBalance, subjectIssuer); + }); - describe.only('when mint is called from Rebalance state', async () => { + describe('when mint is called from Rebalance state', async () => { beforeEach(async () => { const auctionLibrary = libraryAccount; const curveCoefficient = ether(1); diff --git a/utils/contracts.ts b/utils/contracts.ts index f69ea0b0a..0cc830d0f 100644 --- a/utils/contracts.ts +++ b/utils/contracts.ts @@ -2,6 +2,7 @@ import { AuthorizableContract } from '../types/generated/authorizable'; import { Bytes32MockContract } from '../types/generated/bytes32_mock'; import { CommonMathMockContract } from '../types/generated/common_math_mock'; import { CoreContract } from '../types/generated/core'; +import { CoreMockContract } from '../types/generated/core_mock'; import { ERC20WrapperMockContract } from '../types/generated/erc20_wrapper_mock'; import { InvalidReturnTokenMockContract } from '../types/generated/invalid_return_token_mock'; import { NoXferReturnTokenMockContract } from '../types/generated/no_xfer_return_token_mock'; @@ -24,6 +25,7 @@ export { Bytes32MockContract, CommonMathMockContract, CoreContract, + CoreMockContract, ERC20WrapperMockContract, InvalidReturnTokenMockContract, NoXferReturnTokenMockContract, diff --git a/utils/coreWrapper.ts b/utils/coreWrapper.ts index 1627b679a..697808e1c 100644 --- a/utils/coreWrapper.ts +++ b/utils/coreWrapper.ts @@ -4,6 +4,7 @@ import { SetProtocolUtils, Address } from 'set-protocol-utils'; import { AuthorizableContract, CoreContract, + CoreMockContract, OrderLibraryMockContract, SetTokenContract, RebalancingSetTokenContract, @@ -21,6 +22,7 @@ import { stringToBytes32 } from './encoding'; const Authorizable = artifacts.require('Authorizable'); const Core = artifacts.require('Core'); +const CoreMock = artifacts.require('CoreMock'); const ERC20Wrapper = artifacts.require('ERC20Wrapper'); const OrderLibrary = artifacts.require('OrderLibrary'); const OrderLibraryMock = artifacts.require('OrderLibraryMock'); @@ -31,6 +33,7 @@ const SetTokenFactory = artifacts.require('SetTokenFactory'); const TransferProxy = artifacts.require('TransferProxy'); const Vault = artifacts.require('Vault'); +declare type CoreLikeContract = CoreMockContract | CoreContract; export class CoreWrapper { private _tokenOwnerAddress: Address; @@ -266,11 +269,40 @@ export class CoreWrapper { ); } + public async deployCoreMockAsync( + transferProxy: TransferProxyContract, + vault: VaultContract, + from: Address = this._tokenOwnerAddress + ): Promise { + if (!this._truffleOrderLibrary) { + this._truffleOrderLibrary = await OrderLibrary.new( + { from: this._tokenOwnerAddress }, + ); + } + + await Core.link('OrderLibrary', this._truffleOrderLibrary.address); + const truffleCore = await CoreMock.new( + transferProxy.address, + vault.address, + { from }, + ); + + return new CoreMockContract( + web3.eth.contract(truffleCore.abi).at(truffleCore.address), + { from, gas: DEFAULT_GAS }, + ); + } + /* ============ CoreInternal Extension ============ */ public async enableFactoryAsync( +<<<<<<< HEAD core: CoreContract, setTokenFactory: SetTokenFactoryContract | RebalancingSetTokenFactoryContract, +======= + core: CoreLikeContract, + setTokenFactory: SetTokenFactoryContract | RebalancingTokenFactoryContract, +>>>>>>> Pre-rebase commit (#185) from: Address = this._contractOwnerAddress, ) { await core.enableFactory.sendTransactionAsync( @@ -282,7 +314,7 @@ export class CoreWrapper { /* ============ Authorizable ============ */ public async setDefaultStateAndAuthorizationsAsync( - core: CoreContract, + core: CoreLikeContract, vault: VaultContract, transferProxy: TransferProxyContract, setTokenFactory: SetTokenFactoryContract, @@ -343,7 +375,7 @@ export class CoreWrapper { /* ============ CoreFactory Extension ============ */ public async createSetTokenAsync( - core: CoreContract, + core: CoreLikeContract, factory: Address, componentAddresses: Address[], units: BigNumber[], @@ -378,7 +410,7 @@ export class CoreWrapper { } public async createRebalancingTokenAsync( - core: CoreContract, + core: CoreLikeContract, factory: Address, componentAddresses: Address[], units: BigNumber[], @@ -415,7 +447,7 @@ export class CoreWrapper { /* ============ CoreAccounting Extension ============ */ public async depositFromUser( - core: CoreContract, + core: CoreLikeContract, token: Address, quantity: BigNumber, from: Address = this._contractOwnerAddress, @@ -443,7 +475,7 @@ export class CoreWrapper { /* ============ CoreIssuance Extension ============ */ public async issueSetTokenAsync( - core: CoreContract, + core: CoreLikeContract, token: Address, quantity: BigNumber, from: Address = this._tokenOwnerAddress, @@ -476,7 +508,7 @@ export class CoreWrapper { /* ============ CoreExchangeDispatcher Extension ============ */ public async registerDefaultExchanges( - core: CoreContract, + core: CoreLikeContract, from: Address = this._contractOwnerAddress, ) { const approvePromises = _.map(_.values(SetProtocolUtils.EXCHANGES), exchangeId => @@ -486,7 +518,7 @@ export class CoreWrapper { } public async registerExchange( - core: CoreContract, + core: CoreLikeContract, exchangeId: number, exchangeAddress: Address, from: Address = this._contractOwnerAddress, From ed95a4c6b042a47f4c78f20fb8db2eaa70e0581a Mon Sep 17 00:00:00 2001 From: felix2feng Date: Wed, 22 Aug 2018 11:12:25 -0700 Subject: [PATCH 04/11] Finish renaming --- test/core/rebalancingSetToken.spec.ts | 6 +++--- utils/coreWrapper.ts | 11 +++-------- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/test/core/rebalancingSetToken.spec.ts b/test/core/rebalancingSetToken.spec.ts index c50a9a09a..7fb06bbaa 100644 --- a/test/core/rebalancingSetToken.spec.ts +++ b/test/core/rebalancingSetToken.spec.ts @@ -419,10 +419,10 @@ contract('RebalancingSetToken', accounts => { const proposalPeriod = new BigNumber(100000); const rebalanceInterval = new BigNumber(100000); - rebalancingFactory = await coreWrapper.deployRebalancingTokenFactoryAsync(coreAccount); + rebalancingFactory = await coreWrapper.deployRebalancingSetTokenFactoryAsync(coreAccount); await coreWrapper.enableFactoryAsync(core, rebalancingFactory); - rebalancingToken = await coreWrapper.deployRebalancingTokenAsync( + rebalancingToken = await coreWrapper.deployRebalancingSetTokenAsync( rebalancingFactory.address, manager, initialSet, @@ -456,7 +456,7 @@ contract('RebalancingSetToken', accounts => { ); } - it.only('updates the balances of the user correctly', async () => { + it('updates the balances of the user correctly', async () => { const existingBalance = await rebalancingToken.balanceOf.callAsync(subjectIssuer); await subject(); diff --git a/utils/coreWrapper.ts b/utils/coreWrapper.ts index 697808e1c..068dd5f9d 100644 --- a/utils/coreWrapper.ts +++ b/utils/coreWrapper.ts @@ -296,13 +296,8 @@ export class CoreWrapper { /* ============ CoreInternal Extension ============ */ public async enableFactoryAsync( -<<<<<<< HEAD - core: CoreContract, - setTokenFactory: SetTokenFactoryContract | RebalancingSetTokenFactoryContract, -======= core: CoreLikeContract, - setTokenFactory: SetTokenFactoryContract | RebalancingTokenFactoryContract, ->>>>>>> Pre-rebase commit (#185) + setTokenFactory: SetTokenFactoryContract | RebalancingSetTokenFactoryContract, from: Address = this._contractOwnerAddress, ) { await core.enableFactory.sendTransactionAsync( @@ -419,7 +414,7 @@ export class CoreWrapper { symbol: string = 'SET', callData: string = '', from: Address = this._tokenOwnerAddress, - ): Promise { + ): Promise { const encodedName = stringToBytes32(name); const encodedSymbol = stringToBytes32(symbol); @@ -437,7 +432,7 @@ export class CoreWrapper { const logs = await getFormattedLogsFromTxHash(txHash); const setAddress = extractNewSetTokenAddressFromLogs(logs); - return await RebalancingTokenContract.at( + return await RebalancingSetTokenContract.at( setAddress, web3, { from } From f1ee0af848e36d171961380c3a4cafd7c702722c Mon Sep 17 00:00:00 2001 From: Brian Weickmann Date: Wed, 22 Aug 2018 16:02:11 -0700 Subject: [PATCH 05/11] Tests fixed using CoreMock. --- package.json | 2 +- test/core/rebalancingSetToken.spec.ts | 271 +++++++++++++++++--------- utils/coreWrapper.ts | 4 +- 3 files changed, 184 insertions(+), 93 deletions(-) diff --git a/package.json b/package.json index 67136a23d..b60c1e2fb 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,7 @@ "clean": "rm -rf build; rm -rf transpiled; rm -rf types/generated", "compile": "truffle compile", "prepare-test": "yarn clean && truffle compile --all && yarn run generate-typings && yarn run transpile", - "test": "yarn prepare-test && truffle test `find transpiled/test -name 'rebalancingSetToken.spec.js'`", + "test": "yarn prepare-test && truffle test `find transpiled/test -name '*.spec.js'`", "test-continuous": "truffle test", "transpile": "tsc", "generate-typings": "abi-gen --abis './build/contracts/*.json' --out './types/generated' --template './types/contract_templates/contract.mustache' --partials './types/contract_templates/partials/*.mustache'", diff --git a/test/core/rebalancingSetToken.spec.ts b/test/core/rebalancingSetToken.spec.ts index 7fb06bbaa..2ce32794e 100644 --- a/test/core/rebalancingSetToken.spec.ts +++ b/test/core/rebalancingSetToken.spec.ts @@ -35,7 +35,10 @@ BigNumberSetup.configure(); ChaiSetup.configure(); const { expect } = chai; const RebalancingSetToken = artifacts.require('RebalancingSetToken'); -const Core = artifacts.require('Core'); +const Core = artifacts.require('CoreMock'); + +// import { injectInTruffle } from 'sol-trace-set'; +// injectInTruffle(web3, artifacts); contract('RebalancingSetToken', accounts => { const [ @@ -77,7 +80,10 @@ contract('RebalancingSetToken', accounts => { vault = await coreWrapper.deployVaultAsync(); core = await coreWrapper.deployCoreMockAsync(transferProxy, vault); factory = await coreWrapper.deploySetTokenFactoryAsync(core.address); + rebalancingFactory = await coreWrapper.deployRebalancingSetTokenFactoryAsync(core.address); + await coreWrapper.setDefaultStateAndAuthorizationsAsync(core, vault, transferProxy, factory); + await coreWrapper.enableFactoryAsync(core, rebalancingFactory); }); describe('#constructor', async () => { @@ -276,7 +282,6 @@ contract('RebalancingSetToken', accounts => { }); describe('#mint called directly', async () => { - let rebalancingToken: RebalancingSetTokenContract; let subjectIssuer: Address; let subjectQuantity: BigNumber; let subjectCaller: Address; @@ -287,7 +292,6 @@ contract('RebalancingSetToken', accounts => { beforeEach(async () => { components = await erc20Wrapper.deployTokensAsync(2, deployerAccount); - factory = await coreWrapper.deploySetTokenFactoryAsync(coreAccount); await erc20Wrapper.approveTransfersAsync(components, transferProxy.address); const currentComponentAddresses = _.map(components.slice(0, 2), token => token.address); @@ -310,7 +314,7 @@ contract('RebalancingSetToken', accounts => { await coreWrapper.enableFactoryAsync(core, rebalancingFactory); rebalancingSetToken = await coreWrapper.deployRebalancingSetTokenAsync( - factory.address, + rebalancingFactory.address, manager, initialSet, initialUnitShares, @@ -419,9 +423,6 @@ contract('RebalancingSetToken', accounts => { const proposalPeriod = new BigNumber(100000); const rebalanceInterval = new BigNumber(100000); - rebalancingFactory = await coreWrapper.deployRebalancingSetTokenFactoryAsync(coreAccount); - await coreWrapper.enableFactoryAsync(core, rebalancingFactory); - rebalancingToken = await coreWrapper.deployRebalancingSetTokenAsync( rebalancingFactory.address, manager, @@ -435,19 +436,10 @@ contract('RebalancingSetToken', accounts => { subjectIssuer = deployerAccount, subjectQuantity = ether(5); - subjectCaller = coreAccount; + subjectCaller = managerAccount; }); async function subject(): Promise { - console.log( - 'Args', - rebalancingToken.address, - subjectIssuer, - subjectQuantity - ); - - console.log('Subject caller', subjectCaller); - return core.mint.sendTransactionAsync( rebalancingToken.address, subjectIssuer, @@ -474,8 +466,8 @@ contract('RebalancingSetToken', accounts => { const caller = managerAccount; const timeFastForward = 100000; - blockchain.increaseTimeAsync(timeFastForward); - await rebalancingSetToken.propose.sendTransactionAsync( + await blockchain.increaseTimeAsync(timeFastForward); + await rebalancingToken.propose.sendTransactionAsync( newRebalancingSetToken.address, auctionLibrary, curveCoefficient, @@ -484,8 +476,8 @@ contract('RebalancingSetToken', accounts => { { from: caller, gas: DEFAULT_GAS} ); - blockchain.increaseTimeAsync(timeFastForward); - await rebalancingSetToken.rebalance.sendTransactionAsync( + await blockchain.increaseTimeAsync(timeFastForward); + await rebalancingToken.rebalance.sendTransactionAsync( { from: caller, gas: DEFAULT_GAS } ); }); @@ -496,23 +488,126 @@ contract('RebalancingSetToken', accounts => { }); }); - describe('#burn', async () => { + describe('#burn called directly', async () => { + let subjectIssuer: Address; + let subjectQuantity: BigNumber; + let subjectCaller: Address; + + let currentSetToken: SetTokenContract; + + const naturalUnit: BigNumber = ether(2); + + beforeEach(async () => { + components = await erc20Wrapper.deployTokensAsync(2, deployerAccount); + await erc20Wrapper.approveTransfersAsync(components, transferProxy.address); + + const currentComponentAddresses = _.map(components.slice(0, 2), token => token.address); + const currentComponentUnits = _.map(components.slice(0, 2), () => naturalUnit.mul(2)); // Multiple of naturalUnit + currentSetToken = await coreWrapper.createSetTokenAsync( + core, + factory.address, + currentComponentAddresses, + currentComponentUnits, + naturalUnit, + ); + + const manager = managerAccount; + const initialSet = currentSetToken.address; + const initialUnitShares = ether(1); + const proposalPeriod = new BigNumber(100000); + const rebalanceInterval = new BigNumber(100000); + + rebalancingFactory = await coreWrapper.deployRebalancingSetTokenFactoryAsync(coreAccount); + await coreWrapper.enableFactoryAsync(core, rebalancingFactory); + + rebalancingSetToken = await coreWrapper.deployRebalancingSetTokenAsync( + rebalancingFactory.address, + manager, + initialSet, + initialUnitShares, + proposalPeriod, + rebalanceInterval, + ); + + subjectIssuer = deployerAccount, + subjectQuantity = ether(5); + subjectCaller = coreAccount; + + return rebalancingSetToken.mint.sendTransactionAsync( + subjectIssuer, + subjectQuantity, + { from: subjectCaller, gas: DEFAULT_GAS} + ); + }); + + async function subject(): Promise { + return rebalancingSetToken.burn.sendTransactionAsync( + subjectIssuer, + subjectQuantity, + { from: subjectCaller, gas: DEFAULT_GAS} + ); + } + + it('updates the balances of the user correctly', async () => { + const existingBalance = await rebalancingSetToken.balanceOf.callAsync(subjectIssuer); + + await subject(); + + const expectedNewBalance = existingBalance.sub(subjectQuantity); + assertTokenBalance(rebalancingSetToken, expectedNewBalance, subjectIssuer); + }); + + it('updates the totalSupply_ correctly', async () => { + const existingTokenSupply = await rebalancingSetToken.totalSupply.callAsync(); + + await subject(); + + const expectedTokenSupply = existingTokenSupply.sub(subjectQuantity); + const newTokenSupply = await rebalancingSetToken.totalSupply.callAsync(); + expect(newTokenSupply).to.be.bignumber.equal(expectedTokenSupply); + }); + + it('emits a Transfer log denoting a burning', async () => { + const txHash = await subject(); + + const formattedLogs = await getFormattedLogsFromTxHash(txHash); + const expectedLogs = getExpectedTransferLog( + subjectIssuer, + NULL_ADDRESS, + subjectQuantity, + rebalancingSetToken.address + ); + + await assertLogEquivalence(formattedLogs, expectedLogs); + }); + + describe('when the caller is not Core', async () => { + beforeEach(async () => { + subjectCaller = otherAccount; + }); + + it('should revert', async () => { + await expectRevertError(subject()); + }); + }); + }); + + describe('#burn called from Core', async () => { let rebalancingSetToken: RebalancingSetTokenContract; let subjectBurner: Address; let subjectQuantity: BigNumber; let subjectCaller: Address; - let proposalPeriod: BigNumber; let currentSetToken: SetTokenContract; let newRebalancingSetToken: SetTokenContract; - let newRebalancingToken: Address; - const naturalUnit: BigNumber = ether(2); + const setName: string = 'Rebalancing Set'; + const setSymbol: string = 'RBSET'; + beforeEach(async () => { - components = await erc20Wrapper.deployTokensAsync(2, deployerAccount); - factory = await coreWrapper.deploySetTokenFactoryAsync(coreAccount); + components = await erc20Wrapper.deployTokensAsync(3, deployerAccount); await erc20Wrapper.approveTransfersAsync(components, transferProxy.address); const currentComponentAddresses = _.map(components.slice(0, 2), token => token.address); @@ -538,31 +633,29 @@ contract('RebalancingSetToken', accounts => { const manager = managerAccount; const initialSet = currentSetToken.address; const initialUnitShares = ether(1); - const rebalanceInterval = new BigNumber(90000); - proposalPeriod = new BigNumber(90000); - const callData = Utils.bufferArrayToHex([ - Utils.paddedBufferForPrimitive(manager), - Utils.paddedBufferForBigNumber(proposalPeriod), - Utils.paddedBufferForBigNumber(rebalanceInterval), - ]); + const proposalPeriod = new BigNumber(100000); + const rebalanceInterval = new BigNumber(100000); rebalancingSetToken = await coreWrapper.deployRebalancingSetTokenAsync( - factory.address, + rebalancingFactory.address, manager, initialSet, initialUnitShares, proposalPeriod, rebalanceInterval, + setName, + setSymbol, ); const mintedQuantity = ether(5); subjectBurner = deployerAccount, subjectQuantity = ether(5); - subjectCaller = coreAccount; + subjectCaller = managerAccount; - newRebalancingToken = components[1].address; + newRebalancingToken = newRebalancingSetToken.address; - await rebalancingSetToken.mint.sendTransactionAsync( + await core.mint.sendTransactionAsync( + rebalancingSetToken.address, subjectBurner, mintedQuantity, { from: subjectCaller, gas: DEFAULT_GAS} @@ -570,7 +663,8 @@ contract('RebalancingSetToken', accounts => { }); async function subject(): Promise { - return rebalancingSetToken.burn.sendTransactionAsync( + return core.burn.sendTransactionAsync( + rebalancingSetToken.address, subjectBurner, subjectQuantity, { from: subjectCaller, gas: DEFAULT_GAS} @@ -610,16 +704,6 @@ contract('RebalancingSetToken', accounts => { await assertLogEquivalence(formattedLogs, expectedLogs); }); - describe('when the caller is not Core', async () => { - beforeEach(async () => { - subjectCaller = otherAccount; - }); - - it('should revert', async () => { - await expectRevertError(subject()); - }); - }); - describe('when the user does not have enough shares to burn', async () => { beforeEach(async () => { subjectQuantity = ether(10); @@ -639,7 +723,16 @@ contract('RebalancingSetToken', accounts => { const caller = managerAccount; const timeFastForward = 100000; - blockchain.increaseTimeAsync(timeFastForward); + // Must burn otherwise won't get through rebalance call + // TO DO: Instead of mint call issue in set up. + core.burn.sendTransactionAsync( + rebalancingSetToken.address, + subjectBurner, + subjectQuantity, + { from: subjectCaller, gas: DEFAULT_GAS} + ); + + await blockchain.increaseTimeAsync(timeFastForward); await rebalancingSetToken.propose.sendTransactionAsync( newRebalancingSetToken.address, auctionLibrary, @@ -649,7 +742,7 @@ contract('RebalancingSetToken', accounts => { { from: caller, gas: DEFAULT_GAS} ); - blockchain.increaseTimeAsync(timeFastForward); + await blockchain.increaseTimeAsync(timeFastForward); await rebalancingSetToken.rebalance.sendTransactionAsync( { from: caller, gas: DEFAULT_GAS } ); @@ -897,8 +990,7 @@ contract('RebalancingSetToken', accounts => { const naturalUnit: BigNumber = ether(2); beforeEach(async () => { - components = await erc20Wrapper.deployTokensAsync(2, deployerAccount); - factory = await coreWrapper.deploySetTokenFactoryAsync(coreAccount); + components = await erc20Wrapper.deployTokensAsync(3, deployerAccount); await erc20Wrapper.approveTransfersAsync(components, transferProxy.address); const currentComponentAddresses = _.map(components.slice(0, 2), token => token.address); @@ -923,7 +1015,7 @@ contract('RebalancingSetToken', accounts => { const manager = managerAccount; const initialSet = currentSetToken.address; - const initialUnitShares = ether(1); + const initialUnitShares = new BigNumber(1); const rebalanceInterval = new BigNumber(90000); proposalPeriod = new BigNumber(90000); const callData = Utils.bufferArrayToHex([ @@ -932,13 +1024,13 @@ contract('RebalancingSetToken', accounts => { Utils.paddedBufferForBigNumber(rebalanceInterval), ]); - rebalancingSetToken = await coreWrapper.deployRebalancingSetTokenAsync( - factory.address, - manager, - initialSet, - initialUnitShares, - proposalPeriod, - rebalanceInterval, + rebalancingSetToken = await coreWrapper.createRebalancingTokenAsync( + core, + rebalancingFactory.address, + [initialSet], + [initialUnitShares], + new BigNumber(1), + callData, ); subjectRebalancingToken = newRebalancingSetToken.address; @@ -1052,7 +1144,7 @@ contract('RebalancingSetToken', accounts => { rebalancingSetToken.address, ); - await assertLogEquivalence(formattedLogs, expectedLogs); + await assertLogEquivalence(formattedLogs, expectedLogs); }); describe('but the rebalance interval has not elapsed', async () => { @@ -1085,7 +1177,7 @@ contract('RebalancingSetToken', accounts => { const caller = managerAccount; const timeFastForward = 100000; - blockchain.increaseTimeAsync(timeFastForward); + await blockchain.increaseTimeAsync(timeFastForward); await rebalancingSetToken.propose.sendTransactionAsync( newRebalancingSetToken.address, auctionLibrary, @@ -1110,7 +1202,7 @@ contract('RebalancingSetToken', accounts => { const caller = managerAccount; const timeFastForward = 100000; - blockchain.increaseTimeAsync(timeFastForward); + await blockchain.increaseTimeAsync(timeFastForward); await rebalancingSetToken.propose.sendTransactionAsync( newRebalancingSetToken.address, auctionLibrary, @@ -1120,7 +1212,7 @@ contract('RebalancingSetToken', accounts => { { from: caller, gas: DEFAULT_GAS} ); - blockchain.increaseTimeAsync(timeFastForward); + await blockchain.increaseTimeAsync(timeFastForward); await rebalancingSetToken.rebalance.sendTransactionAsync( { from: caller, gas: DEFAULT_GAS } ); @@ -1168,7 +1260,7 @@ contract('RebalancingSetToken', accounts => { const manager = managerAccount; const initialSet = currentSetToken.address; - const initialUnitShares = ether(1); + const initialUnitShares = new BigNumber(1); const rebalanceInterval = new BigNumber(90000); proposalPeriod = new BigNumber(90000); const callData = Utils.bufferArrayToHex([ @@ -1177,13 +1269,13 @@ contract('RebalancingSetToken', accounts => { Utils.paddedBufferForBigNumber(rebalanceInterval), ]); - rebalancingSetToken = await coreWrapper.deployRebalancingSetTokenAsync( - factory.address, - manager, - initialSet, - initialUnitShares, - proposalPeriod, - rebalanceInterval, + rebalancingSetToken = await coreWrapper.createRebalancingTokenAsync( + core, + rebalancingFactory.address, + [initialSet], + [initialUnitShares], + new BigNumber(1), + callData, ); subjectCaller = managerAccount; @@ -1191,7 +1283,7 @@ contract('RebalancingSetToken', accounts => { }); async function subject(): Promise { - blockchain.increaseTimeAsync(subjectTimeFastForward); + await blockchain.increaseTimeAsync(subjectTimeFastForward); return rebalancingSetToken.rebalance.sendTransactionAsync( { from: subjectCaller, gas: DEFAULT_GAS} ); @@ -1212,7 +1304,7 @@ contract('RebalancingSetToken', accounts => { const caller = managerAccount; const timeFastForward = 100000; - blockchain.increaseTimeAsync(timeFastForward); + await blockchain.increaseTimeAsync(timeFastForward); await rebalancingSetToken.propose.sendTransactionAsync( newRebalancingSetToken.address, auctionLibrary, @@ -1263,7 +1355,7 @@ contract('RebalancingSetToken', accounts => { const caller = managerAccount; const timeFastForward = 100000; - blockchain.increaseTimeAsync(timeFastForward); + await blockchain.increaseTimeAsync(timeFastForward); await rebalancingSetToken.propose.sendTransactionAsync( newRebalancingSetToken.address, auctionLibrary, @@ -1273,7 +1365,7 @@ contract('RebalancingSetToken', accounts => { { from: caller, gas: DEFAULT_GAS} ); - blockchain.increaseTimeAsync(timeFastForward); + await blockchain.increaseTimeAsync(timeFastForward); await rebalancingSetToken.rebalance.sendTransactionAsync( { from: caller, gas: DEFAULT_GAS } ); @@ -1295,7 +1387,7 @@ contract('RebalancingSetToken', accounts => { const naturalUnit: BigNumber = ether(2); beforeEach(async () => { - components = await erc20Wrapper.deployTokensAsync(3, deployerAccount); + components = await erc20Wrapper.deployTokensAsync(3, deployerAccount); await erc20Wrapper.approveTransfersAsync(components, transferProxy.address); const currentComponentAddresses = _.map(components.slice(0, 2), token => token.address); @@ -1320,7 +1412,7 @@ contract('RebalancingSetToken', accounts => { const manager = managerAccount; const initialSet = currentSetToken.address; - const initialUnitShares = ether(1); + const initialUnitShares = new BigNumber(1); const rebalanceInterval = new BigNumber(90000); proposalPeriod = new BigNumber(90000); const callData = Utils.bufferArrayToHex([ @@ -1329,13 +1421,13 @@ contract('RebalancingSetToken', accounts => { Utils.paddedBufferForBigNumber(rebalanceInterval), ]); - rebalancingSetToken = await coreWrapper.deployRebalancingSetTokenAsync( - factory.address, - manager, - initialSet, - initialUnitShares, - proposalPeriod, - rebalanceInterval, + rebalancingSetToken = await coreWrapper.createRebalancingTokenAsync( + core, + rebalancingFactory.address, + [initialSet], + [initialUnitShares], + new BigNumber(1), + callData, ); subjectCaller = managerAccount; @@ -1362,7 +1454,7 @@ contract('RebalancingSetToken', accounts => { const caller = managerAccount; const timeFastForward = 100000; - blockchain.increaseTimeAsync(timeFastForward); + await blockchain.increaseTimeAsync(timeFastForward); await rebalancingSetToken.propose.sendTransactionAsync( newRebalancingSetToken.address, auctionLibrary, @@ -1387,8 +1479,7 @@ contract('RebalancingSetToken', accounts => { const caller = managerAccount; const timeFastForward = 100000; - blockchain.increaseTimeAsync(timeFastForward); - + await blockchain.increaseTimeAsync(timeFastForward); await rebalancingSetToken.propose.sendTransactionAsync( newRebalancingSetToken.address, auctionLibrary, @@ -1398,7 +1489,7 @@ contract('RebalancingSetToken', accounts => { { from: caller, gas: DEFAULT_GAS} ); - blockchain.increaseTimeAsync(timeFastForward); + await blockchain.increaseTimeAsync(timeFastForward); await rebalancingSetToken.rebalance.sendTransactionAsync( { from: caller, gas: DEFAULT_GAS } ); diff --git a/utils/coreWrapper.ts b/utils/coreWrapper.ts index 068dd5f9d..cbfb567f0 100644 --- a/utils/coreWrapper.ts +++ b/utils/coreWrapper.ts @@ -410,9 +410,9 @@ export class CoreWrapper { componentAddresses: Address[], units: BigNumber[], naturalUnit: BigNumber, - name: string = 'Set Token', - symbol: string = 'SET', callData: string = '', + name: string = 'Rebalancing Set Token', + symbol: string = 'RBSET', from: Address = this._tokenOwnerAddress, ): Promise { const encodedName = stringToBytes32(name); From 54a67e07fd5efc31e059f31b5bfb2af58adb97f2 Mon Sep 17 00:00:00 2001 From: Brian Weickmann Date: Wed, 22 Aug 2018 16:06:06 -0700 Subject: [PATCH 06/11] Removing unused var. --- test/core/rebalancingSetToken.spec.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/core/rebalancingSetToken.spec.ts b/test/core/rebalancingSetToken.spec.ts index 2ce32794e..e59505c0d 100644 --- a/test/core/rebalancingSetToken.spec.ts +++ b/test/core/rebalancingSetToken.spec.ts @@ -652,8 +652,6 @@ contract('RebalancingSetToken', accounts => { subjectQuantity = ether(5); subjectCaller = managerAccount; - newRebalancingToken = newRebalancingSetToken.address; - await core.mint.sendTransactionAsync( rebalancingSetToken.address, subjectBurner, From fd37b354ceeae069e6865b51c5f74cd07768286c Mon Sep 17 00:00:00 2001 From: Brian Weickmann Date: Wed, 22 Aug 2018 16:48:31 -0700 Subject: [PATCH 07/11] Minor renaming fixes in tests. Changed loop counter from uint16 -> uint256 on rebalancing set token. --- .node-xmlhttprequest-sync-8770 | 0 contracts/core/RebalancingSetToken.sol | 3 ++- test/core/rebalancingSetToken.spec.ts | 8 +++----- 3 files changed, 5 insertions(+), 6 deletions(-) delete mode 100644 .node-xmlhttprequest-sync-8770 diff --git a/.node-xmlhttprequest-sync-8770 b/.node-xmlhttprequest-sync-8770 deleted file mode 100644 index e69de29bb..000000000 diff --git a/contracts/core/RebalancingSetToken.sol b/contracts/core/RebalancingSetToken.sol index 087ae052e..eb1d954bb 100644 --- a/contracts/core/RebalancingSetToken.sol +++ b/contracts/core/RebalancingSetToken.sol @@ -25,6 +25,7 @@ import { Bytes32 } from "../lib/Bytes32.sol"; import { ISetToken } from "./interfaces/ISetToken.sol"; import { AddressArrayUtils } from "../external/cryptofin/AddressArrayUtils.sol"; + /** * @title SetToken * @author Set Protocol @@ -411,7 +412,7 @@ contract RebalancingSetToken is uint256[] memory currentSetUnits = currentSetInterface.getUnits(); uint256[] memory rebalancingSetUnits = rebalancingSetInterface.getUnits(); - for (uint16 i=0; i < combinedTokenArray.length; i++) { + for (uint256 i=0; i < combinedTokenArray.length; i++) { // Check if component in arrays and get index if it is (uint256 indexCurrent, bool isInCurrent) = oldComponents.indexOf(combinedTokenArray[i]); (uint256 indexRebalance, bool isInRebalance) = newComponents.indexOf(combinedTokenArray[i]); diff --git a/test/core/rebalancingSetToken.spec.ts b/test/core/rebalancingSetToken.spec.ts index e59505c0d..bba9fb396 100644 --- a/test/core/rebalancingSetToken.spec.ts +++ b/test/core/rebalancingSetToken.spec.ts @@ -35,10 +35,8 @@ BigNumberSetup.configure(); ChaiSetup.configure(); const { expect } = chai; const RebalancingSetToken = artifacts.require('RebalancingSetToken'); -const Core = artifacts.require('CoreMock'); +const CoreMock = artifacts.require('CoreMock'); -// import { injectInTruffle } from 'sol-trace-set'; -// injectInTruffle(web3, artifacts); contract('RebalancingSetToken', accounts => { const [ @@ -65,12 +63,12 @@ contract('RebalancingSetToken', accounts => { before(async () => { await blockchain.saveSnapshotAsync(); - ABIDecoder.addABI(Core.abi); + ABIDecoder.addABI(CoreMock.abi); ABIDecoder.addABI(RebalancingSetToken.abi); }); after(async () => { - ABIDecoder.removeABI(Core.abi); + ABIDecoder.removeABI(CoreMock.abi); ABIDecoder.removeABI(RebalancingSetToken.abi); await blockchain.revertAsync(); }); From 0f73195ecd65b8fb9a412c9437a945add6525dac Mon Sep 17 00:00:00 2001 From: Brian Weickmann Date: Thu, 23 Aug 2018 11:03:44 -0700 Subject: [PATCH 08/11] Rebalancing tests refactor. Moved common functions into RebalancingTokenWrapper. --- test/core/rebalancingSetToken.spec.ts | 642 +++++++------------------- utils/RebalancingTokenWrapper.ts | 144 ++++++ utils/constants.ts | 7 + 3 files changed, 330 insertions(+), 463 deletions(-) create mode 100644 utils/RebalancingTokenWrapper.ts diff --git a/test/core/rebalancingSetToken.spec.ts b/test/core/rebalancingSetToken.spec.ts index bba9fb396..ac305bc25 100644 --- a/test/core/rebalancingSetToken.spec.ts +++ b/test/core/rebalancingSetToken.spec.ts @@ -3,7 +3,6 @@ import * as ABIDecoder from 'abi-decoder'; import * as chai from 'chai'; import { BigNumber } from 'bignumber.js'; import { Address } from 'set-protocol-utils'; -import { SetProtocolUtils as Utils } from 'set-protocol-utils'; import ChaiSetup from '../../utils/chaiSetup'; import { BigNumberSetup } from '../../utils/bigNumberSetup'; @@ -19,7 +18,14 @@ import { } from '../../utils/contracts'; import { Blockchain } from '../../utils/blockchain'; import { ether } from '../../utils/units'; -import { DEFAULT_GAS, NULL_ADDRESS, REBALANCING_STATE } from '../../utils/constants'; +import { + DEFAULT_GAS, + NULL_ADDRESS, + REBALANCING_STATE, + DEFAULT_PERIOD_INTERVAL, + DEFAULT_UNIT_SHARES, + DEFAULT_TIME_FAST_FORWARD +} from '../../utils/constants'; import { assertLogEquivalence, getFormattedLogsFromTxHash } from '../../utils/logs'; import { getExpectedTransferLog, @@ -30,6 +36,7 @@ import { import { expectRevertError, assertTokenBalance } from '../../utils/tokenAssertions'; import { CoreWrapper } from '../../utils/coreWrapper'; import { ERC20Wrapper } from '../../utils/erc20Wrapper'; +import { RebalancingTokenWrapper } from '../../utils/RebalancingTokenWrapper'; BigNumberSetup.configure(); ChaiSetup.configure(); @@ -51,7 +58,7 @@ contract('RebalancingSetToken', accounts => { let rebalancingSetToken: RebalancingSetTokenContract; let components: StandardTokenMockContract[] = []; - let core: CoreMockContract; + let coreMock: CoreMockContract; let transferProxy: TransferProxyContract; let vault: VaultContract; let factory: SetTokenFactoryContract; @@ -60,6 +67,12 @@ contract('RebalancingSetToken', accounts => { const coreWrapper = new CoreWrapper(deployerAccount, deployerAccount); const erc20Wrapper = new ERC20Wrapper(deployerAccount); const blockchain = new Blockchain(web3); + const rebalancingTokenWrapper = new RebalancingTokenWrapper( + deployerAccount, + coreWrapper, + erc20Wrapper, + blockchain + ); before(async () => { await blockchain.saveSnapshotAsync(); @@ -76,12 +89,12 @@ contract('RebalancingSetToken', accounts => { beforeEach(async () => { transferProxy = await coreWrapper.deployTransferProxyAsync(); vault = await coreWrapper.deployVaultAsync(); - core = await coreWrapper.deployCoreMockAsync(transferProxy, vault); - factory = await coreWrapper.deploySetTokenFactoryAsync(core.address); - rebalancingFactory = await coreWrapper.deployRebalancingSetTokenFactoryAsync(core.address); + coreMock = await coreWrapper.deployCoreMockAsync(transferProxy, vault); + factory = await coreWrapper.deploySetTokenFactoryAsync(coreMock.address); + rebalancingFactory = await coreWrapper.deployRebalancingSetTokenFactoryAsync(coreMock.address); - await coreWrapper.setDefaultStateAndAuthorizationsAsync(core, vault, transferProxy, factory); - await coreWrapper.enableFactoryAsync(core, rebalancingFactory); + await coreWrapper.setDefaultStateAndAuthorizationsAsync(coreMock, vault, transferProxy, factory); + await coreWrapper.enableFactoryAsync(coreMock, rebalancingFactory); }); describe('#constructor', async () => { @@ -100,9 +113,9 @@ contract('RebalancingSetToken', accounts => { subjectFactory = factoryAccount; subjectManager = managerAccount; subjectInitialSet = components[0].address, - subjectInitialUnitShares = ether(1); - subjectProposalPeriod = new BigNumber(100000); - subjectRebalanceInterval = new BigNumber(100000); + subjectInitialUnitShares = DEFAULT_UNIT_SHARES; + subjectProposalPeriod = DEFAULT_PERIOD_INTERVAL; + subjectRebalanceInterval = DEFAULT_PERIOD_INTERVAL; }); async function subject(): Promise { @@ -210,9 +223,9 @@ contract('RebalancingSetToken', accounts => { initialSet = components[0].address; const manager = managerAccount; - const initialUnitShares = ether(1); - const proposalPeriod = new BigNumber(100000); - const rebalanceInterval = new BigNumber(100000); + const initialUnitShares = DEFAULT_UNIT_SHARES; + const proposalPeriod = DEFAULT_PERIOD_INTERVAL; + const rebalanceInterval = DEFAULT_PERIOD_INTERVAL; rebalancingSetToken = await coreWrapper.deployRebalancingSetTokenAsync( factoryAccount, @@ -223,7 +236,7 @@ contract('RebalancingSetToken', accounts => { rebalanceInterval, ); - subjectCaller = manager; + subjectCaller = managerAccount; }); async function subject(): Promise { @@ -246,11 +259,11 @@ contract('RebalancingSetToken', accounts => { beforeEach(async () => { components = await erc20Wrapper.deployTokensAsync(1, deployerAccount); - initialUnitShares = ether(1); const initialSet = components[0].address; const manager = managerAccount; - const proposalPeriod = new BigNumber(100000); - const rebalanceInterval = new BigNumber(100000); + initialUnitShares = DEFAULT_UNIT_SHARES; + const proposalPeriod = DEFAULT_PERIOD_INTERVAL; + const rebalanceInterval = DEFAULT_PERIOD_INTERVAL; rebalancingSetToken = await coreWrapper.deployRebalancingSetTokenAsync( factoryAccount, @@ -261,7 +274,7 @@ contract('RebalancingSetToken', accounts => { rebalanceInterval, ); - subjectCaller = manager; + subjectCaller = managerAccount; }); async function subject(): Promise { @@ -279,23 +292,20 @@ contract('RebalancingSetToken', accounts => { }); }); - describe('#mint called directly', async () => { + describe('#mint: Called on Rebalancing Token', async () => { let subjectIssuer: Address; let subjectQuantity: BigNumber; let subjectCaller: Address; - let currentSetToken: SetTokenContract; - - const naturalUnit: BigNumber = ether(2); - beforeEach(async () => { + const naturalUnit: BigNumber = ether(2); components = await erc20Wrapper.deployTokensAsync(2, deployerAccount); await erc20Wrapper.approveTransfersAsync(components, transferProxy.address); - const currentComponentAddresses = _.map(components.slice(0, 2), token => token.address); - const currentComponentUnits = _.map(components.slice(0, 2), () => naturalUnit.mul(2)); // Multiple of naturalUnit - currentSetToken = await coreWrapper.createSetTokenAsync( - core, + const currentComponentAddresses = _.map(components, token => token.address); + const currentComponentUnits = _.map(components, () => naturalUnit.mul(2)); // Multiple of naturalUnit + const currentSetToken = await coreWrapper.createSetTokenAsync( + coreMock, factory.address, currentComponentAddresses, currentComponentUnits, @@ -304,12 +314,12 @@ contract('RebalancingSetToken', accounts => { const manager = managerAccount; const initialSet = currentSetToken.address; - const initialUnitShares = ether(1); - const proposalPeriod = new BigNumber(100000); - const rebalanceInterval = new BigNumber(100000); + const initialUnitShares = DEFAULT_UNIT_SHARES; + const proposalPeriod = DEFAULT_PERIOD_INTERVAL; + const rebalanceInterval = DEFAULT_PERIOD_INTERVAL; - rebalancingFactory = await coreWrapper.deployRebalancingSetTokenFactoryAsync(coreAccount); - await coreWrapper.enableFactoryAsync(core, rebalancingFactory); + const rebalancingFactory = await coreWrapper.deployRebalancingSetTokenFactoryAsync(coreAccount); + await coreWrapper.enableFactoryAsync(coreMock, rebalancingFactory); rebalancingSetToken = await coreWrapper.deployRebalancingSetTokenAsync( rebalancingFactory.address, @@ -342,30 +352,6 @@ contract('RebalancingSetToken', accounts => { assertTokenBalance(rebalancingSetToken, expectedNewBalance, subjectIssuer); }); - it('updates the totalSupply_ correctly', async () => { - const existingTokenSupply = await rebalancingSetToken.totalSupply.callAsync(); - - await subject(); - - const expectedTokenSupply = existingTokenSupply.add(subjectQuantity); - const newTokenSupply = await rebalancingSetToken.totalSupply.callAsync(); - expect(newTokenSupply).to.be.bignumber.equal(expectedTokenSupply); - }); - - it('emits a Transfer log denoting a minting', async () => { - const txHash = await subject(); - - const formattedLogs = await getFormattedLogsFromTxHash(txHash); - const expectedLogs = getExpectedTransferLog( - NULL_ADDRESS, - subjectIssuer, - subjectQuantity, - rebalancingSetToken.address - ); - - await assertLogEquivalence(formattedLogs, expectedLogs); - }); - describe('when the caller is not Core', async () => { beforeEach(async () => { subjectCaller = otherAccount; @@ -377,69 +363,37 @@ contract('RebalancingSetToken', accounts => { }); }); - describe('#mint called from Core', async () => { - let rebalancingToken: RebalancingSetTokenContract; + describe('#mint: Called from CoreMock', async () => { + let rebalancingSetToken: RebalancingSetTokenContract; let subjectIssuer: Address; let subjectQuantity: BigNumber; let subjectCaller: Address; - let currentSetToken: SetTokenContract; let newRebalancingSetToken: SetTokenContract; - const naturalUnit: BigNumber = ether(2); - - const setName: string = 'Rebalancing Set'; - const setSymbol: string = 'RBSET'; - beforeEach(async () => { - components = await erc20Wrapper.deployTokensAsync(3, deployerAccount); - await erc20Wrapper.approveTransfersAsync(components, transferProxy.address); - - const currentComponentAddresses = _.map(components.slice(0, 2), token => token.address); - const currentComponentUnits = _.map(components.slice(0, 2), () => naturalUnit.mul(2)); // Multiple of naturalUnit - currentSetToken = await coreWrapper.createSetTokenAsync( - core, - factory.address, - currentComponentAddresses, - currentComponentUnits, - naturalUnit, - ); - - const newComponentAddresses = _.map(components.slice(1, 3), token => token.address); - const newComponentUnits = _.map(components.slice(1, 3), () => naturalUnit.mul(1)); // Multiple of naturalUnit - newRebalancingSetToken = await coreWrapper.createSetTokenAsync( - core, - factory.address, - newComponentAddresses, - newComponentUnits, - naturalUnit, - ); - - const manager = managerAccount; - const initialSet = currentSetToken.address; - const initialUnitShares = ether(1); - const proposalPeriod = new BigNumber(100000); - const rebalanceInterval = new BigNumber(100000); + const setTokens = await rebalancingTokenWrapper.createSetTokens(coreMock, factory.address, transferProxy.address); + const currentSetToken = setTokens[0]; + newRebalancingSetToken = setTokens[1]; - rebalancingToken = await coreWrapper.deployRebalancingSetTokenAsync( + const proposalPeriod = DEFAULT_PERIOD_INTERVAL; + rebalancingSetToken = await rebalancingTokenWrapper.createDefaultRebalancingSetToken( + coreMock, rebalancingFactory.address, - manager, - initialSet, - initialUnitShares, - proposalPeriod, - rebalanceInterval, - setName, - setSymbol, + managerAccount, + currentSetToken.address, + proposalPeriod ); + subjectIssuer = deployerAccount, subjectQuantity = ether(5); subjectCaller = managerAccount; }); async function subject(): Promise { - return core.mint.sendTransactionAsync( - rebalancingToken.address, + return coreMock.mint.sendTransactionAsync( + rebalancingSetToken.address, subjectIssuer, subjectQuantity, { from: subjectCaller, gas: DEFAULT_GAS} @@ -447,36 +401,45 @@ contract('RebalancingSetToken', accounts => { } it('updates the balances of the user correctly', async () => { - const existingBalance = await rebalancingToken.balanceOf.callAsync(subjectIssuer); + const existingBalance = await rebalancingSetToken.balanceOf.callAsync(subjectIssuer); await subject(); const expectedNewBalance = existingBalance.add(subjectQuantity); - assertTokenBalance(rebalancingToken, expectedNewBalance, subjectIssuer); + assertTokenBalance(rebalancingSetToken, expectedNewBalance, subjectIssuer); + }); + + it('updates the totalSupply_ correctly', async () => { + const existingTokenSupply = await rebalancingSetToken.totalSupply.callAsync(); + + await subject(); + + const expectedTokenSupply = existingTokenSupply.add(subjectQuantity); + const newTokenSupply = await rebalancingSetToken.totalSupply.callAsync(); + expect(newTokenSupply).to.be.bignumber.equal(expectedTokenSupply); + }); + + it('emits a Transfer log denoting a minting', async () => { + const txHash = await subject(); + + const formattedLogs = await getFormattedLogsFromTxHash(txHash); + const expectedLogs = getExpectedTransferLog( + NULL_ADDRESS, + subjectIssuer, + subjectQuantity, + rebalancingSetToken.address + ); + + await assertLogEquivalence(formattedLogs, expectedLogs); }); describe('when mint is called from Rebalance state', async () => { beforeEach(async () => { - const auctionLibrary = libraryAccount; - const curveCoefficient = ether(1); - const auctionStartPrice = ether(5); - const auctionPriceDivisor = ether(10); - const caller = managerAccount; - const timeFastForward = 100000; - - await blockchain.increaseTimeAsync(timeFastForward); - await rebalancingToken.propose.sendTransactionAsync( + await rebalancingTokenWrapper.defaultTransitionToRebalance( + rebalancingSetToken, newRebalancingSetToken.address, - auctionLibrary, - curveCoefficient, - auctionStartPrice, - auctionPriceDivisor, - { from: caller, gas: DEFAULT_GAS} - ); - - await blockchain.increaseTimeAsync(timeFastForward); - await rebalancingToken.rebalance.sendTransactionAsync( - { from: caller, gas: DEFAULT_GAS } + libraryAccount, + managerAccount ); }); @@ -486,23 +449,20 @@ contract('RebalancingSetToken', accounts => { }); }); - describe('#burn called directly', async () => { + describe('#burn: Called on Rebalancing Token', async () => { let subjectIssuer: Address; let subjectQuantity: BigNumber; let subjectCaller: Address; - let currentSetToken: SetTokenContract; - - const naturalUnit: BigNumber = ether(2); - beforeEach(async () => { + const naturalUnit: BigNumber = ether(2); components = await erc20Wrapper.deployTokensAsync(2, deployerAccount); await erc20Wrapper.approveTransfersAsync(components, transferProxy.address); - const currentComponentAddresses = _.map(components.slice(0, 2), token => token.address); - const currentComponentUnits = _.map(components.slice(0, 2), () => naturalUnit.mul(2)); // Multiple of naturalUnit - currentSetToken = await coreWrapper.createSetTokenAsync( - core, + const currentComponentAddresses = _.map(components, token => token.address); + const currentComponentUnits = _.map(components, () => naturalUnit.mul(2)); // Multiple of naturalUnit + const currentSetToken = await coreWrapper.createSetTokenAsync( + coreMock, factory.address, currentComponentAddresses, currentComponentUnits, @@ -511,12 +471,12 @@ contract('RebalancingSetToken', accounts => { const manager = managerAccount; const initialSet = currentSetToken.address; - const initialUnitShares = ether(1); - const proposalPeriod = new BigNumber(100000); - const rebalanceInterval = new BigNumber(100000); + const initialUnitShares = DEFAULT_UNIT_SHARES; + const proposalPeriod = DEFAULT_PERIOD_INTERVAL; + const rebalanceInterval = DEFAULT_PERIOD_INTERVAL; - rebalancingFactory = await coreWrapper.deployRebalancingSetTokenFactoryAsync(coreAccount); - await coreWrapper.enableFactoryAsync(core, rebalancingFactory); + const rebalancingFactory = await coreWrapper.deployRebalancingSetTokenFactoryAsync(coreAccount); + await coreWrapper.enableFactoryAsync(coreMock, rebalancingFactory); rebalancingSetToken = await coreWrapper.deployRebalancingSetTokenAsync( rebalancingFactory.address, @@ -555,30 +515,6 @@ contract('RebalancingSetToken', accounts => { assertTokenBalance(rebalancingSetToken, expectedNewBalance, subjectIssuer); }); - it('updates the totalSupply_ correctly', async () => { - const existingTokenSupply = await rebalancingSetToken.totalSupply.callAsync(); - - await subject(); - - const expectedTokenSupply = existingTokenSupply.sub(subjectQuantity); - const newTokenSupply = await rebalancingSetToken.totalSupply.callAsync(); - expect(newTokenSupply).to.be.bignumber.equal(expectedTokenSupply); - }); - - it('emits a Transfer log denoting a burning', async () => { - const txHash = await subject(); - - const formattedLogs = await getFormattedLogsFromTxHash(txHash); - const expectedLogs = getExpectedTransferLog( - subjectIssuer, - NULL_ADDRESS, - subjectQuantity, - rebalancingSetToken.address - ); - - await assertLogEquivalence(formattedLogs, expectedLogs); - }); - describe('when the caller is not Core', async () => { beforeEach(async () => { subjectCaller = otherAccount; @@ -590,59 +526,26 @@ contract('RebalancingSetToken', accounts => { }); }); - describe('#burn called from Core', async () => { + describe('#burn: Called from CoreMock', async () => { let rebalancingSetToken: RebalancingSetTokenContract; let subjectBurner: Address; let subjectQuantity: BigNumber; let subjectCaller: Address; - let currentSetToken: SetTokenContract; let newRebalancingSetToken: SetTokenContract; - const naturalUnit: BigNumber = ether(2); - - const setName: string = 'Rebalancing Set'; - const setSymbol: string = 'RBSET'; - beforeEach(async () => { - components = await erc20Wrapper.deployTokensAsync(3, deployerAccount); - await erc20Wrapper.approveTransfersAsync(components, transferProxy.address); + const setTokens = await rebalancingTokenWrapper.createSetTokens(coreMock, factory.address, transferProxy.address); + const currentSetToken = setTokens[0]; + newRebalancingSetToken = setTokens[1]; - const currentComponentAddresses = _.map(components.slice(0, 2), token => token.address); - const currentComponentUnits = _.map(components.slice(0, 2), () => naturalUnit.mul(2)); // Multiple of naturalUnit - currentSetToken = await coreWrapper.createSetTokenAsync( - core, - factory.address, - currentComponentAddresses, - currentComponentUnits, - naturalUnit, - ); - - const newComponentAddresses = _.map(components.slice(1, 3), token => token.address); - const newComponentUnits = _.map(components.slice(1, 3), () => naturalUnit.mul(1)); // Multiple of naturalUnit - newRebalancingSetToken = await coreWrapper.createSetTokenAsync( - core, - factory.address, - newComponentAddresses, - newComponentUnits, - naturalUnit, - ); - - const manager = managerAccount; - const initialSet = currentSetToken.address; - const initialUnitShares = ether(1); - const proposalPeriod = new BigNumber(100000); - const rebalanceInterval = new BigNumber(100000); - - rebalancingSetToken = await coreWrapper.deployRebalancingSetTokenAsync( + const proposalPeriod = DEFAULT_PERIOD_INTERVAL; + rebalancingSetToken = await rebalancingTokenWrapper.createDefaultRebalancingSetToken( + coreMock, rebalancingFactory.address, - manager, - initialSet, - initialUnitShares, - proposalPeriod, - rebalanceInterval, - setName, - setSymbol, + managerAccount, + currentSetToken.address, + proposalPeriod ); const mintedQuantity = ether(5); @@ -650,7 +553,7 @@ contract('RebalancingSetToken', accounts => { subjectQuantity = ether(5); subjectCaller = managerAccount; - await core.mint.sendTransactionAsync( + await coreMock.mint.sendTransactionAsync( rebalancingSetToken.address, subjectBurner, mintedQuantity, @@ -659,7 +562,7 @@ contract('RebalancingSetToken', accounts => { }); async function subject(): Promise { - return core.burn.sendTransactionAsync( + return coreMock.burn.sendTransactionAsync( rebalancingSetToken.address, subjectBurner, subjectQuantity, @@ -712,35 +615,20 @@ contract('RebalancingSetToken', accounts => { describe('when burn is called from Rebalance state', async () => { beforeEach(async () => { - const auctionLibrary = libraryAccount; - const curveCoefficient = ether(1); - const auctionStartPrice = ether(5); - const auctionPriceDivisor = ether(10); - const caller = managerAccount; - const timeFastForward = 100000; - // Must burn otherwise won't get through rebalance call // TO DO: Instead of mint call issue in set up. - core.burn.sendTransactionAsync( + coreMock.burn.sendTransactionAsync( rebalancingSetToken.address, subjectBurner, subjectQuantity, { from: subjectCaller, gas: DEFAULT_GAS} ); - await blockchain.increaseTimeAsync(timeFastForward); - await rebalancingSetToken.propose.sendTransactionAsync( + await rebalancingTokenWrapper.defaultTransitionToRebalance( + rebalancingSetToken, newRebalancingSetToken.address, - auctionLibrary, - curveCoefficient, - auctionStartPrice, - auctionPriceDivisor, - { from: caller, gas: DEFAULT_GAS} - ); - - await blockchain.increaseTimeAsync(timeFastForward); - await rebalancingSetToken.rebalance.sendTransactionAsync( - { from: caller, gas: DEFAULT_GAS } + libraryAccount, + managerAccount ); }); @@ -758,11 +646,11 @@ contract('RebalancingSetToken', accounts => { beforeEach(async () => { components = await erc20Wrapper.deployTokensAsync(1, deployerAccount); - const manager = managerAccount; const initialSet = components[0].address; - const initialUnitShares = ether(1); - const proposalPeriod = new BigNumber(100000); - const rebalanceInterval = new BigNumber(100000); + const manager = managerAccount; + const initialUnitShares = DEFAULT_UNIT_SHARES; + const proposalPeriod = DEFAULT_PERIOD_INTERVAL; + const rebalanceInterval = DEFAULT_PERIOD_INTERVAL; rebalancingSetToken = await coreWrapper.deployRebalancingSetTokenAsync( factoryAccount, @@ -774,7 +662,7 @@ contract('RebalancingSetToken', accounts => { ); subjectNewManager = otherAccount, - subjectCaller = manager; + subjectCaller = managerAccount; }); async function subject(): Promise { @@ -826,9 +714,9 @@ contract('RebalancingSetToken', accounts => { const manager = managerAccount; const initialSet = components[0].address; - const initialUnitShares = ether(1); - const proposalPeriod = new BigNumber(100000); - const rebalanceInterval = new BigNumber(100000); + const initialUnitShares = DEFAULT_UNIT_SHARES; + const proposalPeriod = DEFAULT_PERIOD_INTERVAL; + const rebalanceInterval = DEFAULT_PERIOD_INTERVAL; rebalancingSetToken = await coreWrapper.deployRebalancingSetTokenAsync( factory.address, @@ -902,9 +790,9 @@ contract('RebalancingSetToken', accounts => { const manager = managerAccount; const initialSet = components[0].address; - const initialUnitShares = ether(1); - const proposalPeriod = new BigNumber(100000); - const rebalanceInterval = new BigNumber(100000); + const initialUnitShares = DEFAULT_UNIT_SHARES; + const proposalPeriod = DEFAULT_PERIOD_INTERVAL; + const rebalanceInterval = DEFAULT_PERIOD_INTERVAL; rebalancingSetToken = await coreWrapper.deployRebalancingSetTokenAsync( factory.address, @@ -983,50 +871,18 @@ contract('RebalancingSetToken', accounts => { let currentSetToken: SetTokenContract; let newRebalancingSetToken: SetTokenContract; - const naturalUnit: BigNumber = ether(2); - beforeEach(async () => { - components = await erc20Wrapper.deployTokensAsync(3, deployerAccount); - await erc20Wrapper.approveTransfersAsync(components, transferProxy.address); + const setTokens = await rebalancingTokenWrapper.createSetTokens(coreMock, factory.address, transferProxy.address); + currentSetToken = setTokens[0]; + newRebalancingSetToken = setTokens[1]; - const currentComponentAddresses = _.map(components.slice(0, 2), token => token.address); - const currentComponentUnits = _.map(components.slice(0, 2), () => naturalUnit.mul(2)); // Multiple of naturalUnit - currentSetToken = await coreWrapper.createSetTokenAsync( - core, - factory.address, - currentComponentAddresses, - currentComponentUnits, - naturalUnit, - ); - - const newComponentAddresses = _.map(components.slice(1, 3), token => token.address); - const newComponentUnits = _.map(components.slice(1, 3), () => naturalUnit.mul(1)); // Multiple of naturalUnit - newRebalancingSetToken = await coreWrapper.createSetTokenAsync( - core, - factory.address, - newComponentAddresses, - newComponentUnits, - naturalUnit, - ); - - const manager = managerAccount; - const initialSet = currentSetToken.address; - const initialUnitShares = new BigNumber(1); - const rebalanceInterval = new BigNumber(90000); - proposalPeriod = new BigNumber(90000); - const callData = Utils.bufferArrayToHex([ - Utils.paddedBufferForPrimitive(manager), - Utils.paddedBufferForBigNumber(proposalPeriod), - Utils.paddedBufferForBigNumber(rebalanceInterval), - ]); - - rebalancingSetToken = await coreWrapper.createRebalancingTokenAsync( - core, + proposalPeriod = DEFAULT_PERIOD_INTERVAL; + rebalancingSetToken = await rebalancingTokenWrapper.createDefaultRebalancingSetToken( + coreMock, rebalancingFactory.address, - [initialSet], - [initialUnitShares], - new BigNumber(1), - callData, + managerAccount, + currentSetToken.address, + proposalPeriod ); subjectRebalancingToken = newRebalancingSetToken.address; @@ -1035,7 +891,7 @@ contract('RebalancingSetToken', accounts => { subjectAuctionStartPrice = ether(5); subjectAuctionPriceDivisor = ether(10); subjectCaller = managerAccount; - subjectTimeFastForward = 100000; + subjectTimeFastForward = DEFAULT_TIME_FAST_FORWARD; }); async function subject(): Promise { @@ -1166,21 +1022,11 @@ contract('RebalancingSetToken', accounts => { describe('when propose is called from Proposal state', async () => { beforeEach(async () => { - const auctionLibrary = libraryAccount; - const curveCoefficient = ether(1); - const auctionStartPrice = ether(5); - const auctionPriceDivisor = ether(10); - const caller = managerAccount; - const timeFastForward = 100000; - - await blockchain.increaseTimeAsync(timeFastForward); - await rebalancingSetToken.propose.sendTransactionAsync( + await rebalancingTokenWrapper.defaultTransitionToPropose( + rebalancingSetToken, newRebalancingSetToken.address, - auctionLibrary, - curveCoefficient, - auctionStartPrice, - auctionPriceDivisor, - { from: caller, gas: DEFAULT_GAS} + libraryAccount, + managerAccount ); }); @@ -1191,26 +1037,11 @@ contract('RebalancingSetToken', accounts => { describe('when propose is called from Rebalance state', async () => { beforeEach(async () => { - const auctionLibrary = libraryAccount; - const curveCoefficient = ether(1); - const auctionStartPrice = ether(5); - const auctionPriceDivisor = ether(10); - const caller = managerAccount; - const timeFastForward = 100000; - - await blockchain.increaseTimeAsync(timeFastForward); - await rebalancingSetToken.propose.sendTransactionAsync( + await rebalancingTokenWrapper.defaultTransitionToRebalance( + rebalancingSetToken, newRebalancingSetToken.address, - auctionLibrary, - curveCoefficient, - auctionStartPrice, - auctionPriceDivisor, - { from: caller, gas: DEFAULT_GAS} - ); - - await blockchain.increaseTimeAsync(timeFastForward); - await rebalancingSetToken.rebalance.sendTransactionAsync( - { from: caller, gas: DEFAULT_GAS } + libraryAccount, + managerAccount ); }); @@ -1228,54 +1059,22 @@ contract('RebalancingSetToken', accounts => { let currentSetToken: SetTokenContract; let newRebalancingSetToken: SetTokenContract; - const naturalUnit: BigNumber = ether(2); - beforeEach(async () => { - components = await erc20Wrapper.deployTokensAsync(3, deployerAccount); - await erc20Wrapper.approveTransfersAsync(components, transferProxy.address); - - const currentComponentAddresses = _.map(components.slice(0, 2), token => token.address); - const currentComponentUnits = _.map(components.slice(0, 2), () => naturalUnit.mul(2)); // Multiple of naturalUnit - currentSetToken = await coreWrapper.createSetTokenAsync( - core, - factory.address, - currentComponentAddresses, - currentComponentUnits, - naturalUnit, - ); + const setTokens = await rebalancingTokenWrapper.createSetTokens(coreMock, factory.address, transferProxy.address); + currentSetToken = setTokens[0]; + newRebalancingSetToken = setTokens[1]; - const newComponentAddresses = _.map(components.slice(1, 3), token => token.address); - const newComponentUnits = _.map(components.slice(1, 3), () => naturalUnit.mul(1)); // Multiple of naturalUnit - newRebalancingSetToken = await coreWrapper.createSetTokenAsync( - core, - factory.address, - newComponentAddresses, - newComponentUnits, - naturalUnit, - ); - - const manager = managerAccount; - const initialSet = currentSetToken.address; - const initialUnitShares = new BigNumber(1); - const rebalanceInterval = new BigNumber(90000); - proposalPeriod = new BigNumber(90000); - const callData = Utils.bufferArrayToHex([ - Utils.paddedBufferForPrimitive(manager), - Utils.paddedBufferForBigNumber(proposalPeriod), - Utils.paddedBufferForBigNumber(rebalanceInterval), - ]); - - rebalancingSetToken = await coreWrapper.createRebalancingTokenAsync( - core, + proposalPeriod = DEFAULT_PERIOD_INTERVAL; + rebalancingSetToken = await rebalancingTokenWrapper.createDefaultRebalancingSetToken( + coreMock, rebalancingFactory.address, - [initialSet], - [initialUnitShares], - new BigNumber(1), - callData, + managerAccount, + currentSetToken.address, + proposalPeriod ); subjectCaller = managerAccount; - subjectTimeFastForward = 100000; + subjectTimeFastForward = DEFAULT_TIME_FAST_FORWARD; }); async function subject(): Promise { @@ -1293,21 +1092,11 @@ contract('RebalancingSetToken', accounts => { describe('when rebalance is called from Propose State', async () => { beforeEach(async () => { - const auctionLibrary = libraryAccount; - const curveCoefficient = ether(1); - const auctionStartPrice = ether(5); - const auctionPriceDivisor = ether(10); - const caller = managerAccount; - const timeFastForward = 100000; - - await blockchain.increaseTimeAsync(timeFastForward); - await rebalancingSetToken.propose.sendTransactionAsync( + await rebalancingTokenWrapper.defaultTransitionToPropose( + rebalancingSetToken, newRebalancingSetToken.address, - auctionLibrary, - curveCoefficient, - auctionStartPrice, - auctionPriceDivisor, - { from: caller, gas: DEFAULT_GAS} + libraryAccount, + managerAccount ); }); @@ -1344,26 +1133,11 @@ contract('RebalancingSetToken', accounts => { describe('when rebalance is called from Rebalance State', async () => { beforeEach(async () => { - const auctionLibrary = libraryAccount; - const curveCoefficient = ether(1); - const auctionStartPrice = ether(5); - const auctionPriceDivisor = ether(10); - const caller = managerAccount; - const timeFastForward = 100000; - - await blockchain.increaseTimeAsync(timeFastForward); - await rebalancingSetToken.propose.sendTransactionAsync( + await rebalancingTokenWrapper.defaultTransitionToRebalance( + rebalancingSetToken, newRebalancingSetToken.address, - auctionLibrary, - curveCoefficient, - auctionStartPrice, - auctionPriceDivisor, - { from: caller, gas: DEFAULT_GAS} - ); - - await blockchain.increaseTimeAsync(timeFastForward); - await rebalancingSetToken.rebalance.sendTransactionAsync( - { from: caller, gas: DEFAULT_GAS } + libraryAccount, + managerAccount ); }); @@ -1377,53 +1151,20 @@ contract('RebalancingSetToken', accounts => { let subjectCaller: Address; let proposalPeriod: BigNumber; - let currentSetToken: SetTokenContract; let newRebalancingSetToken: SetTokenContract; - const naturalUnit: BigNumber = ether(2); - beforeEach(async () => { - components = await erc20Wrapper.deployTokensAsync(3, deployerAccount); - await erc20Wrapper.approveTransfersAsync(components, transferProxy.address); + const setTokens = await rebalancingTokenWrapper.createSetTokens(coreMock, factory.address, transferProxy.address); + const currentSetToken = setTokens[0]; + newRebalancingSetToken = setTokens[1]; - const currentComponentAddresses = _.map(components.slice(0, 2), token => token.address); - const currentComponentUnits = _.map(components.slice(0, 2), () => naturalUnit.mul(2)); // Multiple of naturalUnit - currentSetToken = await coreWrapper.createSetTokenAsync( - core, - factory.address, - currentComponentAddresses, - currentComponentUnits, - naturalUnit, - ); - - const newComponentAddresses = _.map(components.slice(1, 3), token => token.address); - const newComponentUnits = _.map(components.slice(1, 3), () => naturalUnit.mul(1)); // Multiple of naturalUnit - newRebalancingSetToken = await coreWrapper.createSetTokenAsync( - core, - factory.address, - newComponentAddresses, - newComponentUnits, - naturalUnit, - ); - - const manager = managerAccount; - const initialSet = currentSetToken.address; - const initialUnitShares = new BigNumber(1); - const rebalanceInterval = new BigNumber(90000); - proposalPeriod = new BigNumber(90000); - const callData = Utils.bufferArrayToHex([ - Utils.paddedBufferForPrimitive(manager), - Utils.paddedBufferForBigNumber(proposalPeriod), - Utils.paddedBufferForBigNumber(rebalanceInterval), - ]); - - rebalancingSetToken = await coreWrapper.createRebalancingTokenAsync( - core, + proposalPeriod = DEFAULT_PERIOD_INTERVAL; + rebalancingSetToken = await rebalancingTokenWrapper.createDefaultRebalancingSetToken( + coreMock, rebalancingFactory.address, - [initialSet], - [initialUnitShares], - new BigNumber(1), - callData, + managerAccount, + currentSetToken.address, + proposalPeriod ); subjectCaller = managerAccount; @@ -1443,21 +1184,11 @@ contract('RebalancingSetToken', accounts => { describe('when settlement is called from Proposal State', async () => { beforeEach(async () => { - const auctionLibrary = libraryAccount; - const curveCoefficient = ether(1); - const auctionStartPrice = ether(5); - const auctionPriceDivisor = ether(10); - const caller = managerAccount; - const timeFastForward = 100000; - - await blockchain.increaseTimeAsync(timeFastForward); - await rebalancingSetToken.propose.sendTransactionAsync( + await rebalancingTokenWrapper.defaultTransitionToPropose( + rebalancingSetToken, newRebalancingSetToken.address, - auctionLibrary, - curveCoefficient, - auctionStartPrice, - auctionPriceDivisor, - { from: caller, gas: DEFAULT_GAS} + libraryAccount, + managerAccount ); }); @@ -1468,26 +1199,11 @@ contract('RebalancingSetToken', accounts => { describe('when settlement is called from Rebalance State', async () => { beforeEach(async () => { - const auctionLibrary = libraryAccount; - const curveCoefficient = ether(1); - const auctionStartPrice = ether(5); - const auctionPriceDivisor = ether(10); - const caller = managerAccount; - const timeFastForward = 100000; - - await blockchain.increaseTimeAsync(timeFastForward); - await rebalancingSetToken.propose.sendTransactionAsync( + await rebalancingTokenWrapper.defaultTransitionToRebalance( + rebalancingSetToken, newRebalancingSetToken.address, - auctionLibrary, - curveCoefficient, - auctionStartPrice, - auctionPriceDivisor, - { from: caller, gas: DEFAULT_GAS} - ); - - await blockchain.increaseTimeAsync(timeFastForward); - await rebalancingSetToken.rebalance.sendTransactionAsync( - { from: caller, gas: DEFAULT_GAS } + libraryAccount, + managerAccount ); }); diff --git a/utils/RebalancingTokenWrapper.ts b/utils/RebalancingTokenWrapper.ts new file mode 100644 index 000000000..1d24b9273 --- /dev/null +++ b/utils/RebalancingTokenWrapper.ts @@ -0,0 +1,144 @@ +import * as _ from 'lodash'; +import { SetProtocolUtils, Address } from 'set-protocol-utils'; + +import { + CoreContract, + CoreMockContract, + SetTokenContract, + RebalancingSetTokenContract +} from './contracts'; +import { BigNumber } from 'bignumber.js'; + +import { ether } from './units'; +import { + DEFAULT_GAS, + DEFAULT_PERIOD_INTERVAL, + DEFAULT_UNIT_SHARES, + DEFAULT_REBALANCING_NATURAL_UNIT, + DEFAULT_TIME_FAST_FORWARD +} from './constants'; + +import { CoreWrapper } from './coreWrapper'; +import { ERC20Wrapper } from './erc20Wrapper'; +import { Blockchain } from './blockchain'; + +declare type CoreLikeContract = CoreMockContract | CoreContract; + +export class RebalancingTokenWrapper { + private _tokenOwnerAddress: Address; + private _coreWrapper: CoreWrapper; + private _erc20Wrapper: ERC20Wrapper; + private _blockchain: Blockchain; + + constructor( + tokenOwnerAddress: Address, + coreWrapper: CoreWrapper, + erc20Wrapper: ERC20Wrapper, + blockchain: Blockchain, + ) { + this._tokenOwnerAddress = tokenOwnerAddress; + + this._coreWrapper = coreWrapper; + this._erc20Wrapper = erc20Wrapper; + this._blockchain = blockchain; + } + + public async createSetTokens( + core: CoreLikeContract, + factory: Address, + transferProxy: Address, + from: Address = this._tokenOwnerAddress, + ): Promise { + const naturalUnit = ether(2); + const components = await this._erc20Wrapper.deployTokensAsync(3, this._tokenOwnerAddress); + await this._erc20Wrapper.approveTransfersAsync(components, transferProxy); + + const set1Components = components.slice(0, 2); + const set1ComponentAddresses = _.map(set1Components, token => token.address); + const set1ComponentUnits = _.map(set1Components, () => naturalUnit.mul(2)); // Multiple of naturalUnit + const setToken1 = await this._coreWrapper.createSetTokenAsync( + core, + factory, + set1ComponentAddresses, + set1ComponentUnits, + naturalUnit, + ); + + const set2Components = components.slice(1, 3); + const set2ComponentAddresses = _.map(set2Components, token => token.address); + const set2ComponentUnits = _.map(set2Components, () => naturalUnit.mul(1)); // Multiple of naturalUnit + const setToken2 = await this._coreWrapper.createSetTokenAsync( + core, + factory, + set2ComponentAddresses, + set2ComponentUnits, + naturalUnit, + ); + return [setToken1, setToken2]; + } + + public async createDefaultRebalancingSetToken( + core: CoreLikeContract, + factory: Address, + manager: Address, + initialSet: Address, + proposalPeriod: BigNumber, + ): Promise { + const initialUnitShares = DEFAULT_UNIT_SHARES; + const rebalanceInterval = DEFAULT_PERIOD_INTERVAL; + const callData = SetProtocolUtils.bufferArrayToHex([ + SetProtocolUtils.paddedBufferForPrimitive(manager), + SetProtocolUtils.paddedBufferForBigNumber(proposalPeriod), + SetProtocolUtils.paddedBufferForBigNumber(rebalanceInterval), + ]); + + return await this._coreWrapper.createRebalancingTokenAsync( + core, + factory, + [initialSet], + [initialUnitShares], + DEFAULT_REBALANCING_NATURAL_UNIT, + callData, + ); + } + + public async defaultTransitionToPropose( + rebalancingSetToken: RebalancingSetTokenContract, + newRebalancingSetToken: Address, + auctionLibrary: Address, + caller: Address + ): Promise { + const curveCoefficient = ether(1); + const auctionStartPrice = ether(5); + const auctionPriceDivisor = ether(10); + + await this._blockchain.increaseTimeAsync(DEFAULT_TIME_FAST_FORWARD); + await rebalancingSetToken.propose.sendTransactionAsync( + newRebalancingSetToken, + auctionLibrary, + curveCoefficient, + auctionStartPrice, + auctionPriceDivisor, + { from: caller, gas: DEFAULT_GAS} + ); + } + + public async defaultTransitionToRebalance( + rebalancingSetToken: RebalancingSetTokenContract, + newRebalancingSetToken: Address, + auctionLibrary: Address, + caller: Address + ): Promise { + await this.defaultTransitionToPropose( + rebalancingSetToken, + newRebalancingSetToken, + auctionLibrary, + caller + ); + + await this._blockchain.increaseTimeAsync(DEFAULT_TIME_FAST_FORWARD); + await rebalancingSetToken.rebalance.sendTransactionAsync( + { from: caller, gas: DEFAULT_GAS } + ); + } +} \ No newline at end of file diff --git a/utils/constants.ts b/utils/constants.ts index fea508bf7..ca6cced29 100644 --- a/utils/constants.ts +++ b/utils/constants.ts @@ -14,6 +14,13 @@ export const UNLIMITED_ALLOWANCE_IN_BASE_UNITS = new BigNumber(2).pow(256).minus export const MAX_DIGITS_IN_UNSIGNED_256_INT = 78; export const ZRX_TOKEN_ADDRESS = '0x871dd7c2b4b25e1aa18728e9d5f2af4c4e431f5c'; +// Rebalancing Constants +export const DEFAULT_PERIOD_INTERVAL = new BigNumber(90000); +export const DEFAULT_UNIT_SHARES = new BigNumber(1); +export const DEFAULT_REBALANCING_NATURAL_UNIT = new BigNumber(1); +export const DEFAULT_TIME_FAST_FORWARD = 100000; + + // TODO: Move this into set-protocol-utils export const REBALANCING_STATE = { DEFAULT: 0, From 27152effda4897cf20d320bac2ca37e2056a43b4 Mon Sep 17 00:00:00 2001 From: Brian Weickmann Date: Thu, 23 Aug 2018 12:29:24 -0700 Subject: [PATCH 09/11] Added getters for arrays created during propose. Revised tests to generate expected arrays during propose. --- contracts/core/RebalancingSetToken.sol | 41 +++++++++++++++++++++++++- test/core/rebalancingSetToken.spec.ts | 22 +++++++------- utils/RebalancingTokenWrapper.ts | 24 ++++++++++++++- 3 files changed, 75 insertions(+), 12 deletions(-) diff --git a/contracts/core/RebalancingSetToken.sol b/contracts/core/RebalancingSetToken.sol index eb1d954bb..af393fe1d 100644 --- a/contracts/core/RebalancingSetToken.sol +++ b/contracts/core/RebalancingSetToken.sol @@ -345,6 +345,45 @@ contract RebalancingSetToken is return [unitShares]; } + /* + * Get combinedTokenArray of Rebalancing Set + * + * @return combinedTokenArray + */ + function getCombinedTokenArray() + external + view + returns(address[]) + { + return combinedTokenArray; + } + + /* + * Get combinedCurrentUnits of Rebalancing Set + * + * @return combinedCurrentUnits + */ + function getCombinedCurrentUnits() + external + view + returns(uint256[]) + { + return combinedCurrentUnits; + } + + /* + * Get combinedRebalanceUnits of Rebalancing Set + * + * @return combinedRebalanceUnits + */ + function getCombinedRebalanceUnits() + external + view + returns(uint256[]) + { + return combinedRebalanceUnits; + } + /* ============ Transfer Overrides ============ */ /* @@ -438,7 +477,7 @@ contract RebalancingSetToken is } /** - * Function to calculate the transfer value of a component given quantity of Set + * Function to calculate the transfer value of a component given 1 Set * * @param _unit The units of the component token * @param _naturalUnit The natural unit of the Set token diff --git a/test/core/rebalancingSetToken.spec.ts b/test/core/rebalancingSetToken.spec.ts index ac305bc25..d7e74b9c9 100644 --- a/test/core/rebalancingSetToken.spec.ts +++ b/test/core/rebalancingSetToken.spec.ts @@ -965,21 +965,23 @@ contract('RebalancingSetToken', accounts => { it('creates the correct combinedCurrentUnits', async () => { await subject(); - const expectedCombinedCurrentUnits = [ether(2), ether(2), ether(0)]; - expectedCombinedCurrentUnits.forEach(async (expectUnit, index) => { - const actualUnit = await rebalancingSetToken.combinedCurrentUnits.callAsync(new BigNumber(index)); - expect(actualUnit).to.be.bignumber.equal(expectUnit); - }); + const expectedCombinedCurrentUnits = await rebalancingTokenWrapper.constructCombinedUnitArray( + rebalancingSetToken, + currentSetToken + ); + const actualCombinedCurrentUnits = await rebalancingSetToken.getCombinedCurrentUnits.callAsync(); + expect(JSON.stringify(actualCombinedCurrentUnits)).to.eql(JSON.stringify(expectedCombinedCurrentUnits)); }); it('creates the correct combinedRebalanceUnits', async () => { await subject(); - const expectedCombinedRebalanceUnits = [ether(0), ether(1), ether(1)]; - expectedCombinedRebalanceUnits.forEach(async (expectUnit, index) => { - const actualUnit = await rebalancingSetToken.combinedRebalanceUnits.callAsync(new BigNumber(index)); - expect(actualUnit).to.be.bignumber.equal(expectUnit); - }); + const expectedCombinedRebalanceUnits = await rebalancingTokenWrapper.constructCombinedUnitArray( + rebalancingSetToken, + newRebalancingSetToken + ); + const actualCombinedRebalanceUnits = await rebalancingSetToken.getCombinedRebalanceUnits.callAsync(); + expect(JSON.stringify(actualCombinedRebalanceUnits)).to.eql(JSON.stringify(expectedCombinedRebalanceUnits)); }); it('emits the correct RebalanceProposed event', async () => { diff --git a/utils/RebalancingTokenWrapper.ts b/utils/RebalancingTokenWrapper.ts index 1d24b9273..8a1c6e315 100644 --- a/utils/RebalancingTokenWrapper.ts +++ b/utils/RebalancingTokenWrapper.ts @@ -141,4 +141,26 @@ export class RebalancingTokenWrapper { { from: caller, gas: DEFAULT_GAS } ); } -} \ No newline at end of file + + public async constructCombinedUnitArray( + rebalancingSetToken: RebalancingSetTokenContract, + setToken: SetTokenContract, + ): Promise { + const combinedTokenArray = await rebalancingSetToken.getCombinedTokenArray.callAsync(); + const setTokenComponents = await setToken.getComponents.callAsync(); + const setTokenUnits = await setToken.getUnits.callAsync(); + const setNaturalUnit = await setToken.naturalUnit.callAsync(); + + const combinedSetTokenUnits: BigNumber[] = []; + combinedTokenArray.forEach(address => { + const index = setTokenComponents.indexOf(address); + if (index != -1) { + const totalTokenAmount = setTokenUnits[index].mul(new BigNumber(10 ** 18)).div(setNaturalUnit); + combinedSetTokenUnits.push(totalTokenAmount); + } else { + combinedSetTokenUnits.push(new BigNumber(0)); + } + }); + return combinedSetTokenUnits; + } +} From 49a1a2aa4e71607b01096c98039c87f5028c57c9 Mon Sep 17 00:00:00 2001 From: Brian Weickmann Date: Thu, 23 Aug 2018 14:47:53 -0700 Subject: [PATCH 10/11] Renamed constants and gave new values. Changed helper function names. Renamed some variables. --- test/core/rebalancingSetToken.spec.ts | 118 +++++++++++++++----------- test/lib/authorizable.spec.ts | 6 +- utils/RebalancingTokenWrapper.ts | 48 +++++------ utils/blockchain.ts | 6 +- utils/constants.ts | 4 +- 5 files changed, 100 insertions(+), 82 deletions(-) diff --git a/test/core/rebalancingSetToken.spec.ts b/test/core/rebalancingSetToken.spec.ts index d7e74b9c9..3b35bd84b 100644 --- a/test/core/rebalancingSetToken.spec.ts +++ b/test/core/rebalancingSetToken.spec.ts @@ -22,9 +22,8 @@ import { DEFAULT_GAS, NULL_ADDRESS, REBALANCING_STATE, - DEFAULT_PERIOD_INTERVAL, + ONE_DAY_IN_SECONDS, DEFAULT_UNIT_SHARES, - DEFAULT_TIME_FAST_FORWARD } from '../../utils/constants'; import { assertLogEquivalence, getFormattedLogsFromTxHash } from '../../utils/logs'; import { @@ -114,8 +113,8 @@ contract('RebalancingSetToken', accounts => { subjectManager = managerAccount; subjectInitialSet = components[0].address, subjectInitialUnitShares = DEFAULT_UNIT_SHARES; - subjectProposalPeriod = DEFAULT_PERIOD_INTERVAL; - subjectRebalanceInterval = DEFAULT_PERIOD_INTERVAL; + subjectProposalPeriod = ONE_DAY_IN_SECONDS; + subjectRebalanceInterval = ONE_DAY_IN_SECONDS; }); async function subject(): Promise { @@ -224,8 +223,8 @@ contract('RebalancingSetToken', accounts => { initialSet = components[0].address; const manager = managerAccount; const initialUnitShares = DEFAULT_UNIT_SHARES; - const proposalPeriod = DEFAULT_PERIOD_INTERVAL; - const rebalanceInterval = DEFAULT_PERIOD_INTERVAL; + const proposalPeriod = ONE_DAY_IN_SECONDS; + const rebalanceInterval = ONE_DAY_IN_SECONDS; rebalancingSetToken = await coreWrapper.deployRebalancingSetTokenAsync( factoryAccount, @@ -262,8 +261,8 @@ contract('RebalancingSetToken', accounts => { const initialSet = components[0].address; const manager = managerAccount; initialUnitShares = DEFAULT_UNIT_SHARES; - const proposalPeriod = DEFAULT_PERIOD_INTERVAL; - const rebalanceInterval = DEFAULT_PERIOD_INTERVAL; + const proposalPeriod = ONE_DAY_IN_SECONDS; + const rebalanceInterval = ONE_DAY_IN_SECONDS; rebalancingSetToken = await coreWrapper.deployRebalancingSetTokenAsync( factoryAccount, @@ -315,8 +314,8 @@ contract('RebalancingSetToken', accounts => { const manager = managerAccount; const initialSet = currentSetToken.address; const initialUnitShares = DEFAULT_UNIT_SHARES; - const proposalPeriod = DEFAULT_PERIOD_INTERVAL; - const rebalanceInterval = DEFAULT_PERIOD_INTERVAL; + const proposalPeriod = ONE_DAY_IN_SECONDS; + const rebalanceInterval = ONE_DAY_IN_SECONDS; const rebalancingFactory = await coreWrapper.deployRebalancingSetTokenFactoryAsync(coreAccount); await coreWrapper.enableFactoryAsync(coreMock, rebalancingFactory); @@ -372,12 +371,16 @@ contract('RebalancingSetToken', accounts => { let newRebalancingSetToken: SetTokenContract; beforeEach(async () => { - const setTokens = await rebalancingTokenWrapper.createSetTokens(coreMock, factory.address, transferProxy.address); + const setTokens = await rebalancingTokenWrapper.createSetTokensAsync( + coreMock, + factory.address, + transferProxy.address + ); const currentSetToken = setTokens[0]; newRebalancingSetToken = setTokens[1]; - const proposalPeriod = DEFAULT_PERIOD_INTERVAL; - rebalancingSetToken = await rebalancingTokenWrapper.createDefaultRebalancingSetToken( + const proposalPeriod = ONE_DAY_IN_SECONDS; + rebalancingSetToken = await rebalancingTokenWrapper.createDefaultRebalancingSetTokenAsync( coreMock, rebalancingFactory.address, managerAccount, @@ -385,7 +388,6 @@ contract('RebalancingSetToken', accounts => { proposalPeriod ); - subjectIssuer = deployerAccount, subjectQuantity = ether(5); subjectCaller = managerAccount; @@ -435,7 +437,7 @@ contract('RebalancingSetToken', accounts => { describe('when mint is called from Rebalance state', async () => { beforeEach(async () => { - await rebalancingTokenWrapper.defaultTransitionToRebalance( + await rebalancingTokenWrapper.defaultTransitionToRebalanceAsync( rebalancingSetToken, newRebalancingSetToken.address, libraryAccount, @@ -472,8 +474,8 @@ contract('RebalancingSetToken', accounts => { const manager = managerAccount; const initialSet = currentSetToken.address; const initialUnitShares = DEFAULT_UNIT_SHARES; - const proposalPeriod = DEFAULT_PERIOD_INTERVAL; - const rebalanceInterval = DEFAULT_PERIOD_INTERVAL; + const proposalPeriod = ONE_DAY_IN_SECONDS; + const rebalanceInterval = ONE_DAY_IN_SECONDS; const rebalancingFactory = await coreWrapper.deployRebalancingSetTokenFactoryAsync(coreAccount); await coreWrapper.enableFactoryAsync(coreMock, rebalancingFactory); @@ -535,12 +537,16 @@ contract('RebalancingSetToken', accounts => { let newRebalancingSetToken: SetTokenContract; beforeEach(async () => { - const setTokens = await rebalancingTokenWrapper.createSetTokens(coreMock, factory.address, transferProxy.address); + const setTokens = await rebalancingTokenWrapper.createSetTokensAsync( + coreMock, + factory.address, + transferProxy.address + ); const currentSetToken = setTokens[0]; newRebalancingSetToken = setTokens[1]; - const proposalPeriod = DEFAULT_PERIOD_INTERVAL; - rebalancingSetToken = await rebalancingTokenWrapper.createDefaultRebalancingSetToken( + const proposalPeriod = ONE_DAY_IN_SECONDS; + rebalancingSetToken = await rebalancingTokenWrapper.createDefaultRebalancingSetTokenAsync( coreMock, rebalancingFactory.address, managerAccount, @@ -624,7 +630,7 @@ contract('RebalancingSetToken', accounts => { { from: subjectCaller, gas: DEFAULT_GAS} ); - await rebalancingTokenWrapper.defaultTransitionToRebalance( + await rebalancingTokenWrapper.defaultTransitionToRebalanceAsync( rebalancingSetToken, newRebalancingSetToken.address, libraryAccount, @@ -649,8 +655,8 @@ contract('RebalancingSetToken', accounts => { const initialSet = components[0].address; const manager = managerAccount; const initialUnitShares = DEFAULT_UNIT_SHARES; - const proposalPeriod = DEFAULT_PERIOD_INTERVAL; - const rebalanceInterval = DEFAULT_PERIOD_INTERVAL; + const proposalPeriod = ONE_DAY_IN_SECONDS; + const rebalanceInterval = ONE_DAY_IN_SECONDS; rebalancingSetToken = await coreWrapper.deployRebalancingSetTokenAsync( factoryAccount, @@ -715,8 +721,8 @@ contract('RebalancingSetToken', accounts => { const manager = managerAccount; const initialSet = components[0].address; const initialUnitShares = DEFAULT_UNIT_SHARES; - const proposalPeriod = DEFAULT_PERIOD_INTERVAL; - const rebalanceInterval = DEFAULT_PERIOD_INTERVAL; + const proposalPeriod = ONE_DAY_IN_SECONDS; + const rebalanceInterval = ONE_DAY_IN_SECONDS; rebalancingSetToken = await coreWrapper.deployRebalancingSetTokenAsync( factory.address, @@ -791,8 +797,8 @@ contract('RebalancingSetToken', accounts => { const manager = managerAccount; const initialSet = components[0].address; const initialUnitShares = DEFAULT_UNIT_SHARES; - const proposalPeriod = DEFAULT_PERIOD_INTERVAL; - const rebalanceInterval = DEFAULT_PERIOD_INTERVAL; + const proposalPeriod = ONE_DAY_IN_SECONDS; + const rebalanceInterval = ONE_DAY_IN_SECONDS; rebalancingSetToken = await coreWrapper.deployRebalancingSetTokenAsync( factory.address, @@ -865,19 +871,23 @@ contract('RebalancingSetToken', accounts => { let subjectAuctionStartPrice: BigNumber; let subjectAuctionPriceDivisor: BigNumber; let subjectCaller: Address; - let subjectTimeFastForward: number; + let subjectTimeFastForward: BigNumber; let proposalPeriod: BigNumber; let currentSetToken: SetTokenContract; let newRebalancingSetToken: SetTokenContract; beforeEach(async () => { - const setTokens = await rebalancingTokenWrapper.createSetTokens(coreMock, factory.address, transferProxy.address); + const setTokens = await rebalancingTokenWrapper.createSetTokensAsync( + coreMock, + factory.address, + transferProxy.address + ); currentSetToken = setTokens[0]; newRebalancingSetToken = setTokens[1]; - proposalPeriod = DEFAULT_PERIOD_INTERVAL; - rebalancingSetToken = await rebalancingTokenWrapper.createDefaultRebalancingSetToken( + proposalPeriod = ONE_DAY_IN_SECONDS; + rebalancingSetToken = await rebalancingTokenWrapper.createDefaultRebalancingSetTokenAsync( coreMock, rebalancingFactory.address, managerAccount, @@ -891,7 +901,7 @@ contract('RebalancingSetToken', accounts => { subjectAuctionStartPrice = ether(5); subjectAuctionPriceDivisor = ether(10); subjectCaller = managerAccount; - subjectTimeFastForward = DEFAULT_TIME_FAST_FORWARD; + subjectTimeFastForward = ONE_DAY_IN_SECONDS.add(1); }); async function subject(): Promise { @@ -965,7 +975,7 @@ contract('RebalancingSetToken', accounts => { it('creates the correct combinedCurrentUnits', async () => { await subject(); - const expectedCombinedCurrentUnits = await rebalancingTokenWrapper.constructCombinedUnitArray( + const expectedCombinedCurrentUnits = await rebalancingTokenWrapper.constructCombinedUnitArrayAsync( rebalancingSetToken, currentSetToken ); @@ -976,7 +986,7 @@ contract('RebalancingSetToken', accounts => { it('creates the correct combinedRebalanceUnits', async () => { await subject(); - const expectedCombinedRebalanceUnits = await rebalancingTokenWrapper.constructCombinedUnitArray( + const expectedCombinedRebalanceUnits = await rebalancingTokenWrapper.constructCombinedUnitArrayAsync( rebalancingSetToken, newRebalancingSetToken ); @@ -1003,7 +1013,7 @@ contract('RebalancingSetToken', accounts => { describe('but the rebalance interval has not elapsed', async () => { beforeEach(async () => { - subjectTimeFastForward = 1000; + subjectTimeFastForward = ONE_DAY_IN_SECONDS.sub(1); }); it('should revert', async () => { @@ -1024,7 +1034,7 @@ contract('RebalancingSetToken', accounts => { describe('when propose is called from Proposal state', async () => { beforeEach(async () => { - await rebalancingTokenWrapper.defaultTransitionToPropose( + await rebalancingTokenWrapper.defaultTransitionToProposeAsync( rebalancingSetToken, newRebalancingSetToken.address, libraryAccount, @@ -1039,7 +1049,7 @@ contract('RebalancingSetToken', accounts => { describe('when propose is called from Rebalance state', async () => { beforeEach(async () => { - await rebalancingTokenWrapper.defaultTransitionToRebalance( + await rebalancingTokenWrapper.defaultTransitionToRebalanceAsync( rebalancingSetToken, newRebalancingSetToken.address, libraryAccount, @@ -1055,19 +1065,23 @@ contract('RebalancingSetToken', accounts => { describe('#rebalance', async () => { let subjectCaller: Address; - let subjectTimeFastForward: number; + let subjectTimeFastForward: BigNumber; let proposalPeriod: BigNumber; let currentSetToken: SetTokenContract; let newRebalancingSetToken: SetTokenContract; beforeEach(async () => { - const setTokens = await rebalancingTokenWrapper.createSetTokens(coreMock, factory.address, transferProxy.address); + const setTokens = await rebalancingTokenWrapper.createSetTokensAsync( + coreMock, + factory.address, + transferProxy.address + ); currentSetToken = setTokens[0]; newRebalancingSetToken = setTokens[1]; - proposalPeriod = DEFAULT_PERIOD_INTERVAL; - rebalancingSetToken = await rebalancingTokenWrapper.createDefaultRebalancingSetToken( + proposalPeriod = ONE_DAY_IN_SECONDS; + rebalancingSetToken = await rebalancingTokenWrapper.createDefaultRebalancingSetTokenAsync( coreMock, rebalancingFactory.address, managerAccount, @@ -1076,7 +1090,7 @@ contract('RebalancingSetToken', accounts => { ); subjectCaller = managerAccount; - subjectTimeFastForward = DEFAULT_TIME_FAST_FORWARD; + subjectTimeFastForward = ONE_DAY_IN_SECONDS.add(1); }); async function subject(): Promise { @@ -1094,7 +1108,7 @@ contract('RebalancingSetToken', accounts => { describe('when rebalance is called from Propose State', async () => { beforeEach(async () => { - await rebalancingTokenWrapper.defaultTransitionToPropose( + await rebalancingTokenWrapper.defaultTransitionToProposeAsync( rebalancingSetToken, newRebalancingSetToken.address, libraryAccount, @@ -1124,7 +1138,7 @@ contract('RebalancingSetToken', accounts => { describe('but not enough time has passed before proposal period has elapsed', async () => { beforeEach(async () => { - subjectTimeFastForward = 1000; + subjectTimeFastForward = ONE_DAY_IN_SECONDS.sub(1); }); it('should revert', async () => { @@ -1135,7 +1149,7 @@ contract('RebalancingSetToken', accounts => { describe('when rebalance is called from Rebalance State', async () => { beforeEach(async () => { - await rebalancingTokenWrapper.defaultTransitionToRebalance( + await rebalancingTokenWrapper.defaultTransitionToRebalanceAsync( rebalancingSetToken, newRebalancingSetToken.address, libraryAccount, @@ -1156,12 +1170,16 @@ contract('RebalancingSetToken', accounts => { let newRebalancingSetToken: SetTokenContract; beforeEach(async () => { - const setTokens = await rebalancingTokenWrapper.createSetTokens(coreMock, factory.address, transferProxy.address); + const setTokens = await rebalancingTokenWrapper.createSetTokensAsync( + coreMock, + factory.address, + transferProxy.address + ); const currentSetToken = setTokens[0]; newRebalancingSetToken = setTokens[1]; - proposalPeriod = DEFAULT_PERIOD_INTERVAL; - rebalancingSetToken = await rebalancingTokenWrapper.createDefaultRebalancingSetToken( + proposalPeriod = ONE_DAY_IN_SECONDS; + rebalancingSetToken = await rebalancingTokenWrapper.createDefaultRebalancingSetTokenAsync( coreMock, rebalancingFactory.address, managerAccount, @@ -1186,7 +1204,7 @@ contract('RebalancingSetToken', accounts => { describe('when settlement is called from Proposal State', async () => { beforeEach(async () => { - await rebalancingTokenWrapper.defaultTransitionToPropose( + await rebalancingTokenWrapper.defaultTransitionToProposeAsync( rebalancingSetToken, newRebalancingSetToken.address, libraryAccount, @@ -1201,7 +1219,7 @@ contract('RebalancingSetToken', accounts => { describe('when settlement is called from Rebalance State', async () => { beforeEach(async () => { - await rebalancingTokenWrapper.defaultTransitionToRebalance( + await rebalancingTokenWrapper.defaultTransitionToRebalanceAsync( rebalancingSetToken, newRebalancingSetToken.address, libraryAccount, diff --git a/test/lib/authorizable.spec.ts b/test/lib/authorizable.spec.ts index 28cb0ce8a..919c20594 100644 --- a/test/lib/authorizable.spec.ts +++ b/test/lib/authorizable.spec.ts @@ -114,7 +114,7 @@ contract('Authorizable', accounts => { }); describe('when the timestamp is beyond the grace period of 4 weeks', async () => { - const timeToIncrease = gracePeriod.plus(new BigNumber(1000)).toNumber(); + const timeToIncrease = gracePeriod.plus(new BigNumber(1000)); beforeEach(async () => { await blockchain.saveSnapshotAsync(); @@ -207,7 +207,7 @@ contract('Authorizable', accounts => { }); describe('when the timestamp is beyond the grace period of 4 weeks', async () => { - const timeToIncrease = gracePeriod.plus(new BigNumber(1000)).toNumber(); + const timeToIncrease = gracePeriod.plus(new BigNumber(1000)); beforeEach(async () => { await blockchain.saveSnapshotAsync(); @@ -314,7 +314,7 @@ contract('Authorizable', accounts => { }); describe('when the timestamp is beyond the grace period of 4 weeks', async () => { - const timeToIncrease = gracePeriod.plus(new BigNumber(1000)).toNumber(); + const timeToIncrease = gracePeriod.plus(new BigNumber(1000)); beforeEach(async () => { await blockchain.saveSnapshotAsync(); diff --git a/utils/RebalancingTokenWrapper.ts b/utils/RebalancingTokenWrapper.ts index 8a1c6e315..80f2cab37 100644 --- a/utils/RebalancingTokenWrapper.ts +++ b/utils/RebalancingTokenWrapper.ts @@ -12,10 +12,9 @@ import { BigNumber } from 'bignumber.js'; import { ether } from './units'; import { DEFAULT_GAS, - DEFAULT_PERIOD_INTERVAL, + ONE_DAY_IN_SECONDS, DEFAULT_UNIT_SHARES, DEFAULT_REBALANCING_NATURAL_UNIT, - DEFAULT_TIME_FAST_FORWARD } from './constants'; import { CoreWrapper } from './coreWrapper'; @@ -43,7 +42,7 @@ export class RebalancingTokenWrapper { this._blockchain = blockchain; } - public async createSetTokens( + public async createSetTokensAsync( core: CoreLikeContract, factory: Address, transferProxy: Address, @@ -53,31 +52,31 @@ export class RebalancingTokenWrapper { const components = await this._erc20Wrapper.deployTokensAsync(3, this._tokenOwnerAddress); await this._erc20Wrapper.approveTransfersAsync(components, transferProxy); - const set1Components = components.slice(0, 2); - const set1ComponentAddresses = _.map(set1Components, token => token.address); - const set1ComponentUnits = _.map(set1Components, () => naturalUnit.mul(2)); // Multiple of naturalUnit - const setToken1 = await this._coreWrapper.createSetTokenAsync( + const firstSetComponents = components.slice(0, 2); + const firstSetComponentAddresses = _.map(firstSetComponents, token => token.address); + const firstSetComponentUnits = _.map(firstSetComponents, () => naturalUnit.mul(2)); // Multiple of naturalUnit + const firstSetToken = await this._coreWrapper.createSetTokenAsync( core, factory, - set1ComponentAddresses, - set1ComponentUnits, + firstSetComponentAddresses, + firstSetComponentUnits, naturalUnit, ); - const set2Components = components.slice(1, 3); - const set2ComponentAddresses = _.map(set2Components, token => token.address); - const set2ComponentUnits = _.map(set2Components, () => naturalUnit.mul(1)); // Multiple of naturalUnit - const setToken2 = await this._coreWrapper.createSetTokenAsync( + const secondSetComponents = components.slice(1, 3); + const secondSetComponentAddresses = _.map(secondSetComponents, token => token.address); + const secondSetComponentUnits = _.map(secondSetComponents, () => naturalUnit.mul(1)); // Multiple of naturalUnit + const secondSetToken = await this._coreWrapper.createSetTokenAsync( core, factory, - set2ComponentAddresses, - set2ComponentUnits, + secondSetComponentAddresses, + secondSetComponentUnits, naturalUnit, ); - return [setToken1, setToken2]; + return [firstSetToken, secondSetToken]; } - public async createDefaultRebalancingSetToken( + public async createDefaultRebalancingSetTokenAsync( core: CoreLikeContract, factory: Address, manager: Address, @@ -85,7 +84,7 @@ export class RebalancingTokenWrapper { proposalPeriod: BigNumber, ): Promise { const initialUnitShares = DEFAULT_UNIT_SHARES; - const rebalanceInterval = DEFAULT_PERIOD_INTERVAL; + const rebalanceInterval = ONE_DAY_IN_SECONDS; const callData = SetProtocolUtils.bufferArrayToHex([ SetProtocolUtils.paddedBufferForPrimitive(manager), SetProtocolUtils.paddedBufferForBigNumber(proposalPeriod), @@ -102,7 +101,7 @@ export class RebalancingTokenWrapper { ); } - public async defaultTransitionToPropose( + public async defaultTransitionToProposeAsync( rebalancingSetToken: RebalancingSetTokenContract, newRebalancingSetToken: Address, auctionLibrary: Address, @@ -112,7 +111,7 @@ export class RebalancingTokenWrapper { const auctionStartPrice = ether(5); const auctionPriceDivisor = ether(10); - await this._blockchain.increaseTimeAsync(DEFAULT_TIME_FAST_FORWARD); + await this._blockchain.increaseTimeAsync(ONE_DAY_IN_SECONDS.add(1)); await rebalancingSetToken.propose.sendTransactionAsync( newRebalancingSetToken, auctionLibrary, @@ -123,26 +122,27 @@ export class RebalancingTokenWrapper { ); } - public async defaultTransitionToRebalance( + public async defaultTransitionToRebalanceAsync( rebalancingSetToken: RebalancingSetTokenContract, newRebalancingSetToken: Address, auctionLibrary: Address, caller: Address ): Promise { - await this.defaultTransitionToPropose( + await this.defaultTransitionToProposeAsync( rebalancingSetToken, newRebalancingSetToken, auctionLibrary, caller ); - await this._blockchain.increaseTimeAsync(DEFAULT_TIME_FAST_FORWARD); + await this._blockchain.increaseTimeAsync(ONE_DAY_IN_SECONDS.add(1)); await rebalancingSetToken.rebalance.sendTransactionAsync( { from: caller, gas: DEFAULT_GAS } ); } - public async constructCombinedUnitArray( + // Used to construct expected comined unit arrays made during propose calls + public async constructCombinedUnitArrayAsync( rebalancingSetToken: RebalancingSetTokenContract, setToken: SetTokenContract, ): Promise { diff --git a/utils/blockchain.ts b/utils/blockchain.ts index f063062cc..671027dca 100644 --- a/utils/blockchain.ts +++ b/utils/blockchain.ts @@ -1,6 +1,8 @@ import * as promisify from 'tiny-promisify'; import * as Web3 from 'web3'; +import { BigNumber } from 'bignumber.js'; + export class Blockchain { private _web3: Web3; @@ -22,9 +24,9 @@ export class Blockchain { } public async increaseTimeAsync( - duration: number, + duration: BigNumber, ): Promise { - await this.sendJSONRpcRequestAsync('evm_increaseTime', [duration]); + await this.sendJSONRpcRequestAsync('evm_increaseTime', [duration.toNumber()]); } private async sendJSONRpcRequestAsync( diff --git a/utils/constants.ts b/utils/constants.ts index ca6cced29..405adf0c6 100644 --- a/utils/constants.ts +++ b/utils/constants.ts @@ -15,11 +15,9 @@ export const MAX_DIGITS_IN_UNSIGNED_256_INT = 78; export const ZRX_TOKEN_ADDRESS = '0x871dd7c2b4b25e1aa18728e9d5f2af4c4e431f5c'; // Rebalancing Constants -export const DEFAULT_PERIOD_INTERVAL = new BigNumber(90000); +export const ONE_DAY_IN_SECONDS = new BigNumber(86400); export const DEFAULT_UNIT_SHARES = new BigNumber(1); export const DEFAULT_REBALANCING_NATURAL_UNIT = new BigNumber(1); -export const DEFAULT_TIME_FAST_FORWARD = 100000; - // TODO: Move this into set-protocol-utils export const REBALANCING_STATE = { From aa3933efeafc7b2c9a16fbee62d2965fd91d0517 Mon Sep 17 00:00:00 2001 From: Brian Weickmann Date: Thu, 23 Aug 2018 14:59:34 -0700 Subject: [PATCH 11/11] Small fix to make sure no block time error. --- test/core/rebalancingSetToken.spec.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/core/rebalancingSetToken.spec.ts b/test/core/rebalancingSetToken.spec.ts index 3b35bd84b..d1625f903 100644 --- a/test/core/rebalancingSetToken.spec.ts +++ b/test/core/rebalancingSetToken.spec.ts @@ -1013,7 +1013,7 @@ contract('RebalancingSetToken', accounts => { describe('but the rebalance interval has not elapsed', async () => { beforeEach(async () => { - subjectTimeFastForward = ONE_DAY_IN_SECONDS.sub(1); + subjectTimeFastForward = ONE_DAY_IN_SECONDS.sub(10); }); it('should revert', async () => { @@ -1138,7 +1138,7 @@ contract('RebalancingSetToken', accounts => { describe('but not enough time has passed before proposal period has elapsed', async () => { beforeEach(async () => { - subjectTimeFastForward = ONE_DAY_IN_SECONDS.sub(1); + subjectTimeFastForward = ONE_DAY_IN_SECONDS.sub(10); }); it('should revert', async () => {