Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multiple stable coins support in USD Tiered STO #437

Merged
merged 42 commits into from
Dec 19, 2018
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
04763c3
Added multiple stable coins support
maxsam4 Nov 27, 2018
7a82d72
Updated tests
maxsam4 Nov 27, 2018
1cb1346
Updated function signature and selector
maxsam4 Nov 27, 2018
3270166
tests fixed
maxsam4 Nov 27, 2018
ff5fca0
Fixed simulation test
maxsam4 Nov 27, 2018
c878e9a
Added separate function for changing usd tokens
maxsam4 Nov 27, 2018
728276f
Merge modifyUSDTokens function
maxsam4 Nov 27, 2018
420fdc6
Added test case
maxsam4 Nov 27, 2018
d30f809
Added a comment
maxsam4 Nov 27, 2018
dd20c06
Updated changelog
maxsam4 Nov 27, 2018
a462f7f
Merge branch 'dev-2.1.0' into multiple-stable-coins
maxsam4 Nov 27, 2018
557d286
Combined modifiers
maxsam4 Nov 27, 2018
8c7f45e
Merge branch 'multiple-stable-coins' of https://github.com/PolymathNe…
maxsam4 Nov 27, 2018
5fdd2e2
Added data validation
maxsam4 Nov 27, 2018
846d03f
test case fixed
maxsam4 Nov 27, 2018
987bfac
Merge branch 'dev-2.1.0' into multiple-stable-coins
satyamakgec Nov 29, 2018
992d9c5
Merge branch 'dev-2.1.0' into multiple-stable-coins
VictorVicente Dec 3, 2018
78bf819
adding and listing stable coins in sto manager
shuffledex Dec 3, 2018
b040af4
investor portal improvements
shuffledex Dec 4, 2018
db68e80
Merge branch 'dev-2.1.0' into multiple-stable-coins
adamdossa Dec 4, 2018
11b6fe3
Add getUsdTokens
adamdossa Dec 5, 2018
a4c31ce
Fix typo
adamdossa Dec 5, 2018
b6bf04d
manual merge
shuffledex Dec 5, 2018
3b983b5
code advances
shuffledex Dec 6, 2018
e14a18e
final code
shuffledex Dec 6, 2018
48f06e4
Merge branch 'dev-2.1.0' into multiple-stable-coins
VictorVicente Dec 7, 2018
4d9af7b
PR fixes
shuffledex Dec 10, 2018
bc93fdd
Merge pull request #453 from PolymathNetwork/private/fede/cli/stableCoin
VictorVicente Dec 10, 2018
09be6cc
Update CLI/commands/sto_manager.js
F-OBrien Dec 10, 2018
4a94e71
Added native currency raised
maxsam4 Dec 10, 2018
985dca4
Merge branch 'multiple-stable-coins' of https://github.com/PolymathNe…
maxsam4 Dec 10, 2018
55e0cb9
Merge branch 'dev-2.1.0' into multiple-stable-coins
maxsam4 Dec 11, 2018
ac7541f
Merge fix
maxsam4 Dec 11, 2018
3477f0f
Minor fixes
maxsam4 Dec 11, 2018
6e2768a
Updated changelog
maxsam4 Dec 11, 2018
9410fbe
Changed currencyRaised to stableCoinsRaised
maxsam4 Dec 11, 2018
9b4e7b2
individual balance for every stable coin listed
shuffledex Dec 11, 2018
3fd1e07
CLI minor fixes
shuffledex Dec 12, 2018
4054bcb
Minor fix
VictorVicente Dec 12, 2018
549f83e
CLI - Script to generate tokens, whitelist investors and mint to them
VictorVicente Dec 17, 2018
629613f
Merge branch 'dev-2.1.0' into multiple-stable-coins
maxsam4 Dec 18, 2018
15d9dcf
Merge branch 'dev-2.1.0' into multiple-stable-coins
VictorVicente Dec 19, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
4 changes: 2 additions & 2 deletions contracts/modules/STO/ISTO.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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;
}
Expand Down
105 changes: 62 additions & 43 deletions contracts/modules/STO/USDTieredSTO.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -44,20 +45,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;

Expand All @@ -73,6 +75,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;

Expand Down Expand Up @@ -116,7 +121,7 @@ contract USDTieredSTO is ISTO, ReentrancyGuard {
event SetAddresses(
address indexed _wallet,
address indexed _reserveWallet,
address indexed _usdToken
address[] _usdTokens
);
event SetLimits(
uint256 _nonAccreditedLimitUSD,
Expand Down Expand Up @@ -149,8 +154,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) {
satyamakgec marked this conversation as resolved.
Show resolved Hide resolved
require(usdTokenEnabled[_usdToken], "Invalid USD token");
_;
}

Expand All @@ -176,7 +186,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,
Expand All @@ -190,14 +200,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);
}

Expand Down Expand Up @@ -261,16 +271,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);
// require(now < startTime, "STO already started");
_modifyAddresses(_wallet, _reserveWallet, _usdTokens);
}

function _modifyLimits(
Expand Down Expand Up @@ -319,16 +329,23 @@ 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);
_modifyUSDTokens(_usdTokens);
}

function _modifyUSDTokens(address[] _usdTokens) internal {
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;
maxsam4 marked this conversation as resolved.
Show resolved Hide resolved
}
emit SetAddresses(wallet, reserveWallet, _usdTokens);
}

////////////////////
Expand Down Expand Up @@ -417,8 +434,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);
}

/**
Expand Down Expand Up @@ -448,39 +465,41 @@ 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);
require(getTokensMinted().sub(initialMinted) >= _minTokens, "Insufficient tokens minted");
// 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,
Expand Down Expand Up @@ -530,8 +549,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,
Expand All @@ -542,7 +561,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);
Expand Down Expand Up @@ -749,7 +768,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");
Expand Down Expand Up @@ -803,7 +822,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) {
Expand All @@ -824,7 +843,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;
}

Expand All @@ -838,7 +857,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;
}

Expand Down Expand Up @@ -868,7 +887,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);
Expand All @@ -880,7 +899,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,
Expand All @@ -899,7 +918,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) {
Expand Down
Loading