From 04763c3766d5537944bf07c6019fbe7f46c22f83 Mon Sep 17 00:00:00 2001 From: Mudit Gupta Date: Tue, 27 Nov 2018 11:46:00 +0530 Subject: [PATCH 01/30] Added multiple stable coins support --- contracts/modules/STO/ISTO.sol | 4 +- contracts/modules/STO/USDTieredSTO.sol | 98 +++++++++++++++----------- 2 files changed, 58 insertions(+), 44 deletions(-) diff --git a/contracts/modules/STO/ISTO.sol b/contracts/modules/STO/ISTO.sol index 6ed1d5ba1..6f61e6d5b 100644 --- a/contracts/modules/STO/ISTO.sol +++ b/contracts/modules/STO/ISTO.sol @@ -11,7 +11,7 @@ import "openzeppelin-solidity/contracts/math/SafeMath.sol"; contract ISTO is Module, Pausable { using SafeMath for uint256; - enum FundRaiseType { ETH, POLY, DAI } + enum FundRaiseType { ETH, POLY, SC } mapping (uint8 => bool) public fundRaiseTypes; mapping (uint8 => uint256) public fundsRaised; @@ -76,7 +76,7 @@ contract ISTO is Module, Pausable { require(_fundRaiseTypes.length > 0, "Raise type is not specified"); fundRaiseTypes[uint8(FundRaiseType.ETH)] = false; fundRaiseTypes[uint8(FundRaiseType.POLY)] = false; - fundRaiseTypes[uint8(FundRaiseType.DAI)] = false; + fundRaiseTypes[uint8(FundRaiseType.SC)] = false; for (uint8 j = 0; j < _fundRaiseTypes.length; j++) { fundRaiseTypes[uint8(_fundRaiseTypes[j])] = true; } diff --git a/contracts/modules/STO/USDTieredSTO.sol b/contracts/modules/STO/USDTieredSTO.sol index baba70472..984cfe362 100644 --- a/contracts/modules/STO/USDTieredSTO.sol +++ b/contracts/modules/STO/USDTieredSTO.sol @@ -44,20 +44,21 @@ contract USDTieredSTO is ISTO, ReentrancyGuard { string public ETH_ORACLE = "EthUsdOracle"; mapping (bytes32 => mapping (bytes32 => string)) oracleKeys; - IERC20 public usdToken; - // Determine whether users can invest on behalf of a beneficiary bool public allowBeneficialInvestments = false; // Whether or not the STO has been finalized bool public isFinalized; - // Address where ETH, POLY & DAI funds are delivered + // Address where ETH, POLY & Stable Coin funds are delivered address public wallet; // Address of issuer reserve wallet for unsold tokens address public reserveWallet; + // List of stable coin addresses + address[] public usdTokens; + // Current tier uint256 public currentTier; @@ -73,6 +74,9 @@ contract USDTieredSTO is ISTO, ReentrancyGuard { // List of accredited investors mapping (address => bool) public accredited; + // List of active stable coin addresses + mapping (address => bool) public usdTokenEnabled; + // Default limit in USD for non-accredited investors multiplied by 10**18 uint256 public nonAccreditedLimitUSD; @@ -116,7 +120,7 @@ contract USDTieredSTO is ISTO, ReentrancyGuard { event SetAddresses( address indexed _wallet, address indexed _reserveWallet, - address indexed _usdToken + address[] _usdTokens ); event SetLimits( uint256 _nonAccreditedLimitUSD, @@ -149,8 +153,13 @@ contract USDTieredSTO is ISTO, ReentrancyGuard { _; } - modifier validDAI { - require(fundRaiseTypes[uint8(FundRaiseType.DAI)], "DAI not allowed"); + modifier validSC { + require(fundRaiseTypes[uint8(FundRaiseType.SC)], "Stable coins not allowed"); + _; + } + + modifier validUSDToken(address _usdToken) { + require(usdTokenEnabled[_usdToken], "Invalid USD token"); _; } @@ -176,7 +185,7 @@ contract USDTieredSTO is ISTO, ReentrancyGuard { * @param _fundRaiseTypes Types of currency used to collect the funds * @param _wallet Ethereum account address to hold the funds * @param _reserveWallet Ethereum account address to receive unsold tokens - * @param _usdToken Contract address of the stable coin + * @param _usdTokens Array of contract addressess of the stable coins */ function configure( uint256 _startTime, @@ -190,14 +199,14 @@ contract USDTieredSTO is ISTO, ReentrancyGuard { FundRaiseType[] _fundRaiseTypes, address _wallet, address _reserveWallet, - address _usdToken + address[] _usdTokens ) public onlyFactory { require(endTime == 0, "Already configured"); _modifyTimes(_startTime, _endTime); _modifyTiers(_ratePerTier, _ratePerTierDiscountPoly, _tokensPerTierTotal, _tokensPerTierDiscountPoly); // NB - _setFundRaiseType must come before modifyAddresses _setFundRaiseType(_fundRaiseTypes); - _modifyAddresses(_wallet, _reserveWallet, _usdToken); + _modifyAddresses(_wallet, _reserveWallet, _usdTokens); _modifyLimits(_nonAccreditedLimitUSD, _minimumInvestmentUSD); } @@ -253,7 +262,7 @@ contract USDTieredSTO is ISTO, ReentrancyGuard { uint256 _endTime ) external onlyOwner { /*solium-disable-next-line security/no-block-members*/ - require(now < startTime, "STO already started"); + //require(now < startTime, "STO already started"); _modifyTimes(_startTime, _endTime); } @@ -261,16 +270,16 @@ contract USDTieredSTO is ISTO, ReentrancyGuard { * @dev Modifies addresses used as wallet, reserve wallet and usd token * @param _wallet Address of wallet where funds are sent * @param _reserveWallet Address of wallet where unsold tokens are sent - * @param _usdToken Address of usd token (DAI) + * @param _usdTokens Address of usd tokens */ function modifyAddresses( address _wallet, address _reserveWallet, - address _usdToken + address[] _usdTokens ) external onlyOwner { /*solium-disable-next-line security/no-block-members*/ require(now < startTime, "STO already started"); - _modifyAddresses(_wallet, _reserveWallet, _usdToken); + _modifyAddresses(_wallet, _reserveWallet, _usdTokens); } function _modifyLimits( @@ -319,16 +328,19 @@ contract USDTieredSTO is ISTO, ReentrancyGuard { function _modifyAddresses( address _wallet, address _reserveWallet, - address _usdToken + address[] _usdTokens ) internal { require(_wallet != address(0) && _reserveWallet != address(0), "Invalid wallet"); - if (fundRaiseTypes[uint8(FundRaiseType.DAI)]) { - require(_usdToken != address(0), "Invalid usdToken"); - } wallet = _wallet; reserveWallet = _reserveWallet; - usdToken = IERC20(_usdToken); - emit SetAddresses(_wallet, _reserveWallet, _usdToken); + for(uint256 i = 0; i < usdTokens.length; i++) { + usdTokenEnabled[usdTokens[i]] = false; + } + usdTokens = _usdTokens; + for(i = 0; i < _usdTokens.length; i++) { + usdTokenEnabled[_usdTokens[i]] = true; + } + emit SetAddresses(_wallet, _reserveWallet, _usdTokens); } //////////////////// @@ -417,8 +429,8 @@ contract USDTieredSTO is ISTO, ReentrancyGuard { buyWithPOLYRateLimited(_beneficiary, _investedPOLY, 0); } - function buyWithUSD(address _beneficiary, uint256 _investedDAI) external { - buyWithUSDRateLimited(_beneficiary, _investedDAI, 0); + function buyWithUSD(address _beneficiary, uint256 _investedSC, IERC20 _usdToken) external { + buyWithUSDRateLimited(_beneficiary, _investedSC, 0, _usdToken); } /** @@ -448,21 +460,24 @@ contract USDTieredSTO is ISTO, ReentrancyGuard { * @param _minTokens Minumum number of tokens to buy or else revert */ function buyWithPOLYRateLimited(address _beneficiary, uint256 _investedPOLY, uint256 _minTokens) public validPOLY { - _buyWithTokens(_beneficiary, _investedPOLY, FundRaiseType.POLY, _minTokens); + _buyWithTokens(_beneficiary, _investedPOLY, FundRaiseType.POLY, _minTokens, polyToken); } /** - * @notice Purchase tokens using DAI + * @notice Purchase tokens using Stable coins * @param _beneficiary Address where security tokens will be sent - * @param _investedDAI Amount of DAI invested + * @param _investedSC Amount of Stable coins invested * @param _minTokens Minumum number of tokens to buy or else revert + * @param _usdToken Address of USD stable coin to buy tokens with */ - function buyWithUSDRateLimited(address _beneficiary, uint256 _investedDAI, uint256 _minTokens) public validDAI { - _buyWithTokens(_beneficiary, _investedDAI, FundRaiseType.DAI, _minTokens); + function buyWithUSDRateLimited(address _beneficiary, uint256 _investedSC, uint256 _minTokens, IERC20 _usdToken) + public validSC validUSDToken(_usdToken) + { + _buyWithTokens(_beneficiary, _investedSC, FundRaiseType.SC, _minTokens, _usdToken); } - function _buyWithTokens(address _beneficiary, uint256 _tokenAmount, FundRaiseType _fundRaiseType, uint256 _minTokens) internal { - require(_fundRaiseType == FundRaiseType.POLY || _fundRaiseType == FundRaiseType.DAI, "Invalid raise type"); + function _buyWithTokens(address _beneficiary, uint256 _tokenAmount, FundRaiseType _fundRaiseType, uint256 _minTokens, IERC20 _token) internal { + require(_fundRaiseType == FundRaiseType.POLY || _fundRaiseType == FundRaiseType.SC, "Invalid raise type"); uint256 initialMinted = getTokensMinted(); uint256 rate = getRate(_fundRaiseType); (uint256 spentUSD, uint256 spentValue) = _buyTokens(_beneficiary, _tokenAmount, rate, _fundRaiseType); @@ -470,17 +485,16 @@ contract USDTieredSTO is ISTO, ReentrancyGuard { // Modify storage investorInvested[_beneficiary][uint8(_fundRaiseType)] = investorInvested[_beneficiary][uint8(_fundRaiseType)].add(spentValue); fundsRaised[uint8(_fundRaiseType)] = fundsRaised[uint8(_fundRaiseType)].add(spentValue); - // Forward DAI to issuer wallet - IERC20 token = _fundRaiseType == FundRaiseType.POLY ? polyToken : usdToken; - require(token.transferFrom(msg.sender, wallet, spentValue), "Transfer failed"); + // Forward coins to issuer wallet + require(_token.transferFrom(msg.sender, wallet, spentValue), "Transfer failed"); emit FundsReceived(msg.sender, _beneficiary, spentUSD, _fundRaiseType, _tokenAmount, spentValue, rate); } /** * @notice Low level token purchase * @param _beneficiary Address where security tokens will be sent - * @param _investmentValue Amount of POLY, ETH or DAI invested - * @param _fundRaiseType Fund raise type (POLY, ETH, DAI) + * @param _investmentValue Amount of POLY, ETH or Stable coins invested + * @param _fundRaiseType Fund raise type (POLY, ETH, SC) */ function _buyTokens( address _beneficiary, @@ -530,8 +544,8 @@ contract USDTieredSTO is ISTO, ReentrancyGuard { /** * @notice Getter function for buyer to calculate how many tokens will they get * @param _beneficiary Address where security tokens are to be sent - * @param _investmentValue Amount of POLY, ETH or DAI invested - * @param _fundRaiseType Fund raise type (POLY, ETH, DAI) + * @param _investmentValue Amount of POLY, ETH or Stable coins invested + * @param _fundRaiseType Fund raise type (POLY, ETH, SC) */ function buyTokensView( address _beneficiary, @@ -542,7 +556,7 @@ contract USDTieredSTO is ISTO, ReentrancyGuard { view returns(uint256 spentUSD, uint256 spentValue, uint256 tokensMinted) { - require(_fundRaiseType == FundRaiseType.POLY || _fundRaiseType == FundRaiseType.DAI || _fundRaiseType == FundRaiseType.ETH, "Invalid raise type"); + require(_fundRaiseType == FundRaiseType.POLY || _fundRaiseType == FundRaiseType.SC || _fundRaiseType == FundRaiseType.ETH, "Invalid raise type"); uint256 rate = getRate(_fundRaiseType); uint256 originalUSD = DecimalMath.mul(rate, _investmentValue); uint256 allowedUSD = _buyTokensChecks(_beneficiary, _investmentValue, originalUSD); @@ -749,7 +763,7 @@ contract USDTieredSTO is ISTO, ReentrancyGuard { return IOracle(_getOracle(bytes32("ETH"), bytes32("USD"))).getPrice(); } else if (_fundRaiseType == FundRaiseType.POLY) { return IOracle(_getOracle(bytes32("POLY"), bytes32("USD"))).getPrice(); - } else if (_fundRaiseType == FundRaiseType.DAI) { + } else if (_fundRaiseType == FundRaiseType.SC) { return 1 * 10**18; } else { revert("Incorrect funding"); @@ -803,7 +817,7 @@ contract USDTieredSTO is ISTO, ReentrancyGuard { /** * @notice Return the total no. of tokens sold for the given fund raise type - * param _fundRaiseType The fund raising currency (e.g. ETH, POLY, DAI) to calculate sold tokens for + * param _fundRaiseType The fund raising currency (e.g. ETH, POLY, SC) to calculate sold tokens for * @return uint256 Total number of tokens sold for ETH */ function getTokensSoldFor(FundRaiseType _fundRaiseType) public view returns (uint256) { @@ -824,7 +838,7 @@ contract USDTieredSTO is ISTO, ReentrancyGuard { uint256[] memory tokensMinted = new uint256[](3); tokensMinted[0] = tiers[_tier].minted[uint8(FundRaiseType.ETH)]; tokensMinted[1] = tiers[_tier].minted[uint8(FundRaiseType.POLY)]; - tokensMinted[2] = tiers[_tier].minted[uint8(FundRaiseType.DAI)]; + tokensMinted[2] = tiers[_tier].minted[uint8(FundRaiseType.SC)]; return tokensMinted; } @@ -838,7 +852,7 @@ contract USDTieredSTO is ISTO, ReentrancyGuard { uint256 tokensSold; tokensSold = tokensSold.add(tiers[_tier].minted[uint8(FundRaiseType.ETH)]); tokensSold = tokensSold.add(tiers[_tier].minted[uint8(FundRaiseType.POLY)]); - tokensSold = tokensSold.add(tiers[_tier].minted[uint8(FundRaiseType.DAI)]); + tokensSold = tokensSold.add(tiers[_tier].minted[uint8(FundRaiseType.SC)]); return tokensSold; } @@ -868,7 +882,7 @@ contract USDTieredSTO is ISTO, ReentrancyGuard { * @return Amount of funds raised * @return Number of individual investors this STO have. * @return Amount of tokens sold. - * @return Array of bools to show if funding is allowed in ETH, POLY, DAI respectively + * @return Array of bools to show if funding is allowed in ETH, POLY, SC respectively */ function getSTODetails() public view returns(uint256, uint256, uint256, uint256[], uint256[], uint256, uint256, uint256, bool[]) { uint256[] memory cap = new uint256[](tiers.length); @@ -880,7 +894,7 @@ contract USDTieredSTO is ISTO, ReentrancyGuard { bool[] memory _fundRaiseTypes = new bool[](3); _fundRaiseTypes[0] = fundRaiseTypes[uint8(FundRaiseType.ETH)]; _fundRaiseTypes[1] = fundRaiseTypes[uint8(FundRaiseType.POLY)]; - _fundRaiseTypes[2] = fundRaiseTypes[uint8(FundRaiseType.DAI)]; + _fundRaiseTypes[2] = fundRaiseTypes[uint8(FundRaiseType.SC)]; return ( startTime, endTime, From 7a82d720f4ede6cb24dba4a35879a22143424d5a Mon Sep 17 00:00:00 2001 From: Mudit Gupta Date: Tue, 27 Nov 2018 11:46:16 +0530 Subject: [PATCH 02/30] Updated tests --- test/p_usd_tiered_sto.js | 34 +++++++++++++++++----------------- test/q_usd_tiered_sto_sim.js | 2 +- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/test/p_usd_tiered_sto.js b/test/p_usd_tiered_sto.js index 604eb1aaf..b25791dfb 100644 --- a/test/p_usd_tiered_sto.js +++ b/test/p_usd_tiered_sto.js @@ -315,7 +315,7 @@ contract("USDTieredSTO", accounts => { _fundRaiseTypes[stoId], _wallet[stoId], _reserveWallet[stoId], - _usdToken[stoId] + [_usdToken[stoId]] ]; let bytesSTO = web3.eth.abi.encodeFunctionCall(functionSignature, config); @@ -388,7 +388,7 @@ contract("USDTieredSTO", accounts => { _fundRaiseTypes[stoId], _wallet[stoId], _reserveWallet[stoId], - _usdToken[stoId] + [_usdToken[stoId]] ]; let bytesSTO = web3.eth.abi.encodeFunctionCall(functionSignature, config); @@ -412,7 +412,7 @@ contract("USDTieredSTO", accounts => { _fundRaiseTypes[stoId], _wallet[stoId], _reserveWallet[stoId], - _usdToken[stoId] + [_usdToken[stoId]] ]; let bytesSTO = web3.eth.abi.encodeFunctionCall(functionSignature, config); @@ -500,7 +500,7 @@ contract("USDTieredSTO", accounts => { _fundRaiseTypes[stoId], _wallet[stoId], _reserveWallet[stoId], - _usdToken[stoId] + [_usdToken[stoId]] ]; let bytesSTO = web3.eth.abi.encodeFunctionCall(functionSignature, config); @@ -587,7 +587,7 @@ contract("USDTieredSTO", accounts => { _fundRaiseTypes[stoId], _wallet[stoId], _reserveWallet[stoId], - _usdToken[stoId] + [_usdToken[stoId]] ]; let bytesSTO = web3.eth.abi.encodeFunctionCall(functionSignature, config); @@ -626,7 +626,7 @@ contract("USDTieredSTO", accounts => { _fundRaiseTypes[stoId], _wallet[stoId], _reserveWallet[stoId], - _usdToken[stoId] + [_usdToken[stoId]] ]; let bytesSTO = web3.eth.abi.encodeFunctionCall(functionSignature, config); @@ -665,7 +665,7 @@ contract("USDTieredSTO", accounts => { _fundRaiseTypes[stoId], _wallet[stoId], _reserveWallet[stoId], - _usdToken[stoId] + [_usdToken[stoId]] ]; let bytesSTO = web3.eth.abi.encodeFunctionCall(functionSignature, config); @@ -696,7 +696,7 @@ contract("USDTieredSTO", accounts => { _fundRaiseTypes[stoId], _wallet[stoId], _reserveWallet[stoId], - _usdToken[stoId] + [_usdToken[stoId]] ], [ _startTime[stoId], @@ -710,7 +710,7 @@ contract("USDTieredSTO", accounts => { _fundRaiseTypes[stoId], _wallet[stoId], _reserveWallet[stoId], - _usdToken[stoId] + [_usdToken[stoId]] ], [ _startTime[stoId], @@ -724,7 +724,7 @@ contract("USDTieredSTO", accounts => { _fundRaiseTypes[stoId], _wallet[stoId], _reserveWallet[stoId], - _usdToken[stoId] + [_usdToken[stoId]] ], [ _startTime[stoId], @@ -738,7 +738,7 @@ contract("USDTieredSTO", accounts => { _fundRaiseTypes[stoId], _wallet[stoId], _reserveWallet[stoId], - _usdToken[stoId] + [_usdToken[stoId]] ] ]; for (var i = 0; i < config.length; i++) { @@ -764,7 +764,7 @@ contract("USDTieredSTO", accounts => { _fundRaiseTypes[stoId], _wallet[stoId], _reserveWallet[stoId], - _usdToken[stoId] + [_usdToken[stoId]] ]; let bytesSTO = web3.eth.abi.encodeFunctionCall(functionSignature, config); @@ -787,7 +787,7 @@ contract("USDTieredSTO", accounts => { _fundRaiseTypes[stoId], wallet, _reserveWallet[stoId], - _usdToken[stoId] + [_usdToken[stoId]] ]; let bytesSTO = web3.eth.abi.encodeFunctionCall(functionSignature, config); @@ -833,7 +833,7 @@ contract("USDTieredSTO", accounts => { _fundRaiseTypes[stoId], _wallet[stoId], _reserveWallet[stoId], - _usdToken[stoId] + [_usdToken[stoId]] ]; let bytesSTO = web3.eth.abi.encodeFunctionCall(functionSignature, config); @@ -857,7 +857,7 @@ contract("USDTieredSTO", accounts => { _fundRaiseTypes[stoId], _wallet[stoId], _reserveWallet[stoId], - _usdToken[stoId] + [_usdToken[stoId]] ]; let bytesSTO = web3.eth.abi.encodeFunctionCall(functionSignature, config); @@ -934,7 +934,7 @@ contract("USDTieredSTO", accounts => { await I_USDTieredSTO_Array[stoId].modifyAddresses( "0x0000000000000000000000000400000000000000", "0x0000000000000000000003000000000000000000", - address_zero, + [address_zero], { from: ISSUER } ); assert.equal( @@ -985,7 +985,7 @@ contract("USDTieredSTO", accounts => { I_USDTieredSTO_Array[stoId].modifyAddresses( "0x0000000000000000000000000400000000000000", "0x0000000000000000000003000000000000000000", - I_DaiToken.address, + [I_DaiToken.address], { from: ISSUER } ) ); diff --git a/test/q_usd_tiered_sto_sim.js b/test/q_usd_tiered_sto_sim.js index 55f84f2b3..9a5ee963a 100644 --- a/test/q_usd_tiered_sto_sim.js +++ b/test/q_usd_tiered_sto_sim.js @@ -285,7 +285,7 @@ contract("USDTieredSTO Sim", accounts => { _fundRaiseTypes[stoId], _wallet[stoId], _reserveWallet[stoId], - _usdToken[stoId] + [_usdToken[stoId]] ]; let bytesSTO = web3.eth.abi.encodeFunctionCall(functionSignature, config); From 1cb134658aaaacb5b2c5d3b2df74a831c1cb51df Mon Sep 17 00:00:00 2001 From: Mudit Gupta Date: Tue, 27 Nov 2018 12:04:59 +0530 Subject: [PATCH 03/30] Updated function signature and selector --- contracts/modules/STO/USDTieredSTO.sol | 2 +- test/p_usd_tiered_sto.js | 44 +++++++++++++------------- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/contracts/modules/STO/USDTieredSTO.sol b/contracts/modules/STO/USDTieredSTO.sol index 984cfe362..fde74b55d 100644 --- a/contracts/modules/STO/USDTieredSTO.sol +++ b/contracts/modules/STO/USDTieredSTO.sol @@ -913,7 +913,7 @@ contract USDTieredSTO is ISTO, ReentrancyGuard { * @return bytes4 Configure function signature */ function getInitFunction() public pure returns (bytes4) { - return 0xb0ff041e; + return 0xeac2f9e4; } function _getOracle(bytes32 _currency, bytes32 _denominatedCurrency) internal view returns (address) { diff --git a/test/p_usd_tiered_sto.js b/test/p_usd_tiered_sto.js index b25791dfb..d4308ea9f 100644 --- a/test/p_usd_tiered_sto.js +++ b/test/p_usd_tiered_sto.js @@ -157,8 +157,8 @@ contract("USDTieredSTO", accounts => { name: "_reserveWallet" }, { - type: "address", - name: "_usdToken" + type: "address[]", + name: "_usdTokens" } ] }; @@ -301,7 +301,7 @@ contract("USDTieredSTO", accounts => { _fundRaiseTypes.push([0, 1, 2]); _wallet.push(WALLET); _reserveWallet.push(RESERVEWALLET); - _usdToken.push(I_DaiToken.address); + _usdToken.push([I_DaiToken.address]); let config = [ _startTime[stoId], @@ -315,7 +315,7 @@ contract("USDTieredSTO", accounts => { _fundRaiseTypes[stoId], _wallet[stoId], _reserveWallet[stoId], - [_usdToken[stoId]] + _usdToken[stoId] ]; let bytesSTO = web3.eth.abi.encodeFunctionCall(functionSignature, config); @@ -388,7 +388,7 @@ contract("USDTieredSTO", accounts => { _fundRaiseTypes[stoId], _wallet[stoId], _reserveWallet[stoId], - [_usdToken[stoId]] + _usdToken[stoId] ]; let bytesSTO = web3.eth.abi.encodeFunctionCall(functionSignature, config); @@ -412,7 +412,7 @@ contract("USDTieredSTO", accounts => { _fundRaiseTypes[stoId], _wallet[stoId], _reserveWallet[stoId], - [_usdToken[stoId]] + _usdToken[stoId] ]; let bytesSTO = web3.eth.abi.encodeFunctionCall(functionSignature, config); @@ -486,7 +486,7 @@ contract("USDTieredSTO", accounts => { _fundRaiseTypes.push([0, 1, 2]); _wallet.push(WALLET); _reserveWallet.push(RESERVEWALLET); - _usdToken.push(I_DaiToken.address); + _usdToken.push([I_DaiToken.address]); let config = [ _startTime[stoId], @@ -500,7 +500,7 @@ contract("USDTieredSTO", accounts => { _fundRaiseTypes[stoId], _wallet[stoId], _reserveWallet[stoId], - [_usdToken[stoId]] + _usdToken[stoId] ]; let bytesSTO = web3.eth.abi.encodeFunctionCall(functionSignature, config); @@ -573,7 +573,7 @@ contract("USDTieredSTO", accounts => { _fundRaiseTypes.push([0, 1, 2]); _wallet.push(WALLET); _reserveWallet.push(RESERVEWALLET); - _usdToken.push(I_DaiToken.address); + _usdToken.push([I_DaiToken.address]); let config = [ _startTime[stoId], @@ -587,7 +587,7 @@ contract("USDTieredSTO", accounts => { _fundRaiseTypes[stoId], _wallet[stoId], _reserveWallet[stoId], - [_usdToken[stoId]] + _usdToken[stoId] ]; let bytesSTO = web3.eth.abi.encodeFunctionCall(functionSignature, config); @@ -612,7 +612,7 @@ contract("USDTieredSTO", accounts => { _fundRaiseTypes.push([0, 1, 2]); _wallet.push(WALLET); _reserveWallet.push(RESERVEWALLET); - _usdToken.push(I_DaiToken.address); + _usdToken.push([I_DaiToken.address]); let config = [ _startTime[stoId], @@ -626,7 +626,7 @@ contract("USDTieredSTO", accounts => { _fundRaiseTypes[stoId], _wallet[stoId], _reserveWallet[stoId], - [_usdToken[stoId]] + _usdToken[stoId] ]; let bytesSTO = web3.eth.abi.encodeFunctionCall(functionSignature, config); @@ -651,7 +651,7 @@ contract("USDTieredSTO", accounts => { _fundRaiseTypes.push([0, 1, 2]); _wallet.push(WALLET); _reserveWallet.push(RESERVEWALLET); - _usdToken.push(I_DaiToken.address); + _usdToken.push([I_DaiToken.address]); let config = [ _startTime[stoId], @@ -665,7 +665,7 @@ contract("USDTieredSTO", accounts => { _fundRaiseTypes[stoId], _wallet[stoId], _reserveWallet[stoId], - [_usdToken[stoId]] + _usdToken[stoId] ]; let bytesSTO = web3.eth.abi.encodeFunctionCall(functionSignature, config); @@ -696,7 +696,7 @@ contract("USDTieredSTO", accounts => { _fundRaiseTypes[stoId], _wallet[stoId], _reserveWallet[stoId], - [_usdToken[stoId]] + _usdToken[stoId] ], [ _startTime[stoId], @@ -710,7 +710,7 @@ contract("USDTieredSTO", accounts => { _fundRaiseTypes[stoId], _wallet[stoId], _reserveWallet[stoId], - [_usdToken[stoId]] + _usdToken[stoId] ], [ _startTime[stoId], @@ -724,7 +724,7 @@ contract("USDTieredSTO", accounts => { _fundRaiseTypes[stoId], _wallet[stoId], _reserveWallet[stoId], - [_usdToken[stoId]] + _usdToken[stoId] ], [ _startTime[stoId], @@ -738,7 +738,7 @@ contract("USDTieredSTO", accounts => { _fundRaiseTypes[stoId], _wallet[stoId], _reserveWallet[stoId], - [_usdToken[stoId]] + _usdToken[stoId] ] ]; for (var i = 0; i < config.length; i++) { @@ -764,7 +764,7 @@ contract("USDTieredSTO", accounts => { _fundRaiseTypes[stoId], _wallet[stoId], _reserveWallet[stoId], - [_usdToken[stoId]] + _usdToken[stoId] ]; let bytesSTO = web3.eth.abi.encodeFunctionCall(functionSignature, config); @@ -787,7 +787,7 @@ contract("USDTieredSTO", accounts => { _fundRaiseTypes[stoId], wallet, _reserveWallet[stoId], - [_usdToken[stoId]] + _usdToken[stoId] ]; let bytesSTO = web3.eth.abi.encodeFunctionCall(functionSignature, config); @@ -833,7 +833,7 @@ contract("USDTieredSTO", accounts => { _fundRaiseTypes[stoId], _wallet[stoId], _reserveWallet[stoId], - [_usdToken[stoId]] + _usdToken[stoId] ]; let bytesSTO = web3.eth.abi.encodeFunctionCall(functionSignature, config); @@ -857,7 +857,7 @@ contract("USDTieredSTO", accounts => { _fundRaiseTypes[stoId], _wallet[stoId], _reserveWallet[stoId], - [_usdToken[stoId]] + _usdToken[stoId] ]; let bytesSTO = web3.eth.abi.encodeFunctionCall(functionSignature, config); From 3270166ce991566b49e91d37adbbedb499f65803 Mon Sep 17 00:00:00 2001 From: Mudit Gupta Date: Tue, 27 Nov 2018 12:27:55 +0530 Subject: [PATCH 04/30] tests fixed --- test/p_usd_tiered_sto.js | 45 ++++++++++++++++++++-------------------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/test/p_usd_tiered_sto.js b/test/p_usd_tiered_sto.js index d4308ea9f..1853c645b 100644 --- a/test/p_usd_tiered_sto.js +++ b/test/p_usd_tiered_sto.js @@ -365,7 +365,7 @@ contract("USDTieredSTO", accounts => { _reserveWallet[stoId], "Incorrect _reserveWallet in config" ); - assert.equal(await I_USDTieredSTO_Array[stoId].usdToken.call(), _usdToken[stoId], "Incorrect _usdToken in config"); + assert.equal(await I_USDTieredSTO_Array[stoId].usdTokens.call(0), _usdToken[stoId][0], "Incorrect _usdToken in config"); assert.equal( await I_USDTieredSTO_Array[stoId].getNumberOfTiers(), _tokensPerTierTotal[stoId].length, @@ -550,7 +550,7 @@ contract("USDTieredSTO", accounts => { _reserveWallet[stoId], "Incorrect _reserveWallet in config" ); - assert.equal(await I_USDTieredSTO_Array[stoId].usdToken.call(), _usdToken[stoId], "Incorrect _usdToken in config"); + assert.equal(await I_USDTieredSTO_Array[stoId].usdTokens.call(0), _usdToken[stoId][0], "Incorrect _usdToken in config"); assert.equal( await I_USDTieredSTO_Array[stoId].getNumberOfTiers(), _tokensPerTierTotal[stoId].length, @@ -809,7 +809,8 @@ contract("USDTieredSTO", accounts => { _minimumInvestmentUSD[stoId], _fundRaiseTypes[stoId], _wallet[stoId], - reserveWallet + reserveWallet, + _usdToken[stoId] ]; let bytesSTO = web3.eth.abi.encodeFunctionCall(functionSignature, config); @@ -948,7 +949,7 @@ contract("USDTieredSTO", accounts => { "STO Configuration doesn't set as expected" ); assert.equal( - await I_USDTieredSTO_Array[stoId].usdToken.call(), + await I_USDTieredSTO_Array[stoId].usdTokens.call(0), address_zero, "STO Configuration doesn't set as expected" ); @@ -1033,13 +1034,13 @@ contract("USDTieredSTO", accounts => { // NONACCREDITED POLY await catchRevert(I_USDTieredSTO_Array[stoId].buyWithPOLY(NONACCREDITED1, investment_POLY, { from: NONACCREDITED1 })); // NONACCREDITED DAI - await catchRevert(I_USDTieredSTO_Array[stoId].buyWithUSD(NONACCREDITED1, investment_DAI, { from: NONACCREDITED1 })); + await catchRevert(I_USDTieredSTO_Array[stoId].buyWithUSD(NONACCREDITED1, investment_DAI, I_DaiToken.address, { from: NONACCREDITED1 })); // ACCREDITED ETH await catchRevert(I_USDTieredSTO_Array[stoId].buyWithETH(ACCREDITED1, { from: ACCREDITED1, value: investment_ETH })); // ACCREDITED POLY await catchRevert(I_USDTieredSTO_Array[stoId].buyWithPOLY(ACCREDITED1, investment_POLY, { from: ACCREDITED1 })); // ACCREDITED DAI - await catchRevert(I_USDTieredSTO_Array[stoId].buyWithUSD(ACCREDITED1, investment_DAI, { from: ACCREDITED1 })); + await catchRevert(I_USDTieredSTO_Array[stoId].buyWithUSD(ACCREDITED1, investment_DAI, I_DaiToken.address, { from: ACCREDITED1 })); await revertToSnapshot(snapId); }); @@ -1082,7 +1083,7 @@ contract("USDTieredSTO", accounts => { await catchRevert(I_USDTieredSTO_Array[stoId].buyWithPOLY(NONACCREDITED1, investment_POLY, { from: NONACCREDITED1 })); // NONACCREDITED DAI - await catchRevert(I_USDTieredSTO_Array[stoId].buyWithUSD(NONACCREDITED1, investment_DAI, { from: NONACCREDITED1 })); + await catchRevert(I_USDTieredSTO_Array[stoId].buyWithUSD(NONACCREDITED1, investment_DAI, I_DaiToken.address, { from: NONACCREDITED1 })); // ACCREDITED ETH await catchRevert(I_USDTieredSTO_Array[stoId].buyWithETH(ACCREDITED1, { from: ACCREDITED1, value: investment_ETH })); @@ -1091,7 +1092,7 @@ contract("USDTieredSTO", accounts => { await catchRevert(I_USDTieredSTO_Array[stoId].buyWithPOLY(ACCREDITED1, investment_POLY, { from: ACCREDITED1 })); // ACCREDITED DAI - await catchRevert(I_USDTieredSTO_Array[stoId].buyWithUSD(ACCREDITED1, investment_DAI, { from: ACCREDITED1 })); + await catchRevert(I_USDTieredSTO_Array[stoId].buyWithUSD(ACCREDITED1, investment_DAI, I_DaiToken.address, { from: ACCREDITED1 })); await revertToSnapshot(snapId); }); @@ -1138,7 +1139,7 @@ contract("USDTieredSTO", accounts => { await catchRevert(I_USDTieredSTO_Array[stoId].buyWithPOLY(NONACCREDITED1, investment_POLY, { from: NONACCREDITED1 })); // NONACCREDITED DAI - await catchRevert(I_USDTieredSTO_Array[stoId].buyWithUSD(NONACCREDITED1, investment_DAI, { from: NONACCREDITED1 })); + await catchRevert(I_USDTieredSTO_Array[stoId].buyWithUSD(NONACCREDITED1, investment_DAI, I_DaiToken.address, { from: NONACCREDITED1 })); // ACCREDITED ETH await catchRevert(I_USDTieredSTO_Array[stoId].buyWithETH(ACCREDITED1, { from: ACCREDITED1, value: investment_ETH })); @@ -1147,7 +1148,7 @@ contract("USDTieredSTO", accounts => { await catchRevert(I_USDTieredSTO_Array[stoId].buyWithPOLY(ACCREDITED1, investment_POLY, { from: ACCREDITED1 })); // ACCREDITED DAI - await catchRevert(I_USDTieredSTO_Array[stoId].buyWithUSD(ACCREDITED1, investment_DAI, { from: ACCREDITED1 })); + await catchRevert(I_USDTieredSTO_Array[stoId].buyWithUSD(ACCREDITED1, investment_DAI, I_DaiToken.address, { from: ACCREDITED1 })); await revertToSnapshot(snapId); }); @@ -1196,7 +1197,7 @@ contract("USDTieredSTO", accounts => { await catchRevert(I_USDTieredSTO_Array[stoId].buyWithPOLY(NONACCREDITED1, investment_POLY, { from: NONACCREDITED1 })); // NONACCREDITED DAI - await catchRevert(I_USDTieredSTO_Array[stoId].buyWithUSD(NONACCREDITED1, investment_DAI, { from: NONACCREDITED1 })); + await catchRevert(I_USDTieredSTO_Array[stoId].buyWithUSD(NONACCREDITED1, investment_DAI, I_DaiToken.address, { from: NONACCREDITED1 })); // ACCREDITED ETH await catchRevert(I_USDTieredSTO_Array[stoId].buyWithETH(ACCREDITED1, { from: ACCREDITED1, value: investment_ETH })); @@ -1205,7 +1206,7 @@ contract("USDTieredSTO", accounts => { await catchRevert(I_USDTieredSTO_Array[stoId].buyWithPOLY(ACCREDITED1, investment_POLY, { from: ACCREDITED1 })); // ACCREDITED DAI - await catchRevert(I_USDTieredSTO_Array[stoId].buyWithUSD(ACCREDITED1, investment_DAI, { from: ACCREDITED1 })); + await catchRevert(I_USDTieredSTO_Array[stoId].buyWithUSD(ACCREDITED1, investment_DAI, I_DaiToken.address, { from: ACCREDITED1 })); // Unpause the STO await I_USDTieredSTO_Array[stoId].unpause({ from: ISSUER }); @@ -1213,11 +1214,11 @@ contract("USDTieredSTO", accounts => { await I_USDTieredSTO_Array[stoId].buyWithETH(NONACCREDITED1, { from: NONACCREDITED1, value: investment_ETH }); await I_USDTieredSTO_Array[stoId].buyWithPOLY(NONACCREDITED1, investment_POLY, { from: NONACCREDITED1 }); - await I_USDTieredSTO_Array[stoId].buyWithUSD(NONACCREDITED1, investment_DAI, { from: NONACCREDITED1 }); + await I_USDTieredSTO_Array[stoId].buyWithUSD(NONACCREDITED1, investment_DAI, I_DaiToken.address, { from: NONACCREDITED1 }); await I_USDTieredSTO_Array[stoId].buyWithETH(ACCREDITED1, { from: ACCREDITED1, value: investment_ETH }); await I_USDTieredSTO_Array[stoId].buyWithPOLY(ACCREDITED1, investment_POLY, { from: ACCREDITED1 }); - await I_USDTieredSTO_Array[stoId].buyWithUSD(ACCREDITED1, investment_DAI, { from: ACCREDITED1 }); + await I_USDTieredSTO_Array[stoId].buyWithUSD(ACCREDITED1, investment_DAI, I_DaiToken.address, { from: ACCREDITED1 }); await revertToSnapshot(snapId); }); @@ -1263,7 +1264,7 @@ contract("USDTieredSTO", accounts => { await catchRevert(I_USDTieredSTO_Array[stoId].buyWithPOLY(NONACCREDITED1, investment_POLY, { from: NONACCREDITED1 })); // NONACCREDITED DAI - await catchRevert(I_USDTieredSTO_Array[stoId].buyWithUSD(NONACCREDITED1, investment_DAI, { from: NONACCREDITED1 })); + await catchRevert(I_USDTieredSTO_Array[stoId].buyWithUSD(NONACCREDITED1, investment_DAI, I_DaiToken.address, { from: NONACCREDITED1 })); // ACCREDITED ETH await catchRevert(I_USDTieredSTO_Array[stoId].buyWithETH(ACCREDITED1, { from: ACCREDITED1, value: investment_ETH })); @@ -1272,7 +1273,7 @@ contract("USDTieredSTO", accounts => { await catchRevert(I_USDTieredSTO_Array[stoId].buyWithPOLY(ACCREDITED1, investment_POLY, { from: ACCREDITED1 })); // ACCREDITED DAI - await catchRevert(I_USDTieredSTO_Array[stoId].buyWithUSD(ACCREDITED1, investment_DAI, { from: ACCREDITED1 })); + await catchRevert(I_USDTieredSTO_Array[stoId].buyWithUSD(ACCREDITED1, investment_DAI, I_DaiToken.address, { from: ACCREDITED1 })); await revertToSnapshot(snapId); }); @@ -1325,7 +1326,7 @@ contract("USDTieredSTO", accounts => { await catchRevert(I_USDTieredSTO_Array[stoId].buyWithPOLY(NONACCREDITED1, investment_POLY, { from: NONACCREDITED1 })); // NONACCREDITED DAI - await catchRevert(I_USDTieredSTO_Array[stoId].buyWithUSD(NONACCREDITED1, investment_DAI, { from: NONACCREDITED1 })); + await catchRevert(I_USDTieredSTO_Array[stoId].buyWithUSD(NONACCREDITED1, investment_DAI, I_DaiToken.address, { from: NONACCREDITED1 })); // ACCREDITED ETH await catchRevert(I_USDTieredSTO_Array[stoId].buyWithETH(ACCREDITED1, { from: ACCREDITED1, value: investment_ETH })); @@ -1334,7 +1335,7 @@ contract("USDTieredSTO", accounts => { await catchRevert(I_USDTieredSTO_Array[stoId].buyWithPOLY(ACCREDITED1, investment_POLY, { from: ACCREDITED1 })); // ACCREDITED DAI - await catchRevert(I_USDTieredSTO_Array[stoId].buyWithUSD(ACCREDITED1, investment_DAI, { from: ACCREDITED1 })); + await catchRevert(I_USDTieredSTO_Array[stoId].buyWithUSD(ACCREDITED1, investment_DAI, I_DaiToken.address, { from: ACCREDITED1 })); await revertToSnapshot(snapId); }); @@ -1724,7 +1725,7 @@ contract("USDTieredSTO", accounts => { let init_WalletDAIBal = await I_DaiToken.balanceOf(WALLET); // Buy With DAI - let tx2 = await I_USDTieredSTO_Array[stoId].buyWithUSD(NONACCREDITED1, investment_DAI, { + let tx2 = await I_USDTieredSTO_Array[stoId].buyWithUSD(NONACCREDITED1, investment_DAI, I_DaiToken.address, { from: NONACCREDITED1, gasPrice: GAS_PRICE }); @@ -2715,7 +2716,7 @@ contract("USDTieredSTO", accounts => { let init_WalletPOLYBal = await I_PolyToken.balanceOf(WALLET); let init_WalletDAIBal = await I_DaiToken.balanceOf(WALLET); - let tx2 = await I_USDTieredSTO_Array[stoId].buyWithUSD(ACCREDITED1, investment_DAI, { from: ACCREDITED1, gasPrice: GAS_PRICE }); + let tx2 = await I_USDTieredSTO_Array[stoId].buyWithUSD(ACCREDITED1, investment_DAI, I_DaiToken.address, { from: ACCREDITED1, gasPrice: GAS_PRICE }); let gasCost2 = BigNumber(GAS_PRICE).mul(tx2.receipt.gasUsed); console.log(" Gas buyWithUSD: ".grey + tx2.receipt.gasUsed.toString().grey); @@ -2954,7 +2955,7 @@ contract("USDTieredSTO", accounts => { // Buy with DAI NONACCREDITED await catchRevert( - I_USDTieredSTO_Array[stoId].buyWithUSD(NONACCREDITED1, investment_DAI, { from: NONACCREDITED1, gasPrice: GAS_PRICE }) + I_USDTieredSTO_Array[stoId].buyWithUSD(NONACCREDITED1, investment_DAI, I_DaiToken.address, { from: NONACCREDITED1, gasPrice: GAS_PRICE }) ); // Buy with ETH ACCREDITED @@ -2972,7 +2973,7 @@ contract("USDTieredSTO", accounts => { // Buy with DAI ACCREDITED await catchRevert( - I_USDTieredSTO_Array[stoId].buyWithUSD(ACCREDITED1, investment_DAI, { from: ACCREDITED1, gasPrice: GAS_PRICE }) + I_USDTieredSTO_Array[stoId].buyWithUSD(ACCREDITED1, investment_DAI, I_DaiToken.address, { from: ACCREDITED1, gasPrice: GAS_PRICE }) ); }); From ff5fca0a86a15914346c5422a80949c7b5363bbb Mon Sep 17 00:00:00 2001 From: Mudit Gupta Date: Tue, 27 Nov 2018 12:46:35 +0530 Subject: [PATCH 05/30] Fixed simulation test --- test/q_usd_tiered_sto_sim.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/q_usd_tiered_sto_sim.js b/test/q_usd_tiered_sto_sim.js index 9a5ee963a..d885c6272 100644 --- a/test/q_usd_tiered_sto_sim.js +++ b/test/q_usd_tiered_sto_sim.js @@ -153,8 +153,8 @@ contract("USDTieredSTO Sim", accounts => { name: "_reserveWallet" }, { - type: "address", - name: "_usdToken" + type: "address[]", + name: "_usdTokens" } ] }; @@ -604,7 +604,7 @@ contract("USDTieredSTO Sim", accounts => { await I_DaiToken.getTokens(investment_DAI, _investor); await I_DaiToken.approve(I_USDTieredSTO_Array[stoId].address, investment_DAI, { from: _investor }); await catchRevert( - I_USDTieredSTO_Array[stoId].buyWithUSD(_investor, investment_DAI, { from: _investor, gasPrice: GAS_PRICE }) + I_USDTieredSTO_Array[stoId].buyWithUSD(_investor, investment_DAI, I_DaiToken.address, { from: _investor, gasPrice: GAS_PRICE }) ); } else await catchRevert( @@ -687,7 +687,7 @@ contract("USDTieredSTO Sim", accounts => { .yellow ); } else if (isDai && investment_DAI.gt(10)) { - tx = await I_USDTieredSTO_Array[stoId].buyWithUSD(_investor, investment_DAI, { from: _investor, gasPrice: GAS_PRICE }); + tx = await I_USDTieredSTO_Array[stoId].buyWithUSD(_investor, investment_DAI, I_DaiToken.address, { from: _investor, gasPrice: GAS_PRICE }); gasCost = BigNumber(GAS_PRICE).mul(tx.receipt.gasUsed); console.log( `buyWithUSD: ${investment_Token.div(10 ** 18)} tokens for ${investment_DAI.div(10 ** 18)} DAI by ${_investor}` From c878e9afee2cac36d9e708e7d8ee4a5be62b3f1a Mon Sep 17 00:00:00 2001 From: Mudit Gupta Date: Tue, 27 Nov 2018 13:04:51 +0530 Subject: [PATCH 06/30] Added separate function for changing usd tokens --- contracts/modules/STO/USDTieredSTO.sol | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/contracts/modules/STO/USDTieredSTO.sol b/contracts/modules/STO/USDTieredSTO.sol index fde74b55d..27587a11f 100644 --- a/contracts/modules/STO/USDTieredSTO.sol +++ b/contracts/modules/STO/USDTieredSTO.sol @@ -262,7 +262,7 @@ contract USDTieredSTO is ISTO, ReentrancyGuard { uint256 _endTime ) external onlyOwner { /*solium-disable-next-line security/no-block-members*/ - //require(now < startTime, "STO already started"); + require(now < startTime, "STO already started"); _modifyTimes(_startTime, _endTime); } @@ -282,6 +282,14 @@ contract USDTieredSTO is ISTO, ReentrancyGuard { _modifyAddresses(_wallet, _reserveWallet, _usdTokens); } + /** + * @dev Modifies addresses used as usd tokens + * @param _usdTokens Address of usd tokens + */ + function modifyUSDTokens(address[] _usdTokens) public onlyOwner { + _modifyUSDTokens(_usdTokens); + } + function _modifyLimits( uint256 _nonAccreditedLimitUSD, uint256 _minimumInvestmentUSD @@ -333,6 +341,10 @@ contract USDTieredSTO is ISTO, ReentrancyGuard { require(_wallet != address(0) && _reserveWallet != address(0), "Invalid wallet"); wallet = _wallet; reserveWallet = _reserveWallet; + _modifyUSDTokens(_usdTokens); + } + + function _modifyUSDTokens(address[] _usdTokens) internal { for(uint256 i = 0; i < usdTokens.length; i++) { usdTokenEnabled[usdTokens[i]] = false; } @@ -340,7 +352,7 @@ contract USDTieredSTO is ISTO, ReentrancyGuard { for(i = 0; i < _usdTokens.length; i++) { usdTokenEnabled[_usdTokens[i]] = true; } - emit SetAddresses(_wallet, _reserveWallet, _usdTokens); + emit SetAddresses(wallet, reserveWallet, _usdTokens); } //////////////////// From 728276f66ed1313b458ef4433141aacca84af493 Mon Sep 17 00:00:00 2001 From: Mudit Gupta Date: Tue, 27 Nov 2018 13:14:02 +0530 Subject: [PATCH 07/30] Merge modifyUSDTokens function --- contracts/modules/STO/USDTieredSTO.sol | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/contracts/modules/STO/USDTieredSTO.sol b/contracts/modules/STO/USDTieredSTO.sol index 27587a11f..e3a16948c 100644 --- a/contracts/modules/STO/USDTieredSTO.sol +++ b/contracts/modules/STO/USDTieredSTO.sol @@ -278,18 +278,10 @@ contract USDTieredSTO is ISTO, ReentrancyGuard { address[] _usdTokens ) external onlyOwner { /*solium-disable-next-line security/no-block-members*/ - require(now < startTime, "STO already started"); + // require(now < startTime, "STO already started"); _modifyAddresses(_wallet, _reserveWallet, _usdTokens); } - /** - * @dev Modifies addresses used as usd tokens - * @param _usdTokens Address of usd tokens - */ - function modifyUSDTokens(address[] _usdTokens) public onlyOwner { - _modifyUSDTokens(_usdTokens); - } - function _modifyLimits( uint256 _nonAccreditedLimitUSD, uint256 _minimumInvestmentUSD From 420fdc6022c6aa9cd491d9a1dfa69cbee4f0fa87 Mon Sep 17 00:00:00 2001 From: Mudit Gupta Date: Tue, 27 Nov 2018 13:14:14 +0530 Subject: [PATCH 08/30] Added test case --- test/p_usd_tiered_sto.js | 58 +++++++++++++++++++++++++++++++++------- 1 file changed, 48 insertions(+), 10 deletions(-) diff --git a/test/p_usd_tiered_sto.js b/test/p_usd_tiered_sto.js index 1853c645b..0af78cf01 100644 --- a/test/p_usd_tiered_sto.js +++ b/test/p_usd_tiered_sto.js @@ -977,20 +977,11 @@ contract("USDTieredSTO", accounts => { ) ); - let tempTime1 = latestTime(); + let tempTime1 = latestTime() + duration.days(1); let tempTime2 = latestTime() + duration.days(3); await catchRevert(I_USDTieredSTO_Array[stoId].modifyTimes(tempTime1, tempTime2, { from: ISSUER })); - await catchRevert( - I_USDTieredSTO_Array[stoId].modifyAddresses( - "0x0000000000000000000000000400000000000000", - "0x0000000000000000000003000000000000000000", - [I_DaiToken.address], - { from: ISSUER } - ) - ); - await revertToSnapshot(snapId); }); }); @@ -1223,6 +1214,53 @@ contract("USDTieredSTO", accounts => { await revertToSnapshot(snapId); }); + it("should allow changing stable coin address in middle of STO", async () => { + let stoId = 0; + let snapId = await takeSnapshot(); + + // Whitelist + let fromTime = latestTime(); + let toTime = latestTime() + duration.days(15); + let expiryTime = toTime + duration.days(100); + let whitelisted = true; + + await I_GeneralTransferManager.modifyWhitelist(ACCREDITED1, fromTime, toTime, expiryTime, whitelisted, { from: ISSUER }); + await I_GeneralTransferManager.modifyWhitelist(NONACCREDITED1, fromTime, toTime, expiryTime, whitelisted, { from: ISSUER }); + + // Advance time to after STO start + await increaseTime(duration.days(3)); + + // Set as accredited + await I_USDTieredSTO_Array[stoId].changeAccredited([ACCREDITED1], [true], { from: ISSUER }); + + // Prep for investments + let investment_DAI = web3.utils.toWei("500", "ether"); // Invest 10000 POLY + await I_DaiToken.getTokens(investment_DAI, NONACCREDITED1); + await I_DaiToken.approve(I_USDTieredSTO_Array[stoId].address, investment_DAI, { from: NONACCREDITED1 }); + await I_DaiToken.getTokens(investment_DAI, ACCREDITED1); + await I_DaiToken.approve(I_USDTieredSTO_Array[stoId].address, investment_DAI, { from: ACCREDITED1 }); + + // Make sure buying works before changing + await I_USDTieredSTO_Array[stoId].buyWithUSD(NONACCREDITED1, investment_DAI, I_DaiToken.address, { from: NONACCREDITED1 }); + + // Change Stable coin address + await I_USDTieredSTO_Array[stoId].modifyAddresses(WALLET, RESERVEWALLET, [I_PolyToken.address], { from: ISSUER }); + + // NONACCREDITED DAI + await catchRevert(I_USDTieredSTO_Array[stoId].buyWithUSD(NONACCREDITED1, investment_DAI, I_DaiToken.address, { from: NONACCREDITED1 })); + + // ACCREDITED DAI + await catchRevert(I_USDTieredSTO_Array[stoId].buyWithUSD(ACCREDITED1, investment_DAI, I_DaiToken.address, { from: ACCREDITED1 })); + + // Revert stable coin address + await I_USDTieredSTO_Array[stoId].modifyAddresses(WALLET, RESERVEWALLET, [I_DaiToken.address], { from: ISSUER }); + + // Make sure buying works again + await I_USDTieredSTO_Array[stoId].buyWithUSD(ACCREDITED1, investment_DAI, I_DaiToken.address, { from: ACCREDITED1 }); + + await revertToSnapshot(snapId); + }); + it("should fail if after STO end time", async () => { let stoId = 3; let snapId = await takeSnapshot(); From d30f80900ae97dbc594305ea246d0f308c4fd26f Mon Sep 17 00:00:00 2001 From: Mudit Gupta Date: Tue, 27 Nov 2018 18:48:38 +0530 Subject: [PATCH 09/30] Added a comment --- contracts/modules/STO/USDTieredSTO.sol | 1 + 1 file changed, 1 insertion(+) diff --git a/contracts/modules/STO/USDTieredSTO.sol b/contracts/modules/STO/USDTieredSTO.sol index e3a16948c..9e6287087 100644 --- a/contracts/modules/STO/USDTieredSTO.sol +++ b/contracts/modules/STO/USDTieredSTO.sol @@ -18,6 +18,7 @@ contract USDTieredSTO is ISTO, ReentrancyGuard { // Storage // ///////////// struct Tier { + // NB rates mentioned below are actually price and are used like price in the logic. // How many token units a buyer gets per USD in this tier (multiplied by 10**18) uint256 rate; From dd20c06bbf7eb914522d9516ff903287206a849d Mon Sep 17 00:00:00 2001 From: Mudit Gupta Date: Tue, 27 Nov 2018 18:52:52 +0530 Subject: [PATCH 10/30] Updated changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index db3d8dcde..74c0a22c1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ All notable changes to this project will be documented in this file. [__2.1.0__](https://www.npmjs.com/package/polymath-core?activeTab=readme) __13-09-18__ # USDTieredSTO 2.0.1 +* Added support for multiple stable coins in USDTSTO. * Added `buyTokensView` and `getTokensMintedByTier` to USDTSTO. * Added `getSTODetails` to USDTSTO. * Added an Array of Tiers that will hold data about every tier in USDTSTO. From 557d286869d85bf5906a2f63576e5a4e4f23d138 Mon Sep 17 00:00:00 2001 From: Mudit Gupta Date: Tue, 27 Nov 2018 19:38:09 +0530 Subject: [PATCH 11/30] Combined modifiers --- contracts/modules/STO/USDTieredSTO.sol | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/contracts/modules/STO/USDTieredSTO.sol b/contracts/modules/STO/USDTieredSTO.sol index 9e6287087..9e838592a 100644 --- a/contracts/modules/STO/USDTieredSTO.sol +++ b/contracts/modules/STO/USDTieredSTO.sol @@ -154,12 +154,8 @@ contract USDTieredSTO is ISTO, ReentrancyGuard { _; } - modifier validSC { + modifier validSC(address _usdToken) { require(fundRaiseTypes[uint8(FundRaiseType.SC)], "Stable coins not allowed"); - _; - } - - modifier validUSDToken(address _usdToken) { require(usdTokenEnabled[_usdToken], "Invalid USD token"); _; } @@ -476,7 +472,7 @@ contract USDTieredSTO is ISTO, ReentrancyGuard { * @param _usdToken Address of USD stable coin to buy tokens with */ function buyWithUSDRateLimited(address _beneficiary, uint256 _investedSC, uint256 _minTokens, IERC20 _usdToken) - public validSC validUSDToken(_usdToken) + public validSC(_usdToken) { _buyWithTokens(_beneficiary, _investedSC, FundRaiseType.SC, _minTokens, _usdToken); } From 5fdd2e2d79b8ba39f4823a98bd767ec0e358ef05 Mon Sep 17 00:00:00 2001 From: Mudit Gupta Date: Tue, 27 Nov 2018 19:42:34 +0530 Subject: [PATCH 12/30] Added data validation --- contracts/modules/STO/USDTieredSTO.sol | 1 + 1 file changed, 1 insertion(+) diff --git a/contracts/modules/STO/USDTieredSTO.sol b/contracts/modules/STO/USDTieredSTO.sol index 9e838592a..58a3bdc2a 100644 --- a/contracts/modules/STO/USDTieredSTO.sol +++ b/contracts/modules/STO/USDTieredSTO.sol @@ -339,6 +339,7 @@ contract USDTieredSTO is ISTO, ReentrancyGuard { } usdTokens = _usdTokens; for(i = 0; i < _usdTokens.length; i++) { + require(_usdTokens[i] != address(0), "Invalid USD token"); usdTokenEnabled[_usdTokens[i]] = true; } emit SetAddresses(wallet, reserveWallet, _usdTokens); From 846d03f463e879116940a297cebbfa5934612153 Mon Sep 17 00:00:00 2001 From: Mudit Gupta Date: Tue, 27 Nov 2018 20:05:15 +0530 Subject: [PATCH 13/30] test case fixed --- test/p_usd_tiered_sto.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/p_usd_tiered_sto.js b/test/p_usd_tiered_sto.js index 0af78cf01..7cc69286a 100644 --- a/test/p_usd_tiered_sto.js +++ b/test/p_usd_tiered_sto.js @@ -935,7 +935,7 @@ contract("USDTieredSTO", accounts => { await I_USDTieredSTO_Array[stoId].modifyAddresses( "0x0000000000000000000000000400000000000000", "0x0000000000000000000003000000000000000000", - [address_zero], + [0x0000000000000000000003000000000000057a00], { from: ISSUER } ); assert.equal( @@ -950,7 +950,7 @@ contract("USDTieredSTO", accounts => { ); assert.equal( await I_USDTieredSTO_Array[stoId].usdTokens.call(0), - address_zero, + "0x0000000000000000000003000000000000057a00", "STO Configuration doesn't set as expected" ); }); From 78bf819f4f9521f1dcf19b20745deabb12f7edc1 Mon Sep 17 00:00:00 2001 From: shuffledex Date: Mon, 3 Dec 2018 16:42:52 -0300 Subject: [PATCH 14/30] adding and listing stable coins in sto manager --- CLI/commands/common/common_functions.js | 11 +- CLI/commands/common/constants.js | 2 +- CLI/commands/helpers/contract_abis.js | 5 + CLI/commands/sto_manager.js | 133 ++++++++++++++++++++---- 4 files changed, 123 insertions(+), 28 deletions(-) diff --git a/CLI/commands/common/common_functions.js b/CLI/commands/common/common_functions.js index 0d21d06f3..e9d4b61a1 100644 --- a/CLI/commands/common/common_functions.js +++ b/CLI/commands/common/common_functions.js @@ -3,7 +3,7 @@ const Tx = require('ethereumjs-tx'); const permissionsList = require('./permissions_list'); const abis = require('../helpers/contract_abis'); -async function connect(abi, address) { +function connect(abi, address) { contractRegistry = new web3.eth.Contract(abi, address); contractRegistry.setProvider(web3.currentProvider); return contractRegistry @@ -15,7 +15,7 @@ async function checkPermission(contractName, functionName, contractRegistry) { return true } else { let stAddress = await contractRegistry.methods.securityToken().call(); - let securityToken = await connect(abis.securityToken(), stAddress); + let securityToken = connect(abis.securityToken(), stAddress); let stOwner = await securityToken.methods.owner().call(); if (stOwner == Issuer.address) { return true @@ -47,11 +47,11 @@ async function getGasLimit(options, action) { } async function checkPermissions(action) { - let contractRegistry = await connect(action._parent.options.jsonInterface, action._parent._address); + let contractRegistry = connect(action._parent.options.jsonInterface, action._parent._address); //NOTE this is a condition to verify if the transaction comes from a module or not. if (contractRegistry.methods.hasOwnProperty('factory')) { let moduleAddress = await contractRegistry.methods.factory().call(); - let moduleRegistry = await connect(abis.moduleFactory(), moduleAddress); + let moduleRegistry = connect(abis.moduleFactory(), moduleAddress); let parentModule = await moduleRegistry.methods.getName().call(); let result = await checkPermission(web3.utils.hexToUtf8(parentModule), action._method.name, contractRegistry); if (!result) { @@ -145,5 +145,8 @@ module.exports = { let eventJsonInterface = jsonInterface.find(o => o.name === eventName && o.type === 'event'); let filteredLogs = logs.filter(l => l.topics.includes(eventJsonInterface.signature)); return filteredLogs.map(l => web3.eth.abi.decodeLog(eventJsonInterface.inputs, l.data, l.topics.slice(1))); + }, + connect: function (abi, address) { + return connect(abi, address) } }; diff --git a/CLI/commands/common/constants.js b/CLI/commands/common/constants.js index c3e2b796c..2f10d6930 100644 --- a/CLI/commands/common/constants.js +++ b/CLI/commands/common/constants.js @@ -29,6 +29,6 @@ module.exports = Object.freeze({ FUND_RAISE_TYPES: { ETH: 0, POLY: 1, - DAI: 2 + STABLE: 2 } }); \ No newline at end of file diff --git a/CLI/commands/helpers/contract_abis.js b/CLI/commands/helpers/contract_abis.js index f93f18ac7..aa98aa5f1 100644 --- a/CLI/commands/helpers/contract_abis.js +++ b/CLI/commands/helpers/contract_abis.js @@ -19,6 +19,7 @@ let ownableABI; let iSTOABI; let iTransferManagerABI; let moduleFactoryABI; +let erc20; try { polymathRegistryABI = JSON.parse(require('fs').readFileSync('./build/contracts/PolymathRegistry.json').toString()).abi; @@ -42,6 +43,7 @@ try { iSTOABI = JSON.parse(require('fs').readFileSync('./build/contracts/ISTO.json').toString()).abi iTransferManagerABI = JSON.parse(require('fs').readFileSync('./build/contracts/ITransferManager.json').toString()).abi moduleFactoryABI = JSON.parse(require('fs').readFileSync('./build/contracts/ModuleFactory.json').toString()).abi; + erc20ABI = JSON.parse(require('fs').readFileSync('./build/contracts/DetailedERC20.json').toString()).abi; } catch (err) { console.log('\x1b[31m%s\x1b[0m', "Couldn't find contracts' artifacts. Make sure you ran truffle compile first"); throw err; @@ -110,5 +112,8 @@ module.exports = { }, moduleFactory: function () { return moduleFactoryABI; + }, + erc20: function () { + return erc20ABI; } } \ No newline at end of file diff --git a/CLI/commands/sto_manager.js b/CLI/commands/sto_manager.js index d0fd1c3ea..18e342e57 100644 --- a/CLI/commands/sto_manager.js +++ b/CLI/commands/sto_manager.js @@ -6,6 +6,7 @@ const contracts = require('./helpers/contract_addresses'); const abis = require('./helpers/contract_abis'); const common = require('./common/common_functions'); const gbl = require('./common/global'); +const STABLE = 'STABLE'; /////////////////// // Crowdsale params @@ -270,7 +271,7 @@ async function cappedSTO_status(currentSTO) { function fundingConfigUSDTieredSTO() { let funding = {}; - let selectedFunding = readlineSync.question('Enter' + chalk.green(` P `) + 'for POLY raise,' + chalk.green(` D `) + 'for DAI raise,' + chalk.green(` E `) + 'for Ether raise or any combination of them (i.e.' + chalk.green(` PED `) + 'for all): ').toUpperCase(); + let selectedFunding = readlineSync.question('Enter' + chalk.green(` P `) + 'for POLY raise,' + chalk.green(` S `) + 'for Stable Coin raise,' + chalk.green(` E `) + 'for Ether raise or any combination of them (i.e.' + chalk.green(` PSE `) + 'for all): ').toUpperCase(); funding.raiseType = []; if (selectedFunding.includes('E')) { @@ -279,17 +280,17 @@ function fundingConfigUSDTieredSTO() { if (selectedFunding.includes('P')) { funding.raiseType.push(gbl.constants.FUND_RAISE_TYPES.POLY); } - if (selectedFunding.includes('D')) { - funding.raiseType.push(gbl.constants.FUND_RAISE_TYPES.DAI); + if (selectedFunding.includes('S')) { + funding.raiseType.push(gbl.constants.FUND_RAISE_TYPES.STABLE); } if (funding.raiseType.length == 0) { - funding.raiseType = [gbl.constants.FUND_RAISE_TYPES.ETH, gbl.constants.FUND_RAISE_TYPES.POLY, gbl.constants.FUND_RAISE_TYPES.DAI]; + funding.raiseType = [gbl.constants.FUND_RAISE_TYPES.ETH, gbl.constants.FUND_RAISE_TYPES.POLY, gbl.constants.FUND_RAISE_TYPES.STABLE]; } return funding; } -function addressesConfigUSDTieredSTO(usdTokenRaise) { +async function addressesConfigUSDTieredSTO(usdTokenRaise) { let addresses = {}; addresses.wallet = readlineSync.question('Enter the address that will receive the funds from the STO (' + Issuer.address + '): ', { @@ -310,22 +311,74 @@ function addressesConfigUSDTieredSTO(usdTokenRaise) { }); if (addresses.reserveWallet == "") addresses.reserveWallet = Issuer.address; + let listOfAddress; + if (usdTokenRaise) { - addresses.usdToken = readlineSync.question('Enter the address of the USD Token or stable coin (' + usdToken.options.address + '): ', { + addresses.usdToken = readlineSync.question('Enter the address (or many addresses that you want separated by comma) of the USD Token or stable coin (' + usdToken.options.address + '): ', { limit: function (input) { - return web3.utils.isAddress(input); + listOfAddress = input.split(','); + let response = true + listOfAddress.forEach((addr) => { + if (!web3.utils.isAddress(addr)) { + response = false + } + }) + return response }, limitMessage: "Must be a valid address", defaultInput: usdToken.options.address }); - if (addresses.usdToken == "") addresses.usdToken = usdToken.options.address; + if (addresses.usdToken == "") { + listOfAddress = [usdToken.options.address] + addresses.usdToken = [usdToken.options.address]; + } } else { - addresses.usdToken = '0x0000000000000000000000000000000000000000'; + listOfAddress = ['0x0000000000000000000000000000000000000000'] + addresses.usdToken = ['0x0000000000000000000000000000000000000000']; + } + + if (!await processArray(listOfAddress)) { + console.log(`Please, verify your stable coins addresses to continue with this process.`) + process.exit(0) + } + + if (typeof addresses.usdToken === 'string') { + addresses.usdToken = addresses.usdToken.split(",") } return addresses; } +async function checkSymbol(address) { + let stableCoin = common.connect(abis.erc20(), address); + try { + return await stableCoin.methods.symbol().call(); + } catch (e) { + return "" + } +} + +async function processArray(array) { + let result = true; + for (const address of array) { + let symbol = await checkSymbol(address); + if (symbol == "") { + result = false; + console.log(`${address} seems not to be a stable coin`) + } + } + return result +} + +async function processAddress(array) { + let list = []; + for (const address of array) { + let symbol = await checkSymbol(address); + list.push(symbol) + } + return list +} + function tiersConfigUSDTieredSTO(polyRaise) { let tiers = {}; @@ -474,7 +527,7 @@ async function usdTieredSTO_launch(stoConfig) { let useConfigFile = typeof stoConfig !== 'undefined'; let funding = useConfigFile ? stoConfig.funding : fundingConfigUSDTieredSTO(); - let addresses = useConfigFile ? stoConfig.addresses : addressesConfigUSDTieredSTO(funding.raiseType.includes(gbl.constants.FUND_RAISE_TYPES.DAI)); + let addresses = useConfigFile ? stoConfig.addresses : await addressesConfigUSDTieredSTO(funding.raiseType.includes(gbl.constants.FUND_RAISE_TYPES.STABLE)); let tiers = useConfigFile ? stoConfig.tiers : tiersConfigUSDTieredSTO(funding.raiseType.includes(gbl.constants.FUND_RAISE_TYPES.POLY)); let limits = useConfigFile ? stoConfig.limits : limitsConfigUSDTieredSTO(); let times = timesConfigUSDTieredSTO(stoConfig); @@ -519,12 +572,17 @@ async function usdTieredSTO_status(currentSTO) { let displayInvestorCount = await currentSTO.methods.investorCount().call(); let displayIsFinalized = await currentSTO.methods.isFinalized().call() ? "YES" : "NO"; let displayTokenSymbol = await securityToken.methods.symbol().call(); - - let tiersLength = await currentSTO.methods.getNumberOfTiers().call();; - + let tiersLength = await currentSTO.methods.getNumberOfTiers().call(); + //REMOVE ONCE SMART CONTRACT SUPPORT METHOD TO GET STABLE COIN ADDRESSES + let listOfStableCoins = ["0xa016B2ae79436E20FBe22Bf230a92A5Fb055762F", "0xae794d38cb481868a8CB19b9d7A5073851bC6dB7"]; let raiseTypes = []; + let stableSymbols = []; + for (const fundType in gbl.constants.FUND_RAISE_TYPES) { if (await currentSTO.methods.fundRaiseTypes(gbl.constants.FUND_RAISE_TYPES[fundType]).call()) { + if (fundType == STABLE) { + stableSymbols = await processAddress(listOfStableCoins) + } raiseTypes.push(fundType); } } @@ -554,8 +612,13 @@ async function usdTieredSTO_status(currentSTO) { } let mintedPerTier = mintedPerTierPerRaiseType[gbl.constants.FUND_RAISE_TYPES[type]]; - displayMintedPerTierPerType += ` - Sold for ${type}:\t\t ${web3.utils.fromWei(mintedPerTier)} ${displayTokenSymbol} ${displayDiscountMinted}`; + if ((type == STABLE) && (stableSymbols.length)) { + displayMintedPerTierPerType += ` + Sold for ${stableSymbols.toString()}:\t ${web3.utils.fromWei(mintedPerTier)} ${displayTokenSymbol} ${displayDiscountMinted}`; + } else { + displayMintedPerTierPerType += ` + Sold for ${type}:\t\t ${web3.utils.fromWei(mintedPerTier)} ${displayTokenSymbol} ${displayDiscountMinted}`; + } } displayTiers += ` @@ -579,28 +642,52 @@ async function usdTieredSTO_status(currentSTO) { let balance = await getBalance(displayWallet, gbl.constants.FUND_RAISE_TYPES[type]); let walletBalance = web3.utils.fromWei(balance); let walletBalanceUSD = web3.utils.fromWei(await currentSTO.methods.convertToUSD(gbl.constants.FUND_RAISE_TYPES[type], balance).call()); - displayWalletBalancePerType += ` + if ((type == STABLE) && (stableSymbols.length)) { + displayWalletBalancePerType += ` + Balance ${stableSymbols.toString()}:\t ${walletBalanceUSD} USD`; + } else { + displayWalletBalancePerType += ` Balance ${type}:\t\t ${walletBalance} ${type} (${walletBalanceUSD} USD)`; - + } + balance = await getBalance(displayReserveWallet, gbl.constants.FUND_RAISE_TYPES[type]); let reserveWalletBalance = web3.utils.fromWei(balance); let reserveWalletBalanceUSD = web3.utils.fromWei(await currentSTO.methods.convertToUSD(gbl.constants.FUND_RAISE_TYPES[type], balance).call()); - displayReserveWalletBalancePerType += ` + if ((type == STABLE) && (stableSymbols.length)) { + displayReserveWalletBalancePerType += ` + Balance ${stableSymbols.toString()}:\t ${reserveWalletBalanceUSD} USD`; + } else { + displayReserveWalletBalancePerType += ` Balance ${type}:\t\t ${reserveWalletBalance} ${type} (${reserveWalletBalanceUSD} USD)`; + } let fundsRaised = web3.utils.fromWei(await currentSTO.methods.fundsRaised(gbl.constants.FUND_RAISE_TYPES[type]).call()); - displayFundsRaisedPerType += ` + if ((type == STABLE) && (stableSymbols.length)) { + displayFundsRaisedPerType += ` + ${stableSymbols.toString()}:\t\t ${fundsRaised} USD`; + } else { + displayFundsRaisedPerType += ` ${type}:\t\t\t ${fundsRaised} ${type}`; + } //Only show sold for if more than one raise type are allowed if (raiseTypes.length > 1) { let tokensSoldPerType = web3.utils.fromWei(await currentSTO.methods.getTokensSoldFor(gbl.constants.FUND_RAISE_TYPES[type]).call()); - displayTokensSoldPerType += ` - Sold for ${type}:\t\t ${tokensSoldPerType} ${displayTokenSymbol}`; + if ((type == STABLE) && (stableSymbols.length)) { + displayTokensSoldPerType += ` + Sold for ${stableSymbols.toString()}:\t ${tokensSoldPerType} ${displayTokenSymbol}`; + } else { + displayTokensSoldPerType += ` + Sold for ${type}:\t\t ${tokensSoldPerType} ${displayTokenSymbol}`; + } } } let displayRaiseType = raiseTypes.join(' - '); + //If STO has stable coins, we list them one by one + if (stableSymbols.length) { + displayRaiseType = displayRaiseType.replace(STABLE, "") + `${stableSymbols.toString().replace(`,`,` - `)}` + } let now = Math.floor(Date.now() / 1000); let timeTitle; @@ -747,7 +834,7 @@ async function modfifyFunding(currentSTO) { } async function modfifyAddresses(currentSTO) { - let addresses = addressesConfigUSDTieredSTO(await currentSTO.methods.fundRaiseTypes(gbl.constants.FUND_RAISE_TYPES.DAI).call()); + let addresses = await addressesConfigUSDTieredSTO(await currentSTO.methods.fundRaiseTypes(gbl.constants.FUND_RAISE_TYPES.STABLE).call()); let modifyAddressesAction = currentSTO.methods.modifyAddresses(addresses.wallet, addresses.reserveWallet, addresses.usdToken); await common.sendTransaction(modifyAddressesAction); } @@ -772,7 +859,7 @@ async function getBalance(from, type) { return await web3.eth.getBalance(from); case gbl.constants.FUND_RAISE_TYPES.POLY: return await polyToken.methods.balanceOf(from).call(); - case gbl.constants.FUND_RAISE_TYPES.DAI: + case gbl.constants.FUND_RAISE_TYPES.STABLE: return await usdToken.methods.balanceOf(from).call(); } } From b040af43908ca3bd6e739a8f46eb799d2a58dc23 Mon Sep 17 00:00:00 2001 From: shuffledex Date: Tue, 4 Dec 2018 08:47:42 -0300 Subject: [PATCH 15/30] investor portal improvements --- CLI/commands/investor_portal.js | 72 ++++++++++++++++++++++++++++----- 1 file changed, 63 insertions(+), 9 deletions(-) diff --git a/CLI/commands/investor_portal.js b/CLI/commands/investor_portal.js index 1ced21ce0..7502d7d8e 100644 --- a/CLI/commands/investor_portal.js +++ b/CLI/commands/investor_portal.js @@ -10,6 +10,7 @@ var contracts = require('./helpers/contract_addresses'); var abis = require('./helpers/contract_abis'); const STO_KEY = 3; +const STABLE = 'STABLE'; let securityTokenRegistry; let securityToken; @@ -157,13 +158,13 @@ async function showUserInfo(_user) { console.log(` ******************* User Information ******************** - Address: ${_user}`); - if (await currentSTO.methods.fundRaiseTypes(gbl.constants.FUND_RAISE_TYPES.POLY)) { + if (await currentSTO.methods.fundRaiseTypes(gbl.constants.FUND_RAISE_TYPES.POLY).call()) { console.log(` - POLY balance:\t ${await polyBalance(_user)}`); } - if (await currentSTO.methods.fundRaiseTypes(gbl.constants.FUND_RAISE_TYPES.ETH)) { + if (await currentSTO.methods.fundRaiseTypes(gbl.constants.FUND_RAISE_TYPES.ETH).call()) { console.log(` - ETH balance:\t ${web3.utils.fromWei(await web3.eth.getBalance(_user))}`); } - if (await currentSTO.methods.fundRaiseTypes(gbl.constants.FUND_RAISE_TYPES.DAI)) { + if (await currentSTO.methods.fundRaiseTypes(gbl.constants.FUND_RAISE_TYPES.STABLE).call()) { console.log(` - DAI balance:\t ${await usdBalance(_user)}`); } } @@ -237,12 +238,41 @@ async function showCappedSTOInfo() { } } +async function processAddress(array) { + let list = []; + for (const address of array) { + let symbol = await checkSymbol(address); + list.push(symbol) + } + return list +} + +async function checkSymbol(address) { + let stableCoin = common.connect(abis.erc20(), address); + try { + return await stableCoin.methods.symbol().call(); + } catch (e) { + return "" + } +} + async function showUserInfoForUSDTieredSTO() { + let stableSymbols = []; + //REMOVE ONCE SMART CONTRACT SUPPORT METHOD TO GET STABLE COIN ADDRESSES + let listOfStableCoins = ["0xa016B2ae79436E20FBe22Bf230a92A5Fb055762F", "0xae794d38cb481868a8CB19b9d7A5073851bC6dB7"]; + for (const fundType in gbl.constants.FUND_RAISE_TYPES) { if (await currentSTO.methods.fundRaiseTypes(gbl.constants.FUND_RAISE_TYPES[fundType]).call()) { + if (fundType == STABLE) { + stableSymbols = await processAddress(listOfStableCoins) + } let displayInvestorInvested = web3.utils.fromWei(await currentSTO.methods.investorInvested(User.address, gbl.constants.FUND_RAISE_TYPES[fundType]).call()); - console.log(` - Invested in ${fundType}:\t ${displayInvestorInvested} ${fundType}`); + if ((fundType == STABLE) && (stableSymbols.length)) { + console.log(` - Invested in ${stableSymbols.toString()}:\t ${displayInvestorInvested} USD`); + } else { + console.log(` - Invested in ${fundType}:\t ${displayInvestorInvested} ${fundType}`); + } } } @@ -280,10 +310,16 @@ async function showUSDTieredSTOInfo() { let displayIsOpen = await currentSTO.methods.isOpen().call(); let displayTokenSymbol = await securityToken.methods.symbol().call(); let tiersLength = await currentSTO.methods.getNumberOfTiers().call(); + let stableSymbols = []; + //REMOVE ONCE SMART CONTRACT SUPPORT METHOD TO GET STABLE COIN ADDRESSES + let listOfStableCoins = ["0xa016B2ae79436E20FBe22Bf230a92A5Fb055762F", "0xae794d38cb481868a8CB19b9d7A5073851bC6dB7"]; for (const fundType in gbl.constants.FUND_RAISE_TYPES) { if (await currentSTO.methods.fundRaiseTypes(gbl.constants.FUND_RAISE_TYPES[fundType]).call()) { raiseTypes.push(fundType); + if (fundType == STABLE) { + stableSymbols = await processAddress(listOfStableCoins) + } } } @@ -313,8 +349,13 @@ async function showUSDTieredSTOInfo() { let mintedPerTier = mintedPerTierPerRaiseType[gbl.constants.FUND_RAISE_TYPES[type]]; - displayMintedPerTierPerType += ` + if ((type == STABLE) && (stableSymbols.length)) { + displayMintedPerTierPerType += ` + Sold for ${stableSymbols.toString()}:\t ${web3.utils.fromWei(mintedPerTier)} ${displayTokenSymbol} ${displayDiscountMinted}`; + } else { + displayMintedPerTierPerType += ` Sold for ${type}:\t\t ${web3.utils.fromWei(mintedPerTier)} ${displayTokenSymbol} ${displayDiscountMinted}`; + } } displayTiers += ` @@ -334,18 +375,31 @@ async function showUSDTieredSTOInfo() { let displayTokensSoldPerType = ''; for (const type of raiseTypes) { let fundsRaised = web3.utils.fromWei(await currentSTO.methods.fundsRaised(gbl.constants.FUND_RAISE_TYPES[type]).call()); - displayFundsRaisedPerType += ` - ${type}:\t\t\t ${fundsRaised} ${type}`; - + if ((type == STABLE) && (stableSymbols.length)) { + displayFundsRaisedPerType += ` + ${stableSymbols.toString()}:\t\t ${fundsRaised} USD`; + } else { + displayFundsRaisedPerType += ` + ${type}:\t\t\t ${fundsRaised} ${type}`; + } //Only show sold per raise type is more than one are allowed if (raiseTypes.length > 1) { let tokensSoldPerType = web3.utils.fromWei(await currentSTO.methods.getTokensSoldFor(gbl.constants.FUND_RAISE_TYPES[type]).call()); - displayTokensSoldPerType += ` + if ((type == STABLE) && (stableSymbols.length)) { + displayTokensSoldPerType += ` + Sold for ${stableSymbols.toString()}:\t ${tokensSoldPerType} ${displayTokenSymbol}`; + } else { + displayTokensSoldPerType += ` Sold for ${type}:\t\t ${tokensSoldPerType} ${displayTokenSymbol}`; + } } } let displayRaiseType = raiseTypes.join(' - '); + //If STO has stable coins, we list them one by one + if (stableSymbols.length) { + displayRaiseType = displayRaiseType.replace(STABLE, "") + `${stableSymbols.toString().replace(`,`,` - `)}` + } let now = Math.floor(Date.now()/1000); let timeTitle; From 11b6fe3d75e41678a3ef0cf5c7d0c30244e44f8a Mon Sep 17 00:00:00 2001 From: Adam Dossa Date: Tue, 4 Dec 2018 21:16:14 -0500 Subject: [PATCH 16/30] Add getUsdTokens --- contracts/modules/STO/USDTieredSTO.sol | 23 +++++++++++++++-------- test/p_usd_tiered_sto.js | 8 +++++--- 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/contracts/modules/STO/USDTieredSTO.sol b/contracts/modules/STO/USDTieredSTO.sol index 58a3bdc2a..d3be00047 100644 --- a/contracts/modules/STO/USDTieredSTO.sol +++ b/contracts/modules/STO/USDTieredSTO.sol @@ -155,8 +155,7 @@ contract USDTieredSTO is ISTO, ReentrancyGuard { } modifier validSC(address _usdToken) { - require(fundRaiseTypes[uint8(FundRaiseType.SC)], "Stable coins not allowed"); - require(usdTokenEnabled[_usdToken], "Invalid USD token"); + require(fundRaiseTypes[uint8(FundRaiseType.SC)] && usdTokenEnabled[_usdToken], "USD not allowed"); _; } @@ -472,8 +471,8 @@ contract USDTieredSTO is ISTO, ReentrancyGuard { * @param _minTokens Minumum number of tokens to buy or else revert * @param _usdToken Address of USD stable coin to buy tokens with */ - function buyWithUSDRateLimited(address _beneficiary, uint256 _investedSC, uint256 _minTokens, IERC20 _usdToken) - public validSC(_usdToken) + function buyWithUSDRateLimited(address _beneficiary, uint256 _investedSC, uint256 _minTokens, IERC20 _usdToken) + public validSC(_usdToken) { _buyWithTokens(_beneficiary, _investedSC, FundRaiseType.SC, _minTokens, _usdToken); } @@ -512,7 +511,7 @@ contract USDTieredSTO is ISTO, ReentrancyGuard { if (!allowBeneficialInvestments) { require(_beneficiary == msg.sender, "Beneficiary != funder"); } - + uint256 originalUSD = DecimalMath.mul(_rate, _investmentValue); uint256 allowedUSD = _buyTokensChecks(_beneficiary, _investmentValue, originalUSD); @@ -634,7 +633,7 @@ contract USDTieredSTO is ISTO, ReentrancyGuard { tierData.mintedTotal = tierData.mintedTotal.add(tierPurchasedTokens); } // Now, if there is any remaining USD to be invested, purchase at non-discounted rate - if (investedUSD > 0 && + if (investedUSD > 0 && tierData.tokenTotal.sub(tierData.mintedTotal) > 0 && (_fundRaiseType != FundRaiseType.POLY || tierData.tokensDiscountPoly <= tierData.mintedDiscountPoly) ) { @@ -669,7 +668,7 @@ contract USDTieredSTO is ISTO, ReentrancyGuard { _investedUSD = _investedUSD.sub(spentUSD); } // Now, if there is any remaining USD to be invested, purchase at non-discounted rate - if (_investedUSD > 0 && + if (_investedUSD > 0 && tierData.tokenTotal.sub(tierData.mintedTotal.add(tokensMinted)) > 0 && (_fundRaiseType != FundRaiseType.POLY || tierData.tokensDiscountPoly <= tierData.mintedDiscountPoly) ) { @@ -693,7 +692,7 @@ contract USDTieredSTO is ISTO, ReentrancyGuard { if (purchasedTokens > 0) { require(ISecurityToken(securityToken).mint(_beneficiary, purchasedTokens), "Error in minting"); emit TokenPurchase(msg.sender, _beneficiary, purchasedTokens, spentUSD, _tierPrice, _tier); - } + } } function _purchaseTierAmount( @@ -866,6 +865,14 @@ contract USDTieredSTO is ISTO, ReentrancyGuard { return tiers.length; } + /** + * @notice Return the usd tokens accepted by the STO + * @return address[] usd tokens + */ + function getUsdTokens() public view returns (address[]) { + return usdTokens; + } + /** * @notice Return the permissions flag that are associated with STO */ diff --git a/test/p_usd_tiered_sto.js b/test/p_usd_tiered_sto.js index 7cc69286a..c0f9febf9 100644 --- a/test/p_usd_tiered_sto.js +++ b/test/p_usd_tiered_sto.js @@ -222,7 +222,7 @@ contract("USDTieredSTO", accounts => { I_SecurityTokenRegistryProxy, I_STRProxied ] = instances; - + I_DaiToken = await PolyTokenFaucet.new({from: POLYMATH}); // STEP 4: Deploy the GeneralDelegateManagerFactory [I_GeneralPermissionManagerFactory] = await deployGPMAndVerifyed(POLYMATH, I_MRProxied, I_PolyToken.address, 0); @@ -635,6 +635,8 @@ contract("USDTieredSTO", accounts => { assert.equal(tx.logs[2].args._types[0], STOKEY, "USDTieredSTO doesn't get deployed"); assert.equal(web3.utils.hexToString(tx.logs[2].args._name), "USDTieredSTO", "USDTieredSTOFactory module was not added"); I_USDTieredSTO_Array.push(USDTieredSTO.at(tx.logs[2].args._module)); + let tokens = await I_USDTieredSTO_Array[I_USDTieredSTO_Array.length - 1].getUsdTokens.call(); + assert.equal(tokens, _usdToken[stoId], "USD Tokens should match"); }); it("Should successfully attach the fifth STO module to the security token", async () => { @@ -4521,7 +4523,7 @@ contract("USDTieredSTO", accounts => { "fundsRaisedUSD not changed as expected" ); }); - + it("should return minted tokens in a tier", async () => { let totalMinted = (await I_USDTieredSTO_Array[0].getTokensSoldByTier.call(0)).toNumber(); let individualMinted = await I_USDTieredSTO_Array[0].getTokensMintedByTier.call(0); @@ -4603,7 +4605,7 @@ contract("USDTieredSTO", accounts => { assert.equal((await I_USDTieredSTOFactory.getSetupCost.call()).toNumber(), STOSetupCost); assert.equal((await I_USDTieredSTOFactory.getTypes.call())[0], 3); assert.equal(web3.utils.hexToString(await I_USDTieredSTOFactory.getName.call()), "USDTieredSTO", "Wrong Module added"); - assert.equal(await I_USDTieredSTOFactory.description.call(), + assert.equal(await I_USDTieredSTOFactory.description.call(), "It allows both accredited and non-accredited investors to contribute into the STO. Non-accredited investors will be capped at a maximum investment limit (as a default or specific to their jurisdiction). Tokens will be sold according to tiers sequentially & each tier has its own price and volume of tokens to sell. Upon receipt of funds (ETH, POLY or DAI), security tokens will automatically transfer to investor’s wallet address", "Wrong Module added"); assert.equal(await I_USDTieredSTOFactory.title.call(), "USD Tiered STO", "Wrong Module added"); From a4c31ceb9a38448cf2cc18e2e72bd294d60039dd Mon Sep 17 00:00:00 2001 From: Adam Dossa Date: Tue, 4 Dec 2018 21:31:07 -0500 Subject: [PATCH 17/30] Fix typo --- test/p_usd_tiered_sto.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/p_usd_tiered_sto.js b/test/p_usd_tiered_sto.js index c0f9febf9..b4e49183a 100644 --- a/test/p_usd_tiered_sto.js +++ b/test/p_usd_tiered_sto.js @@ -636,7 +636,7 @@ contract("USDTieredSTO", accounts => { assert.equal(web3.utils.hexToString(tx.logs[2].args._name), "USDTieredSTO", "USDTieredSTOFactory module was not added"); I_USDTieredSTO_Array.push(USDTieredSTO.at(tx.logs[2].args._module)); let tokens = await I_USDTieredSTO_Array[I_USDTieredSTO_Array.length - 1].getUsdTokens.call(); - assert.equal(tokens, _usdToken[stoId], "USD Tokens should match"); + assert.equal(tokens[0], I_DaiToken.address, "USD Tokens should match"); }); it("Should successfully attach the fifth STO module to the security token", async () => { From 3b983b5eeee2a1c59dec41c28bc22318c3f850db Mon Sep 17 00:00:00 2001 From: shuffledex Date: Thu, 6 Dec 2018 13:10:57 -0300 Subject: [PATCH 18/30] code advances --- CLI/commands/investor_portal.js | 101 ++++++++++++++++++++++++-------- CLI/commands/sto_manager.js | 3 +- package.json | 1 + 3 files changed, 77 insertions(+), 28 deletions(-) diff --git a/CLI/commands/investor_portal.js b/CLI/commands/investor_portal.js index 4b01705b3..54aba70c4 100644 --- a/CLI/commands/investor_portal.js +++ b/CLI/commands/investor_portal.js @@ -10,6 +10,8 @@ var contracts = require('./helpers/contract_addresses'); var abis = require('./helpers/contract_abis'); const STO_KEY = 3; +const ETH = 'ETH'; +const POLY = 'POLY'; const STABLE = 'STABLE'; let securityTokenRegistry; @@ -17,7 +19,6 @@ let securityToken; let selectedSTO; let currentSTO; let polyToken; -let usdToken; let generalTransferManager; let raiseTypes = []; @@ -90,10 +91,6 @@ async function setup() { let polytokenABI = abis.polyToken(); polyToken = new web3.eth.Contract(polytokenABI, polytokenAddress); polyToken.setProvider(web3.currentProvider); - - let usdTokenAddress = await contracts.usdToken(); - usdToken = new web3.eth.Contract(polytokenABI, usdTokenAddress); - usdToken.setProvider(web3.currentProvider); } catch (err) { console.log(err); console.log(chalk.red(`There was a problem getting the contracts. Make sure they are deployed to the selected network.`)); @@ -155,6 +152,8 @@ async function showTokenInfo() { // Show info async function showUserInfo(_user) { + let listOfStableCoins = await currentSTO.methods.getUsdTokens().call(); + console.log(` ******************* User Information ******************** - Address: ${_user}`); @@ -165,7 +164,10 @@ async function showUserInfo(_user) { console.log(` - ETH balance:\t ${web3.utils.fromWei(await web3.eth.getBalance(_user))}`); } if (await currentSTO.methods.fundRaiseTypes(gbl.constants.FUND_RAISE_TYPES.STABLE).call()) { - console.log(` - DAI balance:\t ${await usdBalance(_user)}`); + let stableSymbolsAndBalance = await processAddressWithBalance(listOfStableCoins); + stableSymbolsAndBalance.forEach(stable => { + console.log(` - ${stable.symbol} balance:\t ${web3.utils.fromWei(stable.balance)}`); + }); } } @@ -238,6 +240,16 @@ async function showCappedSTOInfo() { } } +async function processAddressWithBalance(array) { + let list = []; + for (const address of array) { + let symbol = await checkSymbol(address); + let balance = await checkBalance(address); + list.push({'address': address, 'symbol': symbol, 'balance': balance}) + } + return list +} + async function processAddress(array) { let list = []; for (const address of array) { @@ -256,11 +268,19 @@ async function checkSymbol(address) { } } +async function checkBalance(address) { + let stableCoin = common.connect(abis.erc20(), address); + try { + return await stableCoin.methods.balanceOf(User.address).call(); + } catch (e) { + return "" + } +} + async function showUserInfoForUSDTieredSTO() { let stableSymbols = []; - //REMOVE ONCE SMART CONTRACT SUPPORT METHOD TO GET STABLE COIN ADDRESSES - let listOfStableCoins = ["0xa016B2ae79436E20FBe22Bf230a92A5Fb055762F", "0xae794d38cb481868a8CB19b9d7A5073851bC6dB7"]; + let listOfStableCoins = await currentSTO.methods.getUsdTokens().call(); for (const fundType in gbl.constants.FUND_RAISE_TYPES) { if (await currentSTO.methods.fundRaiseTypes(gbl.constants.FUND_RAISE_TYPES[fundType]).call()) { @@ -311,8 +331,7 @@ async function showUSDTieredSTOInfo() { let displayTokenSymbol = await securityToken.methods.symbol().call(); let tiersLength = await currentSTO.methods.getNumberOfTiers().call(); let stableSymbols = []; - //REMOVE ONCE SMART CONTRACT SUPPORT METHOD TO GET STABLE COIN ADDRESSES - let listOfStableCoins = ["0xa016B2ae79436E20FBe22Bf230a92A5Fb055762F", "0xae794d38cb481868a8CB19b9d7A5073851bC6dB7"]; + let listOfStableCoins = await currentSTO.methods.getUsdTokens().call(); for (const fundType in gbl.constants.FUND_RAISE_TYPES) { if (await currentSTO.methods.fundRaiseTypes(gbl.constants.FUND_RAISE_TYPES[fundType]).call()) { @@ -496,7 +515,11 @@ async function investCappedSTO(currency, amount) { // Allow investor to buy tokens. async function investUsdTieredSTO(currency, amount) { + let listOfStableCoins = await currentSTO.methods.getUsdTokens().call(); + let stableSymbols = await processAddress(listOfStableCoins); + let raiseType; + if (typeof currency !== 'undefined') { if (!raiseTypes.inlcudes(currency)) { console.log(chalk.red(`${currency} is not allowed for current STO`)); @@ -507,9 +530,20 @@ async function investUsdTieredSTO(currency, amount) { } else { for (const type of raiseTypes) { let displayPrice = web3.utils.fromWei(await currentSTO.methods.convertToUSD(gbl.constants.FUND_RAISE_TYPES[type], web3.utils.toWei("1")).call()); - console.log(chalk.green(` Current ${type} price:\t\t ${displayPrice} USD`)); + if ((type == STABLE) && (stableSymbols.length)) { + console.log(chalk.green(` Current ${stableSymbols.toString()} price:\t\t ${displayPrice} USD`)); + } else { + console.log(chalk.green(` Current ${type} price:\t\t ${displayPrice} USD`)); + } } if (raiseTypes.length > 1) { + const stableIndex = raiseTypes.indexOf(STABLE); + if (stableIndex > -1) { + raiseTypes.splice(stableIndex, 1) + stableSymbols.forEach((symbol) => { + raiseTypes.push(symbol) + }) + } let index = readlineSync.keyInSelect(raiseTypes, 'Choose one of the allowed raise types: ', { cancel: false }); raiseType = raiseTypes[index]; } else { @@ -522,7 +556,15 @@ async function investUsdTieredSTO(currency, amount) { if (typeof amount === 'undefined') { let investorInvestedUSD = web3.utils.fromWei(await currentSTO.methods.investorInvestedUSD(User.address).call()); let minimumInvestmentUSD = await currentSTO.methods.minimumInvestmentUSD().call(); - let minimumInvestmentRaiseType = await currentSTO.methods.convertFromUSD(gbl.constants.FUND_RAISE_TYPES[raiseType], minimumInvestmentUSD).call(); + let minimumInvestmentRaiseType; + + // if raiseType is different than ETH or POLY, we assume is STABLE + if ((raiseType != ETH) && (raiseType != POLY)) { + minimumInvestmentRaiseType = await currentSTO.methods.convertFromUSD(gbl.constants.FUND_RAISE_TYPES[STABLE], minimumInvestmentUSD).call(); + } else { + minimumInvestmentRaiseType = await currentSTO.methods.convertFromUSD(gbl.constants.FUND_RAISE_TYPES[raiseType], minimumInvestmentUSD).call(); + } + cost = readlineSync.question(chalk.yellow(`Enter the amount of ${raiseType} you would like to invest or press 'Enter' to exit: `), { limit: function (input) { return investorInvestedUSD != 0 || parseInt(input) > parseInt(web3.utils.fromWei(minimumInvestmentRaiseType)); @@ -536,7 +578,14 @@ async function investUsdTieredSTO(currency, amount) { let costWei = web3.utils.toWei(cost.toString()); - let tokensToBuy = await currentSTO.methods.buyTokensView(User.address, costWei, gbl.constants.FUND_RAISE_TYPES[raiseType]).call(); + let tokensToBuy; + // if raiseType is different than ETH or POLY, we assume is STABLE + if ((raiseType != ETH) && (raiseType != POLY)) { + tokensToBuy = await currentSTO.methods.buyTokensView(User.address, costWei, gbl.constants.FUND_RAISE_TYPES[STABLE]).call(); + } else { + tokensToBuy = await currentSTO.methods.buyTokensView(User.address, costWei, gbl.constants.FUND_RAISE_TYPES[raiseType]).call(); + } + let minTokenToBuy = tokensToBuy.tokensMinted; console.log(chalk.yellow(`You are going to spend ${web3.utils.fromWei(tokensToBuy.spentValue)} ${raiseType} (${web3.utils.fromWei(tokensToBuy.spentUSD)} USD) to buy ${web3.utils.fromWei(minTokenToBuy)} ${STSymbol} approx.`)); console.log(chalk.yellow(`Due to ${raiseType} price changes and network delays, it is possible that the final amount of purchased tokens is lower.`)); @@ -544,7 +593,7 @@ async function investUsdTieredSTO(currency, amount) { minTokenToBuy = 0; } - if (raiseType == 'POLY') { + if (raiseType == POLY) { let userBalance = await polyBalance(User.address); if (parseInt(userBalance) >= parseInt(cost)) { let allowance = await polyToken.methods.allowance(STOAddress, User.address).call(); @@ -560,19 +609,24 @@ async function investUsdTieredSTO(currency, amount) { console.log(chalk.red(`Please purchase a smaller amount of tokens or access the POLY faucet to get the POLY to complete this txn.`)); process.exit(); } - } else if (raiseType == 'DAI') { - let userBalance = await usdBalance(User.address); - if (parseInt(userBalance) >= parseInt(cost)) { - let allowance = await usdToken.methods.allowance(STOAddress, User.address).call(); + } else if ((raiseType != POLY) && (raiseType != ETH)) { + + let listOfStableCoins = await currentSTO.methods.getUsdTokens().call(); + let stableSymbolsAndBalance = await processAddressWithBalance(listOfStableCoins); + let stableInfo = stableSymbolsAndBalance.find(o => o.symbol === raiseType); + + if (parseInt(stableInfo.balance) >= parseInt(cost)) { + let stableCoin = common.connect(abis.erc20(), stableInfo.address); + let allowance = await stableCoin.methods.allowance(STOAddress, User.address).call(); if (allowance < costWei) { - let approveAction = usdToken.methods.approve(STOAddress, costWei); + let approveAction = stableCoin.methods.approve(STOAddress, costWei); await common.sendTransaction(approveAction, { from: User }); } - let actionBuyWithUSD = currentSTO.methods.buyWithUSDRateLimited(User.address, costWei, minTokenToBuy); + let actionBuyWithUSD = currentSTO.methods.buyWithUSDRateLimited(User.address, costWei, minTokenToBuy, stableInfo.address); let receipt = await common.sendTransaction(actionBuyWithUSD, { from: User, factor: 1.5 }); logTokensPurchasedUSDTieredSTO(receipt); } else { - console.log(chalk.red(`Not enough balance to Buy tokens, Require ${cost} DAI but have ${userBalance} DAI.`)); + console.log(chalk.red(`Not enough balance to Buy tokens, Require ${cost} ${stableInfo.symbol} but have ${stableInfo.balance} ${stableInfo.symbol}.`)); console.log(chalk.red(`Please purchase a smaller amount of tokens.`)); process.exit(); } @@ -591,11 +645,6 @@ async function polyBalance(_user) { return web3.utils.fromWei(balance); } -async function usdBalance(_user) { - let balance = await usdToken.methods.balanceOf(_user).call(); - return web3.utils.fromWei(balance); -} - function logTokensPurchasedUSDTieredSTO(receipt) { console.log(chalk.green(`Congratulations! The token purchase was successfully completed.`)); let events = common.getMultipleEventsFromLogs(currentSTO._jsonInterface, receipt.logs, 'TokenPurchase'); diff --git a/CLI/commands/sto_manager.js b/CLI/commands/sto_manager.js index 316bdceb7..d9e95f723 100644 --- a/CLI/commands/sto_manager.js +++ b/CLI/commands/sto_manager.js @@ -577,8 +577,7 @@ async function usdTieredSTO_status(currentSTO) { let displayIsFinalized = await currentSTO.methods.isFinalized().call() ? "YES" : "NO"; let displayTokenSymbol = await securityToken.methods.symbol().call(); let tiersLength = await currentSTO.methods.getNumberOfTiers().call(); - //REMOVE ONCE SMART CONTRACT SUPPORT METHOD TO GET STABLE COIN ADDRESSES - let listOfStableCoins = ["0xa016B2ae79436E20FBe22Bf230a92A5Fb055762F", "0xae794d38cb481868a8CB19b9d7A5073851bC6dB7"]; + let listOfStableCoins = await currentSTO.methods.getUsdTokens().call(); let raiseTypes = []; let stableSymbols = []; diff --git a/package.json b/package.json index ef2d76c38..7341ea9da 100644 --- a/package.json +++ b/package.json @@ -63,6 +63,7 @@ "bignumber.js": "^5.0.0", "chalk": "^2.4.1", "coveralls": "^3.0.1", + "csv-parse": "^4.0.1", "ethereumjs-testrpc": "^6.0.3", "ethers": "^3.0.15", "fs": "0.0.1-security", From e14a18e71a761d69c21e773c78907885f50843c5 Mon Sep 17 00:00:00 2001 From: shuffledex Date: Thu, 6 Dec 2018 18:51:43 -0300 Subject: [PATCH 19/30] final code --- CLI/commands/investor_portal.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CLI/commands/investor_portal.js b/CLI/commands/investor_portal.js index 54aba70c4..bbebc003c 100644 --- a/CLI/commands/investor_portal.js +++ b/CLI/commands/investor_portal.js @@ -59,17 +59,18 @@ async function executeApp(investorAddress, investorPrivKey, symbol, currency, am try { await inputSymbol(symbol); - await showUserInfo(User.address); switch (selectedSTO) { case 'CappedSTO': let cappedSTOABI = abis.cappedSTO(); currentSTO = new web3.eth.Contract(cappedSTOABI, STOAddress); + await showUserInfo(User.address); await showCappedSTOInfo(); await investCappedSTO(currency, amount); break; case 'USDTieredSTO': let usdTieredSTOABI = abis.usdTieredSTO(); currentSTO = new web3.eth.Contract(usdTieredSTOABI, STOAddress); + await showUserInfo(User.address); await showUserInfoForUSDTieredSTO(); await showUSDTieredSTOInfo(); await investUsdTieredSTO(currency, amount) From 4d9af7b243f5fce5f89c6b9e02ddd0ecfd6592fd Mon Sep 17 00:00:00 2001 From: shuffledex Date: Mon, 10 Dec 2018 09:42:47 -0300 Subject: [PATCH 20/30] PR fixes --- CLI/commands/sto_manager.js | 93 +++++++++++++++++++------------------ 1 file changed, 49 insertions(+), 44 deletions(-) diff --git a/CLI/commands/sto_manager.js b/CLI/commands/sto_manager.js index d9e95f723..796f7387f 100644 --- a/CLI/commands/sto_manager.js +++ b/CLI/commands/sto_manager.js @@ -295,60 +295,65 @@ function fundingConfigUSDTieredSTO() { } async function addressesConfigUSDTieredSTO(usdTokenRaise) { - let addresses = {}; - addresses.wallet = readlineSync.question('Enter the address that will receive the funds from the STO (' + Issuer.address + '): ', { - limit: function (input) { - return web3.utils.isAddress(input); - }, - limitMessage: "Must be a valid address", - defaultInput: Issuer.address - }); - if (addresses.wallet == "") addresses.wallet = Issuer.address; + let addresses, menu; - addresses.reserveWallet = readlineSync.question('Enter the address that will receive remaining tokens in the case the cap is not met (' + Issuer.address + '): ', { - limit: function (input) { - return web3.utils.isAddress(input); - }, - limitMessage: "Must be a valid address", - defaultInput: Issuer.address - }); - if (addresses.reserveWallet == "") addresses.reserveWallet = Issuer.address; + do { - let listOfAddress; + addresses = {}; - if (usdTokenRaise) { - addresses.usdToken = readlineSync.question('Enter the address (or many addresses that you want separated by comma) of the USD Token or stable coin (' + usdToken.options.address + '): ', { + addresses.wallet = readlineSync.question('Enter the address that will receive the funds from the STO (' + Issuer.address + '): ', { limit: function (input) { - listOfAddress = input.split(','); - let response = true - listOfAddress.forEach((addr) => { - if (!web3.utils.isAddress(addr)) { - response = false - } - }) - return response + return web3.utils.isAddress(input); + }, + limitMessage: "Must be a valid address", + defaultInput: Issuer.address + }); + if (addresses.wallet == "") addresses.wallet = Issuer.address; + + addresses.reserveWallet = readlineSync.question('Enter the address that will receive remaining tokens in the case the cap is not met (' + Issuer.address + '): ', { + limit: function (input) { + return web3.utils.isAddress(input); }, limitMessage: "Must be a valid address", - defaultInput: usdToken.options.address + defaultInput: Issuer.address }); - if (addresses.usdToken == "") { - listOfAddress = [usdToken.options.address] - addresses.usdToken = [usdToken.options.address]; + if (addresses.reserveWallet == "") addresses.reserveWallet = Issuer.address; + + let listOfAddress; + + if (usdTokenRaise) { + addresses.usdToken = readlineSync.question('Enter the address (or many addresses that you want separated by comma) of the USD Token or stable coin (' + usdToken.options.address + '): ', { + limit: function (input) { + listOfAddress = input.split(','); + return listOfAddress.every((addr) => { + return web3.utils.isAddress(addr) + }) + }, + limitMessage: "Must be a valid address", + defaultInput: usdToken.options.address + }); + if (addresses.usdToken == "") { + listOfAddress = [usdToken.options.address] + addresses.usdToken = [usdToken.options.address]; + } + } else { + listOfAddress = ['0x0000000000000000000000000000000000000000'] + addresses.usdToken = ['0x0000000000000000000000000000000000000000']; + } + + if (!await processArray(listOfAddress)) { + console.log(chalk.yellow(`\nPlease, verify your stable coins addresses to continue with this process.\n`)) + menu = true; + } else { + menu = false; + } + + if (typeof addresses.usdToken === 'string') { + addresses.usdToken = addresses.usdToken.split(",") } - } else { - listOfAddress = ['0x0000000000000000000000000000000000000000'] - addresses.usdToken = ['0x0000000000000000000000000000000000000000']; - } - - if (!await processArray(listOfAddress)) { - console.log(`Please, verify your stable coins addresses to continue with this process.`) - process.exit(0) - } - if (typeof addresses.usdToken === 'string') { - addresses.usdToken = addresses.usdToken.split(",") - } + } while (menu); return addresses; } From 09be6ccb43292636370037aadbbf4d93ef469597 Mon Sep 17 00:00:00 2001 From: F O'Brien <42175565+F-OBrien@users.noreply.github.com> Date: Mon, 10 Dec 2018 20:33:07 +0530 Subject: [PATCH 21/30] Update CLI/commands/sto_manager.js Co-Authored-By: maxsam4 --- CLI/commands/sto_manager.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CLI/commands/sto_manager.js b/CLI/commands/sto_manager.js index 796f7387f..c4559b048 100644 --- a/CLI/commands/sto_manager.js +++ b/CLI/commands/sto_manager.js @@ -323,7 +323,7 @@ async function addressesConfigUSDTieredSTO(usdTokenRaise) { let listOfAddress; if (usdTokenRaise) { - addresses.usdToken = readlineSync.question('Enter the address (or many addresses that you want separated by comma) of the USD Token or stable coin (' + usdToken.options.address + '): ', { + addresses.usdToken = readlineSync.question('Enter the address (or multiple addresses separated by commas) of the USD stable coin(s) (' + usdToken.options.address + '): ', { limit: function (input) { listOfAddress = input.split(','); return listOfAddress.every((addr) => { @@ -1045,4 +1045,4 @@ module.exports = { await initialize(_tokenSymbol); return addSTOModule(stoConfig) } -} \ No newline at end of file +} From 4a94e71b265edff915d26cef46bc800a703405be Mon Sep 17 00:00:00 2001 From: Mudit Gupta Date: Mon, 10 Dec 2018 20:36:19 +0530 Subject: [PATCH 22/30] Added native currency raised --- contracts/modules/STO/USDTieredSTO.sol | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/contracts/modules/STO/USDTieredSTO.sol b/contracts/modules/STO/USDTieredSTO.sol index d3be00047..590234a28 100644 --- a/contracts/modules/STO/USDTieredSTO.sol +++ b/contracts/modules/STO/USDTieredSTO.sol @@ -66,6 +66,9 @@ contract USDTieredSTO is ISTO, ReentrancyGuard { // Amount of USD funds raised uint256 public fundsRaisedUSD; + // Amount of native currencies raised. 0x0 address is used for ETH. + mapping (address => uint256) public currencyRaised; + // Amount in USD invested by each address mapping (address => uint256) public investorInvestedUSD; @@ -447,6 +450,7 @@ contract USDTieredSTO is ISTO, ReentrancyGuard { // Modify storage investorInvested[_beneficiary][uint8(FundRaiseType.ETH)] = investorInvested[_beneficiary][uint8(FundRaiseType.ETH)].add(spentValue); fundsRaised[uint8(FundRaiseType.ETH)] = fundsRaised[uint8(FundRaiseType.ETH)].add(spentValue); + currencyRaised[address(0)] = currencyRaised[address(0)].add(spentValue); // Forward ETH to issuer wallet wallet.transfer(spentValue); // Refund excess ETH to investor wallet @@ -486,6 +490,7 @@ contract USDTieredSTO is ISTO, ReentrancyGuard { // Modify storage investorInvested[_beneficiary][uint8(_fundRaiseType)] = investorInvested[_beneficiary][uint8(_fundRaiseType)].add(spentValue); fundsRaised[uint8(_fundRaiseType)] = fundsRaised[uint8(_fundRaiseType)].add(spentValue); + currencyRaised[address(_token)] = currencyRaised[address(_token)].add(spentValue); // Forward coins to issuer wallet require(_token.transferFrom(msg.sender, wallet, spentValue), "Transfer failed"); emit FundsReceived(msg.sender, _beneficiary, spentUSD, _fundRaiseType, _tokenAmount, spentValue, rate); From ac7541f2c59ebec789d1f24a0898fcea04cab722 Mon Sep 17 00:00:00 2001 From: Mudit Gupta Date: Tue, 11 Dec 2018 09:24:15 +0530 Subject: [PATCH 23/30] Merge fix --- contracts/modules/STO/USDTieredSTO.sol | 82 ------------------- contracts/modules/STO/USDTieredSTOStorage.sol | 16 +++- 2 files changed, 13 insertions(+), 85 deletions(-) diff --git a/contracts/modules/STO/USDTieredSTO.sol b/contracts/modules/STO/USDTieredSTO.sol index aedab75d1..c45bb4ac3 100644 --- a/contracts/modules/STO/USDTieredSTO.sol +++ b/contracts/modules/STO/USDTieredSTO.sol @@ -14,88 +14,6 @@ import "./USDTieredSTOStorage.sol"; */ contract USDTieredSTO is USDTieredSTOStorage, ISTO, ReentrancyGuard { using SafeMath for uint256; - - ///////////// - // Storage // - ///////////// - struct Tier { - // NB rates mentioned below are actually price and are used like price in the logic. - // How many token units a buyer gets per USD in this tier (multiplied by 10**18) - uint256 rate; - - // How many token units a buyer gets per USD in this tier (multiplied by 10**18) when investing in POLY up to tokensDiscountPoly - uint256 rateDiscountPoly; - - // How many tokens are available in this tier (relative to totalSupply) - uint256 tokenTotal; - - // How many token units are available in this tier (relative to totalSupply) at the ratePerTierDiscountPoly rate - uint256 tokensDiscountPoly; - - // How many tokens have been minted in this tier (relative to totalSupply) - uint256 mintedTotal; - - // How many tokens have been minted in this tier (relative to totalSupply) for each fund raise type - mapping (uint8 => uint256) minted; - - // How many tokens have been minted in this tier (relative to totalSupply) at discounted POLY rate - uint256 mintedDiscountPoly; - } - - string public POLY_ORACLE = "PolyUsdOracle"; - string public ETH_ORACLE = "EthUsdOracle"; - mapping (bytes32 => mapping (bytes32 => string)) oracleKeys; - - // Determine whether users can invest on behalf of a beneficiary - bool public allowBeneficialInvestments = false; - - // Whether or not the STO has been finalized - bool public isFinalized; - - // Address where ETH, POLY & Stable Coin funds are delivered - address public wallet; - - // Address of issuer reserve wallet for unsold tokens - address public reserveWallet; - - // List of stable coin addresses - address[] public usdTokens; - - // Current tier - uint256 public currentTier; - - // Amount of USD funds raised - uint256 public fundsRaisedUSD; - - // Amount of native currencies raised. 0x0 address is used for ETH. - mapping (address => uint256) public currencyRaised; - - // Amount in USD invested by each address - mapping (address => uint256) public investorInvestedUSD; - - // Amount in fund raise type invested by each investor - mapping (address => mapping (uint8 => uint256)) public investorInvested; - - // List of accredited investors - mapping (address => bool) public accredited; - - // List of active stable coin addresses - mapping (address => bool) public usdTokenEnabled; - - // Default limit in USD for non-accredited investors multiplied by 10**18 - uint256 public nonAccreditedLimitUSD; - - // Overrides for default limit in USD for non-accredited investors multiplied by 10**18 - mapping (address => uint256) public nonAccreditedLimitUSDOverride; - - // Minimum investable amount in USD - uint256 public minimumInvestmentUSD; - - // Final amount of tokens returned to issuer - uint256 public finalAmountReturned; - - // Array of Tiers - Tier[] public tiers; string public constant POLY_ORACLE = "PolyUsdOracle"; string public constant ETH_ORACLE = "EthUsdOracle"; diff --git a/contracts/modules/STO/USDTieredSTOStorage.sol b/contracts/modules/STO/USDTieredSTOStorage.sol index 031792f42..d9184021a 100644 --- a/contracts/modules/STO/USDTieredSTOStorage.sol +++ b/contracts/modules/STO/USDTieredSTOStorage.sol @@ -11,6 +11,7 @@ contract USDTieredSTOStorage { // Storage // ///////////// struct Tier { + // NB rates mentioned below are actually price and are used like price in the logic. // How many token units a buyer gets per USD in this tier (multiplied by 10**18) uint256 rate; @@ -33,28 +34,34 @@ contract USDTieredSTOStorage { uint256 mintedDiscountPoly; } + string public POLY_ORACLE = "PolyUsdOracle"; + string public ETH_ORACLE = "EthUsdOracle"; mapping (bytes32 => mapping (bytes32 => string)) oracleKeys; - IERC20 public usdToken; - // Determine whether users can invest on behalf of a beneficiary bool public allowBeneficialInvestments = false; // Whether or not the STO has been finalized bool public isFinalized; - // Address where ETH, POLY & DAI funds are delivered + // Address where ETH, POLY & Stable Coin funds are delivered address public wallet; // Address of issuer reserve wallet for unsold tokens address public reserveWallet; + // List of stable coin addresses + address[] public usdTokens; + // Current tier uint256 public currentTier; // Amount of USD funds raised uint256 public fundsRaisedUSD; + // Amount of native currencies raised. 0x0 address is used for ETH. + mapping (address => uint256) public currencyRaised; + // Amount in USD invested by each address mapping (address => uint256) public investorInvestedUSD; @@ -64,6 +71,9 @@ contract USDTieredSTOStorage { // List of accredited investors mapping (address => bool) public accredited; + // List of active stable coin addresses + mapping (address => bool) public usdTokenEnabled; + // Default limit in USD for non-accredited investors multiplied by 10**18 uint256 public nonAccreditedLimitUSD; From 3477f0f5fe537f12778442ea9a7ea5e91e55608d Mon Sep 17 00:00:00 2001 From: Mudit Gupta Date: Tue, 11 Dec 2018 09:56:49 +0530 Subject: [PATCH 24/30] Minor fixes --- contracts/modules/STO/ISTO.sol | 17 +---------------- contracts/modules/STO/USDTieredSTO.sol | 2 +- test/p_usd_tiered_sto.js | 2 +- 3 files changed, 3 insertions(+), 18 deletions(-) diff --git a/contracts/modules/STO/ISTO.sol b/contracts/modules/STO/ISTO.sol index 1672a8626..6fb216566 100644 --- a/contracts/modules/STO/ISTO.sol +++ b/contracts/modules/STO/ISTO.sol @@ -13,22 +13,7 @@ contract ISTO is ISTOStorage, Module, Pausable { using SafeMath for uint256; enum FundRaiseType { ETH, POLY, SC } - mapping (uint8 => bool) public fundRaiseTypes; - mapping (uint8 => uint256) public fundsRaised; - - // Start time of the STO - uint256 public startTime; - // End time of the STO - uint256 public endTime; - // Time STO was paused - uint256 public pausedTime; - // Number of individual investors - uint256 public investorCount; - // Address where ETH & POLY funds are delivered - address public wallet; - // Final amount of tokens sold - uint256 public totalTokensSold; - + // Event event SetFundRaiseTypes(FundRaiseType[] _fundRaiseTypes); diff --git a/contracts/modules/STO/USDTieredSTO.sol b/contracts/modules/STO/USDTieredSTO.sol index c45bb4ac3..0fa9bea63 100644 --- a/contracts/modules/STO/USDTieredSTO.sol +++ b/contracts/modules/STO/USDTieredSTO.sol @@ -14,7 +14,7 @@ import "./USDTieredSTOStorage.sol"; */ contract USDTieredSTO is USDTieredSTOStorage, ISTO, ReentrancyGuard { using SafeMath for uint256; - + string public constant POLY_ORACLE = "PolyUsdOracle"; string public constant ETH_ORACLE = "EthUsdOracle"; diff --git a/test/p_usd_tiered_sto.js b/test/p_usd_tiered_sto.js index c368432b7..6a857139c 100644 --- a/test/p_usd_tiered_sto.js +++ b/test/p_usd_tiered_sto.js @@ -325,7 +325,7 @@ contract("USDTieredSTO", accounts => { assert.equal(web3.utils.hexToString(tx.logs[2].args._name), "USDTieredSTO", "USDTieredSTOFactory module was not added"); I_USDTieredSTO_Array.push(USDTieredSTO.at(tx.logs[2].args._module)); - assert.equal(await I_USDTieredSTO_Array[stoId].startTime.call(), _startTime[stoId], "Incorrect _startTime in config"); + assert.equal((await I_USDTieredSTO_Array[stoId].startTime.call()).toNumber(), _startTime[stoId], "Incorrect _startTime in config"); assert.equal(await I_USDTieredSTO_Array[stoId].endTime.call(), _endTime[stoId], "Incorrect _endTime in config"); for (var i = 0; i < _ratePerTier[stoId].length; i++) { assert.equal( From 6e2768a45c3e444c8cdb8e7339023ec1cade0650 Mon Sep 17 00:00:00 2001 From: Mudit Gupta Date: Tue, 11 Dec 2018 10:00:45 +0530 Subject: [PATCH 25/30] Updated changelog --- CHANGELOG.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3879a73e9..b53323fb7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,11 +6,12 @@ All notable changes to this project will be documented in this file. [__2.1.0__](https://www.npmjs.com/package/polymath-core?activeTab=readme) __13-09-18__ -## CappedSTO 2.0.1 +## CappedSTO 2.1.0 * `rate` is now accepted as multiplied by 10^18 to allow settting higher price than 1ETH/POLY per token. * Indivisble tokens are now supported. When trying to buy partial tokens, allowed full units of tokens will be purchased and remaining funds will be returned. -## USDTieredSTO 2.0.1 +## USDTieredSTO 2.1.0 +* Added `currencyRaised` function that returns native currency raised. It takes address of fund coin as parameter, use 0x0 for ETH. * Added support for multiple stable coins in USDTSTO. * Added `buyTokensView` and `getTokensMintedByTier` to USDTSTO. * Added `getSTODetails` to USDTSTO. From 9410fbe1df45f15dbb81cc77564064fbe4fd381c Mon Sep 17 00:00:00 2001 From: Mudit Gupta Date: Tue, 11 Dec 2018 10:58:45 +0530 Subject: [PATCH 26/30] Changed currencyRaised to stableCoinsRaised --- CHANGELOG.md | 2 +- contracts/modules/STO/USDTieredSTO.sol | 4 ++-- contracts/modules/STO/USDTieredSTOStorage.sol | 6 ++---- test/p_usd_tiered_sto.js | 5 +++++ 4 files changed, 10 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b53323fb7..27fc35c18 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,7 +11,7 @@ All notable changes to this project will be documented in this file. * Indivisble tokens are now supported. When trying to buy partial tokens, allowed full units of tokens will be purchased and remaining funds will be returned. ## USDTieredSTO 2.1.0 -* Added `currencyRaised` function that returns native currency raised. It takes address of fund coin as parameter, use 0x0 for ETH. +* Added `stableCoinsRaised` function that returns amount of individual stable coin raised when address of that stable coin is passed. * Added support for multiple stable coins in USDTSTO. * Added `buyTokensView` and `getTokensMintedByTier` to USDTSTO. * Added `getSTODetails` to USDTSTO. diff --git a/contracts/modules/STO/USDTieredSTO.sol b/contracts/modules/STO/USDTieredSTO.sol index 0fa9bea63..81386c1bd 100644 --- a/contracts/modules/STO/USDTieredSTO.sol +++ b/contracts/modules/STO/USDTieredSTO.sol @@ -373,7 +373,6 @@ contract USDTieredSTO is USDTieredSTOStorage, ISTO, ReentrancyGuard { // Modify storage investorInvested[_beneficiary][uint8(FundRaiseType.ETH)] = investorInvested[_beneficiary][uint8(FundRaiseType.ETH)].add(spentValue); fundsRaised[uint8(FundRaiseType.ETH)] = fundsRaised[uint8(FundRaiseType.ETH)].add(spentValue); - currencyRaised[address(0)] = currencyRaised[address(0)].add(spentValue); // Forward ETH to issuer wallet wallet.transfer(spentValue); // Refund excess ETH to investor wallet @@ -413,7 +412,8 @@ contract USDTieredSTO is USDTieredSTOStorage, ISTO, ReentrancyGuard { // Modify storage investorInvested[_beneficiary][uint8(_fundRaiseType)] = investorInvested[_beneficiary][uint8(_fundRaiseType)].add(spentValue); fundsRaised[uint8(_fundRaiseType)] = fundsRaised[uint8(_fundRaiseType)].add(spentValue); - currencyRaised[address(_token)] = currencyRaised[address(_token)].add(spentValue); + if(address(_token) != address(polyToken)) + stableCoinsRaised[address(_token)] = stableCoinsRaised[address(_token)].add(spentValue); // Forward coins to issuer wallet require(_token.transferFrom(msg.sender, wallet, spentValue), "Transfer failed"); emit FundsReceived(msg.sender, _beneficiary, spentUSD, _fundRaiseType, _tokenAmount, spentValue, rate); diff --git a/contracts/modules/STO/USDTieredSTOStorage.sol b/contracts/modules/STO/USDTieredSTOStorage.sol index d9184021a..5d9581408 100644 --- a/contracts/modules/STO/USDTieredSTOStorage.sol +++ b/contracts/modules/STO/USDTieredSTOStorage.sol @@ -34,8 +34,6 @@ contract USDTieredSTOStorage { uint256 mintedDiscountPoly; } - string public POLY_ORACLE = "PolyUsdOracle"; - string public ETH_ORACLE = "EthUsdOracle"; mapping (bytes32 => mapping (bytes32 => string)) oracleKeys; // Determine whether users can invest on behalf of a beneficiary @@ -59,8 +57,8 @@ contract USDTieredSTOStorage { // Amount of USD funds raised uint256 public fundsRaisedUSD; - // Amount of native currencies raised. 0x0 address is used for ETH. - mapping (address => uint256) public currencyRaised; + // Amount of stable coins raised + mapping (address => uint256) public stableCoinsRaised; // Amount in USD invested by each address mapping (address => uint256) public investorInvestedUSD; diff --git a/test/p_usd_tiered_sto.js b/test/p_usd_tiered_sto.js index 6a857139c..5fad46f6f 100644 --- a/test/p_usd_tiered_sto.js +++ b/test/p_usd_tiered_sto.js @@ -1829,6 +1829,11 @@ contract("USDTieredSTO", accounts => { init_WalletDAIBal.add(investment_DAI).toNumber(), "Wallet DAI Balance not changed as expected" ); + assert.equal( + (await I_USDTieredSTO_Array[stoId].stableCoinsRaised.call(I_DaiToken.address)).toNumber(), + investment_DAI.toNumber(), + "DAI Raised not changed as expected" + ); }); it("should successfully buy using fallback at tier 0 for ACCREDITED1", async () => { From 9b4e7b24e9d8358f0c11cf1ff5b14a43135b044f Mon Sep 17 00:00:00 2001 From: shuffledex Date: Tue, 11 Dec 2018 15:52:55 -0300 Subject: [PATCH 27/30] individual balance for every stable coin listed --- CLI/commands/investor_portal.js | 34 +++++++++++++----------- CLI/commands/sto_manager.js | 46 ++++++++++++++++++++++++--------- 2 files changed, 53 insertions(+), 27 deletions(-) diff --git a/CLI/commands/investor_portal.js b/CLI/commands/investor_portal.js index bbebc003c..fb4cc153d 100644 --- a/CLI/commands/investor_portal.js +++ b/CLI/commands/investor_portal.js @@ -9,7 +9,6 @@ var gbl = require('./common/global'); var contracts = require('./helpers/contract_addresses'); var abis = require('./helpers/contract_abis'); -const STO_KEY = 3; const ETH = 'ETH'; const POLY = 'POLY'; const STABLE = 'STABLE'; @@ -255,7 +254,7 @@ async function processAddress(array) { let list = []; for (const address of array) { let symbol = await checkSymbol(address); - list.push(symbol) + list.push({"symbol": symbol, "address": address}) } return list } @@ -290,7 +289,7 @@ async function showUserInfoForUSDTieredSTO() } let displayInvestorInvested = web3.utils.fromWei(await currentSTO.methods.investorInvested(User.address, gbl.constants.FUND_RAISE_TYPES[fundType]).call()); if ((fundType == STABLE) && (stableSymbols.length)) { - console.log(` - Invested in ${stableSymbols.toString()}:\t ${displayInvestorInvested} USD`); + console.log(` - Invested in stable coin(s): ${displayInvestorInvested} USD`); } else { console.log(` - Invested in ${fundType}:\t ${displayInvestorInvested} ${fundType}`); } @@ -298,7 +297,7 @@ async function showUserInfoForUSDTieredSTO() } let displayInvestorInvestedUSD = web3.utils.fromWei(await currentSTO.methods.investorInvestedUSD(User.address).call()); - console.log(` - Invested in USD: ${displayInvestorInvestedUSD} USD`); + console.log(` - Total invested in USD: ${displayInvestorInvestedUSD} USD`); await generalTransferManager.methods.whitelist(User.address).call({}, function (error, result) { displayCanBuy = result.canBuyFromSTO; @@ -371,7 +370,7 @@ async function showUSDTieredSTOInfo() { let mintedPerTier = mintedPerTierPerRaiseType[gbl.constants.FUND_RAISE_TYPES[type]]; if ((type == STABLE) && (stableSymbols.length)) { displayMintedPerTierPerType += ` - Sold for ${stableSymbols.toString()}:\t ${web3.utils.fromWei(mintedPerTier)} ${displayTokenSymbol} ${displayDiscountMinted}`; + Sold for stable coin(s): ${web3.utils.fromWei(mintedPerTier)} ${displayTokenSymbol} ${displayDiscountMinted}`; } else { displayMintedPerTierPerType += ` Sold for ${type}:\t\t ${web3.utils.fromWei(mintedPerTier)} ${displayTokenSymbol} ${displayDiscountMinted}`; @@ -396,8 +395,11 @@ async function showUSDTieredSTOInfo() { for (const type of raiseTypes) { let fundsRaised = web3.utils.fromWei(await currentSTO.methods.fundsRaised(gbl.constants.FUND_RAISE_TYPES[type]).call()); if ((type == STABLE) && (stableSymbols.length)) { - displayFundsRaisedPerType += ` - ${stableSymbols.toString()}:\t\t ${fundsRaised} USD`; + stableSymbols.forEach(async (stable) => { + let raised = await getStableCoinsRaised(currentSTO, stable.address); + displayFundsRaisedPerType += ` + ${stable.symbol}:\t\t\t ${web3.utils.fromWei(raised)} ${stable.symbol}`; + }) } else { displayFundsRaisedPerType += ` ${type}:\t\t\t ${fundsRaised} ${type}`; @@ -407,7 +409,7 @@ async function showUSDTieredSTOInfo() { let tokensSoldPerType = web3.utils.fromWei(await currentSTO.methods.getTokensSoldFor(gbl.constants.FUND_RAISE_TYPES[type]).call()); if ((type == STABLE) && (stableSymbols.length)) { displayTokensSoldPerType += ` - Sold for ${stableSymbols.toString()}:\t ${tokensSoldPerType} ${displayTokenSymbol}`; + Sold for stable coin(s): ${tokensSoldPerType} ${displayTokenSymbol}`; } else { displayTokensSoldPerType += ` Sold for ${type}:\t\t ${tokensSoldPerType} ${displayTokenSymbol}`; @@ -418,7 +420,7 @@ async function showUSDTieredSTOInfo() { let displayRaiseType = raiseTypes.join(' - '); //If STO has stable coins, we list them one by one if (stableSymbols.length) { - displayRaiseType = displayRaiseType.replace(STABLE, "") + `${stableSymbols.toString().replace(`,`,` - `)}` + displayRaiseType = displayRaiseType.replace(STABLE, "") + `${stableSymbols.map((obj) => {return obj.symbol}).toString().replace(`,`,` - `)}` } let now = Math.floor(Date.now() / 1000); @@ -452,7 +454,7 @@ async function showUSDTieredSTOInfo() { - Investor count: ${displayInvestorCount} - Funds Raised` + displayFundsRaisedPerType + ` - USD: ${displayFundsRaisedUSD} USD + Total USD: ${displayFundsRaisedUSD} USD `); if (!displayCanBuy) { @@ -470,6 +472,10 @@ async function showUSDTieredSTOInfo() { } } +async function getStableCoinsRaised(currentSTO, address) { + return await currentSTO.methods.stableCoinsRaised(address).call() +} + // Allow investor to buy tokens. async function investCappedSTO(currency, amount) { if (typeof currency !== 'undefined' && !raiseTypes.inlcudes(currency)) { @@ -531,9 +537,7 @@ async function investUsdTieredSTO(currency, amount) { } else { for (const type of raiseTypes) { let displayPrice = web3.utils.fromWei(await currentSTO.methods.convertToUSD(gbl.constants.FUND_RAISE_TYPES[type], web3.utils.toWei("1")).call()); - if ((type == STABLE) && (stableSymbols.length)) { - console.log(chalk.green(` Current ${stableSymbols.toString()} price:\t\t ${displayPrice} USD`)); - } else { + if (!((type == STABLE) && (stableSymbols.length))) { console.log(chalk.green(` Current ${type} price:\t\t ${displayPrice} USD`)); } } @@ -541,8 +545,8 @@ async function investUsdTieredSTO(currency, amount) { const stableIndex = raiseTypes.indexOf(STABLE); if (stableIndex > -1) { raiseTypes.splice(stableIndex, 1) - stableSymbols.forEach((symbol) => { - raiseTypes.push(symbol) + stableSymbols.forEach((stable) => { + raiseTypes.push(stable.symbol) }) } let index = readlineSync.keyInSelect(raiseTypes, 'Choose one of the allowed raise types: ', { cancel: false }); diff --git a/CLI/commands/sto_manager.js b/CLI/commands/sto_manager.js index 9fec111df..fb8ccc32f 100644 --- a/CLI/commands/sto_manager.js +++ b/CLI/commands/sto_manager.js @@ -389,7 +389,7 @@ async function processAddress(array) { let list = []; for (const address of array) { let symbol = await checkSymbol(address); - list.push(symbol) + list.push({"symbol": symbol, "address": address}) } return list } @@ -628,7 +628,7 @@ async function usdTieredSTO_status(currentSTO) { let mintedPerTier = mintedPerTierPerRaiseType[gbl.constants.FUND_RAISE_TYPES[type]]; if ((type == STABLE) && (stableSymbols.length)) { displayMintedPerTierPerType += ` - Sold for ${stableSymbols.toString()}:\t ${web3.utils.fromWei(mintedPerTier)} ${displayTokenSymbol} ${displayDiscountMinted}`; + Sold for stable coin(s): ${web3.utils.fromWei(mintedPerTier)} ${displayTokenSymbol} ${displayDiscountMinted}`; } else { displayMintedPerTierPerType += ` Sold for ${type}:\t\t ${web3.utils.fromWei(mintedPerTier)} ${displayTokenSymbol} ${displayDiscountMinted}`; @@ -655,11 +655,14 @@ async function usdTieredSTO_status(currentSTO) { for (const type of raiseTypes) { let balance = await getBalance(displayWallet, gbl.constants.FUND_RAISE_TYPES[type]); let walletBalance = web3.utils.fromWei(balance); - let walletBalanceUSD = web3.utils.fromWei(await currentSTO.methods.convertToUSD(gbl.constants.FUND_RAISE_TYPES[type], balance).call()); if ((type == STABLE) && (stableSymbols.length)) { - displayWalletBalancePerType += ` - Balance ${stableSymbols.toString()}:\t ${walletBalanceUSD} USD`; + stableSymbols.forEach(async (stable) => { + let raised = await checkStableBalance(displayWallet, stable.address); + displayWalletBalancePerType += ` + Balance ${stable.symbol}:\t\t ${web3.utils.fromWei(raised)} ${stable.symbol}`; + }) } else { + let walletBalanceUSD = web3.utils.fromWei(await currentSTO.methods.convertToUSD(gbl.constants.FUND_RAISE_TYPES[type], balance).call()); displayWalletBalancePerType += ` Balance ${type}:\t\t ${walletBalance} ${type} (${walletBalanceUSD} USD)`; } @@ -668,8 +671,11 @@ async function usdTieredSTO_status(currentSTO) { let reserveWalletBalance = web3.utils.fromWei(balance); let reserveWalletBalanceUSD = web3.utils.fromWei(await currentSTO.methods.convertToUSD(gbl.constants.FUND_RAISE_TYPES[type], balance).call()); if ((type == STABLE) && (stableSymbols.length)) { - displayReserveWalletBalancePerType += ` - Balance ${stableSymbols.toString()}:\t ${reserveWalletBalanceUSD} USD`; + stableSymbols.forEach(async (stable) => { + let raised = await checkStableBalance(displayReserveWallet, stable.address); + displayReserveWalletBalancePerType += ` + Balance ${stable.symbol}:\t\t ${web3.utils.fromWei(raised)} ${stable.symbol}`; + }) } else { displayReserveWalletBalancePerType += ` Balance ${type}:\t\t ${reserveWalletBalance} ${type} (${reserveWalletBalanceUSD} USD)`; @@ -677,8 +683,11 @@ async function usdTieredSTO_status(currentSTO) { let fundsRaised = web3.utils.fromWei(await currentSTO.methods.fundsRaised(gbl.constants.FUND_RAISE_TYPES[type]).call()); if ((type == STABLE) && (stableSymbols.length)) { - displayFundsRaisedPerType += ` - ${stableSymbols.toString()}:\t\t ${fundsRaised} USD`; + stableSymbols.forEach(async (stable) => { + let raised = await getStableCoinsRaised(currentSTO, stable.address); + displayFundsRaisedPerType += ` + ${stable.symbol}:\t\t\t ${web3.utils.fromWei(raised)} ${stable.symbol}`; + }) } else { displayFundsRaisedPerType += ` ${type}:\t\t\t ${fundsRaised} ${type}`; @@ -689,7 +698,7 @@ async function usdTieredSTO_status(currentSTO) { let tokensSoldPerType = web3.utils.fromWei(await currentSTO.methods.getTokensSoldFor(gbl.constants.FUND_RAISE_TYPES[type]).call()); if ((type == STABLE) && (stableSymbols.length)) { displayTokensSoldPerType += ` - Sold for ${stableSymbols.toString()}:\t ${tokensSoldPerType} ${displayTokenSymbol}`; + Sold for stable coin(s): ${tokensSoldPerType} ${displayTokenSymbol}`; } else { displayTokensSoldPerType += ` Sold for ${type}:\t\t ${tokensSoldPerType} ${displayTokenSymbol}`; @@ -700,7 +709,7 @@ async function usdTieredSTO_status(currentSTO) { let displayRaiseType = raiseTypes.join(' - '); //If STO has stable coins, we list them one by one if (stableSymbols.length) { - displayRaiseType = displayRaiseType.replace(STABLE, "") + `${stableSymbols.toString().replace(`,`,` - `)}` + displayRaiseType = displayRaiseType.replace(STABLE, "") + `${stableSymbols.map((obj) => {return obj.symbol}).toString().replace(`,`,` - `)}` } let now = Math.floor(Date.now() / 1000); @@ -741,12 +750,25 @@ async function usdTieredSTO_status(currentSTO) { - Investor count: ${displayInvestorCount} - Funds Raised` + displayFundsRaisedPerType + ` - USD: ${displayFundsRaisedUSD} USD + Total USD: ${displayFundsRaisedUSD} USD `); console.log(chalk.green(`\n${(web3.utils.fromWei(await getBalance(Issuer.address, gbl.constants.FUND_RAISE_TYPES.POLY)))} POLY balance remaining at issuer address ${Issuer.address}`)); } +async function checkStableBalance(walletAddress, stableAddress) { + let stableCoin = common.connect(abis.erc20(), stableAddress); + try { + return await stableCoin.methods.balanceOf(walletAddress).call(); + } catch (e) { + return "" + } +} + +async function getStableCoinsRaised(currentSTO, address) { + return await currentSTO.methods.stableCoinsRaised(address).call() +} + async function usdTieredSTO_configure(currentSTO) { console.log(chalk.blue('STO Configuration - USD Tiered STO')); From 3fd1e0727147ea001b445f0b4b8c256cb24a1df5 Mon Sep 17 00:00:00 2001 From: shuffledex Date: Tue, 11 Dec 2018 22:22:56 -0300 Subject: [PATCH 28/30] CLI minor fixes --- CLI/commands/investor_portal.js | 19 +++++++++++++++---- CLI/commands/sto_manager.js | 2 +- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/CLI/commands/investor_portal.js b/CLI/commands/investor_portal.js index fb4cc153d..613414a71 100644 --- a/CLI/commands/investor_portal.js +++ b/CLI/commands/investor_portal.js @@ -549,11 +549,18 @@ async function investUsdTieredSTO(currency, amount) { raiseTypes.push(stable.symbol) }) } - let index = readlineSync.keyInSelect(raiseTypes, 'Choose one of the allowed raise types: ', { cancel: false }); - raiseType = raiseTypes[index]; + raiseType = raiseTypes[selectToken('Choose one of the allowed raise types: ')]; } else { - raiseType = raiseTypes[0]; - console.log(''); + if (raiseTypes[0] == STABLE) { + raiseTypes.splice(raiseTypes.indexOf(STABLE), 1) + stableSymbols.forEach((stable) => { + raiseTypes.push(stable.symbol) + }) + raiseType = raiseTypes[selectToken('Choose one of the allowed stable coin(s): ')]; + } else { + raiseType = raiseTypes[0]; + console.log(''); + } } } @@ -645,6 +652,10 @@ async function investUsdTieredSTO(currency, amount) { await showUserInfoForUSDTieredSTO(); } +function selectToken(msg) { + return readlineSync.keyInSelect(raiseTypes, msg, { cancel: false }); +} + async function polyBalance(_user) { let balance = await polyToken.methods.balanceOf(_user).call(); return web3.utils.fromWei(balance); diff --git a/CLI/commands/sto_manager.js b/CLI/commands/sto_manager.js index fb8ccc32f..9156b1fbd 100644 --- a/CLI/commands/sto_manager.js +++ b/CLI/commands/sto_manager.js @@ -348,7 +348,7 @@ async function addressesConfigUSDTieredSTO(usdTokenRaise) { addresses.usdToken = ['0x0000000000000000000000000000000000000000']; } - if (!await processArray(listOfAddress)) { + if ((usdTokenRaise) && (!await processArray(listOfAddress))) { console.log(chalk.yellow(`\nPlease, verify your stable coins addresses to continue with this process.\n`)) menu = true; } else { From 4054bcbec5cbade9af60a0eb8ea943f8fff9ef49 Mon Sep 17 00:00:00 2001 From: Victor Date: Wed, 12 Dec 2018 08:53:43 -0300 Subject: [PATCH 29/30] Minor fix --- CLI/commands/sto_manager.js | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/CLI/commands/sto_manager.js b/CLI/commands/sto_manager.js index 9156b1fbd..c6757359c 100644 --- a/CLI/commands/sto_manager.js +++ b/CLI/commands/sto_manager.js @@ -316,7 +316,7 @@ async function addressesConfigUSDTieredSTO(usdTokenRaise) { defaultInput: Issuer.address }); if (addresses.wallet == "") addresses.wallet = Issuer.address; - + addresses.reserveWallet = readlineSync.question('Enter the address that will receive remaining tokens in the case the cap is not met (' + Issuer.address + '): ', { limit: function (input) { return web3.utils.isAddress(input); @@ -325,9 +325,9 @@ async function addressesConfigUSDTieredSTO(usdTokenRaise) { defaultInput: Issuer.address }); if (addresses.reserveWallet == "") addresses.reserveWallet = Issuer.address; - + let listOfAddress; - + if (usdTokenRaise) { addresses.usdToken = readlineSync.question('Enter the address (or multiple addresses separated by commas) of the USD stable coin(s) (' + usdToken.options.address + '): ', { limit: function (input) { @@ -344,17 +344,17 @@ async function addressesConfigUSDTieredSTO(usdTokenRaise) { addresses.usdToken = [usdToken.options.address]; } } else { - listOfAddress = ['0x0000000000000000000000000000000000000000'] - addresses.usdToken = ['0x0000000000000000000000000000000000000000']; + listOfAddress = [] + addresses.usdToken = []; } - + if ((usdTokenRaise) && (!await processArray(listOfAddress))) { console.log(chalk.yellow(`\nPlease, verify your stable coins addresses to continue with this process.\n`)) menu = true; } else { menu = false; } - + if (typeof addresses.usdToken === 'string') { addresses.usdToken = addresses.usdToken.split(",") } @@ -389,7 +389,7 @@ async function processAddress(array) { let list = []; for (const address of array) { let symbol = await checkSymbol(address); - list.push({"symbol": symbol, "address": address}) + list.push({ "symbol": symbol, "address": address }) } return list } @@ -666,7 +666,7 @@ async function usdTieredSTO_status(currentSTO) { displayWalletBalancePerType += ` Balance ${type}:\t\t ${walletBalance} ${type} (${walletBalanceUSD} USD)`; } - + balance = await getBalance(displayReserveWallet, gbl.constants.FUND_RAISE_TYPES[type]); let reserveWalletBalance = web3.utils.fromWei(balance); let reserveWalletBalanceUSD = web3.utils.fromWei(await currentSTO.methods.convertToUSD(gbl.constants.FUND_RAISE_TYPES[type], balance).call()); @@ -709,7 +709,7 @@ async function usdTieredSTO_status(currentSTO) { let displayRaiseType = raiseTypes.join(' - '); //If STO has stable coins, we list them one by one if (stableSymbols.length) { - displayRaiseType = displayRaiseType.replace(STABLE, "") + `${stableSymbols.map((obj) => {return obj.symbol}).toString().replace(`,`,` - `)}` + displayRaiseType = displayRaiseType.replace(STABLE, "") + `${stableSymbols.map((obj) => { return obj.symbol }).toString().replace(`,`, ` - `)}` } let now = Math.floor(Date.now() / 1000); @@ -759,9 +759,9 @@ async function usdTieredSTO_status(currentSTO) { async function checkStableBalance(walletAddress, stableAddress) { let stableCoin = common.connect(abis.erc20(), stableAddress); try { - return await stableCoin.methods.balanceOf(walletAddress).call(); + return await stableCoin.methods.balanceOf(walletAddress).call(); } catch (e) { - return "" + return "" } } From 549f83e7f5a8b956202bd8ecbc108eaa135545f8 Mon Sep 17 00:00:00 2001 From: Victor Date: Mon, 17 Dec 2018 14:25:12 -0300 Subject: [PATCH 30/30] CLI - Script to generate tokens, whitelist investors and mint to them --- CLI/commands/ST20Generator.js | 4 +- CLI/commands/TickerRollForward.js | 18 ++-- CLI/commands/common/global.js | 4 +- CLI/commands/dividends_manager.js | 108 ++++++++++----------- CLI/commands/helpers/contract_abis.js | 50 +++++----- CLI/commands/helpers/contract_addresses.js | 18 ++-- CLI/commands/sto_manager.js | 6 +- CLI/commands/strMigrator.js | 6 +- CLI/commands/token_manager.js | 4 +- CLI/commands/transfer_manager.js | 48 ++++++--- CLI/data/Transfer/GTM/whitelist_data.csv | 2 +- CLI/package.json | 2 +- CLI/polymath-cli.js | 9 +- CLI/scripts/stable_coin.sh | 18 ++++ 14 files changed, 171 insertions(+), 126 deletions(-) create mode 100755 CLI/scripts/stable_coin.sh diff --git a/CLI/commands/ST20Generator.js b/CLI/commands/ST20Generator.js index 689b2b27f..0863c727e 100644 --- a/CLI/commands/ST20Generator.js +++ b/CLI/commands/ST20Generator.js @@ -32,7 +32,9 @@ async function executeApp(_ticker, _transferOwnership, _name, _details, _divisib await step_transfer_ticker_ownership(_transferOwnership); await step_token_deploy(_name, _details, _divisible); } - await tokenManager.executeApp(tokenSymbol); + if (typeof _divisible === 'undefined') { + await tokenManager.executeApp(tokenSymbol); + } } catch (err) { console.log(err); return; diff --git a/CLI/commands/TickerRollForward.js b/CLI/commands/TickerRollForward.js index 69d36575d..266749da1 100644 --- a/CLI/commands/TickerRollForward.js +++ b/CLI/commands/TickerRollForward.js @@ -20,9 +20,9 @@ let securityTokenRegistry; let securityTokenRegistryAddress; function Ticker(_owner, _symbol, _name) { - this.owner = _owner; - this.symbol = _symbol; - this.name = _name; + this.owner = _owner; + this.symbol = _symbol; + this.name = _name; } function FailedRegistration(_ticker, _error) { @@ -58,11 +58,11 @@ async function startScript() { } async function readFile() { - var stream = fs.createReadStream("./CLI/data/ticker_data.csv"); + var stream = fs.createReadStream(`${__dirname}/../data/ticker_data.csv`); var csvStream = csv() .on("data", function (data) { - ticker_data.push(new Ticker(data[0],data[1],data[2],data[3])); + ticker_data.push(new Ticker(data[0], data[1], data[2], data[3])); }) .on("end", async function () { await registerTickers(); @@ -73,12 +73,12 @@ async function readFile() { async function registerTickers() { // Poly approval for registration fees let polyBalance = BigNumber(await polyToken.methods.balanceOf(Issuer.address).call()); - let fee = web3.utils.fromWei(await securityTokenRegistry.methods.getTickerRegistrationFee().call()); + let fee = web3.utils.fromWei(await securityTokenRegistry.methods.getTickerRegistrationFee().call()); let totalFee = BigNumber(ticker_data.length).mul(fee); if (totalFee.gt(polyBalance)) { console.log(chalk.red(`\n*******************************************************************************`)); - console.log(chalk.red(`Not enough POLY to pay registration fee. Require ${totalFee.div(10**18).toNumber()} POLY but have ${polyBalance.div(10**18).toNumber()} POLY.`)); + console.log(chalk.red(`Not enough POLY to pay registration fee. Require ${totalFee.div(10 ** 18).toNumber()} POLY but have ${polyBalance.div(10 ** 18).toNumber()} POLY.`)); console.log(chalk.red(`*******************************************************************************\n`)); process.exit(0); } else { @@ -100,7 +100,7 @@ async function registerTickers() { } // validate ticker - await securityTokenRegistry.methods.getTickerDetails(ticker_data[i].symbol).call({}, function(error, result){ + await securityTokenRegistry.methods.getTickerDetails(ticker_data[i].symbol).call({}, function (error, result) { if (result[1] != 0) { failed_tickers.push(` ${i} is already registered`); valid = false; @@ -131,7 +131,7 @@ async function logResults() { Successful registrations: ${registered_tickers.length} Failed registrations: ${failed_tickers.length} Total gas consumed: ${totalGas} - Total gas cost: ${defaultGasPrice.mul(totalGas).div(10**18)} ETH + Total gas cost: ${defaultGasPrice.mul(totalGas).div(10 ** 18)} ETH List of failed registrations: ${failed_tickers} diff --git a/CLI/commands/common/global.js b/CLI/commands/common/global.js index 51972ea92..4b8fc5a3a 100644 --- a/CLI/commands/common/global.js +++ b/CLI/commands/common/global.js @@ -44,9 +44,9 @@ module.exports = { console.log("Invalid remote node") process.exit(0) } - await httpProvider(remoteNetwork, './privKey'); + await httpProvider(remoteNetwork, `${__dirname}/../../../privKey`); } else { - await httpProvider("http://localhost:8545", './privKeyLocal'); + await httpProvider("http://localhost:8545", `${__dirname}/../../../privKeyLocal`); } defaultGasPrice = getGasPrice(await web3.eth.net.getId()); } diff --git a/CLI/commands/dividends_manager.js b/CLI/commands/dividends_manager.js index 0f423bf5d..19ef24910 100644 --- a/CLI/commands/dividends_manager.js +++ b/CLI/commands/dividends_manager.js @@ -32,7 +32,7 @@ async function executeApp(type) { } }; -async function setup(){ +async function setup() { try { let securityTokenRegistryAddress = await contracts.securityTokenRegistry(); let securityTokenRegistryABI = abis.securityTokenRegistry(); @@ -45,13 +45,13 @@ async function setup(){ polyToken.setProvider(web3.currentProvider); } catch (err) { console.log(err) - console.log('\x1b[31m%s\x1b[0m',"There was a problem getting the contracts. Make sure they are deployed to the selected network."); + console.log('\x1b[31m%s\x1b[0m', "There was a problem getting the contracts. Make sure they are deployed to the selected network."); process.exit(0); } } -async function start_explorer(){ - console.log('\n\x1b[34m%s\x1b[0m',"Dividends Manager - Main Menu"); +async function start_explorer() { + console.log('\n\x1b[34m%s\x1b[0m', "Dividends Manager - Main Menu"); if (!tokenSymbol) tokenSymbol = readlineSync.question('Enter the token symbol: '); @@ -62,7 +62,7 @@ async function start_explorer(){ console.log(chalk.red(`Token symbol provided is not a registered Security Token.`)); } else { let securityTokenABI = abis.securityToken(); - securityToken = new web3.eth.Contract(securityTokenABI,result); + securityToken = new web3.eth.Contract(securityTokenABI, result); // Get the GTM result = await securityToken.methods.getModulesByName(web3.utils.toHex('GeneralTransferManager')).call(); @@ -76,7 +76,7 @@ async function start_explorer(){ let typeOptions = ['POLY', 'ETH']; if (!typeOptions.includes(dividendsType)) { - let index = readlineSync.keyInSelect(typeOptions, 'What type of dividends do you want work with?', {cancel: false}); + let index = readlineSync.keyInSelect(typeOptions, 'What type of dividends do you want work with?', { cancel: false }); dividendsType = typeOptions[index]; console.log(`Selected: ${dividendsType}`) } @@ -101,50 +101,50 @@ async function start_explorer(){ console.log('Selected:', selected, '\n'); switch (selected) { case 'Mint tokens': - let _to = readlineSync.question('Enter beneficiary of minting: '); - let _amount = readlineSync.question('Enter amount of tokens to mint: '); - await mintTokens(_to,_amount); - break; + let _to = readlineSync.question('Enter beneficiary of minting: '); + let _amount = readlineSync.question('Enter amount of tokens to mint: '); + await mintTokens(_to, _amount); + break; case 'Transfer tokens': - let _to2 = readlineSync.question('Enter beneficiary of tranfer: '); - let _amount2 = readlineSync.question('Enter amount of tokens to transfer: '); - await transferTokens(_to2,_amount2); - break; + let _to2 = readlineSync.question('Enter beneficiary of tranfer: '); + let _amount2 = readlineSync.question('Enter amount of tokens to transfer: '); + await transferTokens(_to2, _amount2); + break; case 'Create checkpoint': let createCheckpointAction = securityToken.methods.createCheckpoint(); await common.sendTransaction(createCheckpointAction); - break; + break; case 'Set default exclusions for dividends': await setDefaultExclusions(); - break; + break; case 'Tax holding settings': await taxHoldingMenu(); - break; + break; case 'Create dividends': let divName = readlineSync.question(`Enter a name or title to indetify this dividend: `); let dividend = readlineSync.question(`How much ${dividendsType} would you like to distribute to token holders?: `); await checkBalance(dividend); let checkpointId = currentCheckpoint == 0 ? 0 : await selectCheckpoint(true); // If there are no checkpoints, it must create a new one await createDividends(divName, dividend, checkpointId); - break; + break; case 'Explore account at checkpoint': - let _address = readlineSync.question('Enter address to explore: '); + let _address = readlineSync.question('Enter address to explore: '); let _checkpoint = await selectCheckpoint(false); await exploreAddress(_address, _checkpoint); - break; + break; case 'Explore total supply at checkpoint': let _checkpoint2 = await selectCheckpoint(false); await exploreTotalSupply(_checkpoint2); - break; + break; case 'Push dividends to accounts': - let _dividend = await selectDividend({valid: true, expired: false, reclaimed: false, withRemaining: true}); + let _dividend = await selectDividend({ valid: true, expired: false, reclaimed: false, withRemaining: true }); if (_dividend !== null) { let _addresses = readlineSync.question('Enter addresses to push dividends to (ex- add1,add2,add3,...): '); await pushDividends(_dividend, _addresses); } - break; + break; case `Explore ${dividendsType} balance`: - let _address3 = readlineSync.question('Enter address to explore: '); + let _address3 = readlineSync.question('Enter address to explore: '); let _dividend3 = await selectDividend(); if (_dividend3 !== null) { let dividendAmounts = await currentDividendsModule.methods.calculateDividend(_dividend3.index, _address3).call(); @@ -157,9 +157,9 @@ async function start_explorer(){ Tax withheld: ${web3.utils.fromWei(dividendTax)} ${dividendsType} `); } - break; + break; case 'Reclaim expired dividends': - let _dividend4 = await selectDividend({expired: true, reclaimed: false}); + let _dividend4 = await selectDividend({ expired: true, reclaimed: false }); if (_dividend4 !== null) { await reclaimedDividend(_dividend4); } @@ -174,14 +174,14 @@ async function start_explorer(){ await start_explorer(); } -async function mintTokens(address, amount){ +async function mintTokens(address, amount) { if (await securityToken.methods.mintingFrozen().call()) { console.log(chalk.red("Minting is not possible - Minting has been permanently frozen by issuer")); } else { await whitelistAddress(address); try { - let mintAction = securityToken.methods.mint(address,web3.utils.toWei(amount)); + let mintAction = securityToken.methods.mint(address, web3.utils.toWei(amount)); let receipt = await common.sendTransaction(mintAction); let event = common.getEventFromLogs(securityToken._jsonInterface, receipt.logs, 'Transfer'); console.log(` @@ -195,12 +195,12 @@ async function mintTokens(address, amount){ } } -async function transferTokens(address, amount){ +async function transferTokens(address, amount) { await whitelistAddress(address); - try{ - let transferAction = securityToken.methods.transfer(address,web3.utils.toWei(amount)); - let receipt = await common.sendTransaction(transferAction, {factor: 1.5}); + try { + let transferAction = securityToken.methods.transfer(address, web3.utils.toWei(amount)); + let receipt = await common.sendTransaction(transferAction, { factor: 1.5 }); let event = common.getEventFromLogs(securityToken._jsonInterface, receipt.logs, 'Transfer'); console.log(` Account ${event.from} @@ -213,17 +213,17 @@ async function transferTokens(address, amount){ } } -async function exploreAddress(address, checkpoint){ +async function exploreAddress(address, checkpoint) { let balance = await securityToken.methods.balanceOf(address).call(); balance = web3.utils.fromWei(balance); console.log(`Balance of ${address} is: ${balance} (Using balanceOf)`); - let balanceAt = await securityToken.methods.balanceOfAt(address,checkpoint).call(); + let balanceAt = await securityToken.methods.balanceOfAt(address, checkpoint).call(); balanceAt = web3.utils.fromWei(balanceAt); console.log(`Balance of ${address} is: ${balanceAt} (Using balanceOfAt - checkpoint ${checkpoint})`); } -async function exploreTotalSupply(checkpoint){ +async function exploreTotalSupply(checkpoint) { let totalSupply = await securityToken.methods.totalSupply().call(); totalSupply = web3.utils.fromWei(totalSupply); console.log(`TotalSupply is: ${totalSupply} (Using totalSupply)`); @@ -246,7 +246,7 @@ async function setDefaultExclusions() { let receipt = await common.sendTransaction(setDefaultExclusionsActions); let event = common.getEventFromLogs(currentDividendsModule._jsonInterface, receipt.logs, 'SetDefaultExcludedAddresses'); console.log(chalk.green(`Exclusions were successfully set.`)); - showExcluded(event._excluded); + showExcluded(event._excluded); } } @@ -254,30 +254,30 @@ async function taxHoldingMenu() { await addDividendsModule(); let options = ['Set a % to withhold from dividends sent to an address', 'Withdraw withholding for dividend', 'Return to main menu']; - let index = readlineSync.keyInSelect(options, 'What do you want to do?', {cancel: false}); + let index = readlineSync.keyInSelect(options, 'What do you want to do?', { cancel: false }); let selected = options[index]; console.log("Selected:", selected); switch (selected) { case 'Set a % to withhold from dividends sent to an address': let address = readlineSync.question('Enter the address of the investor: ', { - limit: function(input) { + limit: function (input) { return web3.utils.isAddress(input); }, limitMessage: "Must be a valid address", }); let percentage = readlineSync.question('Enter the percentage of dividends to withhold (number between 0-100): ', { - limit: function(input) { + limit: function (input) { return (parseInt(input) >= 0 && parseInt(input) <= 100); }, limitMessage: "Must be a value between 0 and 100", }); let percentageWei = web3.utils.toWei((percentage / 100).toString()); let setWithHoldingFixedAction = currentDividendsModule.methods.setWithholdingFixed([address], percentageWei); - let receipt = await common.sendTransaction(setWithHoldingFixedAction); + let receipt = await common.sendTransaction(setWithHoldingFixedAction); console.log(chalk.green(`Successfully set tax withholding of ${percentage}% for ${address}.`)); break; case 'Withdraw withholding for dividend': - let _dividend = await selectDividend({withRemainingWithheld: true}); + let _dividend = await selectDividend({ withRemainingWithheld: true }); if (_dividend !== null) { let withdrawWithholdingAction = currentDividendsModule.methods.withdrawWithholding(_dividend.index); let receipt = await common.sendTransaction(withdrawWithholdingAction); @@ -299,11 +299,11 @@ async function taxHoldingMenu() { async function createDividends(name, dividend, checkpointId) { await addDividendsModule(); - let time = Math.floor(Date.now()/1000); - let maturityTime = readlineSync.questionInt('Enter the dividend maturity time from which dividend can be paid (Unix Epoch time)\n(Now = ' + time + ' ): ', {defaultInput: time}); + let time = Math.floor(Date.now() / 1000); + let maturityTime = readlineSync.questionInt('Enter the dividend maturity time from which dividend can be paid (Unix Epoch time)\n(Now = ' + time + ' ): ', { defaultInput: time }); let defaultTime = time + gbl.constants.DURATION.minutes(10); - let expiryTime = readlineSync.questionInt('Enter the dividend expiry time (Unix Epoch time)\n(10 minutes from now = ' + defaultTime + ' ): ', {defaultInput: defaultTime}); - + let expiryTime = readlineSync.questionInt('Enter the dividend expiry time (Unix Epoch time)\n(10 minutes from now = ' + defaultTime + ' ): ', { defaultInput: defaultTime }); + let useDefaultExcluded = readlineSync.keyInYNStrict(`Do you want to use the default excluded addresses for this dividend? If not, data from 'dividendsExclusions_data.csv' will be used instead.`); let createDividendAction; @@ -344,7 +344,7 @@ async function createDividends(name, dividend, checkpointId) { createDividendAction = currentDividendsModule.methods.createDividendWithExclusions(maturityTime, expiryTime, excluded, web3.utils.toHex(name)); } } - let receipt = await common.sendTransaction(createDividendAction, {value: web3.utils.toWei(dividend)}); + let receipt = await common.sendTransaction(createDividendAction, { value: web3.utils.toWei(dividend) }); let event = common.getEventFromLogs(currentDividendsModule._jsonInterface, receipt.logs, 'EtherDividendDeposited'); console.log(` Dividend ${event._dividendIndex} deposited` @@ -352,7 +352,7 @@ async function createDividends(name, dividend, checkpointId) { } } -async function pushDividends(dividend, account){ +async function pushDividends(dividend, account) { let accs = account.split(','); let pushDividendPaymentToAddressesAction = currentDividendsModule.methods.pushDividendPaymentToAddresses(dividend.index, accs); let receipt = await common.sendTransaction(pushDividendPaymentToAddressesAction); @@ -474,19 +474,19 @@ async function addDividendsModule() { async function selectCheckpoint(includeCreate) { let options = []; let fix = 1; //Checkpoint 0 is not included, so I need to add 1 to fit indexes for checkpoints and options - let checkpoints = (await getCheckpoints()).map(function(c) { return c.timestamp }); + let checkpoints = (await getCheckpoints()).map(function (c) { return c.timestamp }); if (includeCreate) { options.push('Create new checkpoint'); fix = 0; //If this option is added, fix isn't needed. } options = options.concat(checkpoints); - return readlineSync.keyInSelect(options, 'Select a checkpoint:', {cancel: false}) + fix; + return readlineSync.keyInSelect(options, 'Select a checkpoint:', { cancel: false }) + fix; } async function getCheckpoints() { let result = []; - + let checkPointsTimestamps = await securityToken.methods.getCheckpointTimes().call(); for (let index = 0; index < checkPointsTimestamps.length; index++) { let checkpoint = {}; @@ -502,7 +502,7 @@ async function selectDividend(filter) { let result = null; let dividends = await getDividends(); - let now = Math.floor(Date.now()/1000); + let now = Math.floor(Date.now() / 1000); if (typeof filter !== 'undefined') { if (typeof filter.valid !== 'undefined') { dividends = dividends.filter(d => filter.valid == (now > d.maturity)); @@ -522,7 +522,7 @@ async function selectDividend(filter) { } if (dividends.length > 0) { - let options = dividends.map(function(d) { + let options = dividends.map(function (d) { return `${web3.utils.toAscii(d.name)} Created: ${moment.unix(d.created).format('MMMM Do YYYY, HH:mm:ss')} Maturity: ${moment.unix(d.maturity).format('MMMM Do YYYY, HH:mm:ss')} @@ -564,7 +564,7 @@ async function getDividends() { } function getExcludedFromDataFile() { - let excludedFromFile = require('fs').readFileSync('./CLI/data/dividendsExclusions_data.csv').toString().split("\n"); + let excludedFromFile = require('fs').readFileSync(`${__dirname}/../data/dividendsExclusions_data.csv`).toString().split("\n"); let excluded = excludedFromFile.filter(function (address) { return web3.utils.isAddress(address); }); @@ -582,7 +582,7 @@ function showExcluded(excluded) { } module.exports = { - executeApp: async function(type) { + executeApp: async function (type) { return executeApp(type); } } diff --git a/CLI/commands/helpers/contract_abis.js b/CLI/commands/helpers/contract_abis.js index b169c9896..329a789ff 100644 --- a/CLI/commands/helpers/contract_abis.js +++ b/CLI/commands/helpers/contract_abis.js @@ -21,33 +21,33 @@ let ownableABI; let iSTOABI; let iTransferManagerABI; let moduleFactoryABI; -let erc20; +let erc20ABI; try { - polymathRegistryABI = JSON.parse(require('fs').readFileSync('./build/contracts/PolymathRegistry.json').toString()).abi; - securityTokenRegistryABI = JSON.parse(require('fs').readFileSync('./build/contracts/SecurityTokenRegistry.json').toString()).abi; - featureRegistryABI = JSON.parse(require('fs').readFileSync('./build/contracts/FeatureRegistry.json').toString()).abi; - moduleRegistryABI = JSON.parse(require('fs').readFileSync('./build/contracts/ModuleRegistry.json').toString()).abi; - securityTokenABI = JSON.parse(require('fs').readFileSync('./build/contracts/SecurityToken.json').toString()).abi; - stoInterfaceABI = JSON.parse(require('fs').readFileSync('./build/contracts/ISTO.json').toString()).abi; - cappedSTOABI = JSON.parse(require('fs').readFileSync('./build/contracts/CappedSTO.json').toString()).abi; - usdTieredSTOABI = JSON.parse(require('fs').readFileSync('./build/contracts/USDTieredSTO.json').toString()).abi; - generalTransferManagerABI = JSON.parse(require('fs').readFileSync('./build/contracts/GeneralTransferManager.json').toString()).abi; - manualApprovalTransferManagerABI = JSON.parse(require('fs').readFileSync('./build/contracts/ManualApprovalTransferManager.json').toString()).abi; - countTransferManagerABI = JSON.parse(require('fs').readFileSync('./build/contracts/CountTransferManager.json').toString()).abi; - percentageTransferManagerABI = JSON.parse(require('fs').readFileSync('./build/contracts/PercentageTransferManager.json').toString()).abi; - generalPermissionManagerABI = JSON.parse(require('fs').readFileSync('./build/contracts/GeneralPermissionManager.json').toString()).abi; - polyTokenABI = JSON.parse(require('fs').readFileSync('./build/contracts/PolyTokenFaucet.json').toString()).abi; - cappedSTOFactoryABI = JSON.parse(require('fs').readFileSync('./build/contracts/CappedSTOFactory.json').toString()).abi; - usdTieredSTOFactoryABI = JSON.parse(require('fs').readFileSync('./build/contracts/USDTieredSTOFactory.json').toString()).abi; - erc20DividendCheckpointABI = JSON.parse(require('fs').readFileSync('./build/contracts/ERC20DividendCheckpoint.json').toString()).abi; - etherDividendCheckpointABI = JSON.parse(require('fs').readFileSync('./build/contracts/EtherDividendCheckpoint.json').toString()).abi; - moduleInterfaceABI = JSON.parse(require('fs').readFileSync('./build/contracts/IModule.json').toString()).abi; - ownableABI = JSON.parse(require('fs').readFileSync('./build/contracts/Ownable.json').toString()).abi; - iSTOABI = JSON.parse(require('fs').readFileSync('./build/contracts/ISTO.json').toString()).abi - iTransferManagerABI = JSON.parse(require('fs').readFileSync('./build/contracts/ITransferManager.json').toString()).abi - moduleFactoryABI = JSON.parse(require('fs').readFileSync('./build/contracts/ModuleFactory.json').toString()).abi; - erc20ABI = JSON.parse(require('fs').readFileSync('./build/contracts/DetailedERC20.json').toString()).abi; + polymathRegistryABI = JSON.parse(require('fs').readFileSync(`${__dirname}/../../../build/contracts/PolymathRegistry.json`).toString()).abi; + securityTokenRegistryABI = JSON.parse(require('fs').readFileSync(`${__dirname}/../../../build/contracts/SecurityTokenRegistry.json`).toString()).abi; + featureRegistryABI = JSON.parse(require('fs').readFileSync(`${__dirname}/../../../build/contracts/FeatureRegistry.json`).toString()).abi; + moduleRegistryABI = JSON.parse(require('fs').readFileSync(`${__dirname}/../../../build/contracts/ModuleRegistry.json`).toString()).abi; + securityTokenABI = JSON.parse(require('fs').readFileSync(`${__dirname}/../../../build/contracts/SecurityToken.json`).toString()).abi; + stoInterfaceABI = JSON.parse(require('fs').readFileSync(`${__dirname}/../../../build/contracts/ISTO.json`).toString()).abi; + cappedSTOABI = JSON.parse(require('fs').readFileSync(`${__dirname}/../../../build/contracts/CappedSTO.json`).toString()).abi; + usdTieredSTOABI = JSON.parse(require('fs').readFileSync(`${__dirname}/../../../build/contracts/USDTieredSTO.json`).toString()).abi; + generalTransferManagerABI = JSON.parse(require('fs').readFileSync(`${__dirname}/../../../build/contracts/GeneralTransferManager.json`).toString()).abi; + manualApprovalTransferManagerABI = JSON.parse(require('fs').readFileSync(`${__dirname}/../../../build/contracts/ManualApprovalTransferManager.json`).toString()).abi; + countTransferManagerABI = JSON.parse(require('fs').readFileSync(`${__dirname}/../../../build/contracts/CountTransferManager.json`).toString()).abi; + percentageTransferManagerABI = JSON.parse(require('fs').readFileSync(`${__dirname}/../../../build/contracts/PercentageTransferManager.json`).toString()).abi; + generalPermissionManagerABI = JSON.parse(require('fs').readFileSync(`${__dirname}/../../../build/contracts/GeneralPermissionManager.json`).toString()).abi; + polyTokenABI = JSON.parse(require('fs').readFileSync(`${__dirname}/../../../build/contracts/PolyTokenFaucet.json`).toString()).abi; + cappedSTOFactoryABI = JSON.parse(require('fs').readFileSync(`${__dirname}/../../../build/contracts/CappedSTOFactory.json`).toString()).abi; + usdTieredSTOFactoryABI = JSON.parse(require('fs').readFileSync(`${__dirname}/../../../build/contracts/USDTieredSTOFactory.json`).toString()).abi; + erc20DividendCheckpointABI = JSON.parse(require('fs').readFileSync(`${__dirname}/../../../build/contracts/ERC20DividendCheckpoint.json`).toString()).abi; + etherDividendCheckpointABI = JSON.parse(require('fs').readFileSync(`${__dirname}/../../../build/contracts/EtherDividendCheckpoint.json`).toString()).abi; + moduleInterfaceABI = JSON.parse(require('fs').readFileSync(`${__dirname}/../../../build/contracts/IModule.json`).toString()).abi; + ownableABI = JSON.parse(require('fs').readFileSync(`${__dirname}/../../../build/contracts/Ownable.json`).toString()).abi; + iSTOABI = JSON.parse(require('fs').readFileSync(`${__dirname}/../../../build/contracts/ISTO.json`).toString()).abi + iTransferManagerABI = JSON.parse(require('fs').readFileSync(`${__dirname}/../../../build/contracts/ITransferManager.json`).toString()).abi + moduleFactoryABI = JSON.parse(require('fs').readFileSync(`${__dirname}/../../../build/contracts/ModuleFactory.json`).toString()).abi; + erc20ABI = JSON.parse(require('fs').readFileSync(`${__dirname}/../../../build/contracts/DetailedERC20.json`).toString()).abi; } catch (err) { console.log('\x1b[31m%s\x1b[0m', "Couldn't find contracts' artifacts. Make sure you ran truffle compile first"); throw err; diff --git a/CLI/commands/helpers/contract_addresses.js b/CLI/commands/helpers/contract_addresses.js index 59a2f39c4..79f5cba10 100644 --- a/CLI/commands/helpers/contract_addresses.js +++ b/CLI/commands/helpers/contract_addresses.js @@ -13,7 +13,7 @@ function getPolymathRegistryAddress(networkId) { result = ""; break; case 15: // GANACHE - result = JSON.parse(require('fs').readFileSync('./build/contracts/PolymathRegistry.json').toString()).networks[networkId].address; + result = JSON.parse(require('fs').readFileSync(`${__dirname}/../../../build/contracts/PolymathRegistry.json`).toString()).networks[networkId].address; break; case 42: // KOVAN result = "0x5b215a7d39ee305ad28da29bf2f0425c6c2a00b3"; @@ -52,35 +52,35 @@ module.exports = { let networkId = await web3.eth.net.getId(); return getPolymathRegistryAddress(networkId); }, - securityTokenRegistry: async function() { + securityTokenRegistry: async function () { let polymathRegistry = await getPolymathRegistry(); return await polymathRegistry.methods.getAddress("SecurityTokenRegistry").call(); }, - moduleRegistry: async function() { + moduleRegistry: async function () { let polymathRegistry = await getPolymathRegistry(); return await polymathRegistry.methods.getAddress("ModuleRegistry").call(); }, - featureRegistry: async function() { + featureRegistry: async function () { let polymathRegistry = await getPolymathRegistry(); return await polymathRegistry.methods.getAddress("FeatureRegistry").call(); }, - polyToken: async function() { + polyToken: async function () { let polymathRegistry = await getPolymathRegistry(); return await polymathRegistry.methods.getAddress("PolyToken").call(); }, - usdToken: async function() { + usdToken: async function () { let networkId = await web3.eth.net.getId(); if (networkId == 1) return "0x89d24a6b4ccb1b6faa2625fe562bdd9a23260359"; else if (networkId == 42) return "0xc4375b7de8af5a38a93548eb8453a498222c4ff2"; else - return JSON.parse(require('fs').readFileSync('./build/contracts/PolyTokenFaucet.json').toString()).networks[networkId].address; + return JSON.parse(require('fs').readFileSync(`${__dirname}/../../../build/contracts/PolyTokenFaucet.json`).toString()).networks[networkId].address; }, - getModuleFactoryAddressByName: async function(stAddress, moduleType, moduleName) { + getModuleFactoryAddressByName: async function (stAddress, moduleType, moduleName) { let moduleRegistry = await getModuleRegistry(); let availableModules = await moduleRegistry.methods.getModulesByTypeAndToken(moduleType, stAddress).call(); - + let result = null; let counter = 0; let moduleFactoryABI = abis.moduleFactory(); diff --git a/CLI/commands/sto_manager.js b/CLI/commands/sto_manager.js index c6757359c..35df918e4 100644 --- a/CLI/commands/sto_manager.js +++ b/CLI/commands/sto_manager.js @@ -9,8 +9,8 @@ const STABLE = 'STABLE'; /////////////////// // Constants -const ACCREDIT_DATA_CSV = './CLI/data/STO/USDTieredSTO/accredited_data.csv'; -const NON_ACCREDIT_LIMIT_DATA_CSV = './CLI/data/STO/USDTieredSTO/nonAccreditedLimits_data.csv' +const ACCREDIT_DATA_CSV = `${__dirname}/../data/STO/USDTieredSTO/accredited_data.csv`; +const NON_ACCREDIT_LIMIT_DATA_CSV = `${__dirname}/../data/STO/USDTieredSTO/nonAccreditedLimits_data.csv`; /////////////////// // Crowdsale params @@ -974,7 +974,7 @@ async function getAllModulesByType(type) { let nameTemp = web3.utils.hexToUtf8(details[0]); let pausedTemp = null; if (type == gbl.constants.MODULES_TYPES.STO || type == gbl.constants.MODULES_TYPES.TRANSFER) { - let abiTemp = JSON.parse(require('fs').readFileSync(`./build/contracts/${nameTemp}.json`).toString()).abi; + let abiTemp = JSON.parse(require('fs').readFileSync(`${__dirname}/../../build/contracts/${nameTemp}.json`).toString()).abi; let contractTemp = new web3.eth.Contract(abiTemp, details[1]); pausedTemp = await contractTemp.methods.paused().call(); } diff --git a/CLI/commands/strMigrator.js b/CLI/commands/strMigrator.js index 4a8dabfe0..f27d0bfbd 100644 --- a/CLI/commands/strMigrator.js +++ b/CLI/commands/strMigrator.js @@ -216,7 +216,7 @@ async function step_get_deployed_tokens(securityTokenRegistry, singleTicker) { let event = common.getEventFromLogs(securityTokenRegistry._jsonInterface, [log], 'LogNewSecurityToken'); if (typeof singleTicker === 'undefined' || event._ticker == singleTicker) { let tokenAddress = event._securityTokenAddress; - let securityTokenABI = JSON.parse(require('fs').readFileSync('./CLI/data/SecurityToken1-4-0.json').toString()).abi; + let securityTokenABI = JSON.parse(require('fs').readFileSync(`${__dirname}/../data/SecurityToken1-4-0.json`).toString()).abi; console.log(`Creating SecurityToken contract instance of address: ${tokenAddress}...`); let token = new web3.eth.Contract(securityTokenABI, tokenAddress); token.setProvider(web3.currentProvider); @@ -230,7 +230,7 @@ async function step_get_deployed_tokens(securityTokenRegistry, singleTicker) { let gmtAddress = (await token.methods.getModule(2, 0).call())[1]; - let gtmABI = JSON.parse(require('fs').readFileSync('./CLI/data/GeneralTransferManager1-4-0.json').toString()).abi; + let gtmABI = JSON.parse(require('fs').readFileSync(`${__dirname}/../data/GeneralTransferManager1-4-0.json`).toString()).abi; let gmt = new web3.eth.Contract(gtmABI, gmtAddress); //let gtmEvents = await gmt.getPastEvents('LogModifyWhitelist', { fromBlock: event.blockNumber}); let gtmLogs = await getLogsFromEtherscan(gmt.options.address, 0, 'latest', 'LogModifyWhitelist(address,uint256,address,uint256,uint256,uint256,bool)'); @@ -284,7 +284,7 @@ async function step_launch_STs(tokens, securityTokenRegistry, tokenAddress) { let failed = []; let totalGas = new web3.utils.BN(0); let polymathRegistryAddress = await contracts.polymathRegistry(); - let STFactoryABI = JSON.parse(require('fs').readFileSync('./build/contracts/STFactory.json').toString()).abi; + let STFactoryABI = JSON.parse(require('fs').readFileSync(`${__dirname}/../../build/contracts/STFactory.json`).toString()).abi; let STFactoryAddress = await securityTokenRegistry.methods.getSTFactoryAddress().call(); let STFactory = new web3.eth.Contract(STFactoryABI, STFactoryAddress); for (const t of tokens) { diff --git a/CLI/commands/token_manager.js b/CLI/commands/token_manager.js index b74505257..54a5d215b 100644 --- a/CLI/commands/token_manager.js +++ b/CLI/commands/token_manager.js @@ -8,7 +8,7 @@ const gbl = require('./common/global'); const csvParse = require('./helpers/csv'); // Constants -const MULTIMINT_DATA_CSV = './CLI/data/ST/multi_mint_data.csv'; +const MULTIMINT_DATA_CSV = `${__dirname}/../data/ST/multi_mint_data.csv`; // Load contract artifacts const contracts = require('./helpers/contract_addresses'); @@ -617,7 +617,7 @@ async function getAllModules() { let nameTemp = web3.utils.hexToUtf8(details[0]); let pausedTemp = null; if (type == gbl.constants.MODULES_TYPES.STO || type == gbl.constants.MODULES_TYPES.TRANSFER) { - let abiTemp = JSON.parse(require('fs').readFileSync(`./build/contracts/${nameTemp}.json`).toString()).abi; + let abiTemp = JSON.parse(require('fs').readFileSync(`${__dirname}/../../build/contracts/${nameTemp}.json`).toString()).abi; let contractTemp = new web3.eth.Contract(abiTemp, details[1]); pausedTemp = await contractTemp.methods.paused().call(); } diff --git a/CLI/commands/transfer_manager.js b/CLI/commands/transfer_manager.js index 16af0e34e..3bb7f42d7 100644 --- a/CLI/commands/transfer_manager.js +++ b/CLI/commands/transfer_manager.js @@ -10,8 +10,8 @@ const { table } = require('table') /////////////////// // Constants -const WHITELIST_DATA_CSV = './CLI/data/Transfer/GTM/whitelist_data.csv'; -const PERCENTAGE_WHITELIST_DATA_CSV = './CLI/data/Transfer/PercentageTM/whitelist_data.csv'; +const WHITELIST_DATA_CSV = `${__dirname}/../data/Transfer/GTM/whitelist_data.csv`; +const PERCENTAGE_WHITELIST_DATA_CSV = `${__dirname}/../data/Transfer/PercentageTM/whitelist_data.csv`; // App flow let tokenSymbol; @@ -532,17 +532,27 @@ function showWhitelistTable(investorsArray, fromTimeArray, toTimeArray, expiryTi console.log(table(dataTable)); } -async function modifyWhitelistInBatch() { - let csvFilePath = readlineSync.question(`Enter the path for csv data file (${WHITELIST_DATA_CSV}): `, { - defaultInput: WHITELIST_DATA_CSV - }); - let batchSize = readlineSync.question(`Enter the max number of records per transaction or batch size (${gbl.constants.DEFAULT_BATCH_SIZE}): `, { - limit: function (input) { - return parseInt(input) > 0; - }, - limitMessage: 'Must be greater than 0', - defaultInput: gbl.constants.DEFAULT_BATCH_SIZE - }); +async function modifyWhitelistInBatch(_csvFilePath, _batchSize) { + let csvFilePath; + if (typeof _csvFilePath === 'undefined') { + csvFilePath = readlineSync.question(`Enter the path for csv data file (${WHITELIST_DATA_CSV}): `, { + defaultInput: WHITELIST_DATA_CSV + }); + } else { + csvFilePath = _csvFilePath; + } + let batchSize; + if (typeof _batchSize === 'undefined') { + batchSize = readlineSync.question(`Enter the max number of records per transaction or batch size (${gbl.constants.DEFAULT_BATCH_SIZE}): `, { + limit: function (input) { + return parseInt(input) > 0; + }, + limitMessage: 'Must be greater than 0', + defaultInput: gbl.constants.DEFAULT_BATCH_SIZE + }); + } else { + batchSize = _batchSize; + } let parsedData = csvParse(csvFilePath); let validData = parsedData.filter(row => web3.utils.isAddress(row[0]) && @@ -1092,7 +1102,7 @@ async function getAllModulesByType(type) { let nameTemp = web3.utils.hexToUtf8(details[0]); let pausedTemp = null; if (type == gbl.constants.MODULES_TYPES.STO || type == gbl.constants.MODULES_TYPES.TRANSFER) { - let abiTemp = JSON.parse(require('fs').readFileSync(`./build/contracts/${nameTemp}.json`).toString()).abi; + let abiTemp = JSON.parse(require('fs').readFileSync(`${__dirname}/../../build/contracts/${nameTemp}.json`).toString()).abi; let contractTemp = new web3.eth.Contract(abiTemp, details[1]); pausedTemp = await contractTemp.methods.paused().call(); } @@ -1194,6 +1204,14 @@ module.exports = { }, addTransferManagerModule: async function (_tokenSymbol) { await initialize(_tokenSymbol); - return addTransferManagerModule() + return addTransferManagerModule(); + }, + modifyWhitelistInBatch: async function (_tokenSymbol, _csvFilePath, _batchSize) { + await initialize(_tokenSymbol); + let gmtModules = await securityToken.methods.getModulesByName(web3.utils.toHex('GeneralTransferManager')).call(); + let generalTransferManagerAddress = gmtModules[0]; + currentTransferManager = new web3.eth.Contract(abis.generalTransferManager(), generalTransferManagerAddress); + currentTransferManager.setProvider(web3.currentProvider); + return modifyWhitelistInBatch(_csvFilePath, _batchSize); } } \ No newline at end of file diff --git a/CLI/data/Transfer/GTM/whitelist_data.csv b/CLI/data/Transfer/GTM/whitelist_data.csv index 3276dec64..236cac436 100644 --- a/CLI/data/Transfer/GTM/whitelist_data.csv +++ b/CLI/data/Transfer/GTM/whitelist_data.csv @@ -7,4 +7,4 @@ 0xabf60de3265b3017db7a1be66fc8b364ec1dbb98,5/5/2018,1/8/2018,10/10/2019,false 0xb841fe5a89da1bbef2d0805fbd7ffcbbb2fca5e3,5/5/2018,1/8/2018,10/10/2019,false 0x56be93088141b16ebaa9416122fd1d928da25ecf,5/5/2018,1/8/2018,10/10/2019,false -0xbb276b6f68f0a41d54b7e0a608fe8eb1ebdee7b0,5/5/2018,1/8/2018,10/10/2019,true +0xbb276b6f68f0a41d54b7e0a608fe8eb1ebdee7b0,5/5/2018,1/8/2018,10/10/2019,true \ No newline at end of file diff --git a/CLI/package.json b/CLI/package.json index 4dfcbf3a4..cc2773b74 100644 --- a/CLI/package.json +++ b/CLI/package.json @@ -4,7 +4,7 @@ "description": "CLI for Polymath-core", "main": "polymath-cli.js", "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" + "stable_coin": "scripts/stable_coin.sh" }, "author": "Polymath Inc", "license": "MIT", diff --git a/CLI/polymath-cli.js b/CLI/polymath-cli.js index d2b4c68fa..94df66e0a 100644 --- a/CLI/polymath-cli.js +++ b/CLI/polymath-cli.js @@ -120,10 +120,17 @@ program .command('transfer_manager') .alias('tm') .option('-t, --securityToken ', 'Selects a ST to manage transfer modules') + .option('-w, --whitelist ', 'Whitelists addresses according to a csv file') + .option('-b, --batchSize ', 'Max number of records per transaction') .description('Runs transfer_manager') .action(async function (cmd) { await gbl.initialize(program.remoteNode); - await transfer_manager.executeApp(cmd.securityToken); + if (cmd.whitelist) { + let batchSize = cmd.batchSize ? cmd.batchSize : gbl.constants.DEFAULT_BATCH_SIZE; + await transfer_manager.modifyWhitelistInBatch(cmd.securityToken, cmd.whitelist, batchSize); + } else { + await transfer_manager.executeApp(cmd.securityToken); + } }); program diff --git a/CLI/scripts/stable_coin.sh b/CLI/scripts/stable_coin.sh new file mode 100755 index 000000000..98415d7cc --- /dev/null +++ b/CLI/scripts/stable_coin.sh @@ -0,0 +1,18 @@ +#!/usr/bin/env bash + +# Exit script as soon as a command fails. +set -o errexit + +# Token symbol read from args. +TOKEN_SYMBOL=$1 + +# Paths +CWD="$(pwd)" +WHITELIST='/data/Transfer/GTM/whitelist_data.csv' +MULTIMINT='/data/ST/multi_mint_data.csv' + +# Scripts + +node polymath-cli st -t $TOKEN_SYMBOL -o false -n $TOKEN_SYMBOL -d '' -D true +node polymath-cli tm -t $TOKEN_SYMBOL -w $CWD$WHITELIST +node polymath-cli stm -t $TOKEN_SYMBOL -m $CWD$MULTIMINT