From cc6991c2e0c0447ec1ee6aa9db68e77118542f8e Mon Sep 17 00:00:00 2001 From: Kerman Kohli Date: Thu, 21 Feb 2019 10:29:50 -0800 Subject: [PATCH 1/2] Implement new deployment script --- .env.default | 7 + .gitignore | 3 + deployments/README.md | 26 ++ deployments/constants.ts | 30 ++ deployments/dependencies.ts | 54 +++ deployments/deploy.ts | 14 + deployments/manager.ts | 101 +++++ deployments/network-constants.ts | 64 ++++ deployments/outputs.json | 73 ++++ deployments/outputs.ts | 80 ++++ deployments/stages/1_libraries.ts | 155 ++++++++ deployments/stages/2_core.ts | 243 ++++++++++++ deployments/stages/3_modules.ts | 289 ++++++++++++++ deployments/stages/4_authorization.ts | 216 +++++++++++ deployments/stages/5_rebalancing.ts | 179 +++++++++ deployments/test/1_core.spec.ts | 216 +++++++++++ deployments/test/2_libraries.spec.ts | 109 ++++++ deployments/test/3_modules.spec.ts | 360 ++++++++++++++++++ deployments/test/4_authorization.spec.ts | 280 ++++++++++++++ deployments/test/5_rebalancing.spec.ts | 236 ++++++++++++ deployments/utils/blockchain.ts | 90 +++++ deployments/utils/output-helper.ts | 80 ++++ deployments/utils/rebalancing.ts | 66 ++++ migrations/2_token_whitelist.js | 2 +- migrations/3_core.js | 2 +- ...cingSetToken.js => 5_rebalancing_token.js} | 2 +- package.json | 26 +- scripts/generate_outputs.sh | 12 + truffle.js | 51 +-- types/deployment_stage_interface.ts | 3 + utils/array.ts | 6 + utils/contracts.ts | 14 + 32 files changed, 3052 insertions(+), 37 deletions(-) create mode 100644 .env.default create mode 100644 deployments/README.md create mode 100644 deployments/constants.ts create mode 100644 deployments/dependencies.ts create mode 100644 deployments/deploy.ts create mode 100644 deployments/manager.ts create mode 100644 deployments/network-constants.ts create mode 100644 deployments/outputs.json create mode 100644 deployments/outputs.ts create mode 100644 deployments/stages/1_libraries.ts create mode 100644 deployments/stages/2_core.ts create mode 100644 deployments/stages/3_modules.ts create mode 100644 deployments/stages/4_authorization.ts create mode 100644 deployments/stages/5_rebalancing.ts create mode 100755 deployments/test/1_core.spec.ts create mode 100755 deployments/test/2_libraries.spec.ts create mode 100755 deployments/test/3_modules.spec.ts create mode 100644 deployments/test/4_authorization.spec.ts create mode 100644 deployments/test/5_rebalancing.spec.ts create mode 100755 deployments/utils/blockchain.ts create mode 100644 deployments/utils/output-helper.ts create mode 100644 deployments/utils/rebalancing.ts rename migrations/{5_BTCETHRebalancingSetToken.js => 5_rebalancing_token.js} (99%) create mode 100644 scripts/generate_outputs.sh create mode 100644 types/deployment_stage_interface.ts create mode 100644 utils/array.ts diff --git a/.env.default b/.env.default new file mode 100644 index 000000000..901e65c1c --- /dev/null +++ b/.env.default @@ -0,0 +1,7 @@ +INFURAKEY="" +MNEMONIC="" +PRIVATE_KEY="" + +DEPLOYMENT_PRIVATE_KEY="" +DEPLOYMENT_NETWORK_NAME="development" +DEPLOYMENT_NETWORK_ID=50; \ No newline at end of file diff --git a/.gitignore b/.gitignore index 65d9a4e5e..569f89103 100644 --- a/.gitignore +++ b/.gitignore @@ -29,3 +29,6 @@ yarn-error.log* # Snapshot blockchain/ + +# Ouputs +outputs.ts \ No newline at end of file diff --git a/deployments/README.md b/deployments/README.md new file mode 100644 index 000000000..69b41d97f --- /dev/null +++ b/deployments/README.md @@ -0,0 +1,26 @@ +# Getting Started + +Before getting started, make sure you have the following `env` variables set: + +``` +// Private key to deploy with +DEPLOYMENT_PRIVATE_KEY="0xA..." + +// The network name set here will use constants from `network-constants.ts` +DEPLOYMENT_NETWORK_NAME="development" + +// Network id will indicate which chain to run on (testnet, mainnet etc) +DEPLOYMENT_NETWORK_ID=50; +``` + +## Deploying + +The deployment script will output all addresses to `outputs.json`. + +To deploy, simply run `yarn deploy`. Test are automatically run at the end. + +## Tests + +In order to run tests the outputs file should contain addresses for the contracts you would like to test against and the `.env` file should have the same `DEPLOYMENT_NETWORK_NAME` and `DEPLOYMENT_NETWORK_ID`. + +Then run `yarn test-development` \ No newline at end of file diff --git a/deployments/constants.ts b/deployments/constants.ts new file mode 100644 index 000000000..a08f41700 --- /dev/null +++ b/deployments/constants.ts @@ -0,0 +1,30 @@ +import BigNumber from 'bignumber.js'; + +export default { + EXCHANGES: { + ZERO_EX: 1, + KYBER: 2, + TAKER_WALLET: 3, + }, + WBTC_PRICE: new BigNumber(3711), + WBTC_FULL_TOKEN_UNITS: new BigNumber(10 ** 8), + WBTC_MULTIPLIER: new BigNumber(1), + DEFAULT_WBTC_UNIT: new BigNumber(1), + WETH_FULL_TOKEN_UNITS: new BigNumber(10 ** 18), + WETH_PRICE: new BigNumber(128), + WETH_MULTIPLIER: new BigNumber(1), + WETH_DOMINANT_REBALANCING_NATURAL_UNIT: new BigNumber(10 ** 12), + PRICE_PRECISION: new BigNumber(100), + DEFAULT_REBALANCING_NATURAL_UNIT: new BigNumber(10 ** 10), + REBALANCING_SET_USD_PRICE: new BigNumber(100), + DEFAULT_AUCTION_PRICE_NUMERATOR: 1374, + DEFAULT_AUCTION_PRICE_DENOMINATOR: 1000, + SET_FULL_TOKEN_UNITS: new BigNumber(10 ** 18), + ONE_MINUTE_IN_SECONDS: 60, + THIRTY_MINUTES_IN_SECONDS: 1800, + ONE_HOUR_IN_SECONDS: 3600, + ONE_DAY_IN_SECONDS: 86400, + THIRTY_DAYS_IN_SECONDS: 2592000, + MINIMUM_REBALANCING_NATURAL_UNIT: 10000, + MAXIMUM_REBALANCING_NATURAL_UNIT: 100000000000000 +}; \ No newline at end of file diff --git a/deployments/dependencies.ts b/deployments/dependencies.ts new file mode 100644 index 000000000..d8d9506bc --- /dev/null +++ b/deployments/dependencies.ts @@ -0,0 +1,54 @@ +/** + * + * The keys are numerical network ids to avoid confusion between multiple contract + * staging environments on the same Ethereum network (main-staging, main-production) + * + * 1 = main net + * 3 = ropsten + * 42 = kovan + * 531 = Set's test-rpc (5 = S, 3 = E, 1 = T) + * + */ + +export default { + WBTC: { + 1: '0x2260fac5e5542a773aa44fbcfedf7c193bc2c599', + 42: '0x595f8DaB94b9c718cbf5c693cD539Fd00b286D3d', + }, + WETH: { + 1: '0x2260fac5e5542a773aa44fbcfedf7c193bc2c599', + 42: '0x4C5E0CAbAA6B376D565cF2be865a03F43E361770', + }, + ZERO_EX_EXCHANGE: { + 42: '0x35dD2932454449b14Cee11A94d3674a936d5d7b2', + 50: '0x48BaCB9266a570d521063EF5dD96e61686DbE788', + }, + ZERO_EX_PROXY: { + 42: '0xF1eC01d6236D3CD881a0bF0130eA25fe4234003E', + 50: '0x1dC4c1cEFEF38a777b15aA20260a54E584b16C48', + }, + ZERO_EX_ZRX: { + 42: '0x2002D3812F58e35F0EA1fFbf80A75a38c32175fA', + 50: '0x871DD7C2B4b25E1Aa18728e9D5f2Af4C4e431f5c', + }, + KYBER_PROXY: { + 42: '0x7e6b8b9510d71bf8ef0f893902ebb9c865eef4df', + 3: '0x818e6fecd516ecc3849daf6845e3ec868087b755', + 50: '0x371b13d97f4bF77d724E78c16B7dC74099f40e84', + }, + WBTC_MEDIANIZER: { + 1: '', + 42: '0x02186378d8e723e11643b4cd520E31655be3B0E9', + 50: '0x2002d3812F58E35F0ea1fFbF80A75a38C32173Fa', + }, + WETH_MEDIANIZER: { + 1: '', + 42: '0x9Fe0D478D0E290d50EF8DFc08760C4ad9D2C7AE9', + 50: '0x2002d3812f58E35f0EA1Ffbf80a75A38c32174fA', + }, + INFURA_SUBDOMAIN: { + 1: 'https://mainnet.infura.io', + 42: 'https://kovan.infura.io', + }, +}; + diff --git a/deployments/deploy.ts b/deployments/deploy.ts new file mode 100644 index 000000000..d8f2e3294 --- /dev/null +++ b/deployments/deploy.ts @@ -0,0 +1,14 @@ +import { Manager } from './manager'; + +async function start() { + const newManager = new Manager(); + + try { + await newManager.deploy(); + } catch (error) { + console.log(error); + } +} + +start(); + diff --git a/deployments/manager.ts b/deployments/manager.ts new file mode 100644 index 000000000..337fa3809 --- /dev/null +++ b/deployments/manager.ts @@ -0,0 +1,101 @@ +import { DeploymentStageInterface } from '../types/deployment_stage_interface'; + +import { LibrariesStage } from './stages/1_libraries'; +import { CoreStage } from './stages/2_core'; +import { ModulesStage } from './stages/3_modules'; +import { RebalancingStage } from './stages/5_rebalancing'; +import { AuthorizationStage } from './stages/4_authorization'; + +import { asyncForEach } from '../utils/array'; +import { getWeb3Instance } from './utils/blockchain'; + +import { + getNetworkName, + getNetworkId, + returnOutputs, + writeStateToOutputs, + removeNetwork, + getContractCode +} from './utils/output-helper'; + +export class Manager { + + private _networkName: string; + private _networkId: number; + + private _stages: { [id: number]: DeploymentStageInterface } = { + 1: new LibrariesStage(), + 2: new CoreStage(), + 3: new ModulesStage(), + 4: new AuthorizationStage(), + 5: new RebalancingStage(), + }; + + constructor() { + this._networkName = getNetworkName(); + this._networkId = getNetworkId(); + } + + async deploy() { + await this.configureIfDevelopment(); + + let toDeploy = await this.getDeploymentStages(); + let web3 = await getWeb3Instance(); + let correctNetworkId = await this.isCorrectNetworkId(); + + if (!correctNetworkId) { + throw Error('ENV variable `DEPLOYMENT_NETWORK_ID` does not match `network_id` in outputs.json'); + } + + await asyncForEach(toDeploy, async stage => { + console.log(`Stage: ${stage}/${Object.keys(this._stages).length}`); + + const currentStage = this._stages[stage]; + + await currentStage.deploy(web3); + await writeStateToOutputs(this._networkName, 'last_deployment_stage', parseInt(stage)); + }); + } + + async getDeploymentStages() { + const lastStage = await this.getLastDeploymentStage(); + const stageKeys = Object.keys(this._stages); + return stageKeys.filter(value => parseInt(value) > lastStage).sort(); + } + + async getLastDeploymentStage(): Promise { + try { + const output = await returnOutputs(); + return output[this._networkName]['state']['last_deployment_stage'] || 0; + } catch { + return 0; + } + } + + async isCorrectNetworkId(): Promise { + try { + const output = await returnOutputs(); + const existingId = output[this._networkName]['state']['network_id']; + if (!existingId) { + await writeStateToOutputs(this._networkName, 'network_id', this._networkId); + return true; + } + return existingId == this._networkId; + } catch { + return true; + } + } + + async configureIfDevelopment() { + try { + const web3 = await getWeb3Instance(); + const code = await getContractCode('Core', web3); + if (this._networkId == 50 && code.length <= 3) { + console.log(`\n*** Clearing all addresses for ${this._networkName} ***\n`); + await removeNetwork(this._networkName); + } + } catch (error) { + console.log(error); + } + } +} diff --git a/deployments/network-constants.ts b/deployments/network-constants.ts new file mode 100644 index 000000000..01841dbe2 --- /dev/null +++ b/deployments/network-constants.ts @@ -0,0 +1,64 @@ +import constants from './constants'; + +export default { + minimumRebalanceInterval: { + main: constants.ONE_DAY_IN_SECONDS, + kovan: constants.ONE_MINUTE_IN_SECONDS, + development: constants.ONE_MINUTE_IN_SECONDS, + }, + minimumRebalanceProposalPeriod: { + main: constants.ONE_DAY_IN_SECONDS, + kovan: constants.ONE_MINUTE_IN_SECONDS, + development: constants.ONE_MINUTE_IN_SECONDS, + }, + minimumRebalanceTimeToPivot: { + main: (constants.ONE_DAY_IN_SECONDS / 4), + kovan: 0, + development: 0, + }, + maximumRebalanceTimeToPivot: { + main: (constants.ONE_DAY_IN_SECONDS * 3), + kovan: (constants.ONE_DAY_IN_SECONDS * 3), + development: (constants.ONE_DAY_IN_SECONDS * 3), + }, + bitEthRebalanceManagerProposalPeriod: { + main: constants.ONE_DAY_IN_SECONDS, + kovan: constants.THIRTY_MINUTES_IN_SECONDS, + development: constants.ONE_DAY_IN_SECONDS, + }, + bitEthRebalanceManagerRebalanceInterval: { + main: constants.THIRTY_DAYS_IN_SECONDS, + kovan: constants.THIRTY_MINUTES_IN_SECONDS, + development: constants.ONE_DAY_IN_SECONDS, + }, + bitEthRebalanceManagerAuctionTimeToPivot: { + main: constants.ONE_DAY_IN_SECONDS, + kovan: constants.ONE_HOUR_IN_SECONDS, + development: constants.ONE_DAY_IN_SECONDS, + }, + bitEthRebalanceManagerAllocationUpperBound: { + main: 52, + kovan: 50, + development: 50, + }, + bitEthRebalanceManagerAllocationLowerBound: { + main: 48, + kovan: 50, + development: 50, + }, + timeLockPeriod: { + main: 0, + kovan: 0, + development: 0, + }, + linearAuctionPriceCurve: { + main: true, + kovan: true, + development: true, + }, + constantsAuctionPriceCurve: { + main: false, + kovan: true, + development: true, + }, +}; \ No newline at end of file diff --git a/deployments/outputs.json b/deployments/outputs.json new file mode 100644 index 000000000..6dac937aa --- /dev/null +++ b/deployments/outputs.json @@ -0,0 +1,73 @@ +{ + "kovan": { + "addresses": { + "Vault": "0x1f66Aa3d97Fb94b931bC83060fEeCB20DBc04536", + "TransferProxy": "0x04a1F0E1C03B69BbA6E9aFB7B02EBa94328613c6", + "Core": "0xf9d7b625F95b7F73736f1F95C4EB54D109344750", + "SetTokenFactory": "0x69Bba2231afCDa0225F67FA4B49065cc4bFAB52E", + "WhiteList": "0xaD78e5570f24A268687C6CC0F73966e9978568A7", + "RebalancingSetTokenFactory": "0x88E76a3cDFb58bD04b3F3d9914EB65a94d5dEC9C", + "EIP712Library": "0x421C9A9dD08f2f0BE6E85e46F0E30775b4d57C95", + "ERC20Wrapper": "0x90b242eDd278E636E02C2054C861Fd46A7B96271", + "ExchangeIssueLibrary": "0xf6EA83409627Ed05349A10A86c36348063F9faB3", + "OrderLibrary": "0x98e1C92d666C83fdaafA1b55F7E60B15FE0181AB", + "RebalancingHelperLibrary": "0xAe284f5c545d554833dc8Aa45772f62FFD8525Fe", + "StandardProposeLibrary": "0xCE1b0a43f410652Cda8E55394c0e5223F56143a3", + "StandardStartRebalanceLibrary": "0xbBD58feba6c85D3fdf4F046073a6Bc508E07c2a1", + "StandardPlaceBidLibrary": "0xE72fce2A41E3e1f49e2A6D9FaE3ac3943ee50A26", + "StandardSettleRebalanceLibrary": "0x438071B6E7CcDeCeBe7a9407348CbBF5a5A5Fa1C", + "StandardFailAuctionLibrary": "0xCaAd13362B7DcE580Bcb875654DA75250925d977", + "RebalanceAuctionModule": "0x02CC43cDD5E8183092E5184b7e02de98D030e4D5", + "IssuanceOrderModule": "0xcEDA8318522D348f1d1aca48B24629b8FbF09020", + "ExchangeIssueModule": "0x0cA24B8Ce996ea8b50786b991E41Eab97c9A3BA7", + "TakerWalletWrapper": "0x8292D26925a537541b56e00D5F90e48dcA511F37", + "RebalancingTokenIssuanceModule": "0x9015254B65AfeC5DCF0bB6657414843C7616dA61", + "ZeroExExchangeWrapper": "0xe8f04c0A0404260f5340aed7b276a9aD72937A87", + "PayableExchangeIssue": "0xb633b5d605Eb6148C531307E7840b5fB4B6539f9", + "KyberNetworkWrapper": "0x56dB0438B1341e81e4C6E62F875fC3607FD1b911", + "LinearAuctionPriceCurve": "0x73C29F323f1D192B139c4a128b0aC9652F2Da490", + "ConstantAuctionPriceCurve": "0x75834f8929EcE6b7a0D09E7200FDE14E5b4C1F65", + "InitialCollateralSet": "0xC8655Dd4AE34f61Ce2eA28E7069707fdc8E1fdfb", + "BitEthRebalanceManager": "0xad0E4a2bD56Cf01bB3D4A68600700c2cd955Af81", + "BitEthRebalancingSetToken": "0x38aE0ac4bf28eb9A7EbC45f78C5400E4db718E73" + }, + "state": { + "last_deployment_stage": 5, + "network_id": 1 + } + }, + "development": { + "addresses": { + "ERC20Wrapper": "0x6975548516e60ce4d005Aa42De1e96DddD9d23eD", + "ExchangeIssueLibrary": "0x57b7aB09008dD5eda262b160A7b0f0d17c8754B4", + "RebalancingHelperLibrary": "0x9Ac5A7B338d90A85c6201D2691983Cc37F81AD2d", + "StandardProposeLibrary": "0x1590311C922a283024f0363777478C6b8c3d8c6c", + "StandardSettleRebalanceLibrary": "0x751D5E793577B2fd5dF9356729f8ddabF0800F20", + "StandardStartRebalanceLibrary": "0xa9a65D631f8c8577f543B64B35909030C84676A2", + "StandardPlaceBidLibrary": "0x038C860fd0d598B3DF5577B466c8b0a074867f56", + "StandardFailAuctionLibrary": "0x16C057c0494A0d7FB83974356Ce44323793BcFb2", + "WBTC": "0xdEf2bdc320e6EB892FEF7F406ff8C4BaB0735379", + "WETH": "0x58787E5441be9548440086495EA8583394e3427f", + "Vault": "0x841789fe96a433b49450E37E8CB513117712F63F", + "TransferProxy": "0x56D919871bDb3009590392fb05060e625276041d", + "Core": "0x4A86aD5f263260f24483Df2B1B3a23ea4788B6AB", + "SetTokenFactory": "0x0221652D4306F6b3CB7B23E43C3e25D8F9a142Ca", + "WhiteList": "0x4F4DB3fBEeAb70E6425c72E8f103fcbefDBDE2FE", + "RebalancingSetTokenFactory": "0xC307C1ecb7d3C546357598b9D4c6434481A18308", + "ExchangeIssueModule": "0x5578Dd96d5179Aa78d56EDbA9eBEf1Cb98077cfA", + "RebalanceAuctionModule": "0xe6244DDc4E21660308989DB20b6a5c10931c1B35", + "RebalancingTokenIssuanceModule": "0x6AC7189BD267c81c55d7d7d0321f9B23639cB9db", + "PayableExchangeIssue": "0x3E809c563c15a295E832e37053798DdC8d6C8dab", + "KyberNetworkWrapper": "0x8E1fF02637Cb5E39f2fA36c14706aa348B065B09", + "ZeroExExchangeWrapper": "0x2727E688B8fD40b198cd5Fe6E408e00494a06F07", + "LinearAuctionPriceCurve": "0x22ebc052F43A88Efa06379426120718170F2204e", + "ConstantAuctionPriceCurve": "0x1dA52d1D3a3AcFa0A1836b737393b4e9931268Fc", + "BitEthRebalanceManager": "0xc51B43db0Cea40E36207993F0aB1883E7A865417", + "InitialCollateralSet": "0x7E71f21a7ef0EbC24b0865D17DFF03fD874C5cFf", + "BitEthRebalancingSetToken": "0x4ae4BD9B4A5600Db8f28e9a01EE73094Eb4814A7" + }, + "state": { + "last_deployment_stage": 5 + } + } +} \ No newline at end of file diff --git a/deployments/outputs.ts b/deployments/outputs.ts new file mode 100644 index 000000000..f6c7e25d2 --- /dev/null +++ b/deployments/outputs.ts @@ -0,0 +1,80 @@ +export const outputs = +{ + "kovan": { + "addresses": { + "Vault": "0x1f66Aa3d97Fb94b931bC83060fEeCB20DBc04536", + "TransferProxy": "0x04a1F0E1C03B69BbA6E9aFB7B02EBa94328613c6", + "Core": "0xf9d7b625F95b7F73736f1F95C4EB54D109344750", + "SetTokenFactory": "0x69Bba2231afCDa0225F67FA4B49065cc4bFAB52E", + "WhiteList": "0xaD78e5570f24A268687C6CC0F73966e9978568A7", + "RebalancingSetTokenFactory": "0x88E76a3cDFb58bD04b3F3d9914EB65a94d5dEC9C", + "EIP712Library": "0x421C9A9dD08f2f0BE6E85e46F0E30775b4d57C95", + "ERC20Wrapper": "0x90b242eDd278E636E02C2054C861Fd46A7B96271", + "ExchangeIssueLibrary": "0xf6EA83409627Ed05349A10A86c36348063F9faB3", + "OrderLibrary": "0x98e1C92d666C83fdaafA1b55F7E60B15FE0181AB", + "RebalancingHelperLibrary": "0xAe284f5c545d554833dc8Aa45772f62FFD8525Fe", + "StandardProposeLibrary": "0xCE1b0a43f410652Cda8E55394c0e5223F56143a3", + "StandardStartRebalanceLibrary": "0xbBD58feba6c85D3fdf4F046073a6Bc508E07c2a1", + "StandardPlaceBidLibrary": "0xE72fce2A41E3e1f49e2A6D9FaE3ac3943ee50A26", + "StandardSettleRebalanceLibrary": "0x438071B6E7CcDeCeBe7a9407348CbBF5a5A5Fa1C", + "StandardFailAuctionLibrary": "0xCaAd13362B7DcE580Bcb875654DA75250925d977", + "RebalanceAuctionModule": "0x02CC43cDD5E8183092E5184b7e02de98D030e4D5", + "IssuanceOrderModule": "0xcEDA8318522D348f1d1aca48B24629b8FbF09020", + "ExchangeIssueModule": "0x0cA24B8Ce996ea8b50786b991E41Eab97c9A3BA7", + "TakerWalletWrapper": "0x8292D26925a537541b56e00D5F90e48dcA511F37", + "RebalancingTokenIssuanceModule": "0x9015254B65AfeC5DCF0bB6657414843C7616dA61", + "ZeroExExchangeWrapper": "0xe8f04c0A0404260f5340aed7b276a9aD72937A87", + "PayableExchangeIssue": "0xb633b5d605Eb6148C531307E7840b5fB4B6539f9", + "KyberNetworkWrapper": "0x56dB0438B1341e81e4C6E62F875fC3607FD1b911", + "LinearAuctionPriceCurve": "0x73C29F323f1D192B139c4a128b0aC9652F2Da490", + "ConstantAuctionPriceCurve": "0x75834f8929EcE6b7a0D09E7200FDE14E5b4C1F65", + "InitialCollateralSet": "0xC8655Dd4AE34f61Ce2eA28E7069707fdc8E1fdfb", + "BitEthRebalanceManager": "0xad0E4a2bD56Cf01bB3D4A68600700c2cd955Af81", + "BitEthRebalancingSetToken": "0x38aE0ac4bf28eb9A7EbC45f78C5400E4db718E73" + }, + "state": { + "last_deployment_stage": 5, + "network_id": 1 + } + }, + "development": { + "addresses": { + "ERC20Wrapper": "0x751D5E793577B2fd5dF9356729f8ddabF0800F20", + "EIP712Library": "0xa9a65D631f8c8577f543B64B35909030C84676A2", + "OrderLibrary": "0x038C860fd0d598B3DF5577B466c8b0a074867f56", + "ExchangeIssueLibrary": "0x16C057c0494A0d7FB83974356Ce44323793BcFb2", + "RebalancingHelperLibrary": "0xdEf2bdc320e6EB892FEF7F406ff8C4BaB0735379", + "StandardProposeLibrary": "0x58787E5441be9548440086495EA8583394e3427f", + "StandardSettleRebalanceLibrary": "0x841789fe96a433b49450E37E8CB513117712F63F", + "StandardStartRebalanceLibrary": "0x56D919871bDb3009590392fb05060e625276041d", + "StandardPlaceBidLibrary": "0x4A86aD5f263260f24483Df2B1B3a23ea4788B6AB", + "StandardFailAuctionLibrary": "0x0221652D4306F6b3CB7B23E43C3e25D8F9a142Ca", + "WBTC": "0x4F4DB3fBEeAb70E6425c72E8f103fcbefDBDE2FE", + "WETH": "0xC307C1ecb7d3C546357598b9D4c6434481A18308", + "Vault": "0x5578Dd96d5179Aa78d56EDbA9eBEf1Cb98077cfA", + "TransferProxy": "0xe6244DDc4E21660308989DB20b6a5c10931c1B35", + "Core": "0x6AC7189BD267c81c55d7d7d0321f9B23639cB9db", + "SetTokenFactory": "0x3E809c563c15a295E832e37053798DdC8d6C8dab", + "WhiteList": "0x8E1fF02637Cb5E39f2fA36c14706aa348B065B09", + "RebalancingSetTokenFactory": "0x2727E688B8fD40b198cd5Fe6E408e00494a06F07", + "SignatureValidator": "0x22ebc052F43A88Efa06379426120718170F2204e", + "ExchangeIssueModule": "0x1dA52d1D3a3AcFa0A1836b737393b4e9931268Fc", + "IssuanceOrderModule": "0xcBAe15A320F56Fc9ad6c8319D55Be1FeF0750070", + "RebalanceAuctionModule": "0x51815ebd3C922B215b0d0B9b41a4Daa0dB70b841", + "RebalancingTokenIssuanceModule": "0xF26eBD03adD32c23C10042e456f269AA600EBCA0", + "PayableExchangeIssue": "0x98369acce00cDe5dB8c91D7fB95811eeeBC7b1b7", + "TakerWalletWrapper": "0x126d89acF076aB33bc922902Dd772f80599D90bb", + "KyberNetworkWrapper": "0x47e5F7B969A739328b90E52f200F5dbb45073328", + "ZeroExExchangeWrapper": "0x537732b0bAC4F2A1a97b1a60cEa0df5C3f0DEF16", + "LinearAuctionPriceCurve": "0x1E28a5fB7B112291F088bBB8ab693D2214DB7895", + "ConstantAuctionPriceCurve": "0x60498EE9F32DAC81ca260747ea65F069B2312b0e", + "BitEthRebalanceManager": "0x5614189f34835eE249AEAaD26E1eD52089b92284", + "InitialCollateralSet": "0xcac1bd5Bf6BB3B604cB4215C6b0C810E3FdA0B10", + "BitEthRebalancingSetToken": "0x5B44c60DB496587F451C6835ED7Da0f631d4F8C0" + }, + "state": { + "last_deployment_stage": "5", + "network_id": 50 + } + } +} \ No newline at end of file diff --git a/deployments/stages/1_libraries.ts b/deployments/stages/1_libraries.ts new file mode 100644 index 000000000..eaf65d437 --- /dev/null +++ b/deployments/stages/1_libraries.ts @@ -0,0 +1,155 @@ +import { DeploymentStageInterface } from '../../types/deployment_stage_interface'; + +import { getContractAddress } from '../utils/output-helper'; +import { deployContract, TX_DEFAULTS, linkLibraries } from '../utils/blockchain'; + +import { + ERC20WrapperContract, + ExchangeIssueLibraryContract, + StandardProposeLibraryContract, + StandardSettleRebalanceLibraryContract, + StandardStartRebalanceLibraryContract, + StandardPlaceBidLibraryContract, + StandardFailAuctionLibraryContract +} from '../../utils/contracts'; + +import { ERC20Wrapper } from '../../artifacts/ts/ERC20Wrapper'; +import { EIP712Library } from '../../artifacts/ts/EIP712Library'; +import { ExchangeIssueLibrary } from '../../artifacts/ts/ExchangeIssueLibrary'; +import { RebalancingHelperLibraryContract } from '../../types/generated/rebalancing_helper_library'; +import { RebalancingHelperLibrary } from '../../artifacts/ts/RebalancingHelperLibrary'; +import { StandardProposeLibrary } from '../../artifacts/ts/StandardProposeLibrary'; +import { StandardStartRebalanceLibrary } from '../../artifacts/ts/StandardStartRebalanceLibrary'; +import { StandardPlaceBidLibrary } from '../../artifacts/ts/StandardPlaceBidLibrary'; +import { StandardFailAuctionLibrary } from '../../artifacts/ts/StandardFailAuctionLibrary'; + +export class LibrariesStage implements DeploymentStageInterface { + + private _web3: any; + + async deploy(web3: any): Promise { + console.log('Deploying libraries...'); + + this._web3 = web3; + + await this.deployERC20Wrapper(); + await this.deployExchangeIssueLibrary(); + await this.deployRebalancingHelperLibrary(); + + await this.deployStandardProposeLibrary(); + await this.deployStandardSettleRebalanceLibrary(); + + await this.deployStandardStartRebalanceLibrary(); + await this.deployStandardPlaceBidLibrary(); + await this.deployStandardFailAuctionLibrary(); + } + + private async deployERC20Wrapper(): Promise { + const name = 'ERC20Wrapper'; + let address = await getContractAddress(name); + + if (address) { + return await ERC20WrapperContract.at(address, this._web3, TX_DEFAULTS); + } + + address = await deployContract(ERC20Wrapper.bytecode, this._web3, name); + return await ERC20WrapperContract.at(address, this._web3, TX_DEFAULTS); + } + + private async deployExchangeIssueLibrary(): Promise { + const name = 'ExchangeIssueLibrary'; + let address = await getContractAddress(name); + + if (address) { + return await ExchangeIssueLibraryContract.at(address, this._web3, TX_DEFAULTS); + } + + address = await deployContract(ExchangeIssueLibrary.bytecode, this._web3, name); + return await ExchangeIssueLibraryContract.at(address, this._web3, TX_DEFAULTS); + } + + private async deployRebalancingHelperLibrary(): Promise { + const name = 'RebalancingHelperLibrary'; + let address = await getContractAddress(name); + + if (address) { + return await RebalancingHelperLibraryContract.at(address, this._web3, TX_DEFAULTS); + } + + address = await deployContract(RebalancingHelperLibrary.bytecode, this._web3, name); + return await RebalancingHelperLibraryContract.at(address, this._web3, TX_DEFAULTS); + } + + private async deployStandardProposeLibrary(): Promise { + const name = 'StandardProposeLibrary'; + let address = await getContractAddress(name); + + if (address) { + return await StandardProposeLibraryContract.at(address, this._web3, TX_DEFAULTS); + } + + address = await deployContract(StandardProposeLibrary.bytecode, this._web3, name); + return await StandardProposeLibraryContract.at(address, this._web3, TX_DEFAULTS); + } + + private async deployStandardSettleRebalanceLibrary(): Promise { + const name = 'StandardSettleRebalanceLibrary'; + let address = await getContractAddress(name); + + if (address) { + return await StandardSettleRebalanceLibraryContract.at(address, this._web3, TX_DEFAULTS); + } + + address = await deployContract(StandardProposeLibrary.bytecode, this._web3, name); + return await StandardSettleRebalanceLibraryContract.at(address, this._web3, TX_DEFAULTS); + } + + private async deployStandardStartRebalanceLibrary(): Promise { + const name = 'StandardStartRebalanceLibrary'; + let address = await getContractAddress(name); + + if (address) { + return await StandardStartRebalanceLibraryContract.at(address, this._web3, TX_DEFAULTS); + } + + const rebalanceHelperLibraryAddress = await getContractAddress('RebalancingHelperLibrary'); + const originalByteCode = StandardStartRebalanceLibrary.bytecode; + const linkedByteCode = linkLibraries([ + { name: 'RebalancingHelperLibrary', address: rebalanceHelperLibraryAddress }, + ], originalByteCode); + + address = await deployContract(linkedByteCode, this._web3, name); + return await StandardStartRebalanceLibraryContract.at(address, this._web3, TX_DEFAULTS); + } + + private async deployStandardPlaceBidLibrary(): Promise { + const name = 'StandardPlaceBidLibrary'; + let address = await getContractAddress(name); + + if (address) { + return await StandardPlaceBidLibraryContract.at(address, this._web3, TX_DEFAULTS); + } + + const rebalanceHelperLibraryAddress = await getContractAddress('RebalancingHelperLibrary'); + const originalByteCode = StandardPlaceBidLibrary.bytecode; + const linkedByteCode = linkLibraries([ + { name: 'RebalancingHelperLibrary', address: rebalanceHelperLibraryAddress }, + ], originalByteCode); + + address = await deployContract(linkedByteCode, this._web3, name); + return await StandardPlaceBidLibraryContract.at(address, this._web3, TX_DEFAULTS); + } + + private async deployStandardFailAuctionLibrary(): Promise { + const name = 'StandardFailAuctionLibrary'; + let address = await getContractAddress(name); + + if (address) { + return await StandardFailAuctionLibraryContract.at(address, this._web3, TX_DEFAULTS); + } + + address = await deployContract(StandardFailAuctionLibrary.bytecode, this._web3, name); + return await StandardFailAuctionLibraryContract.at(address, this._web3, TX_DEFAULTS); + } + +} \ No newline at end of file diff --git a/deployments/stages/2_core.ts b/deployments/stages/2_core.ts new file mode 100644 index 000000000..d1337fc74 --- /dev/null +++ b/deployments/stages/2_core.ts @@ -0,0 +1,243 @@ +import { DeploymentStageInterface } from '../../types/deployment_stage_interface'; + +import { + getNetworkName, + getNetworkId, + getContractAddress, + getPrivateKey, + findDependency +} from '../utils/output-helper'; + +import { deployContract, TX_DEFAULTS, linkLibraries } from '../utils/blockchain'; + +import BigNumber from 'bignumber.js'; + +import { + VaultContract, + TransferProxyContract, + CoreContract, + SetTokenFactoryContract, + WhiteListContract, + RebalancingSetTokenFactoryContract, + StandardTokenMockContract +} from '../../utils/contracts'; + +import { TransferProxy } from '../../artifacts/ts/TransferProxy'; +import { Core } from '../../artifacts/ts/Core'; +import { SetTokenFactory } from '../../artifacts/ts/SetTokenFactory'; +import { WhiteList } from '../../artifacts/ts/WhiteList'; +import { Vault } from '../../artifacts/ts/Vault'; +import { RebalancingSetTokenFactory } from '../../artifacts/ts/RebalancingSetTokenFactory'; + +import dependencies from '../dependencies'; +import networkConstants from '../network-constants'; +import constants from '../constants'; + +import { StandardTokenMock } from '../../artifacts/ts/StandardTokenMock'; + +export class CoreStage implements DeploymentStageInterface { + + private _web3: any; + private _networkName: string; + private _erc20WrapperAddress: string; + private _privateKey: string; + + async deploy(web3: any): Promise { + console.log('Deploying core...'); + + this._web3 = web3; + this._networkName = getNetworkName(); + this._privateKey = getPrivateKey(); + + this._erc20WrapperAddress = await getContractAddress('ERC20Wrapper'); + + const networkId = getNetworkId(); + + if (!dependencies.WBTC[networkId]) { + await this.deployDummyToken('WBTC'); + } + + if (!dependencies.WETH[networkId]) { + await this.deployDummyToken('WETH'); + } + + await this.deployVault(); + await this.deployTransferProxy(); + await this.deployCoreContract(); + await this.deploySetTokenFactory(); + await this.deployWhiteList(); + await this.deployRebalancingTokenFactory(); + } + + private async deployVault(): Promise { + const name = 'Vault'; + let address = await getContractAddress(name); + + if (address) { + return await VaultContract.at(address, this._web3, TX_DEFAULTS); + } + + const originalByteCode = Vault.bytecode; + const linkedByteCode = linkLibraries([ + { name: 'ERC20Wrapper', address: this._erc20WrapperAddress }, + ], originalByteCode); + + address = await deployContract(linkedByteCode, this._web3, name); + return await VaultContract.at(address, this._web3, TX_DEFAULTS); + } + + private async deployTransferProxy(): Promise { + const name = 'TransferProxy'; + let address = await getContractAddress(name); + + if (address) { + return await TransferProxyContract.at(address, this._web3, TX_DEFAULTS); + } + + const originalByteCode = TransferProxy.bytecode; + const linkedByteCode = linkLibraries([ + { name: 'ERC20Wrapper', address: this._erc20WrapperAddress }, + ], originalByteCode); + + address = await deployContract(linkedByteCode, this._web3, name); + return await TransferProxyContract.at(address, this._web3, TX_DEFAULTS); + } + + private async deployCoreContract(): Promise { + const name = 'Core'; + let address = await getContractAddress(name); + + if (address) { + return await CoreContract.at(address, this._web3, TX_DEFAULTS); + } + + const transferProxyAddress = await getContractAddress('TransferProxy'); + const vaultAddress = await getContractAddress('Vault'); + + const originalByteCode = Core.bytecode; + const linkedByteCode = linkLibraries([ + { name: 'ERC20Wrapper', address: this._erc20WrapperAddress }, + ], originalByteCode); + + const data = new this._web3.eth.Contract(Core.abi).deploy({ + data: linkedByteCode, + arguments: [ + transferProxyAddress, + vaultAddress, + ], + }).encodeABI(); + + address = await deployContract(data, this._web3, name); + return await CoreContract.at(address, this._web3, TX_DEFAULTS); + } + + private async deploySetTokenFactory(): Promise { + const name = 'SetTokenFactory'; + let address = await getContractAddress(name); + + if (address) { + return await SetTokenFactoryContract.at(address, this._web3, TX_DEFAULTS); + } + + const coreAddress = await getContractAddress('Core'); + + const data = new this._web3.eth.Contract(SetTokenFactory.abi).deploy({ + data: SetTokenFactory.bytecode, + arguments: [coreAddress], + }).encodeABI(); + + address = await deployContract(data, this._web3, name); + return await SetTokenFactoryContract.at(address, this._web3, TX_DEFAULTS); + } + + private async deployWhiteList(): Promise { + const name = 'WhiteList'; + let address = await getContractAddress(name); + + if (address) { + return await WhiteListContract.at(address, this._web3, TX_DEFAULTS); + } + + const wbtc = await findDependency('WBTC'); + const weth = await findDependency('WETH'); + + const data = new this._web3.eth.Contract(WhiteList.abi).deploy({ + data: WhiteList.bytecode, + arguments: [ + [wbtc, weth], + ], + }).encodeABI(); + + address = await deployContract(data, this._web3, name); + return await WhiteListContract.at(address, this._web3, TX_DEFAULTS); + } + + private async deployRebalancingTokenFactory(): Promise { + const name = 'RebalancingSetTokenFactory'; + let address = await getContractAddress(name); + + if (address) { + return await RebalancingSetTokenFactoryContract.at(address, this._web3, TX_DEFAULTS); + } + + const coreAddress = await getContractAddress('Core'); + const whiteListAddress = await getContractAddress('WhiteList'); + + const standardStartRebalanceLibrary = await getContractAddress('StandardStartRebalanceLibrary'); + const standardFailAuctionLibrary = await getContractAddress('StandardFailAuctionLibrary'); + const standardProposeLibrary = await getContractAddress('StandardProposeLibrary'); + const standardPlaceBidLibrary = await getContractAddress('StandardPlaceBidLibrary'); + const standardSettleRebalanceLibrary = await getContractAddress('StandardSettleRebalanceLibrary'); + const rebalancingHelperLibrary = await getContractAddress('RebalancingHelperLibrary'); + + const originalByteCode = RebalancingSetTokenFactory.bytecode; + const linkedByteCode = linkLibraries([ + { name: 'StandardStartRebalanceLibrary', address: standardStartRebalanceLibrary }, + { name: 'StandardFailAuctionLibrary', address: standardFailAuctionLibrary }, + { name: 'StandardProposeLibrary', address: standardProposeLibrary }, + { name: 'StandardPlaceBidLibrary', address: standardPlaceBidLibrary }, + { name: 'StandardSettleRebalanceLibrary', address: standardSettleRebalanceLibrary }, + { name: 'RebalancingHelperLibrary', address: rebalancingHelperLibrary }, + ], originalByteCode); + + const data = new this._web3.eth.Contract(RebalancingSetTokenFactory.abi).deploy({ + data: linkedByteCode, + arguments: [ + coreAddress, + whiteListAddress, + networkConstants.minimumRebalanceInterval[this._networkName], + networkConstants.minimumRebalanceProposalPeriod[this._networkName], + networkConstants.minimumRebalanceTimeToPivot[this._networkName], + networkConstants.maximumRebalanceTimeToPivot[this._networkName], + constants.MINIMUM_REBALANCING_NATURAL_UNIT, + constants.MAXIMUM_REBALANCING_NATURAL_UNIT + ], + }).encodeABI(); + + address = await deployContract(data, this._web3, name); + return await RebalancingSetTokenFactoryContract.at(address, this._web3, TX_DEFAULTS); + } + + private async deployDummyToken(name: string): Promise { + let address = await getContractAddress(name); + + if (address) { + return await StandardTokenMockContract.at(address, this._web3, TX_DEFAULTS); + } + + const data = new this._web3.eth.Contract(StandardTokenMock.abi).deploy({ + data: StandardTokenMock.bytecode, + arguments: [ + this._web3.eth.accounts.privateKeyToAccount(this._privateKey).address, + new BigNumber(10000).pow(18).toString(), + name, + name, + 18, + ], + }).encodeABI(); + + address = await deployContract(data, this._web3, name); + return await StandardTokenMockContract.at(address, this._web3, TX_DEFAULTS); + } + +} \ No newline at end of file diff --git a/deployments/stages/3_modules.ts b/deployments/stages/3_modules.ts new file mode 100644 index 000000000..a39c57d65 --- /dev/null +++ b/deployments/stages/3_modules.ts @@ -0,0 +1,289 @@ +import { DeploymentStageInterface } from '../../types/deployment_stage_interface'; + +import { getNetworkName, getNetworkId, getContractAddress, findDependency } from '../utils/output-helper'; +import { deployContract, TX_DEFAULTS, linkLibraries } from '../utils/blockchain'; + +import { + ExchangeIssueModuleContract, + RebalanceAuctionModuleContract, + RebalancingTokenIssuanceModuleContract, + KyberNetworkWrapperContract, + ZeroExExchangeWrapperContract, + PayableExchangeIssueContract, + LinearAuctionPriceCurveContract, + ConstantAuctionPriceCurveContract +} from '../../utils/contracts'; + +import { ExchangeIssueModule } from '../../artifacts/ts/ExchangeIssueModule'; +import { RebalanceAuctionModule } from '../../artifacts/ts/RebalanceAuctionModule'; +import { RebalancingTokenIssuanceModule } from '../../artifacts/ts/RebalancingTokenIssuanceModule'; +import { KyberNetworkWrapper } from '../../artifacts/ts/KyberNetworkWrapper'; +import { PayableExchangeIssue } from '../../artifacts/ts/PayableExchangeIssue'; +import { LinearAuctionPriceCurve } from '../../artifacts/ts/LinearAuctionPriceCurve'; +import { ConstantAuctionPriceCurve } from '../../artifacts/ts/ConstantAuctionPriceCurve'; + +import constants from '../constants'; +import networkConstants from '../network-constants'; +import dependencies from '../dependencies'; +import { ZeroExExchangeWrapper } from '../../artifacts/ts/ZeroExExchangeWrapper'; + +export class ModulesStage implements DeploymentStageInterface { + + private _web3: any; + private _networkName: string; + + async deploy(web3: any): Promise { + console.log('Deploying modules...'); + + this._web3 = web3; + this._networkName = getNetworkName(); + + await this.deployExchangeIssueModule(); + await this.deployRebalancingAuctionModule(); + await this.deployRebalanceTokenIssuanceModule(); + await this.deployPayableExchangeIssue(); + + await this.deployKyberWrapper(); + await this.deployZeroExWrapper(); + + await this.deployLinearAuctionPriceCurve(); + await this.deployConstantAuctionPriceCurve(); + } + + private async deployExchangeIssueModule(): Promise { + const name = 'ExchangeIssueModule'; + let address = await getContractAddress(name); + + if (address) { + return await ExchangeIssueModuleContract.at(address, this._web3, TX_DEFAULTS); + } + + const coreAddress = await getContractAddress('Core'); + const transferProxyAddress = await getContractAddress('TransferProxy'); + const vaultAddress = await getContractAddress('Vault'); + + const data = new this._web3.eth.Contract(ExchangeIssueModule.abi).deploy({ + data: ExchangeIssueModule.bytecode, + arguments: [ + coreAddress, + transferProxyAddress, + vaultAddress, + ], + }).encodeABI(); + + address = await deployContract(data, this._web3, name); + return await ExchangeIssueModuleContract.at(address, this._web3, TX_DEFAULTS); + } + + private async deployRebalancingAuctionModule(): Promise { + const name = 'RebalanceAuctionModule'; + let address = await getContractAddress(name); + + if (address) { + return await RebalanceAuctionModuleContract.at(address, this._web3, TX_DEFAULTS); + } + + const coreAddress = await getContractAddress('Core'); + const vaultAddress = await getContractAddress('Vault'); + + const data = new this._web3.eth.Contract(RebalanceAuctionModule.abi).deploy({ + data: RebalanceAuctionModule.bytecode, + arguments: [ + coreAddress, + vaultAddress, + ], + }).encodeABI(); + + address = await deployContract(data, this._web3, name); + return await RebalanceAuctionModuleContract.at(address, this._web3, TX_DEFAULTS); + } + + private async deployRebalanceTokenIssuanceModule(): Promise { + const name = 'RebalancingTokenIssuanceModule'; + let address = await getContractAddress(name); + + if (address) { + return await RebalancingTokenIssuanceModuleContract.at(address, this._web3, TX_DEFAULTS); + } + + const coreAddress = await getContractAddress('Core'); + const transferProxyAddress = await getContractAddress('TransferProxy'); + const vaultAddress = await getContractAddress('Vault'); + + const data = new this._web3.eth.Contract(RebalancingTokenIssuanceModule.abi).deploy({ + data: RebalancingTokenIssuanceModule.bytecode, + arguments: [ + coreAddress, + transferProxyAddress, + vaultAddress, + ], + }).encodeABI(); + + address = await deployContract(data, this._web3, name); + return await RebalancingTokenIssuanceModuleContract.at(address, this._web3, TX_DEFAULTS); + } + + private async deployPayableExchangeIssue(): Promise { + const name = 'PayableExchangeIssue'; + let address = await getContractAddress(name); + + if (address) { + return await PayableExchangeIssueContract.at(address, this._web3, TX_DEFAULTS); + } + + const coreAddress = await getContractAddress('Core'); + const transferProxyAddress = await getContractAddress('TransferProxy'); + const exchangeIssueAddress = await getContractAddress('ExchangeIssueModule'); + const erc20WrapperAddress = await getContractAddress('ERC20Wrapper'); + const wethAddress = await findDependency('WETH'); + + const originalByteCode = PayableExchangeIssue.bytecode; + const linkedByteCode = linkLibraries([ + { name: 'ERC20Wrapper', address: erc20WrapperAddress }, + ], originalByteCode); + + const data = new this._web3.eth.Contract(PayableExchangeIssue.abi).deploy({ + data: linkedByteCode, + arguments: [ + coreAddress, + transferProxyAddress, + exchangeIssueAddress, + wethAddress, + ], + }).encodeABI(); + + address = await deployContract(data, this._web3, name); + return await PayableExchangeIssueContract.at(address, this._web3, TX_DEFAULTS); + } + + private async deployKyberWrapper(): Promise { + const name = 'KyberNetworkWrapper'; + let address = await getContractAddress(name); + const networkId = getNetworkId(); + + if (!dependencies.KYBER_PROXY[networkId]) { + console.log(dependencies.KYBER_PROXY); + console.log(networkId); + return; + } + + if (address) { + return await KyberNetworkWrapperContract.at(address, this._web3, TX_DEFAULTS); + } + + const coreAddress = await getContractAddress('Core'); + const erc20WrapperAddress = await getContractAddress('ERC20Wrapper'); + const transferProxyAddress = await getContractAddress('TransferProxy'); + const kyberTransferProxyAddress = dependencies.KYBER_PROXY[networkId]; + + const originalByteCode = KyberNetworkWrapper.bytecode; + const linkedByteCode = linkLibraries([ + { name: 'ERC20Wrapper', address: erc20WrapperAddress }, + ], originalByteCode); + + const data = new this._web3.eth.Contract(KyberNetworkWrapper.abi).deploy({ + data: linkedByteCode, + arguments: [ + coreAddress, + kyberTransferProxyAddress, + transferProxyAddress, + ], + }).encodeABI(); + + address = await deployContract(data, this._web3, name); + return await KyberNetworkWrapperContract.at(address, this._web3, TX_DEFAULTS); + } + + private async deployZeroExWrapper(): Promise { + const name = 'ZeroExExchangeWrapper'; + let address = await getContractAddress(name); + const networkId = getNetworkId(); + + if ( + !dependencies.ZERO_EX_EXCHANGE[networkId] || + !dependencies.ZERO_EX_PROXY[networkId] || + !dependencies.ZERO_EX_ZRX[networkId] + ) { + return; + } + + if (address) { + return await ZeroExExchangeWrapperContract.at(address, this._web3, TX_DEFAULTS); + } + + const coreAddress = await getContractAddress('Core'); + const transferProxyAddress = await getContractAddress('TransferProxy'); + const erc20WrapperAddress = await getContractAddress('ERC20Wrapper'); + const zeroExExchangeAddress = dependencies.ZERO_EX_EXCHANGE[networkId]; + const zeroExProxyAddress = dependencies.ZERO_EX_PROXY[networkId]; + const zeroExTokenAddress = dependencies.ZERO_EX_ZRX[networkId]; + + const originalByteCode = ZeroExExchangeWrapper.bytecode; + const linkedByteCode = linkLibraries([ + { name: 'ERC20Wrapper', address: erc20WrapperAddress }, + ], originalByteCode); + + const data = new this._web3.eth.Contract(ZeroExExchangeWrapper.abi).deploy({ + data: linkedByteCode, + arguments: [ + coreAddress, + zeroExExchangeAddress, + zeroExProxyAddress, + zeroExTokenAddress, + transferProxyAddress, + ], + }).encodeABI(); + + address = await deployContract(data, this._web3, name); + return await ZeroExExchangeWrapperContract.at(address, this._web3, TX_DEFAULTS); + } + + private async deployLinearAuctionPriceCurve(): Promise { + const name = 'LinearAuctionPriceCurve'; + let address = await getContractAddress(name); + + if (networkConstants.linearAuctionPriceCurve[this._networkName] !== true) { + return; + } + + if (address) { + return await LinearAuctionPriceCurveContract.at(address, this._web3, TX_DEFAULTS); + } + + const data = new this._web3.eth.Contract(LinearAuctionPriceCurve.abi).deploy({ + data: LinearAuctionPriceCurve.bytecode, + arguments: [ + constants.DEFAULT_AUCTION_PRICE_DENOMINATOR, + true, + ], + }).encodeABI(); + + address = await deployContract(data, this._web3, name); + return await LinearAuctionPriceCurveContract.at(address, this._web3, TX_DEFAULTS); + } + + private async deployConstantAuctionPriceCurve(): Promise { + const name = 'ConstantAuctionPriceCurve'; + let address = await getContractAddress(name); + + if (networkConstants.constantsAuctionPriceCurve[this._networkName] !== true) { + return; + } + + if (address) { + return await ConstantAuctionPriceCurveContract.at(address, this._web3, TX_DEFAULTS); + } + + const data = new this._web3.eth.Contract(ConstantAuctionPriceCurve.abi).deploy({ + data: ConstantAuctionPriceCurve.bytecode, + arguments: [ + constants.DEFAULT_AUCTION_PRICE_NUMERATOR, + constants.DEFAULT_AUCTION_PRICE_DENOMINATOR, + ], + }).encodeABI(); + + address = await deployContract(data, this._web3, name); + return await ConstantAuctionPriceCurveContract.at(address, this._web3, TX_DEFAULTS); + } + +} \ No newline at end of file diff --git a/deployments/stages/4_authorization.ts b/deployments/stages/4_authorization.ts new file mode 100644 index 000000000..262ee3fed --- /dev/null +++ b/deployments/stages/4_authorization.ts @@ -0,0 +1,216 @@ +import { DeploymentStageInterface } from '../../types/deployment_stage_interface'; + +import BigNumber from 'bignumber.js'; + +import { getNetworkName, getContractAddress, getPrivateKey } from '../utils/output-helper'; +import { TX_DEFAULTS } from '../utils/blockchain'; + +import { + CoreContract, + VaultContract, + TransferProxyContract, + WhiteListContract, +} from '../../utils/contracts'; + +import { asyncForEach } from '../../utils/array'; +import networkConstants from '../network-constants'; +import constants from '../constants'; + +interface ExchangeMapping { + name: string; + key: number; +} + +export class AuthorizationStage implements DeploymentStageInterface { + + private _web3: any; + private _networkName: string; + private _deployerAccount: any; + + private _coreContract: CoreContract; + private _vaultContract: VaultContract; + private _transferProxyContract: TransferProxyContract; + + async deploy(web3: any): Promise { + console.log('Deploying authorizations...'); + + this._web3 = web3; + this._networkName = getNetworkName(); + this._deployerAccount = this._web3.eth.accounts.privateKeyToAccount(getPrivateKey()); + + const coreAddress = await getContractAddress('Core'); + this._coreContract = await CoreContract.at( + coreAddress, + this._web3, + {from: this._deployerAccount.address} + ); + + const vaultAddress = await getContractAddress('Vault'); + this._vaultContract = await VaultContract.at( + vaultAddress, this._web3, {from: this._deployerAccount.address} + ); + + const transferProxyAddress = await getContractAddress('TransferProxy'); + this._transferProxyContract = await TransferProxyContract.at( + transferProxyAddress, + this._web3, + {from: this._deployerAccount.address} + ); + + const initialTimeLock = 0; + const finalTimeLock = networkConstants.timeLockPeriod[this._networkName]; + + await this.updateTimeLockPeriod(initialTimeLock); + + await this.addAuthorizedAddressesToVault([ + 'Core', + 'ExchangeIssueModule', + 'RebalanceAuctionModule', + 'RebalancingTokenIssuanceModule', + ]); + + await this.addAuthorizedAddressesToTransferProxy([ + 'Core', + 'ExchangeIssueModule', + 'RebalanceAuctionModule', + 'RebalancingTokenIssuanceModule', + ]); + + await this.registerCoreFactories([ + 'SetTokenFactory', + 'RebalancingSetTokenFactory', + ]); + + await this.registerCoreModules([ + 'ExchangeIssueModule', + 'RebalanceAuctionModule', + 'RebalancingTokenIssuanceModule', + ]); + + await this.registerCoreExchanges([ + { name: 'ZeroExExchangeWrapper', key: constants.EXCHANGES.ZERO_EX } as ExchangeMapping, + { name: 'KyberNetworkWrapper', key: constants.EXCHANGES.KYBER } as ExchangeMapping, + ]); + + await this.registerCorePriceCurves(); + + await this.updateTimeLockPeriod(finalTimeLock); + } + + async addAuthorizedAddressesToVault(names: string[]) { + const authorizedAddresses = await this._vaultContract.getAuthorizedAddresses.callAsync(); + await asyncForEach(names, async name => { + const contractAddress = await getContractAddress(name); + + if (authorizedAddresses.includes(contractAddress)) { + return; + } + + console.log(`* Authorizing ${name} with Vault`); + await this._vaultContract.addAuthorizedAddress.sendTransactionAsync(contractAddress); + }); + } + + async addAuthorizedAddressesToTransferProxy(names: string[]) { + const authorizedAddresses = await this._transferProxyContract.getAuthorizedAddresses.callAsync(); + await asyncForEach(names, async name => { + const contractAddress = await getContractAddress(name); + + if (authorizedAddresses.includes(contractAddress)) { + return; + } + + console.log(`* Authorizing ${name} with TransferProxy`); + await this._transferProxyContract.addAuthorizedAddress.sendTransactionAsync(contractAddress); + }); + } + + async registerCoreFactories(names: string[]) { + const factories = await this._coreContract.factories.callAsync(); + await asyncForEach(names, async name => { + const contractAddress = await getContractAddress(name); + + if (factories.includes(contractAddress)) { + return; + } + + console.log(`* Register ${name} as Core Factory`); + await this._coreContract.addFactory.sendTransactionAsync(contractAddress); + }); + } + + async registerCoreModules(names: string[]) { + const modules = await this._coreContract.modules.callAsync(); + await asyncForEach(names, async name => { + const contractAddress = await getContractAddress(name); + + if (modules.includes(contractAddress)) { + return; + } + + console.log(`* Register ${name} as Core Module`); + await this._coreContract.addModule.sendTransactionAsync(contractAddress); + }); + } + + async registerCoreExchanges(items: ExchangeMapping[]) { + await asyncForEach(items, async item => { + const contractAddress = await getContractAddress(item.name); + + const exchanges = await this._coreContract.exchanges.callAsync(); + if (exchanges.includes(contractAddress) || !contractAddress) { + return; + } + + console.log(`* Register ${item.name} as Core Exchange`); + await this._coreContract.addExchange.sendTransactionAsync(item.key, contractAddress); + }); + } + + async registerCorePriceCurves() { + const priceLibraries = await this._coreContract.priceLibraries.callAsync(); + const linearAuctionPriceCurveAddress = await getContractAddress('LinearAuctionPriceCurve'); + const constantAuctionPriceCurveAddress = await getContractAddress('ConstantAuctionPriceCurve'); + + if (networkConstants.linearAuctionPriceCurve[this._networkName] && + !priceLibraries.includes(linearAuctionPriceCurveAddress) + ) { + console.log('* Adding Linear Auction Price Curve'); + await this._coreContract.addPriceLibrary.sendTransactionAsync(linearAuctionPriceCurveAddress); + } + + if (networkConstants.constantsAuctionPriceCurve[this._networkName] && + !priceLibraries.includes(constantAuctionPriceCurveAddress) + ) { + console.log('* Adding Constant Auction Price Curve'); + await this._coreContract.addPriceLibrary.sendTransactionAsync(constantAuctionPriceCurveAddress); + } + } + + async updateTimeLockPeriod(period: number) { + const bigNumberPeriod = new BigNumber(period); + + const whiteListAddress = await getContractAddress('WhiteList'); + const issuanceOrderModuleAddress = await getContractAddress('IssuanceOrderModule'); + + const whiteListContract = await WhiteListContract.at( + whiteListAddress, + this._web3, + {from: this._deployerAccount.address} + ); + + console.log('* Updating WhiteList time lock'); + await whiteListContract.setTimeLockPeriod.sendTransactionAsync(bigNumberPeriod, TX_DEFAULTS); + + console.log('* Updating Core time lock'); + await this._coreContract.setTimeLockPeriod.sendTransactionAsync(bigNumberPeriod); + + console.log('* Updating Transfer Proxy time lock'); + await this._transferProxyContract.setTimeLockPeriod.sendTransactionAsync(bigNumberPeriod); + + console.log('* Updating Vault time lock'); + await this._vaultContract.setTimeLockPeriod.sendTransactionAsync(bigNumberPeriod); + + } + +} \ No newline at end of file diff --git a/deployments/stages/5_rebalancing.ts b/deployments/stages/5_rebalancing.ts new file mode 100644 index 000000000..ee309018a --- /dev/null +++ b/deployments/stages/5_rebalancing.ts @@ -0,0 +1,179 @@ +import { SetProtocolUtils, SetProtocolTestUtils } from 'set-protocol-utils'; + +import * as ABIDecoder from 'abi-decoder'; + +import { DeploymentStageInterface } from '../../types/deployment_stage_interface'; + +import { + getNetworkName, + getContractAddress, + getPrivateKey, + findDependency, + writeContractToOutputs, +} from '../utils/output-helper'; +import { deployContract, TX_DEFAULTS } from '../utils/blockchain'; + +import { + BTCETHRebalancingManagerContract, + SetTokenContract, + CoreContract, + RebalancingSetTokenContract +} from '../../utils/contracts'; + +import { BTCETHRebalancingManager } from '../../artifacts/ts/BTCETHRebalancingManager'; +import { Core } from '../../artifacts/ts/Core'; + +import networkConstants from '../network-constants'; +import constants from '../constants'; + +import { calculateInitialSetUnits, calculateRebalancingSetUnitShares } from '../utils/rebalancing'; + +export class RebalancingStage implements DeploymentStageInterface { + + private _web3: any; + private _networkName: string; + private _privateKey: string; + private _setTestUtils: any; + private _coreContract: CoreContract; + + async deploy(web3: any): Promise { + console.log('Deploying rebalancing...'); + + this._web3 = web3; + this._privateKey = getPrivateKey(); + this._networkName = getNetworkName(); + this._setTestUtils = new SetProtocolTestUtils(this._web3); + + ABIDecoder.addABI(Core.abi); + + const coreAddress = await getContractAddress('Core'); + const deployerAccount = this._web3.eth.accounts.privateKeyToAccount(this._privateKey); + this._coreContract = await CoreContract.at(coreAddress, this._web3, {from: deployerAccount.address}); + + await this.deployBitEthRebalancingManager(); + await this.deployInitialCollateralizedSet(); + await this.deployBitEthRebalancingSetToken(); + } + + async deployBitEthRebalancingManager(): Promise { + const name = 'BitEthRebalanceManager'; + let address = await getContractAddress(name); + + if (address) { + return await BTCETHRebalancingManagerContract.at(address, this._web3, TX_DEFAULTS); + } + + const coreAddress = await getContractAddress('Core'); + const setTokenFactoryAddress = await getContractAddress('SetTokenFactory'); + const linearAuctionCurveAddress = await getContractAddress('LinearAuctionPriceCurve'); + const wbtcMedianizerAddress = await findDependency('WBTC_MEDIANIZER'); + const wethMedianizerAddress = await findDependency('WETH_MEDIANIZER'); + const wbtcAddress = await findDependency('WBTC'); + const wethAddress = await findDependency('WETH'); + + const data = new this._web3.eth.Contract(BTCETHRebalancingManager.abi).deploy({ + data: BTCETHRebalancingManager.bytecode, + arguments: [ + coreAddress, + wbtcMedianizerAddress, + wethMedianizerAddress, + wbtcAddress, + wethAddress, + setTokenFactoryAddress, + linearAuctionCurveAddress, + networkConstants.bitEthRebalanceManagerAuctionTimeToPivot[this._networkName], + [ + constants.WBTC_MULTIPLIER.toString(), + constants.WETH_MULTIPLIER.toString()], + [ + networkConstants.bitEthRebalanceManagerAllocationLowerBound[this._networkName], + networkConstants.bitEthRebalanceManagerAllocationUpperBound[this._networkName] + ] + ], + }).encodeABI(); + + address = await deployContract(data, this._web3, name); + return await BTCETHRebalancingManagerContract.at(address, this._web3, TX_DEFAULTS); + } + + async deployInitialCollateralizedSet(): Promise { + const name = 'InitialCollateralSet'; + let address = await getContractAddress(name); + + if (address) { + return await SetTokenContract.at(address, this._web3, TX_DEFAULTS); + } + + const setTokenFactoryAddress = await getContractAddress('SetTokenFactory'); + const wbtcAddress = await findDependency('WBTC'); + const wethAddress = await findDependency('WETH'); + + const initialSetParams = calculateInitialSetUnits(); + const initialSetName = SetProtocolUtils.stringToBytes('BTCETH'); + const initialSymbol = SetProtocolUtils.stringToBytes('BTCETH'); + + const txHash = await this._coreContract.create.sendTransactionAsync( + setTokenFactoryAddress, + [wbtcAddress, wethAddress], + initialSetParams['units'], + initialSetParams['naturalUnit'], + initialSetName, + initialSymbol, + SetProtocolUtils.stringToBytes(''), + TX_DEFAULTS + ); + + const logs = await this._setTestUtils.getLogsFromTxHash(txHash); + address = logs[0].args._setTokenAddress; + + await writeContractToOutputs(this._networkName, name, address); + return await SetTokenContract.at(address, this._web3, TX_DEFAULTS); + } + + async deployBitEthRebalancingSetToken(): Promise { + const name = 'BitEthRebalancingSetToken'; + let address = await getContractAddress(name); + + if (address) { + return await RebalancingSetTokenContract.at(address, this._web3, TX_DEFAULTS); + } + + const initialSetToken = await getContractAddress('InitialCollateralSet'); + const rebalancingSetFactoryAddress = await getContractAddress('RebalancingSetTokenFactory'); + const rebalancingManagerAddress = await getContractAddress('BitEthRebalanceManager'); + + const initialSetParams = calculateInitialSetUnits(); + const rebalancingSetUnitShares = calculateRebalancingSetUnitShares( + initialSetParams['units'], + initialSetParams['naturalUnit'], + ); + + const rebalancingSetNaturalUnit = constants.DEFAULT_REBALANCING_NATURAL_UNIT; + const rebalancingSetName = SetProtocolUtils.stringToBytes('BitEth Set'); + const rebalancingSetSymbol = SetProtocolUtils.stringToBytes('BTCETH'); + const rebalancingSetCallData = SetProtocolUtils.generateRebalancingSetTokenCallData( + rebalancingManagerAddress, + networkConstants.bitEthRebalanceManagerProposalPeriod[this._networkName], + networkConstants.bitEthRebalanceManagerRebalanceInterval[this._networkName] + ); + + const txHash = await this._coreContract.create.sendTransactionAsync( + rebalancingSetFactoryAddress, + [initialSetToken], + rebalancingSetUnitShares, + rebalancingSetNaturalUnit, + rebalancingSetName, + rebalancingSetSymbol, + rebalancingSetCallData, + TX_DEFAULTS + ); + + const logs = await this._setTestUtils.getLogsFromTxHash(txHash); + address = logs[0].args._setTokenAddress; + + await writeContractToOutputs(this._networkName, name, address); + + return await RebalancingSetTokenContract.at(address, this._web3, TX_DEFAULTS); + } + +} diff --git a/deployments/test/1_core.spec.ts b/deployments/test/1_core.spec.ts new file mode 100755 index 000000000..d76d0e65e --- /dev/null +++ b/deployments/test/1_core.spec.ts @@ -0,0 +1,216 @@ +'use strict'; + +import expect from 'expect'; + +import { getWeb3Instance } from '../utils/blockchain'; + +import { Core } from '../../artifacts/ts/Core'; +import { SetTokenFactory } from '../../artifacts/ts/SetTokenFactory'; +import { RebalancingSetTokenFactory } from '../../artifacts/ts/RebalancingSetTokenFactory'; +import { WhiteList } from '../../artifacts/ts/WhiteList'; + +import networkConstants from '../network-constants'; +import { + getNetworkName, + getContractCode, + getContractAddress, + findDependency +} from '../utils/output-helper'; + +describe('Deployment: Core', () => { + + let web3; + let coreAddress; + const networkName = getNetworkName(); + + before(async () => { + web3 = await getWeb3Instance(); + coreAddress = await getContractAddress('Core'); + }); + + describe('Vault', () => { + + /** + * Check it actually got deployed + */ + + it('finds a valid contract at the address', async () => { + const code = await getContractCode('Vault', web3); + expect(code.length).toBeGreaterThan(3); + }); + }); + + describe('Transfer Proxy', () => { + + /** + * Check it actually got deployed + */ + + it('finds a valid contract at the address', async () => { + const code = await getContractCode('TransferProxy', web3); + expect(code.length).toBeGreaterThan(3); + }); + + }); + + describe('Core', () => { + + /** Check it got deployed with: + * - TransferProxy + * - Vault + */ + + let coreContract; + + before(async () => { + const coreAddress = await getContractAddress('Core'); + coreContract = new web3.eth.Contract(Core.abi, coreAddress); + }); + + it('finds a valid contract at the address', async () => { + const code = await getContractCode('Core', web3); + expect(code.length).toBeGreaterThan(3); + }); + + it('got deployed with the transfer proxy', async () => { + const coreState = await coreContract.methods.state().call(); + const transferProxyAddress = await getContractAddress('TransferProxy'); + expect(coreState.transferProxy).toEqual(transferProxyAddress); + }); + + it('got deployed with the vault', async () => { + const coreState = await coreContract.methods.state().call(); + const vaultAddress = await getContractAddress('Vault'); + expect(coreState.vault).toEqual(vaultAddress); + }); + + }); + + describe('Set Token Factory', () => { + + /** + * Check it got deployed with: + * - Core + */ + + let setTokenFactoryContract; + + before(async () => { + const setTokenFactoryAddress = await getContractAddress('SetTokenFactory'); + setTokenFactoryContract = new web3.eth.Contract(SetTokenFactory.abi, setTokenFactoryAddress); + }); + + it('finds a valid contract at the address', async () => { + const code = await getContractCode('SetTokenFactory', web3); + expect(code.length).toBeGreaterThan(3); + }); + + it('got deployed with core', async () => { + const retrievedCoreAddress = await setTokenFactoryContract.methods.core().call(); + expect(coreAddress).toEqual(retrievedCoreAddress); + }); + + }); + + describe('White List', () => { + + /** + * Check it got deployed and the initial token whitelist contains: + * - wETH and wBTC + */ + + let whitelistContract; + + before(async () => { + const whitelistAddress = await getContractAddress('WhiteList'); + whitelistContract = new web3.eth.Contract(WhiteList.abi, whitelistAddress); + }); + + it('finds a valid contract at the address', async () => { + const code = await getContractCode('WhiteList', web3); + expect(code.length).toBeGreaterThan(3); + }); + + it('got deployed with wETH and wBTC to begin with', async () => { + const whitelistAddresses = await whitelistContract.methods.validAddresses().call(); + const WBTCAddress = await findDependency('WBTC'); + const WETHAddress = await findDependency('WETH'); + + expect(whitelistAddresses).toContain(WBTCAddress); + expect(whitelistAddresses).toContain(WETHAddress); + expect(whitelistAddresses.length).toEqual(2); + }); + + }); + + describe('Set Rebalancing Token Factory', () => { + + /** + * Check it got deployed with the following environment specific variables: + * - Core + * - Whitelist + * - minimumRebalanceInterval + * - minimalProposalPeriod + * - minimumTimeToPivot + * - maximumTimeToPivot + */ + + let rebalancingTokenFactoryContract; + + before(async () => { + const rebalancingTokenFactoryAddress = await getContractAddress('RebalancingSetTokenFactory'); + rebalancingTokenFactoryContract = new web3.eth.Contract( + RebalancingSetTokenFactory.abi, + rebalancingTokenFactoryAddress + ); + }); + + it('finds a valid contract at the address', async () => { + const code = await getContractCode('RebalancingSetTokenFactory', web3); + expect(code.length).toBeGreaterThan(3); + }); + + it('got deployed with core', async () => { + const retrievedCoreAddress = + await rebalancingTokenFactoryContract.methods.core().call(); + expect(coreAddress).toEqual(retrievedCoreAddress); + }); + + it('got deployed with the whitelist', async () => { + const retrievedWhitelistAddress = + await rebalancingTokenFactoryContract.methods.rebalanceComponentWhitelist().call(); + const whitelistAddress = await getContractAddress('WhiteList'); + expect(retrievedWhitelistAddress).toEqual(whitelistAddress); + }); + + it('correct minimum rebalance interval was set', async () => { + const retrievedRebalanceInterval = + await rebalancingTokenFactoryContract.methods.minimumRebalanceInterval().call(); + const rebalanceInterval = await networkConstants.minimumRebalanceInterval[networkName]; + expect(parseInt(retrievedRebalanceInterval)).toEqual(rebalanceInterval); + }); + + it('correct minimum prosalPeriod was set', async () => { + const retrievedProposalPeriod = + await rebalancingTokenFactoryContract.methods.minimumProposalPeriod().call(); + const proposalPeriod = await networkConstants.minimumRebalanceProposalPeriod[networkName]; + expect(parseInt(retrievedProposalPeriod)).toEqual(proposalPeriod); + }); + + it('correct minimum rebalance interval was set', async () => { + const retrievedMinimumTimeToPivot = + await rebalancingTokenFactoryContract.methods.minimumTimeToPivot().call(); + const minimumTimeToPivot = await networkConstants.minimumRebalanceTimeToPivot[networkName]; + expect(parseInt(retrievedMinimumTimeToPivot)).toEqual(minimumTimeToPivot); + }); + + it('correct minimum rebalance interval was set', async () => { + const retrievedMaximumTimeToPivot = + await rebalancingTokenFactoryContract.methods.maximumTimeToPivot().call(); + const maximumTimeToPivot = await networkConstants.maximumRebalanceTimeToPivot[networkName]; + expect(parseInt(retrievedMaximumTimeToPivot)).toEqual(maximumTimeToPivot); + }); + + }); + +}); \ No newline at end of file diff --git a/deployments/test/2_libraries.spec.ts b/deployments/test/2_libraries.spec.ts new file mode 100755 index 000000000..664a905a6 --- /dev/null +++ b/deployments/test/2_libraries.spec.ts @@ -0,0 +1,109 @@ +'use strict'; + +import expect from 'expect'; + +import { getContractCode } from '../utils/output-helper'; +import { getWeb3Instance } from '../utils/blockchain'; + +describe('Deployment: Libraries', () => { + + let web3; + + before(async () => { + web3 = await getWeb3Instance(); + }); + + describe('ERC20Wrapper', () => { + + /** + * Deployed the ERC20Wrapper then check it got linked with the following contracts + * - Vault + * - TransferProxy + * - TakerWalletWrapper + * - KyberNetworkWrapper + * - ZeroExExchangeWrapper + * - PayableExchangeIssue + */ + + it('finds a valid library at the address', async () => { + const code = await getContractCode('ERC20Wrapper', web3); + expect(code.length).toBeGreaterThan(3); + }); + + }); + + describe('ExchangeIssueLibrary', () => { + + /** + * Deployed the ExchangeIssuanceLibrary then check it got linked with the following contracts: + * - PayableExchangeIssue + */ + + it('finds a valid library at the address', async () => { + const code = await getContractCode('ExchangeIssueLibrary', web3); + expect(code.length).toBeGreaterThan(3); + }); + + }); + + describe('Rebalancing Libraries', () => { + + /** + * Deployed the RebalancingHelperLibrary + */ + + it('finds a valid RebalancingHelperLibrary at the address', async () => { + const code = await getContractCode('RebalancingHelperLibrary', web3); + expect(code.length).toBeGreaterThan(3); + }); + + /** + * Deployed the following libraries and have been linked to RebalancingHelperLibrary: + * - StandardProposeLibrary + * - StandardStartRebalanceLibrary + * - StandardPlaceBidLibrary + * - StandardSettleRebalanceLibrary + * - StandardFailAuctionLibrary + */ + + it('finds a valid StandardProposeLibrary at the address', async () => { + const code = await getContractCode('StandardProposeLibrary', web3); + expect(code.length).toBeGreaterThan(3); + }); + + it('finds a valid StandardStartRebalanceLibrary at the address', async () => { + const code = await getContractCode('StandardStartRebalanceLibrary', web3); + expect(code.length).toBeGreaterThan(3); + }); + + it('finds a valid StandardPlaceBidLibrary at the address', async () => { + const code = await getContractCode('StandardPlaceBidLibrary', web3); + expect(code.length).toBeGreaterThan(3); + }); + + it('finds a valid StandardSettleRebalanceLibrary at the address', async () => { + const code = await getContractCode('StandardSettleRebalanceLibrary', web3); + expect(code.length).toBeGreaterThan(3); + }); + + it('finds a valid StandardFailAuctionLibrary at the address', async () => { + const code = await getContractCode('StandardFailAuctionLibrary', web3); + expect(code.length).toBeGreaterThan(3); + }); + + }); + + describe('Signature Validator', () => { + + /** + * Deployed the SignatureValidator contract + */ + + it('has deployed SignatureValidator at the address', async () => { + const code = await getContractCode('StandardFailAuctionLibrary', web3); + expect(code.length).toBeGreaterThan(3); + }); + + }); + +}); \ No newline at end of file diff --git a/deployments/test/3_modules.spec.ts b/deployments/test/3_modules.spec.ts new file mode 100755 index 000000000..b6e2d2f5b --- /dev/null +++ b/deployments/test/3_modules.spec.ts @@ -0,0 +1,360 @@ +'use strict'; + +import expect from 'expect'; + +import { + getContractCode, + getNetworkName, + getNetworkId, + getContractAddress, + findDependency +} from '../utils/output-helper'; + +import { getWeb3Instance } from '../utils/blockchain'; + +import { ExchangeIssueModule } from '../../artifacts/ts/ExchangeIssueModule'; +import { IssuanceOrderModule } from '../../artifacts/ts/IssuanceOrderModule'; +import { RebalanceAuctionModule } from '../../artifacts/ts/RebalanceAuctionModule'; +import { TakerWalletWrapper } from '../../artifacts/ts/TakerWalletWrapper'; +import { RebalancingTokenIssuanceModule } from '../../artifacts/ts/RebalancingTokenIssuanceModule'; +import { ZeroExExchangeWrapper } from '../../artifacts/ts/ZeroExExchangeWrapper'; +import { PayableExchangeIssue } from '../../artifacts/ts/PayableExchangeIssue'; +import { KyberNetworkWrapper } from '../../artifacts/ts/KyberNetworkWrapper'; +import { LinearAuctionPriceCurve } from '../../artifacts/ts/LinearAuctionPriceCurve'; +import { ConstantAuctionPriceCurve } from '../../artifacts/ts/ConstantAuctionPriceCurve'; + +import dependencies from '../dependencies'; +import networkConstants from '../network-constants'; +import constants from '../constants'; + +describe('Deployment: Modules', () => { + + let web3; + const networkId = getNetworkId(); + const networkName = getNetworkName(); + + let coreAddress; + let vaultAddress; + let transferProxyAddress; + + before(async () => { + web3 = await getWeb3Instance(); + coreAddress = await getContractAddress('Core'); + vaultAddress = await getContractAddress('Vault'); + transferProxyAddress = await getContractAddress('TransferProxy'); + }); + + describe('Exchange Issue', () => { + + /** + * Check if ExchangeIssueModule has been deployed with the following: + * - Core + * - TransferProxy + * - Vault + */ + + let exchangeIssueContract; + + before(async () => { + const exchangeIssueAddress = await getContractAddress('ExchangeIssueModule'); + exchangeIssueContract = new web3.eth.Contract(ExchangeIssueModule.abi, exchangeIssueAddress); + }); + + it('finds a valid contract at the address', async () => { + const code = await getContractCode('ExchangeIssueModule', web3); + expect(code.length).toBeGreaterThan(3); + }); + + it('got deployed with core', async () => { + const retrievedCoreAddress = await exchangeIssueContract.methods.core().call(); + expect(retrievedCoreAddress).toEqual(coreAddress); + }); + + it('got deployed with the transfer proxy', async () => { + const retrievedTransferProxyAddress = await exchangeIssueContract.methods.transferProxy().call(); + expect(retrievedTransferProxyAddress).toEqual(transferProxyAddress); + }); + + it('got deployed with the vault', async () => { + const retrievedVaultAddress = await exchangeIssueContract.methods.vault().call(); + expect(retrievedVaultAddress).toEqual(vaultAddress); + }); + + }); + + describe('Rebalancing Auction Module', () => { + + /** + * Check if the RebalanceAuctionModule has been deployed with the following: + * - Core + * - Vault + */ + + let rebalanceAuctionModule; + + before(async () => { + const rebalanceAuctionAddress = await getContractAddress('RebalanceAuctionModule'); + rebalanceAuctionModule = new web3.eth.Contract(RebalanceAuctionModule.abi, rebalanceAuctionAddress); + }); + + it('finds a valid contract at the address', async () => { + const code = await getContractCode('RebalanceAuctionModule', web3); + expect(code.length).toBeGreaterThan(3); + }); + + it('got deployed with core', async () => { + const retrievedCoreAddress = await rebalanceAuctionModule.methods.core().call(); + expect(retrievedCoreAddress).toEqual(coreAddress); + }); + + it('got deployed with the vault', async () => { + const retrievedVaultAddress = await rebalanceAuctionModule.methods.vault().call(); + expect(retrievedVaultAddress).toEqual(vaultAddress); + }); + + }); + + describe('Rebalancing Token Issuance Module', () => { + + /** + * Check if the RebalancingTokenIssuanceModule has been deployed with the following: + * - Core + * - TransferProxy + * - Vault + */ + + let rebalanceTokenIssuanceModule; + + before(async () => { + const rebalanceAuctionAddress = await getContractAddress('RebalancingTokenIssuanceModule'); + rebalanceTokenIssuanceModule = new web3.eth.Contract(RebalancingTokenIssuanceModule.abi, rebalanceAuctionAddress); + }); + + it('finds a valid contract at the address', async () => { + const code = await getContractCode('RebalancingTokenIssuanceModule', web3); + expect(code.length).toBeGreaterThan(3); + }); + + it('got deployed with core', async () => { + const retrievedCoreAddress = await rebalanceTokenIssuanceModule.methods.core().call(); + expect(retrievedCoreAddress).toEqual(coreAddress); + }); + + it('got deployed with the transfer proxy', async () => { + const retrievedTransferProxyAddress = await rebalanceTokenIssuanceModule.methods.transferProxy().call(); + expect(retrievedTransferProxyAddress).toEqual(transferProxyAddress); + }); + + it('got deployed with the vault', async () => { + const retrievedVaultAddress = await rebalanceTokenIssuanceModule.methods.vault().call(); + expect(retrievedVaultAddress).toEqual(vaultAddress); + }); + + }); + + describe('Kyber Wrapper', () => { + + /** + * Check if the Kyber Network proxy exists, if so check if KyberNetworkWrapper deployed with: + * - Core + * - KyberNetworkProxy + * - TransferProxy + */ + + let kyberWrapper; + + before(async () => { + const kyberWrapperAddress = await getContractAddress('KyberNetworkWrapper'); + kyberWrapper = new web3.eth.Contract(KyberNetworkWrapper.abi, kyberWrapperAddress); + }); + + it('finds a valid contract at the address', async () => { + const code = await getContractCode('KyberNetworkWrapper', web3); + expect(code.length).toBeGreaterThan(3); + }); + + it('got deployed with core', async () => { + const retrievedCoreAddress = await kyberWrapper.methods.core().call(); + expect(retrievedCoreAddress).toEqual(coreAddress); + }); + + it('got deployed with the kyber network proxy', async () => { + const retrievedKyberNetworkProxy = await kyberWrapper.methods.kyberNetworkProxy().call(); + const kyberNetworkProxyAddress = dependencies.KYBER_PROXY[networkId]; + expect(retrievedKyberNetworkProxy).toEqual(kyberNetworkProxyAddress); + }); + + it('got deployed with the transfer proxy', async () => { + const retrievedTransferProxyAddress = await kyberWrapper.methods.setTransferProxy().call(); + expect(retrievedTransferProxyAddress).toEqual(transferProxyAddress); + }); + + }); + + describe('Zero Ex Exchange', () => { + + /** + * Check if the ZeroEx addresses exist, if so check if ZeroExExchangeWrapper deployed with: + * - Core + * - ZeroExExchange + * - ZeroExTransferProxy + * - ZeroEXZRX + * - TransferProxy + */ + + let zeroExWrapper; + + before(async () => { + const kyberWrapperAddress = await getContractAddress('ZeroExExchangeWrapper'); + zeroExWrapper = new web3.eth.Contract(ZeroExExchangeWrapper.abi, kyberWrapperAddress); + }); + + it('finds a valid contract at the address', async () => { + const code = await getContractCode('ZeroExExchangeWrapper', web3); + expect(code.length).toBeGreaterThan(3); + }); + + it('got deployed with core', async () => { + const retrievedCoreAddress = await zeroExWrapper.methods.core().call(); + expect(retrievedCoreAddress).toEqual(coreAddress); + }); + + it('got deployed with the zero ex exchange', async () => { + const retrievedZeroExExchange = await zeroExWrapper.methods.zeroExExchange().call(); + const zeroExExchangeAddress = dependencies.ZERO_EX_EXCHANGE[networkId]; + expect(retrievedZeroExExchange).toEqual(zeroExExchangeAddress); + }); + + it('got deployed with the zero ex transfer proxy', async () => { + const retrievedZeroExTransferProxy = await zeroExWrapper.methods.zeroExProxy().call(); + const zeroExTransferProxyAddress = dependencies.ZERO_EX_PROXY[networkId]; + expect(retrievedZeroExTransferProxy).toEqual(zeroExTransferProxyAddress); + }); + + it('got deployed with the zero ex token', async () => { + const retrievedZeroExToken = await zeroExWrapper.methods.zeroExToken().call(); + const zeroExTokenAddress = dependencies.ZERO_EX_ZRX[networkId]; + expect(retrievedZeroExToken).toEqual(zeroExTokenAddress); + }); + + it('got deployed with the set transfer proxy', async () => { + const retrievedTransferProxyAddress = await zeroExWrapper.methods.setTransferProxy().call(); + expect(retrievedTransferProxyAddress).toEqual(transferProxyAddress); + }); + + }); + + describe('Payable Exchange', () => { + + /** + * Check if the PayableExchangeIssue has been deployed with: + * - Core + * - TransferProxy + * - ExchangeIssueModel + * - WrappedEtherAddress + */ + + let payableExchangeWrapper; + + before(async () => { + const payableExchangeAddress = await getContractAddress('PayableExchangeIssue'); + payableExchangeWrapper = new web3.eth.Contract(PayableExchangeIssue.abi, payableExchangeAddress); + }); + + it('finds a valid contract at the address', async () => { + const code = await getContractCode('PayableExchangeIssue', web3); + expect(code.length).toBeGreaterThan(3); + }); + + it('got deployed with core', async () => { + const retrievedCoreAddress = await payableExchangeWrapper.methods.core().call(); + expect(retrievedCoreAddress).toEqual(coreAddress); + }); + + it('got deployed with the transfer proxy', async () => { + const retrievedTransferProxyAddress = await payableExchangeWrapper.methods.transferProxy().call(); + expect(retrievedTransferProxyAddress).toEqual(transferProxyAddress); + }); + + it('got deployed with the exchange issue module', async () => { + const retrievedExchangeIssueAddress = await payableExchangeWrapper.methods.exchangeIssueModule().call(); + const exchangeIssueModel = await getContractAddress('ExchangeIssueModule'); + expect(retrievedExchangeIssueAddress).toEqual(exchangeIssueModel); + }); + + it('got deployed with the correct wETH address', async () => { + const retrievedWETHAddress = await payableExchangeWrapper.methods.weth().call(); + const WETHAddress = await findDependency('WETH'); + expect(retrievedWETHAddress).toEqual(WETHAddress); + }); + + }); + + describe('Linear Auction Price Curve', () => { + + /** + * Check if the LinearAuctionPriceCurve has been deployed with the appropriate settings + */ + + if (!networkConstants.linearAuctionPriceCurve[networkName]) { + console.log(networkConstants.linearAuctionPriceCurve); + console.log(networkName); + console.log(getNetworkName()); + return; + } + + let linearAuctionPriceCurveContract; + + before(async () => { + const linearAuctionPriceCurveAddress = await getContractAddress('LinearAuctionPriceCurve'); + linearAuctionPriceCurveContract = new web3.eth.Contract( + LinearAuctionPriceCurve.abi, + linearAuctionPriceCurveAddress + ); + }); + + it('deployed with the correct price denominator', async () => { + const retrievedPriceDenominator = await linearAuctionPriceCurveContract.methods.priceDenominator().call(); + expect(parseInt(retrievedPriceDenominator)).toEqual(constants.DEFAULT_AUCTION_PRICE_DENOMINATOR); + }); + + it('deployed with the uses start price parameter as true', async () => { + const retrievedUseStartPrice = await linearAuctionPriceCurveContract.methods.usesStartPrice().call(); + expect(retrievedUseStartPrice).toEqual(true); + }); + + }); + + describe('Constant Auction Price Curve', () => { + + /** + * Check if the ConstantAuctionPriceCurve has been deployed with the appropriate settings + */ + + if (!networkConstants.constantsAuctionPriceCurve[networkName]) { + return; + } + + let constantAuctionPriceCurveContract; + + before(async () => { + const constantAuctionPriceCurveAddress = await getContractAddress('ConstantAuctionPriceCurve'); + constantAuctionPriceCurveContract = new web3.eth.Contract( + ConstantAuctionPriceCurve.abi, + constantAuctionPriceCurveAddress + ); + }); + + it('deployed with the correct price denominator', async () => { + const retrievedPriceDenominator = await constantAuctionPriceCurveContract.methods.priceDenominator().call(); + expect(parseInt(retrievedPriceDenominator)).toEqual(constants.DEFAULT_AUCTION_PRICE_DENOMINATOR); + }); + + it('deployed with the correct price numerator', async () => { + const retrievedPriceNumerator = await constantAuctionPriceCurveContract.methods.priceNumerator().call(); + expect(parseInt(retrievedPriceNumerator)).toEqual(constants.DEFAULT_AUCTION_PRICE_NUMERATOR); + }); + + }); + +}); \ No newline at end of file diff --git a/deployments/test/4_authorization.spec.ts b/deployments/test/4_authorization.spec.ts new file mode 100644 index 000000000..4ac1b22e0 --- /dev/null +++ b/deployments/test/4_authorization.spec.ts @@ -0,0 +1,280 @@ +'use strict'; + +import expect from 'expect'; + +import { getNetworkName, getContractAddress } from '../utils/output-helper'; +import { getWeb3Instance } from '../utils/blockchain'; + +import { Core } from '../../artifacts/ts/Core'; +import { TransferProxy } from '../../artifacts/ts/TransferProxy'; +import { Vault } from '../../artifacts/ts/Vault'; +import { WhiteList } from '../../artifacts/ts/WhiteList'; +import { IssuanceOrderModule } from '../../artifacts/ts/IssuanceOrderModule'; + +import networkConstants from '../network-constants'; + +describe('Deployment: Authorization', () => { + + let web3; + const networkName = getNetworkName(); + + let coreAddress; + let vaultAddress; + + let coreContract; + + before(async () => { + web3 = await getWeb3Instance(); + + coreAddress = await getContractAddress('Core'); + vaultAddress = await getContractAddress('Vault'); + + coreContract = new web3.eth.Contract(Core.abi, coreAddress); + }); + + describe('Timelocks', () => { + + /** + * Need to check whether a time lock was applied to the following contracts: + * - Core + * - TransferProxy + * - Vault + * - Whitelist + * - Issuance Order Module + */ + + const expectedTimeLockPeriod = networkConstants.timeLockPeriod[networkName]; + + it('correct timelock applied to core', async () => { + const timelock = await coreContract.methods.timeLockPeriod().call(); + expect(parseInt(timelock)).toEqual(expectedTimeLockPeriod); + }); + + it('correct timelock applied to transfer proxy', async () => { + const transferProxyAddress = await getContractAddress('TransferProxy'); + const transferProxyContract = new web3.eth.Contract(TransferProxy.abi, transferProxyAddress); + const timelock = await transferProxyContract.methods.timeLockPeriod().call(); + expect(parseInt(timelock)).toEqual(expectedTimeLockPeriod); + }); + + it('correct timelock applied to vault', async () => { + const vaultContract = new web3.eth.Contract(Vault.abi, vaultAddress); + const timelock = await vaultContract.methods.timeLockPeriod().call(); + expect(parseInt(timelock)).toEqual(expectedTimeLockPeriod); + }); + + it('correct timelock applied to white list', async () => { + const whiteListAddress = await getContractAddress('WhiteList'); + const vaultContract = new web3.eth.Contract(WhiteList.abi, whiteListAddress); + const timelock = await vaultContract.methods.timeLockPeriod().call(); + expect(parseInt(timelock)).toEqual(expectedTimeLockPeriod); + }); + + }); + + describe('Authorized Vault addresses', () => { + + /** + * Check if Vault has the following contracts as authorized addresses: + * - Core + * - ExchangeIssueModule + * - RebalancingAuctionModule + * - RebalancingTokenIssuanceModule + */ + + let vaultContract; + let authorisedAddresses; + + before(async () => { + vaultContract = new web3.eth.Contract(Vault.abi, vaultAddress); + authorisedAddresses = await vaultContract.methods.getAuthorizedAddresses().call(); + }); + + it('vault contains core as authorised address', async () => { + expect(authorisedAddresses).toContain(coreAddress); + }); + + it('vault contains exchange issue module as an authorised address', async () => { + const exchangeIssueModuleAddress = await getContractAddress('ExchangeIssueModule'); + expect(authorisedAddresses).toContain(exchangeIssueModuleAddress); + }); + + it('vault contains rebalance auction module as an authorised address', async () => { + const rebalanceAuctionModuleAddress = await getContractAddress('RebalanceAuctionModule'); + expect(authorisedAddresses).toContain(rebalanceAuctionModuleAddress); + }); + + it('vault contains rebalancing token issuance module as an authorised address', async () => { + const rebalanceTokenIssueAddress = await getContractAddress('RebalancingTokenIssuanceModule'); + expect(authorisedAddresses).toContain(rebalanceTokenIssueAddress); + }); + + }); + + describe('Authorized Transfer Proxy addresses', () => { + + /** + * Check if the following contracts have the Transfer Proxy as an authorized address: + * - Core + * - TakerWalletWrapper + * - ExchangeIssueModule + * - IssuanceOrderModule + * - RebalancingAuctionModule + * - RebalancingTokenIssuanceModule + */ + + let transferProxyContract; + let authorisedAddresses; + + before(async () => { + const transferProxyAddress = await getContractAddress('TransferProxy'); + transferProxyContract = new web3.eth.Contract(TransferProxy.abi, transferProxyAddress); + authorisedAddresses = await transferProxyContract.methods.getAuthorizedAddresses().call(); + }); + + it('transfer proxy contains core as authorised address', async () => { + expect(authorisedAddresses).toContain(coreAddress); + }); + + it('transfer proxy contains exchange issue module as an authorised address', async () => { + const exchangeIssueModuleAddress = await getContractAddress('ExchangeIssueModule'); + expect(authorisedAddresses).toContain(exchangeIssueModuleAddress); + }); + + it('transfer proxy contains rebalance auction module as an authorised address', async () => { + const rebalanceAuctionModuleAddress = await getContractAddress('RebalanceAuctionModule'); + expect(authorisedAddresses).toContain(rebalanceAuctionModuleAddress); + }); + + it('transfer proxy contains rebalancing token issuance module as an authorised address', async () => { + const rebalanceTokenIssueAddress = await getContractAddress('RebalancingTokenIssuanceModule'); + expect(authorisedAddresses).toContain(rebalanceTokenIssueAddress); + }); + + }); + + describe('Factories in Core', () => { + + /** + * Check if the following factories are registered with Core: + * - SetTokenFactory + * - RebalancingSetTokenFactory + */ + + let factories; + + before(async () => { + factories = await coreContract.methods.factories().call(); + }); + + it('core contains set token factory', async () => { + const setTokenFactoryAddress = await getContractAddress('SetTokenFactory'); + expect(factories).toContain(setTokenFactoryAddress); + }); + + it('core contains rebalancing set token factory', async () => { + const rebalancingSetTokenFactoryAddress = await getContractAddress('RebalancingSetTokenFactory'); + expect(factories).toContain(rebalancingSetTokenFactoryAddress); + }); + + }); + + describe('Modules in Core', () => { + + /** + * Check if the following modules have been added to Core: + * - ExchangeIssueModule + * - IssuanceOrderModule + * - RebalanceAuctionModule + * - RebalancingTokenIssuanceModule + */ + + let modules; + + before(async () => { + modules = await coreContract.methods.modules().call(); + }); + + it('core contains exchange issue module', async () => { + const exchangeIssueModuleAddress = await getContractAddress('ExchangeIssueModule'); + expect(modules).toContain(exchangeIssueModuleAddress); + }); + + it('core contains rebalancing auction module', async () => { + const rebalanceAuctionModule = await getContractAddress('RebalanceAuctionModule'); + expect(modules).toContain(rebalanceAuctionModule); + }); + + it('core contains rebalancing token issuance module', async () => { + const rebalanceTokenIssuanceModuleAddress = await getContractAddress('RebalancingTokenIssuanceModule'); + expect(modules).toContain(rebalanceTokenIssuanceModuleAddress); + }); + + }); + + describe('Exchanges in Core', () => { + + /** + * Check if the following exchange wrappers have been added to core: + * - ZeroExExchangeWrapper + * - KyberNetworkWrapper + * - TakerWalletWrapper + */ + + let exchanges; + + before(async () => { + exchanges = await coreContract.methods.exchanges().call(); + }); + + it('core contains zero ex exchange wrapper', async () => { + const zeroExAddress = await getContractAddress('ZeroExExchangeWrapper'); + expect(exchanges).toContain(zeroExAddress); + }); + + it('core contains kyber network exchange wrapper', async () => { + const kyberNetworkAddress = await getContractAddress('KyberNetworkWrapper'); + expect(kyberNetworkAddress).toContain(kyberNetworkAddress); + }); + + it('core contains taker wallet exchange wrapper', async () => { + const takerWalletAddress = await getContractAddress('TakerWalletWrapper'); + expect(takerWalletAddress).toContain(takerWalletAddress); + }); + + }); + + describe('Price Libraries in Core', () => { + + /** + * Check if the following price libraries have been added to Core: + * - LinearAuctionPriceCurve + * - ConstantAuctionPriceCurve + */ + + let priceLibraries; + + before(async () => { + priceLibraries = await coreContract.methods.priceLibraries().call(); + }); + + it('core contains linear auction price curve', async () => { + if (!networkConstants.linearAuctionPriceCurve[networkName]) { + return; + } + const linearAuctionPriceCurveAddress = await getContractAddress('LinearAuctionPriceCurve'); + expect(priceLibraries).toContain(linearAuctionPriceCurveAddress); + + }); + + it('core contains constant auction price curve', async () => { + if (!networkConstants.constantsAuctionPriceCurve[networkName]) { + return; + } + const constantAuctionPriceCurveAddress = await getContractAddress('ConstantAuctionPriceCurve'); + expect(priceLibraries).toContain(constantAuctionPriceCurveAddress); + }); + + }); + +}); \ No newline at end of file diff --git a/deployments/test/5_rebalancing.spec.ts b/deployments/test/5_rebalancing.spec.ts new file mode 100644 index 000000000..fe5d34c70 --- /dev/null +++ b/deployments/test/5_rebalancing.spec.ts @@ -0,0 +1,236 @@ +'use strict'; + +import expect from 'expect'; + +import { + getContractCode, + getNetworkId, + getContractAddress, + findDependency, + getNetworkName +} from '../utils/output-helper'; + +import { calculateInitialSetUnits } from '../utils/rebalancing'; +import { getWeb3Instance } from '../utils/blockchain'; + +import { BTCETHRebalancingManager } from '../../artifacts/ts/BTCETHRebalancingManager'; +import { SetToken } from '../../artifacts/ts/SetToken'; +import { RebalancingSetToken } from '../../artifacts/ts/RebalancingSetToken'; + +import dependencies from '../dependencies'; +import constants from '../constants'; +import networkConstants from '../network-constants'; + +describe('Deployment: Rebalancing', () => { + + let web3; + let networkId = getNetworkId(); + let networkName = getNetworkName(); + + before(async () => { + web3 = await getWeb3Instance(); + }); + + describe('BTCETH Rebalancing Manager', () => { + + /** + * Check if the BTCETHRebalancingManager was deployed correctly with the following: + * - Core + * - wbtcMedianizerAddress + * - wethMedianizerAddress + * - wbtcAddress + * - wethAddress + * - SetTokenFactory + * - LinearAuctionPriceCurve + * - auctionTimeToPivot + * - WBTC_MULTIPLIER + * - WETH_MULTIPLIER + */ + + let rebalancingManagerContract; + + before(async () => { + const rebalancingMangerAddress = await getContractAddress('BitEthRebalanceManager'); + rebalancingManagerContract = new web3.eth.Contract(BTCETHRebalancingManager.abi, rebalancingMangerAddress); + }); + + it('find a valid contract at the address', async () => { + const code = await getContractCode('BitEthRebalanceManager', web3); + expect(code.length).toBeGreaterThan(3); + }); + + it('rebalancing manager has correct wBTC medianizer address', async () => { + const wBTCMedianizerAddress = dependencies.WBTC_MEDIANIZER[networkId]; + const receivedBTCMedianizerAddress = await rebalancingManagerContract.methods.btcPriceFeed().call(); + expect(receivedBTCMedianizerAddress).toEqual(wBTCMedianizerAddress); + }); + + it('rebalancing manager has correct wETH medianizer address', async () => { + const wETHMedianizerAddress = dependencies.WETH_MEDIANIZER[networkId]; + const receivedETHMedianzierAddress = await rebalancingManagerContract.methods.ethPriceFeed().call(); + expect(receivedETHMedianzierAddress).toEqual(wETHMedianizerAddress); + }); + + it('rebalancing manager has correct wBTC address', async () => { + const wBTCAddress = await findDependency('WBTC'); + const receivedBTCAddress = await rebalancingManagerContract.methods.btcAddress().call(); + expect(receivedBTCAddress).toEqual(wBTCAddress); + }); + + it('rebalancing manger has correct wETH address', async () => { + const wETHAddress = await findDependency('WETH'); + const receivedETHAddress = await rebalancingManagerContract.methods.ethAddress().call(); + expect(receivedETHAddress).toEqual(wETHAddress); + }); + + it('rebalancing manager has correct set token factory', async () => { + const setTokenFactoryAddress = await getContractAddress('SetTokenFactory'); + const receivedSetTokenFactoryAddress = await rebalancingManagerContract.methods.setTokenFactory().call(); + expect(receivedSetTokenFactoryAddress).toEqual(setTokenFactoryAddress); + }); + + it('rebalancing manager has correct auction price library instance', async() => { + const linearAuctionPriceLibrary = await getContractAddress('LinearAuctionPriceCurve'); + const receivedLinearAuctionPriceAddress = await rebalancingManagerContract.methods.auctionLibrary().call(); + expect(receivedLinearAuctionPriceAddress).toContain(linearAuctionPriceLibrary); + + }); + + it('rebalancing manager has correct wBTC multiplier', async () => { + const receivedMultiplier = await rebalancingManagerContract.methods.btcMultiplier().call(); + expect(receivedMultiplier.toString()).toEqual(constants.WBTC_MULTIPLIER.toString()); + }); + + it('rebalancing manager has correct wETH multiplier', async () => { + const receivedMultiplier = await rebalancingManagerContract.methods.ethMultiplier().call(); + expect(receivedMultiplier.toString()).toEqual(constants.WETH_MULTIPLIER.toString()); + }); + + it('rebalancing manager has correct lower bound', async () => { + const receivevdLowerBound = await rebalancingManagerContract.methods.maximumLowerThreshold().call(); + let lowerBound = networkConstants.bitEthRebalanceManagerAllocationLowerBound[networkName].toString(); + expect(receivevdLowerBound.toString()).toEqual(lowerBound); + }); + + it('rebalancing manager has correct upper bound', async () => { + const receivedUpperBound = await rebalancingManagerContract.methods.minimumUpperThreshold().call(); + let upperBound = networkConstants.bitEthRebalanceManagerAllocationUpperBound[networkName].toString(); + expect(receivedUpperBound.toString()).toEqual(upperBound); + }); + + }); + + describe('Initial Collateralized Set', () => { + + /** + * Check if there's a InitialCollateralSet address with the correct params: + * - SetTokenFactory + * - rebalancingSetComponents + * - rebalancingSetUnitShares + * - rebalancingSetNaturalUnit + * - rebalancingSetName + * - rebalancingSetSymbol + * - rebalancingSetCallData + */ + + let initialCollateralisedSet; + const calculatedUnitShares = calculateInitialSetUnits(); + + before(async () => { + const initialCollateralSetAddress = await getContractAddress('InitialCollateralSet'); + initialCollateralisedSet = new web3.eth.Contract(SetToken.abi, initialCollateralSetAddress); + }); + + it('find a valid contract at the address', async () => { + const code = await getContractCode('InitialCollateralSet', web3); + expect(code.length).toBeGreaterThan(3); + }); + + it('collateralized set should have have the correct set token factory', async () => { + const setTokenFactoryAddress = await getContractAddress('SetTokenFactory'); + const receivedSetTokenFactoryAddress = await initialCollateralisedSet.methods.factory().call(); + expect(receivedSetTokenFactoryAddress).toEqual(setTokenFactoryAddress); + }); + + it('collateralized set should have the correct components', async () => { + const wETHAddress = await findDependency('WETH'); + const wBTCAddress = await findDependency('WBTC'); + + const setTokenComponents = [wBTCAddress, wETHAddress]; + const receivedSetComponents = await initialCollateralisedSet.methods.getComponents().call(); + expect(receivedSetComponents).toEqual(setTokenComponents); + }); + + it('collateralized set should have the correct natural unit', async () => { + const receivedNaturalUnit = await initialCollateralisedSet.methods.naturalUnit().call(); + expect(receivedNaturalUnit.toString()).toEqual(calculatedUnitShares['naturalUnit'].toString()); + }); + + it('collateralized set should have the correct set name', async () => { + const receivedSetName = await initialCollateralisedSet.methods.name().call(); + expect(receivedSetName).toEqual('BTCETH'); + }); + + it('collateralized set should have the correct set symbol', async () => { + const receivedSetSymbol = await initialCollateralisedSet.methods.symbol().call(); + expect(receivedSetSymbol).toEqual('BTCETH'); + }); + + }); + + describe('Initial Rebalanced Set Token', () => { + + /** + * CHeck if there's a rebalanced set with the correct params: + * - RebalancingSetTokenFactory address + * - rebalancingSetComponents + * - rebalancingSetUnitShares + * - rebalancingSetNaturalUnit + * - rebalancingSetName + * - rebalancingSetSymbol + * - rebalancingSetCallData + */ + + let bitEthRebalancingSetToken; + + before(async () => { + const bitEthRebalancingSetTokenAddress = await getContractAddress('BitEthRebalancingSetToken'); + bitEthRebalancingSetToken = new web3.eth.Contract(RebalancingSetToken.abi, bitEthRebalancingSetTokenAddress); + }); + + it('find a valid contract at the address', async () => { + const code = await getContractCode('BitEthRebalancingSetToken', web3); + expect(code.length).toBeGreaterThan(3); + }); + + it('rebalanced set should have have the correct rebalancing set token factory', async () => { + const rebalancingSetTokenFactoryAddress = await getContractAddress('RebalancingSetTokenFactory'); + const receivedSetTokenFactory = await bitEthRebalancingSetToken.methods.factory().call(); + expect(receivedSetTokenFactory).toEqual(rebalancingSetTokenFactoryAddress); + }); + + it('rebalanced set should have the correct components', async () => { + const collateralSetAddress = await getContractAddress('InitialCollateralSet'); + const setTokenComponents = [collateralSetAddress]; + const receivedSetComponents = await bitEthRebalancingSetToken.methods.getComponents().call(); + expect(receivedSetComponents).toEqual(setTokenComponents); + }); + + it('rebalanced set should have the correct natural unit', async () => { + const receivedNaturalUnits = await bitEthRebalancingSetToken.methods.naturalUnit().call(); + expect(receivedNaturalUnits.toString()).toEqual(constants.DEFAULT_REBALANCING_NATURAL_UNIT.toString()); + }); + + it('rebalanced set should have the correct set name', async () => { + const receivedSetName = await bitEthRebalancingSetToken.methods.name().call(); + expect(receivedSetName).toEqual('BitEth Set'); + }); + + it('rebalanced set should have the correct set symbol', async () => { + const receivedSetSymbol = await bitEthRebalancingSetToken.methods.symbol().call(); + expect(receivedSetSymbol).toEqual('BTCETH'); + }); + + }); + +}); \ No newline at end of file diff --git a/deployments/utils/blockchain.ts b/deployments/utils/blockchain.ts new file mode 100755 index 000000000..3328b8072 --- /dev/null +++ b/deployments/utils/blockchain.ts @@ -0,0 +1,90 @@ +import dependencies from '../dependencies'; +import { getNetworkId, getPrivateKey, writeContractToOutputs, getNetworkName } from './output-helper'; + +require('dotenv').config({ path: './.env'}); + +const Web3 = require('web3'); +const infuraApiKey: string = process.env.INFURAKEY; + +export async function getWeb3Instance(): Promise { + const networkId: number = getNetworkId(); + const infuraDomain = dependencies.INFURA_SUBDOMAIN[networkId]; + + if (infuraDomain) { + return new Web3(`${infuraDomain}/v3/${infuraApiKey}`); + } + + return new Web3('http://127.0.0.1:8545'); +} + +export let TX_DEFAULTS = { + gas: 6700000, // 6.7M + gasPrice: 10000000, // 10 gWei +}; + +export async function deployContract(bytecode, web3, contractName): Promise { + console.log(`* Deploying ${contractName}`); + + if (!contractName) { + console.log('Please provide a valid contract name'); + } + + if (!web3) { + console.log('Please provide a valid web3 instance'); + } + + if (!web3) { + console.log('Please provide bytecode/data'); + } + + const deployerAccount = web3.eth.accounts.privateKeyToAccount(getPrivateKey()); + + const deployTx = { + ...TX_DEFAULTS, + data: bytecode, + from: deployerAccount.address, + }; + + let receipt; + + try { + const signedTx = await web3.eth.accounts.signTransaction(deployTx, deployerAccount.privateKey); + + receipt = await new Promise((resolve, reject) => { + web3.eth.sendSignedTransaction(signedTx.rawTransaction) + // .on('transactionHash', (hash) => { + // console.log(`*** Tx Hash: ${hash}`); + // }) + .on('receipt', result => { + resolve(result); + }).on('error', error => { + console.log(error); + reject(error); + }); + }); + } catch (error) { + console.log('General deploy error ->', error); + return error; + } + + console.log(`*** ${receipt.contractAddress}`); + + const networkName = await getNetworkName(); + await writeContractToOutputs(networkName, contractName, receipt.contractAddress); + + return receipt.contractAddress; + +} + +export function linkLibraries(array, bytecode) { + let finalByteCode = bytecode; + + array.forEach(item => { + finalByteCode = finalByteCode.replace( + new RegExp(`_+${item.name}_+`, 'g'), + item.address.replace('0x', '') + ); + }); + + return finalByteCode; +} \ No newline at end of file diff --git a/deployments/utils/output-helper.ts b/deployments/utils/output-helper.ts new file mode 100644 index 000000000..83fa5875b --- /dev/null +++ b/deployments/utils/output-helper.ts @@ -0,0 +1,80 @@ +import fs from 'fs-extra'; +import dependencies from '../dependencies'; + +require('dotenv').config({ path: './.env'}); + +const OUTPUTS_PATH = './deployments/outputs.json'; + +const privateKey: string = process.env.DEPLOYMENT_PRIVATE_KEY; + +const deploymentNetwork: string = process.env.DEPLOYMENT_NETWORK_NAME; +const deploymentNetworkId: number = parseInt(process.env.DEPLOYMENT_NETWORK_ID); + +export async function returnOutputs(): Promise { + return fs.readJson(OUTPUTS_PATH, { throws: false }) || {}; +} + +export function getNetworkName(): string { + return deploymentNetwork; +} + +export function getNetworkId(): number { + return deploymentNetworkId; +} + +export function getPrivateKey(): string { + return privateKey; +} + +export async function findDependency(name: string) { + const dependencyValue = dependencies[name][getNetworkId()]; + + if (dependencyValue) { + return dependencyValue; + } + + return await getContractAddress(name); +} + +export async function getContractAddress(name: string) { + const outputs: any = await returnOutputs(); + + if (!outputs[deploymentNetwork]) { + return undefined; + } + + return outputs[deploymentNetwork]['addresses'][name] || ''; +} + +export async function getContractCode(name: string, web3: any): Promise { + const vaultAddress = await getContractAddress(name); + return await web3.eth.getCode(vaultAddress); +} + +export async function writeContractToOutputs(networkName: string, name: string, value: string) { + const outputs: any = await returnOutputs(); + + if (!outputs[networkName]) { + outputs[networkName] = {'addresses': {}, 'state': {}}; + } + + outputs[networkName]['addresses'][name] = value; + await fs.outputFile(OUTPUTS_PATH, JSON.stringify(outputs, undefined, 2)); +} + +export async function removeNetwork(name: string) { + const outputs: any = await returnOutputs(); + outputs[name] = {'addresses': {}, 'state': {}}; + await fs.outputFile(OUTPUTS_PATH, JSON.stringify(outputs, undefined, 2)); +} + +export async function writeStateToOutputs(networkName: string, parameter: string, value: any) { + const outputs: any = await returnOutputs(); + + if (!outputs[networkName]) { + outputs[networkName] = {'addresses': {}, 'state': {}}; + } + + outputs[networkName]['state'][parameter] = value; + await fs.outputFile(OUTPUTS_PATH, JSON.stringify(outputs, undefined, 2)); +} \ No newline at end of file diff --git a/deployments/utils/rebalancing.ts b/deployments/utils/rebalancing.ts new file mode 100644 index 000000000..1288c5ef6 --- /dev/null +++ b/deployments/utils/rebalancing.ts @@ -0,0 +1,66 @@ +import constants from '../constants'; +import BigNumber from 'bignumber.js'; + +export function calculateInitialSetUnits() { + let units = []; + let naturalUnit: BigNumber = new BigNumber(0); + + const WBTC_PRICE = constants.WBTC_PRICE; + const WBTC_MULTIPLIER = constants.WBTC_MULTIPLIER; + const WETH_MULTIPLIER = constants.WETH_MULTIPLIER; + const WETH_PRICE = constants.WETH_PRICE; + const DECIMAL_DIFFERENCE_MULTIPLIER = constants.WETH_FULL_TOKEN_UNITS.div(constants.WBTC_FULL_TOKEN_UNITS); + const PRICE_PRECISION = constants.PRICE_PRECISION; + + if (WBTC_PRICE.greaterThanOrEqualTo(WETH_PRICE)) { + const ethUnits = WBTC_PRICE.mul(DECIMAL_DIFFERENCE_MULTIPLIER).div(WETH_PRICE).round(0, 3); + units = [ + constants.DEFAULT_WBTC_UNIT.mul(WBTC_MULTIPLIER).toNumber(), + ethUnits.mul(WETH_MULTIPLIER).toNumber(), + ]; + naturalUnit = constants.DEFAULT_REBALANCING_NATURAL_UNIT; + } else { + const btcUnits = WETH_PRICE.mul(PRICE_PRECISION).div(WBTC_PRICE).round(0, 3); + const ethUnits = PRICE_PRECISION.mul(DECIMAL_DIFFERENCE_MULTIPLIER); + units = [ + btcUnits.mul(WBTC_MULTIPLIER).toNumber(), + ethUnits.mul(WETH_MULTIPLIER).toNumber(), + ]; + naturalUnit = constants.WETH_DOMINANT_REBALANCING_NATURAL_UNIT; + } + + return { + units, + naturalUnit, + }; +} + +export function calculateRebalancingSetUnitShares( + initialSetUnits, + initialSetNaturalUnit +) { + const btcUnitsInFullToken = constants.SET_FULL_TOKEN_UNITS + .mul(initialSetUnits[0]) + .div(initialSetNaturalUnit) + .round(0, 3); + const ethUnitsInFullToken = constants.SET_FULL_TOKEN_UNITS + .mul(initialSetUnits[1]) + .div(initialSetNaturalUnit) + .round(0, 3); + + const btcDollarAmount = constants.WBTC_PRICE + .mul(btcUnitsInFullToken) + .div(constants.WBTC_FULL_TOKEN_UNITS) + .round(0, 3); + + const ethDollarAmount = constants.WETH_PRICE + .mul(ethUnitsInFullToken) + .div(constants.WETH_FULL_TOKEN_UNITS) + .round(0, 3); + + const initialSetDollarAmount = btcDollarAmount.add(ethDollarAmount); + return [constants.REBALANCING_SET_USD_PRICE + .div(initialSetDollarAmount) + .mul(constants.DEFAULT_REBALANCING_NATURAL_UNIT) + .round(0, 3)]; +} \ No newline at end of file diff --git a/migrations/2_token_whitelist.js b/migrations/2_token_whitelist.js index 249eab5eb..5347a27e7 100644 --- a/migrations/2_token_whitelist.js +++ b/migrations/2_token_whitelist.js @@ -61,4 +61,4 @@ async function deployWhitelist(deployer, network, accounts) { } await deployer.deploy(WhiteList, initialTokenWhiteList); -} +} \ No newline at end of file diff --git a/migrations/3_core.js b/migrations/3_core.js index 496d991f5..e93b65dee 100644 --- a/migrations/3_core.js +++ b/migrations/3_core.js @@ -332,4 +332,4 @@ async function deployCoreContracts(deployer, network) { await deployer.deploy(ConstantAuctionPriceCurve, DEFAULT_AUCTION_PRICE_NUMERATOR, DEFAULT_AUCTION_PRICE_DENOMINATOR); break; } -}; +}; \ No newline at end of file diff --git a/migrations/5_BTCETHRebalancingSetToken.js b/migrations/5_rebalancing_token.js similarity index 99% rename from migrations/5_BTCETHRebalancingSetToken.js rename to migrations/5_rebalancing_token.js index 39a41f179..4f0ad43de 100644 --- a/migrations/5_BTCETHRebalancingSetToken.js +++ b/migrations/5_rebalancing_token.js @@ -237,4 +237,4 @@ function calculateRebalancingSetUnitShares( .mul(DEFAULT_REBALANCING_SET_NATURAL_UNIT) .round(0,3) .toNumber()]; -} +} \ No newline at end of file diff --git a/package.json b/package.json index 8e39c4bb1..15e01c509 100644 --- a/package.json +++ b/package.json @@ -26,20 +26,21 @@ "coverage-cleanup": "find artifacts/ts -name \\*.js* -type f -delete && find test -name \\*.js* -type f -delete && find types -name \\*.js* -type f -delete && find utils -name \\*.js* -type f -delete", "coverage-continuous": "./node_modules/.bin/solidity-coverage", "coverage-setup": "yarn transpile && cp -r transpiled/artifacts/ts/* artifacts/ts/. && cp -r transpiled/test/contracts/* test/. && cp -r transpiled/types/* types/. && cp -r transpiled/utils/* utils/.", - "deploy:development": "bash scripts/deploy_development.sh", + "deploy-development": "bash scripts/deploy_development.sh", + "deploy": "yarn ts-node deployments/deploy.ts && bash scripts/generate_outputs.sh && yarn test-deployment", "dist": "yarn setup && bash scripts/prepare_dist.sh", "generate-typings": "set-abi-gen --abis './build/contracts/*.json' --out './types/generated' --template './types/contract_templates/contract.mustache' --partials './types/contract_templates/partials/*.mustache'", "lint": "yarn run lint-sol && yarn run lint-ts", "lint-sol": "solium -d contracts/", - "lint-ts": "tslint -c tslint.json -p tsconfig.json --fix test/**/*.ts utils/**/*.ts", + "lint-ts": "tslint -c tslint.json -p tsconfig.json --fix test/**/*.ts utils/**/*.ts deployments/**/*.ts", "precommit": "lint-staged", "prepare-test": "yarn setup && yarn transpile", "profile-gas": "yarn prepare-test && truffle test `find transpiled/test/profile -name '*.spec.js'`", - "setup": "yarn clean && yarn deploy:development && yarn generate-typings", + "setup": "yarn clean && yarn deploy-development && yarn generate-typings", "test": "yarn prepare-test && yarn truffle-test-contracts", "test-nocompile": "yarn transpile && yarn truffle-test-contracts", - "test-continuous": "yarn deploy:development && truffle test", - "test-deployment": "truffle test test/deployment/*.spec.js", + "test-continuous": "yarn deploy-development && truffle test", + "test-deployment": "yarn ts-mocha deployments/**/*.spec.ts", "test-scenarios": "yarn prepare-test && yarn truffle-test-scenarios", "transpile": "tsc", "truffle-test-scenarios": "truffle test `find transpiled/test/scenarios -name '*.spec.js'`", @@ -56,14 +57,15 @@ "@0xproject/utils": "^2.0.2", "@0xproject/web3-wrapper": "^1.1.2", "@types/bignumber.js": "^4.0.3", + "@types/expect": "^1.20.4", "@types/fs-extra": "^5.0.0", "@types/json-stable-stringify": "^1.0.32", "@types/lodash": "^4.14.86", - "@types/mocha": "^2.2.47", + "@types/mocha": "^5.2.6", "@types/node": "^8.5.1", "abi-decoder": "^1.0.9", "bignumber.js": "^4.1.0", - "chai": "^4.1.2", + "chai": "^4.2.0", "chai-as-promised": "^7.1.1", "chai-bignumber": "^2.0.2", "coveralls": "^3.0.1", @@ -77,19 +79,23 @@ "json-stable-stringify": "^1.0.1", "lodash": "^4.17.4", "set-abi-gen": "1.1.0-beta.1", - "solc": "0.4.25", + "solc": "^0.4.25", "solidity-coverage": "^0.5.11", "truffle": "^5.0.3", + "truffle-deploy-registry": "^0.5.1", + "truffle-hdwallet-provider-privkey": "^1.0.3", "tslint": "^5.8.0", "tslint-no-unused-expression-chai": "0.0.3", "types-ethereumjs-util": "^0.0.5", - "typescript": "^3.1.2", + "typescript": "^3.3.1", "web3-provider-engine": "^14.0.6" }, "dependencies": { "bn-chai": "^1.0.1", "canonical-weth": "1.2.0", + "dotenv": "^6.2.0", "eth-gas-reporter": "^0.1.10", + "expect": "^24.1.0", "fs-extra": "^5.0.0", "husky": "^0.14.3", "lint-staged": "^7.2.0", @@ -99,6 +105,8 @@ "solium": "^1.1.7", "tiny-promisify": "^1.0.0", "truffle-hdwallet-provider": "^1.0.0-web3one.0", + "ts-mocha": "^6.0.0", + "ts-node": "^8.0.2", "tslint-eslint-rules": "^5.3.1", "web3": "1.0.0-beta.36", "web3-utils": "1.0.0-beta.36" diff --git a/scripts/generate_outputs.sh b/scripts/generate_outputs.sh new file mode 100644 index 000000000..2a5d202ac --- /dev/null +++ b/scripts/generate_outputs.sh @@ -0,0 +1,12 @@ +# To make it really easy to work with the outputs file from the manager, +# this script converts the JSON file into a typescript file + +# Remove old transpiled outputs from the deployments/ directory +rm -f deployments/outputs.ts + +# Output a TS file from the generated JSON file + +filename=outputs +filename_base=$(basename $filename .json) +echo -e "export const $filename_base = " > "deployments/$filename_base.ts" +cat "deployments/$filename_base.json" >> "deployments/$filename_base.ts" \ No newline at end of file diff --git a/truffle.js b/truffle.js index 171ac1c58..477f737a0 100644 --- a/truffle.js +++ b/truffle.js @@ -1,13 +1,16 @@ +require('dotenv').config(); + var HDWalletProvider = require("truffle-hdwallet-provider"); +var HDWalletProviderPrivateKey = require("truffle-hdwallet-provider-privkey"); + const NonceTrackerSubprovider = require("web3-provider-engine/subproviders/nonce-tracker") var infura_apikey = process.env.INFURAKEY; +var private_key = process.env.PRIVATE_KEY; + var mnemonic = process.env.MNEMONIC || 'candy maple cake sugar pudding cream honey rich smooth crumble sweet treat'; -console.log("key", infura_apikey); -console.log("mnemonic", mnemonic); - module.exports = { networks: { development: { @@ -18,39 +21,22 @@ module.exports = { gasPrice: 1, }, main: { - provider: () => { - let wallet = new HDWalletProvider(mnemonic, "https://mainnet.infura.io/" + infura_apikey); - let nonceTracker = new NonceTrackerSubprovider() - wallet.engine._providers.unshift(nonceTracker) - nonceTracker.setEngine(wallet.engine) - return wallet - }, + provider: () => returnWallet("https://mainnet.infura.io/" + infura_apikey), network_id: 1, gas: 6700000, gasPrice: 3000000000 }, ropsten: { - provider: () => { - let wallet = new HDWalletProvider(mnemonic, "https://ropsten.infura.io/" + infura_apikey); - let nonceTracker = new NonceTrackerSubprovider() - wallet.engine._providers.unshift(nonceTracker) - nonceTracker.setEngine(wallet.engine) - return wallet - }, + provider: () => returnWallet("https://ropsten.infura.io/" + infura_apikey), network_id: 3, gas: 6700000, }, kovan: { - provider: () => { - let wallet = new HDWalletProvider(mnemonic, "https://kovan.infura.io/" + infura_apikey); - let nonceTracker = new NonceTrackerSubprovider() - wallet.engine._providers.unshift(nonceTracker) - nonceTracker.setEngine(wallet.engine) - return wallet - }, + provider: () => returnWallet("https://kovan.infura.io/" + infura_apikey), network_id: 42, gas: 6700000, - gasPrice: 5000000000 + gasPrice: 5000000000, + skipDryRun: true }, coverage: { host: 'localhost', @@ -80,3 +66,18 @@ module.exports = { // } // } }; + +function returnWallet(url) { + let wallet; + if (private_key) { + wallet = new HDWalletProviderPrivateKey([private_key], url); + } else { + wallet = new HDWalletProvider(mnemonic, url); + } + + let nonceTracker = new NonceTrackerSubprovider() + wallet.engine._providers.unshift(nonceTracker) + nonceTracker.setEngine(wallet.engine) + + return wallet; +} \ No newline at end of file diff --git a/types/deployment_stage_interface.ts b/types/deployment_stage_interface.ts new file mode 100644 index 000000000..a7213da37 --- /dev/null +++ b/types/deployment_stage_interface.ts @@ -0,0 +1,3 @@ +export interface DeploymentStageInterface { + deploy(web3: any): Promise; +} \ No newline at end of file diff --git a/utils/array.ts b/utils/array.ts new file mode 100644 index 000000000..71a835cad --- /dev/null +++ b/utils/array.ts @@ -0,0 +1,6 @@ +export async function asyncForEach(array, callback) { + for (let index = 0; index < array.length; index++) { + await callback(array[index], index, array); + } +} + diff --git a/utils/contracts.ts b/utils/contracts.ts index fbcc90d80..562105d97 100644 --- a/utils/contracts.ts +++ b/utils/contracts.ts @@ -8,7 +8,9 @@ import { ConstantAuctionPriceCurveContract } from '../types/generated/constant_a import { CoreContract } from '../types/generated/core'; import { CoreMockContract } from '../types/generated/core_mock'; import { ERC20DetailedContract } from '../types/generated/erc20_detailed'; +import { ERC20WrapperContract } from '../types/generated/erc20_wrapper'; import { ERC20WrapperMockContract } from '../types/generated/erc20_wrapper_mock'; +import { ExchangeIssueLibraryContract } from '../types/generated/exchange_issue_library'; import { ExchangeIssueModuleContract } from '../types/generated/exchange_issue_module'; import { FeedFactoryContract } from '../types/generated/feed_factory'; import { IAuctionPriceCurveContract } from '../types/generated/i_auction_price_curve'; @@ -27,6 +29,11 @@ import { RebalancingSetTokenFactoryContract } from '../types/generated/rebalanci import { RebalancingTokenIssuanceModuleContract } from '../types/generated/rebalancing_token_issuance_module'; import { SetTokenContract } from '../types/generated/set_token'; import { SetTokenFactoryContract } from '../types/generated/set_token_factory'; +import { StandardProposeLibraryContract } from '../types/generated/standard_propose_library'; +import { StandardStartRebalanceLibraryContract } from '../types/generated/standard_start_rebalance_library'; +import { StandardPlaceBidLibraryContract } from '../types/generated/standard_place_bid_library'; +import { StandardSettleRebalanceLibraryContract } from '../types/generated/standard_settle_rebalance_library'; +import { StandardFailAuctionLibraryContract } from '../types/generated/standard_fail_auction_library'; import { StandardTokenMockContract } from '../types/generated/standard_token_mock'; import { StandardTokenWithFeeMockContract } from '../types/generated/standard_token_with_fee_mock'; import { TimeLockUpgradeMockContract } from '../types/generated/time_lock_upgrade_mock'; @@ -49,8 +56,10 @@ export { CoreContract, CoreMockContract, ConstantAuctionPriceCurveContract, + ERC20WrapperContract, ERC20DetailedContract, ERC20WrapperMockContract, + ExchangeIssueLibraryContract, ExchangeIssueModuleContract, FeedFactoryContract, IAuctionPriceCurveContract, @@ -69,6 +78,11 @@ export { RebalancingTokenIssuanceModuleContract, SetTokenContract, SetTokenFactoryContract, + StandardProposeLibraryContract, + StandardStartRebalanceLibraryContract, + StandardPlaceBidLibraryContract, + StandardSettleRebalanceLibraryContract, + StandardFailAuctionLibraryContract, StandardTokenMockContract, StandardTokenWithFeeMockContract, TimeLockUpgradeContract, From a53d6cfc251836b4468c47c049399aaa265c1fa0 Mon Sep 17 00:00:00 2001 From: Kerman Kohli Date: Thu, 21 Feb 2019 11:00:01 -0800 Subject: [PATCH 2/2] Update gitignore and readme --- .gitignore | 2 +- deployments/README.md | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 569f89103..f6c05d6a9 100644 --- a/.gitignore +++ b/.gitignore @@ -31,4 +31,4 @@ yarn-error.log* blockchain/ # Ouputs -outputs.ts \ No newline at end of file +deployments/outputs.ts \ No newline at end of file diff --git a/deployments/README.md b/deployments/README.md index 69b41d97f..bb03644d0 100644 --- a/deployments/README.md +++ b/deployments/README.md @@ -7,9 +7,11 @@ Before getting started, make sure you have the following `env` variables set: DEPLOYMENT_PRIVATE_KEY="0xA..." // The network name set here will use constants from `network-constants.ts` +// Examples of networks include `main`, `kovan`, `development` DEPLOYMENT_NETWORK_NAME="development" -// Network id will indicate which chain to run on (testnet, mainnet etc) +// Network id will indicate which chain to run on +// Main-net = 1, Kovan = 42, Test_RPC = 50 DEPLOYMENT_NETWORK_ID=50; ```