文件 1 的 22:Address.sol
pragma solidity ^0.5.0;
library Address {
function isContract(address account) internal view returns (bool) {
uint256 size;
assembly { size := extcodesize(account) }
return size > 0;
}
}
文件 2 的 22:CapperRole.sol
pragma solidity ^0.5.0;
import "../Roles.sol";
contract CapperRole {
using Roles for Roles.Role;
event CapperAdded(address indexed account);
event CapperRemoved(address indexed account);
Roles.Role private _cappers;
constructor () internal {
_addCapper(msg.sender);
}
modifier onlyCapper() {
require(isCapper(msg.sender), "CapperRole: caller does not have the Capper role");
_;
}
function isCapper(address account) public view returns (bool) {
return _cappers.has(account);
}
function addCapper(address account) public onlyCapper {
_addCapper(account);
}
function renounceCapper() public {
_removeCapper(msg.sender);
}
function _addCapper(address account) internal {
_cappers.add(account);
emit CapperAdded(account);
}
function _removeCapper(address account) internal {
_cappers.remove(account);
emit CapperRemoved(account);
}
}
文件 3 的 22:CrowdliExchangeVault.sol
pragma solidity 0.5.0;
import "./CrowdliSTO.sol";
import "./Ownable.sol";
import "./SafeMath.sol";
import "./Pausable.sol";
contract CrowdliExchangeVault is Ownable, Pausable {
struct EtherPayment {
address from;
uint64 date;
PaymentStatus status;
uint weiAmount;
uint pendingTokens;
uint mintedTokens;
uint exchangeRate;
}
struct ExchangeOrder {
uint256 exchangeRate;
uint64 date;
address exchangeWallet;
ExchangeStatus status;
}
enum ExchangeStatus { Pending, Confirmed }
enum PaymentStatus { None, Requested, Accepted, TokensDelivered, Rejected, Refunded, PurchaseFailed }
using SafeMath for uint256;
ExchangeOrder[] public exchangeOrders;
EtherPayment[] public payments;
mapping(address => uint) public nrRequestedPayments;
mapping(uint => uint[]) private exchangeOrderForPayments;
address public paymentConfirmer;
CrowdliSTO private crowdliSTO;
event PaymentsEnabled(address indexed sender);
event PaymentsDisabled(address indexed sender);
event EtherPaymentRefunded(address indexed beneficiary, uint256 weiAmount);
event EtherPaymentRequested(address indexed beneficiary, uint256 weiAmount, uint paymentIndex);
event EtherPaymentRejected(address indexed sender, uint etherPaymentIndex);
event LogGasUsed(address indexed sender, uint indexed value);
event EtherPaymentPurchaseFailed(address indexed sender, uint indexed etherPaymentIndex);
event OrderCreated(address indexed sender, uint[] payments);
event OrderConfirmed(address indexed sender, uint indexed etherPaymentIndex);
modifier isInInvestmentState() {
require(crowdliSTO.hasState(CrowdliSTO.States.Investment) || !crowdliSTO.paused(), "bot in state investment");
_;
}
modifier onlyCrowdliSTO() {
require((msg.sender == address(crowdliSTO)), "Sender should be CrowdliSTO");
_;
}
modifier onlyEtherPaymentConfirmer() {
require((msg.sender == paymentConfirmer), "Sender should be EtherPaymentConfirmer");
_;
}
constructor(address _paymentConfirmer) public {
paymentConfirmer = _paymentConfirmer;
}
function setCrowdliSTO(CrowdliSTO _crowdliSTO) external onlyOwner {
crowdliSTO = _crowdliSTO;
}
function initExchangeVault(address _directorsBoard, address _crowdliSTO) external onlyOwner{
crowdliSTO = CrowdliSTO(_crowdliSTO);
transferOwnership(_crowdliSTO);
addPauser(_directorsBoard);
addPauser(_crowdliSTO);
}
function confirmMintedTokensForPayment(uint paymentId, uint _mintedTokens) external onlyOwner {
payments[paymentId].mintedTokens = payments[paymentId].mintedTokens.add(_mintedTokens);
}
function requestPayment() external whenNotPaused payable {
uint[] memory tokenStatement = crowdliSTO.calculateTokenStatementEther(msg.sender, msg.value, CrowdliSTO.Currency.ETH, false, 0);
require(CrowdliSTO.TokenStatementValidation(tokenStatement[8]) == CrowdliSTO.TokenStatementValidation.OK, crowdliSTO.resolvePaymentError(CrowdliSTO.TokenStatementValidation(tokenStatement[8])));
payments.push(EtherPayment(msg.sender, uint64(now), PaymentStatus.Requested, msg.value, 0, 0, 0));
nrRequestedPayments[msg.sender] = nrRequestedPayments[msg.sender].add(1);
emit EtherPaymentRequested(msg.sender, msg.value, payments.length.sub(1));
}
function rejectPayment(uint[] calldata _paymentIds) external onlyEtherPaymentConfirmer {
for(uint id = 0; id < _paymentIds.length; id++){
uint paymentId = _paymentIds[id];
require(payments[paymentId].status == PaymentStatus.Requested, "Payment must be in state Requested");
payments[paymentId].status = PaymentStatus.Rejected;
nrRequestedPayments[payments[paymentId].from] = nrRequestedPayments[payments[paymentId].from].sub(1);
emit EtherPaymentRejected(payments[paymentId].from, paymentId);
}
}
function refundPayment(uint index) external {
EtherPayment storage etherPayment = payments[index];
uint256 depositedValue = etherPayment.weiAmount;
require((etherPayment.from == msg.sender) || (msg.sender == paymentConfirmer), "Refund are not enabled for sender");
require(etherPayment.status == PaymentStatus.Rejected , "etherPayment.status should be PaymentStatus.Rejected");
require(address(this).balance >= depositedValue , "Exchange Vault balance doesn't have enough funds.");
etherPayment.status = PaymentStatus.Refunded;
msg.sender.transfer(depositedValue);
emit EtherPaymentRefunded(msg.sender, depositedValue);
}
function createOrder(address payable _exchangeWallet, uint[] calldata _paymentsToAccept) external whenNotPaused onlyEtherPaymentConfirmer isInInvestmentState {
require(payments.length > 0, "At least one payment is required to exchange");
ExchangeOrder memory exchangeOrder = ExchangeOrder(0, uint64(now), _exchangeWallet, ExchangeStatus.Pending);
exchangeOrders.push(exchangeOrder);
uint weiAmountForTransfering = 0;
uint[] storage orderForPaymentIds = exchangeOrderForPayments[exchangeOrders.length-1];
for (uint64 i = 0; i < _paymentsToAccept.length; i++) {
uint paymentId = _paymentsToAccept[i];
EtherPayment storage etherPayment = payments[paymentId];
require(etherPayment.status == PaymentStatus.Requested, "should be in status requested");
etherPayment.status = PaymentStatus.Accepted;
orderForPaymentIds.push(paymentId);
processPayment(paymentId);
weiAmountForTransfering = weiAmountForTransfering.add(etherPayment.weiAmount);
}
emit OrderCreated(msg.sender, orderForPaymentIds);
_exchangeWallet.transfer(weiAmountForTransfering);
}
function processPayment(uint _paymentId) internal {
EtherPayment storage etherPayment = payments[_paymentId];
nrRequestedPayments[etherPayment.from] = nrRequestedPayments[etherPayment.from].sub(1);
crowdliSTO.processEtherPayment(etherPayment.from, etherPayment.weiAmount, _paymentId);
etherPayment.status = PaymentStatus.TokensDelivered;
etherPayment.exchangeRate = crowdliSTO.exchangeRate();
}
function confirmOrder(uint64 _orderIndex, uint256 _exchangeRate) external whenNotPaused onlyEtherPaymentConfirmer {
require(_orderIndex < exchangeOrders.length, "_orderIndex is too high");
ExchangeOrder storage exchangeOrder = exchangeOrders[_orderIndex];
require(exchangeOrder.status != ExchangeStatus.Confirmed, "exchangeOrder.status is confirmed");
exchangeOrder.exchangeRate = _exchangeRate;
exchangeOrder.status = ExchangeStatus.Confirmed;
}
function hasRequestedPayments(address _beneficiary) public view returns(bool) {
return (nrRequestedPayments[_beneficiary] != 0);
}
function getEtherPaymentsCount() external view returns(uint256) {
return payments.length;
}
function getExchangeOrdersCount() external view returns(uint256) {
return exchangeOrders.length;
}
function getExchangeOrdersToPayments(uint orderId) external view returns (uint[] memory) {
return exchangeOrderForPayments[orderId];
}
}
文件 4 的 22:CrowdliKYCProvider.sol
pragma solidity 0.5.0;
import "./Pausable.sol";
import "./WhitelistAdminRole.sol";
contract CrowdliKYCProvider is Pausable, WhitelistAdminRole {
enum VerificationTier { None, KYCAccepted, VideoVerified, ExternalTokenAgent }
mapping (uint => uint) public maxTokenAmountPerTier;
mapping (address => VerificationTier) public verificationTiers;
event LogKYCConfirmation(address indexed sender, VerificationTier verificationTier);
constructor(address _kycConfirmer, uint _maxTokenForKYCAcceptedTier, uint _maxTokensForVideoVerifiedTier, uint _maxTokensForExternalTokenAgent) public {
addWhitelistAdmin(_kycConfirmer);
maxTokenAmountPerTier[uint(VerificationTier.None)] = 0;
maxTokenAmountPerTier[uint(VerificationTier.KYCAccepted)] = _maxTokenForKYCAcceptedTier;
maxTokenAmountPerTier[uint(VerificationTier.VideoVerified)] = _maxTokensForVideoVerifiedTier;
maxTokenAmountPerTier[uint(VerificationTier.ExternalTokenAgent)] = _maxTokensForExternalTokenAgent;
}
function confirmKYC(address _addressId, VerificationTier _verificationTier) public onlyWhitelistAdmin whenNotPaused {
emit LogKYCConfirmation(_addressId, _verificationTier);
verificationTiers[_addressId] = _verificationTier;
}
function hasVerificationLevel(address _investor, VerificationTier _verificationTier) public view returns (bool) {
return (verificationTiers[_investor] == _verificationTier);
}
function getMaxChfAmountForInvestor(address _investor) public view returns (uint) {
return maxTokenAmountPerTier[uint(verificationTiers[_investor])];
}
}
文件 5 的 22:CrowdliSTO.sol
pragma solidity 0.5.0;
import "./CrowdliToken.sol";
import "./CrowdliKYCProvider.sol";
import "./CrowdliExchangeVault.sol";
import "./Ownable.sol";
import "./Pausable.sol";
import "./SafeMath.sol";
contract CrowdliSTO is Pausable, Ownable {
using SafeMath for uint;
enum States {
PrepareInvestment,
Investment,
Finalizing,
Finalized
}
struct InvestmentPhase {
bytes32 label;
bool allowManualSwitch;
uint discountBPS;
uint capAmount;
uint tokensSoldAmount;
}
struct TokenAllocation {
bytes32 label;
AllocationActionType actionType;
AllocationValueType valueType;
uint valueAmount;
address beneficiary;
bool isAllocated;
}
struct TokenStatement {
uint requestedBase;
uint requestedCHF;
uint feesCHF;
uint currentPhaseDiscount;
uint earlyBirdCreditTokenAmount;
uint voucherCreditTokenAmount;
uint currentPhaseTokenAmount;
uint nextPhaseBaseAmount;
TokenStatementValidation validationCode;
}
enum Currency {
ETH,
CHF,
EUR
}
enum ExecutionType {
SinglePayment,
SplitPayment
}
enum AllocationActionType {
POST_ALLOCATE
}
enum AllocationValueType {
FIXED_TOKEN_AMOUNT,
BPS_OF_TOKENS_SOLD
}
enum TokenStatementValidation {
OK,
BELOW_MIN_LIMIT,
ABOVE_MAX_LIMIT,
EXCEEDS_HARD_CAP
}
event LogPaymentConfirmation(address indexed beneficiary, uint indexed investment, uint[] paymentDetails, uint id, Currency currency, ExecutionType executionType);
event LogRegisterInvestmentPhase(address indexed sender, bool allowManualSwitch, bytes32 indexed label, uint indexed discountBPS, uint cap);
event LogSaleStartUpdated(address indexed sender, uint indexed saleStartDate);
event LogKYCProviderUpdated(address indexed sender, address indexed crowdliKycProvider);
event LogStarted(address indexed sender);
event LogInvestmentPhaseSwitched(uint phaseIndex);
event LogEndDateExtended(address indexed sender, uint endDate);
event LogClosed(address indexed sender, bool stoSucessfull);
event LogFinalized(address indexed sender);
event LogStateChanged(States _states);
event LogPendingVoucherAdded(address _investor);
States public state;
address public directorsBoard;
address public paymentConfirmer;
address public tokenAgentWallet;
CrowdliToken public token;
CrowdliExchangeVault public crowdliExchangeVault;
CrowdliKYCProvider public crowdliKycProvider;
TokenAllocation[] public tokenAllocations;
InvestmentPhase[] public investmentPhases;
uint public hardCapAmount = 0;
uint public hardCapThresholdAmount;
uint public softCapAmount;
uint public minChfAmountPerInvestment;
uint public saleStartDate;
uint public endDateExtensionDecissionDate;
uint public endDateExtensionOffset;
uint public endDate;
uint public videoVerificationCostAmount ;
uint internal gasCostAmount;
uint public investmentPhaseIndex;
bytes32 public boardComment;
uint public earlyBirdTokensThresholdAmount;
uint public earlyBirdAdditionalTokensAmount;
uint public earlyBirdInvestorsCount;
uint public earlyBirdInvestmentPhase = 0;
uint public currentEarlyBirdInvestorsCount = 0;
uint public voucherTokensAmount;
mapping(address => bool) public investorsWithEarlyBird;
mapping(address => bool) public investorsWithPendingVouchers;
mapping(address => bool) public paidForVideoVerification;
uint public tokensSoldAmount;
uint public weiInvested ;
uint public numberOfInvestors;
mapping(address => uint) public allInvestmentsInChf;
mapping(address => uint) public weiPerInvestor;
mapping(address => uint) public chfPerInvestor;
uint public chfOverallInvested;
mapping(address => uint) public eurPerInvestor;
uint public eurOverallInvested;
uint public exchangeRate;
uint public exchangeRateDecimals;
uint constant public arrayMaxEntryLimit = 10;
modifier onlyDirectorsBoard() {
require(msg.sender == directorsBoard, "not directorsBoard");
_;
}
modifier onlyFiatPaymentConfirmer() {
require((msg.sender == paymentConfirmer), "not paymentConfirmer");
_;
}
modifier onlyEtherPaymentProvider() {
require(msg.sender == address(crowdliExchangeVault), "not crowdliExchangeVault");
_;
}
modifier onlyTokenAgentPaymentConfirmer() {
require((msg.sender == paymentConfirmer), "not tokenAgentPaymentConfirmer");
_;
}
modifier inState(States _state) {
require(hasState(_state), "not in required state");
_;
}
constructor (CrowdliToken _token, CrowdliKYCProvider _crowdliKycProvider, CrowdliExchangeVault _crowdliExchangeVault, address _directorsBoard, address _paymentConfirmer, address _tokenAgentWallet) public {
token = _token;
crowdliKycProvider = _crowdliKycProvider;
crowdliExchangeVault = _crowdliExchangeVault;
directorsBoard = _directorsBoard;
paymentConfirmer = _paymentConfirmer;
tokenAgentWallet = _tokenAgentWallet;
}
function initSTO(uint _saleStartDate, uint _earlyBirdTokensThreshold,
uint _earlyBirdAdditionalTokens,
uint _earlyBirdInvestorsCount,
uint _earlyBirdInvestmentPhase,
uint _voucherTokensAmount,
uint _videoVerificationCost,
uint _softCap,
uint _endDate,
uint _endDateExtensionDecissionDate,
uint _endDateExtensionOffset,
uint _gasCost,
uint _hardCapThresholdAmount,
uint _minChfAmountPerInvestment) external onlyOwner {
token.pause();
saleStartDate = _saleStartDate;
state = States.PrepareInvestment;
earlyBirdTokensThresholdAmount = _earlyBirdTokensThreshold;
earlyBirdInvestorsCount = _earlyBirdInvestorsCount;
earlyBirdAdditionalTokensAmount = _earlyBirdAdditionalTokens;
earlyBirdInvestmentPhase = _earlyBirdInvestmentPhase;
voucherTokensAmount = _voucherTokensAmount;
videoVerificationCostAmount = _videoVerificationCost;
gasCostAmount = _gasCost;
softCapAmount = _softCap;
endDate = _endDate;
endDateExtensionDecissionDate = _endDateExtensionDecissionDate;
endDateExtensionOffset = _endDateExtensionOffset;
hardCapThresholdAmount = _hardCapThresholdAmount;
minChfAmountPerInvestment = _minChfAmountPerInvestment;
}
function registerPostAllocation(bytes32 _label, AllocationValueType _valueType, address _beneficiary, uint _value) external onlyOwner inState(States.PrepareInvestment) {
require(_label != 0, "_label not set");
require(_beneficiary != address(0), "_beneficiary not set");
require(_value > 0, "_value not set");
require(tokenAllocations.length < arrayMaxEntryLimit, "tokenAllocations.length is too high");
tokenAllocations.push(TokenAllocation(_label, AllocationActionType.POST_ALLOCATE, _valueType, _value, _beneficiary, false));
}
function updateStartDate(uint _saleStartDate) external onlyOwner inState(States.PrepareInvestment) {
require(_saleStartDate >= now, "saleStartDate not in future");
saleStartDate = _saleStartDate;
emit LogSaleStartUpdated(msg.sender, _saleStartDate);
}
function updateCrowdliKYCProvider(CrowdliKYCProvider _crowdliKycProvider) external onlyOwner inState(States.PrepareInvestment) {
crowdliKycProvider = _crowdliKycProvider;
emit LogKYCProviderUpdated(msg.sender, address(_crowdliKycProvider));
}
function updateExchangeRate(uint _exchangeRate, uint _exchangeRateDecimals) external onlyOwner {
exchangeRate = _exchangeRate;
exchangeRateDecimals = _exchangeRateDecimals;
}
function registerInvestmentPhase(bytes32 _label, bool allowManualSwitch, uint _discountBPS, uint _cap) external onlyOwner inState(States.PrepareInvestment) {
require(_label != 0, "label should not be empty");
require(investmentPhases.length <= arrayMaxEntryLimit, "investmentPhases.length is too high");
investmentPhases.push(InvestmentPhase(_label, allowManualSwitch, _discountBPS, _cap, 0));
emit LogRegisterInvestmentPhase(msg.sender, allowManualSwitch, _label, _discountBPS, _cap);
hardCapAmount = hardCapAmount.add(_cap);
}
function processEtherPayment(address _beneficiary, uint _weiAmount, uint _paymentId) external whenNotPaused onlyEtherPaymentProvider inState(States.Investment) {
processPayment(_beneficiary, _paymentId, _weiAmount, Currency.ETH, exchangeRate, exchangeRateDecimals, false, true);
}
function processBankPaymentCHF(address _beneficiary, uint _chfAmount, bool _hasRequestedPayments, uint _paymentId) external whenNotPaused onlyFiatPaymentConfirmer inState(States.Investment) {
processPayment(_beneficiary, _paymentId, _chfAmount, Currency.CHF, 1, 1, _hasRequestedPayments, true);
}
function processBankPaymentEUR(address _beneficiary, uint _eurAmount, uint _exchangeRate, uint _exchangeRateDecimals, bool _hasRequestedPayments, uint _paymentId) external whenNotPaused onlyFiatPaymentConfirmer inState(States.Investment) {
processPayment(_beneficiary, _paymentId, _eurAmount, Currency.EUR, _exchangeRate, _exchangeRateDecimals, _hasRequestedPayments, true);
}
function processTokenAgentPaymentCHF(uint _chfAmount, uint _paymentId) external whenNotPaused onlyTokenAgentPaymentConfirmer inState(States.Investment) {
processPayment(tokenAgentWallet, _paymentId, _chfAmount, Currency.CHF, 1, 1, false, true);
}
function addPendingVoucher(address _investor) external onlyOwner whenNotPaused {
investorsWithPendingVouchers[_investor] = true;
emit LogPendingVoucherAdded(_investor);
}
function switchCurrentPhaseManually() external onlyDirectorsBoard inState(States.Investment) {
require(isCurrentPhaseManuallySwitchable(), "manual switch disallowed");
InvestmentPhase memory currentInvestmentPhase = investmentPhases[investmentPhaseIndex];
uint phaseDeltaTokenAmount = currentInvestmentPhase.capAmount.sub(currentInvestmentPhase.tokensSoldAmount);
uint lastIndex = investmentPhases.length.sub(1);
investmentPhases[lastIndex].capAmount = investmentPhases[lastIndex].capAmount.add(phaseDeltaTokenAmount);
nextInvestmentPhase();
}
function start() external onlyDirectorsBoard inState(States.PrepareInvestment) {
startInternal();
}
function calculateTokenStatementFiat(address _investor, uint _currencyAmount, Currency _currency, uint256 _exchangeRate, uint256 _exchangeRateDecimals, bool _hasRequestedBankPayments, uint investmentPhaseOffset) external view returns(uint[] memory) {
TokenStatement memory tokenStatement = calculateTokenStatementStruct(_investor, _currencyAmount, _currency, _exchangeRate, _exchangeRateDecimals, _hasRequestedBankPayments, (investmentPhaseOffset == 0), investmentPhaseOffset);
return convertTokenStatementToArray(tokenStatement);
}
function calculateTokenStatementEther(address _investor, uint _currencyAmount, Currency _currency, bool _hasRequestedBankPayments, uint investmentPhaseOffset) external view returns(uint[] memory) {
TokenStatement memory tokenStatement = calculateTokenStatementStruct(_investor, _currencyAmount, _currency, exchangeRate, exchangeRateDecimals, _hasRequestedBankPayments, (investmentPhaseOffset == 0), investmentPhaseOffset);
return convertTokenStatementToArray(tokenStatement);
}
function evalTimedStates() external view returns (bool) {
return ((isEndDateReached() && state == States.Investment) || isStartRequired());
}
function extendEndDate() external inState(States.Investment) onlyDirectorsBoard {
require(isEndDateExtendable(), "isEndDateExtendable() is false");
endDate = endDate.add(endDateExtensionOffset);
endDateExtensionOffset = 0;
emit LogEndDateExtended(msg.sender, endDate);
}
function updateTimedStates() external {
if (isEndDateReached()) {
if (isSoftCapReached()) {
close(true);
} else {
close(false);
}
}
if(isStartRequired()){
startInternal();
}
}
function closeManually() external onlyDirectorsBoard inState(States.Investment) {
require(isHardCapWithinCapThresholdReached(), "Cap is not reached.");
close(true);
}
function finalize(bytes32 _message) external onlyDirectorsBoard inState(States.Finalizing) {
setBoardComment(_message);
allocateTokens(AllocationActionType.POST_ALLOCATE);
token.unpause();
updateState(States.Finalized);
emit LogFinalized(msg.sender);
}
function getInvestmentPhaseCount() public view returns(uint) {
return investmentPhases.length;
}
function getTokenAllocationsCount() public view returns(uint) {
return tokenAllocations.length;
}
function validate() public view returns (uint) {
uint statusCode = 0;
if (address(token) == address(0)) {
statusCode = 1;
} else if (address(crowdliKycProvider) == address(0)) {
statusCode = 2;
} else if (softCapAmount == 0) {
statusCode = 3;
} else if (hardCapAmount == 0) {
statusCode = 4;
} else if (hardCapAmount < softCapAmount) {
statusCode = 5;
} else if (minChfAmountPerInvestment == 0) {
statusCode = 6;
} else if (investmentPhases.length == 0) {
statusCode = 9;
}
return statusCode;
}
function resolvePaymentError(TokenStatementValidation validationCode) public pure returns(string memory) {
if (validationCode == TokenStatementValidation.BELOW_MIN_LIMIT) {
return "BELOW_MIN_LIMIT";
} else if (validationCode == TokenStatementValidation.ABOVE_MAX_LIMIT) {
return "ABOVE_MAX_LIMIT";
} else if (validationCode == TokenStatementValidation.EXCEEDS_HARD_CAP) {
return "EXCEEDS_HARD_CAP";
}
}
function pause() public {
super.pause();
crowdliExchangeVault.pause();
crowdliKycProvider.pause();
}
function unpause () public {
super.unpause();
crowdliExchangeVault.unpause();
crowdliKycProvider.unpause();
}
function setBoardComment(bytes32 _boardComment) public onlyDirectorsBoard {
boardComment = _boardComment;
}
function hasPendingVideoVerificationFees(address _investor, bool _hasRequestedBankPayments) private view returns (bool) {
return (crowdliKycProvider.hasVerificationLevel(_investor, CrowdliKYCProvider.VerificationTier.VideoVerified) && (!paidForVideoVerification[_investor]) && !hasUnconfirmedPayments(_investor, _hasRequestedBankPayments));
}
function isEntitledForEarlyBird(address _investor, uint tokenAmount, uint _investmentPhaseIndex) private view returns (bool) {
return ((tokenAmount >= earlyBirdTokensThresholdAmount) && (currentEarlyBirdInvestorsCount < earlyBirdInvestorsCount) && !investorsWithEarlyBird[_investor] && _investmentPhaseIndex >= earlyBirdInvestmentPhase);
}
function hasPendingVouchers(address _investor, bool _hasRequestedBankPayments) private view returns (bool) {
return (investorsWithPendingVouchers[_investor] && !hasUnconfirmedPayments(_investor, _hasRequestedBankPayments));
}
function hasUnconfirmedPayments(address _investor, bool _hasRequestedBankPayments) private view returns (bool) {
return (_hasRequestedBankPayments || crowdliExchangeVault.hasRequestedPayments(_investor));
}
function isEndDateExtendable() public view returns (bool) {
return ((endDateExtensionDecissionDate >= now) && (endDateExtensionOffset > 0));
}
function isCurrentPhaseManuallySwitchable() public view returns(bool) {
return investmentPhases[investmentPhaseIndex].allowManualSwitch;
}
function isLastPhase() public view returns (bool) {
return (investmentPhaseIndex.add(1) >= investmentPhases.length);
}
function isCurrentPhaseCapReached() public view returns(bool) {
InvestmentPhase memory investmentPhase = investmentPhases[investmentPhaseIndex];
return (investmentPhase.tokensSoldAmount >= investmentPhase.capAmount);
}
function isHardCapWithinCapThresholdReached() public view returns (bool) {
return (isLastPhase() && (investmentPhases[investmentPhaseIndex].tokensSoldAmount >= investmentPhases[investmentPhaseIndex].capAmount.sub(hardCapThresholdAmount)));
}
function isHardCapReached() public view returns (bool) {
return (isLastPhase() && (investmentPhases[investmentPhaseIndex].tokensSoldAmount >= investmentPhases[investmentPhaseIndex].capAmount));
}
function isSoftCapReached() public view returns (bool) {
return (tokensSoldAmount >= softCapAmount);
}
function isManuallyClosable() public view returns(bool) {
return(hasState(States.Investment) && isHardCapWithinCapThresholdReached());
}
function isEndDateReached() public view returns (bool) {
return (now > endDate);
}
function hasState(States _state) public view returns (bool) {
return (state == _state);
}
function getStatisticsData() public view returns(uint[] memory) {
uint[] memory result= new uint[](7);
result[0] = softCapAmount;
result[1] = hardCapAmount;
result[2] = weiInvested;
result[3] = chfOverallInvested;
result[4] = eurOverallInvested;
result[5] = tokensSoldAmount;
result[6] = numberOfInvestors;
return result;
}
function nextInvestmentPhase() private returns (bool) {
if (!isLastPhase()) {
investmentPhaseIndex = investmentPhaseIndex.add(1);
emit LogInvestmentPhaseSwitched(investmentPhaseIndex);
return false;
} else {
revert("payment exceeds hard cap");
}
}
function confirmMintedTokensForPayment(uint _paymentId, uint tokenAmountToBuy, Currency _currency) private {
if (Currency.ETH == _currency) {
crowdliExchangeVault.confirmMintedTokensForPayment(_paymentId, tokenAmountToBuy);
}
}
function updateState(States _state) private {
require (_state > state, "the state can never transit backwards");
state = _state;
emit LogStateChanged(state);
}
function close(bool stoSucessfull) private inState(States.Investment) {
require(hasState(States.Investment), "Requires state Investment");
updateState(States.Finalizing);
emit LogClosed(msg.sender, stoSucessfull);
}
function allocateTokens(AllocationActionType _actionType) private {
for (uint i = 0; i < tokenAllocations.length;i++) {
if (tokenAllocations[i].actionType == _actionType) {
uint tokensToAllocate = 0;
if (tokenAllocations[i].valueType == AllocationValueType.BPS_OF_TOKENS_SOLD) {
tokensToAllocate = tokensSoldAmount.mul(tokenAllocations[i].valueAmount).div(10000);
} else if (tokenAllocations[i].valueType == AllocationValueType.FIXED_TOKEN_AMOUNT) {
tokensToAllocate = tokenAllocations[i].valueAmount;
}
token.mint(tokenAllocations[i].beneficiary, tokensToAllocate);
tokenAllocations[i].isAllocated = true;
}
}
}
function processPayment(address _investor, uint _paymentId, uint _currencyAmount, Currency _currency, uint _exchangeRate, uint _exchangeRateDecimals, bool _hasRequestedBankPayments, bool _isFirstPayment) private {
require(crowdliKycProvider.verificationTiers(_investor) > CrowdliKYCProvider.VerificationTier.None, "Verification tier not > 0");
InvestmentPhase storage investmentPhase = investmentPhases[investmentPhaseIndex];
TokenStatement memory tokenStatement = calculateTokenStatementStruct(_investor, _currencyAmount, _currency, _exchangeRate, _exchangeRateDecimals, _hasRequestedBankPayments, _isFirstPayment, 0);
require(tokenStatement.validationCode == TokenStatementValidation.OK, resolvePaymentError(tokenStatement.validationCode));
investmentPhase.tokensSoldAmount = investmentPhase.tokensSoldAmount.add(tokenStatement.currentPhaseTokenAmount);
tokensSoldAmount = tokensSoldAmount.add(tokenStatement.currentPhaseTokenAmount);
if (_isFirstPayment) {
allInvestmentsInChf[_investor] = allInvestmentsInChf[_investor].add(tokenStatement.requestedCHF);
if (tokenStatement.feesCHF > gasCostAmount)
paidForVideoVerification[_investor] = true;
if (Currency.CHF == _currency) {
chfPerInvestor[_investor] = chfPerInvestor[_investor].add(_currencyAmount);
chfOverallInvested = chfOverallInvested.add(_currencyAmount);
} else if (Currency.EUR == _currency) {
eurPerInvestor[_investor] = eurPerInvestor[_investor].add(_currencyAmount);
eurOverallInvested = eurOverallInvested.add(_currencyAmount);
} else if (Currency.ETH == _currency) {
weiPerInvestor[_investor] = weiPerInvestor[_investor].add(_currencyAmount);
weiInvested = weiInvested.add(_currencyAmount);
}
if (token.balanceOf(_investor) == 0) {
numberOfInvestors = numberOfInvestors.add(1);
}
}
if (tokenStatement.earlyBirdCreditTokenAmount > 0) {
currentEarlyBirdInvestorsCount = currentEarlyBirdInvestorsCount.add(1);
investorsWithEarlyBird[_investor] = true;
}
if (tokenStatement.voucherCreditTokenAmount > 0) {
investorsWithPendingVouchers[_investor] = false;
}
token.mint(_investor, tokenStatement.currentPhaseTokenAmount);
if (tokenStatement.nextPhaseBaseAmount > 0) {
ExecutionType executionType;
executionType = ExecutionType.SplitPayment;
emit LogPaymentConfirmation(_investor, tokenStatement.requestedBase, convertTokenStatementToArray(tokenStatement), _paymentId, _currency, executionType);
confirmMintedTokensForPayment(_paymentId, tokenStatement.currentPhaseTokenAmount, _currency);
nextInvestmentPhase();
processPayment(_investor, _paymentId, tokenStatement.nextPhaseBaseAmount, _currency, _exchangeRate, _exchangeRateDecimals, _hasRequestedBankPayments, false);
} else {
emit LogPaymentConfirmation(_investor, tokenStatement.requestedBase, convertTokenStatementToArray(tokenStatement), _paymentId, _currency, ExecutionType.SinglePayment);
confirmMintedTokensForPayment(_paymentId, tokenStatement.currentPhaseTokenAmount, _currency);
}
}
function convertTokenStatementToArray(TokenStatement memory tokenStatement) private pure returns(uint[] memory) {
uint[] memory tokenStatementArray = new uint[](9);
tokenStatementArray[0] = tokenStatement.requestedBase;
tokenStatementArray[1] = tokenStatement.requestedCHF;
tokenStatementArray[2] = tokenStatement.feesCHF;
tokenStatementArray[3] = tokenStatement.currentPhaseDiscount;
tokenStatementArray[4] = tokenStatement.earlyBirdCreditTokenAmount;
tokenStatementArray[5] = tokenStatement.voucherCreditTokenAmount;
tokenStatementArray[6] = tokenStatement.currentPhaseTokenAmount;
tokenStatementArray[7] = tokenStatement.nextPhaseBaseAmount;
tokenStatementArray[8] = uint(tokenStatement.validationCode);
return tokenStatementArray;
}
function calculateDiscountUptick(uint _amount, InvestmentPhase memory investmentPhase) private pure returns (uint) {
uint currentRate = uint(10000).sub(investmentPhase.discountBPS);
return _amount.mul(investmentPhase.discountBPS).div(currentRate);
}
function calculateDiscount(uint _amount, InvestmentPhase memory investmentPhase) private pure returns (uint) {
return _amount.mul(investmentPhase.discountBPS).div(10000);
}
function calculateConversionFromBase(uint _currencyAmount, uint _exchangeRate, uint _exchangeRateDecimals) private pure returns (uint) {
return _currencyAmount.mul(_exchangeRate).div(_exchangeRateDecimals);
}
function calculateConversionToBase(uint _currencyAmount, uint _exchangeRate, uint _exchangeRateDecimals) private pure returns (uint) {
return _currencyAmount.mul(_exchangeRateDecimals).div(_exchangeRate);
}
function calculateTokenStatementStruct(address _investor, uint _currencyAmount, Currency _currency, uint256 _exchangeRate, uint256 _exchangeRateDecimals, bool _hasRequestedBankPayments, bool _isFirstPayment, uint _investmentPhaseOffset) private view returns(TokenStatement memory) {
TokenStatement memory tokenStatement;
uint investmentPhaseWithOffset = investmentPhaseIndex.add(_investmentPhaseOffset);
InvestmentPhase memory investmentPhase = investmentPhases[investmentPhaseWithOffset];
tokenStatement.requestedBase = _currencyAmount;
if (Currency.CHF == _currency) {
tokenStatement.requestedCHF = tokenStatement.requestedBase;
} else if (Currency.EUR == _currency) {
tokenStatement.requestedCHF = calculateConversionFromBase(tokenStatement.requestedBase, _exchangeRate, _exchangeRateDecimals);
} else if (Currency.ETH == _currency) {
tokenStatement.requestedCHF = calculateConversionFromBase(tokenStatement.requestedBase, _exchangeRate, _exchangeRateDecimals);
} else revert("Currency not supported");
tokenStatement.requestedCHF = roundUp(tokenStatement.requestedCHF);
uint investmentNetCHF = tokenStatement.requestedCHF;
if (_isFirstPayment) {
tokenStatement.validationCode = validatePayment(_investor, tokenStatement);
tokenStatement.feesCHF = gasCostAmount;
if (hasPendingVideoVerificationFees(_investor, _hasRequestedBankPayments)) {
tokenStatement.feesCHF = tokenStatement.feesCHF.add(videoVerificationCostAmount);
}
investmentNetCHF = tokenStatement.requestedCHF.sub(tokenStatement.feesCHF);
}
uint phaseDeltaTokenAmount = investmentPhase.capAmount.sub(investmentPhase.tokensSoldAmount);
tokenStatement.currentPhaseDiscount = roundUp(calculateDiscountUptick(investmentNetCHF, investmentPhase));
uint tokenAmountWithCurrentPhaseDiscount = investmentNetCHF.add(tokenStatement.currentPhaseDiscount);
if (tokenAmountWithCurrentPhaseDiscount > phaseDeltaTokenAmount) {
if (isLastPhase())
tokenStatement.validationCode = TokenStatementValidation.EXCEEDS_HARD_CAP;
tokenStatement.currentPhaseTokenAmount = roundUp(phaseDeltaTokenAmount);
tokenStatement.currentPhaseDiscount = roundUp(calculateDiscount(phaseDeltaTokenAmount, investmentPhase));
uint currentPhaseNetTokenAmount = phaseDeltaTokenAmount.sub(tokenStatement.currentPhaseDiscount);
tokenStatement.nextPhaseBaseAmount = calculateConversionToBase(investmentNetCHF.sub(currentPhaseNetTokenAmount), _exchangeRate, _exchangeRateDecimals);
} else {
tokenStatement.currentPhaseDiscount = tokenStatement.currentPhaseDiscount;
tokenStatement.currentPhaseTokenAmount = tokenAmountWithCurrentPhaseDiscount;
tokenStatement.nextPhaseBaseAmount = 0;
if (isEntitledForEarlyBird(_investor, tokenStatement.currentPhaseTokenAmount, investmentPhaseWithOffset)) {
tokenStatement.earlyBirdCreditTokenAmount = earlyBirdAdditionalTokensAmount;
tokenStatement.currentPhaseTokenAmount = tokenStatement.currentPhaseTokenAmount.add(tokenStatement.earlyBirdCreditTokenAmount);
}
if (hasPendingVouchers(_investor, _hasRequestedBankPayments)) {
tokenStatement.voucherCreditTokenAmount = voucherTokensAmount;
tokenStatement.currentPhaseTokenAmount = tokenStatement.currentPhaseTokenAmount.add(tokenStatement.voucherCreditTokenAmount);
}
}
tokenStatement.currentPhaseTokenAmount = roundUp(tokenStatement.currentPhaseTokenAmount);
return tokenStatement;
}
function validatePayment(address _investor, TokenStatement memory tokenStatement) private view returns(TokenStatementValidation) {
if (tokenStatement.requestedCHF < minChfAmountPerInvestment) {
return TokenStatementValidation.BELOW_MIN_LIMIT;
} else if (allInvestmentsInChf[_investor].add(tokenStatement.requestedCHF) > crowdliKycProvider.getMaxChfAmountForInvestor(_investor)) {
return TokenStatementValidation.ABOVE_MAX_LIMIT;
} else return TokenStatementValidation.OK;
}
function isStartRequired() private view returns(bool) {
return ((now > saleStartDate) && state == States.PrepareInvestment);
}
function startInternal() private {
require(validate() == 0, "Start validation failed");
saleStartDate = now;
updateState(States.Investment);
emit LogStarted(msg.sender);
}
function roundUp(uint amount) private pure returns(uint) {
uint decimals = 10 ** 18;
uint result = amount;
uint remainder = amount % decimals;
if (remainder > 0) {
result = amount - remainder + decimals;
}
return result;
}
}
文件 6 的 22:CrowdliToken.sol
pragma solidity 0.5.0;
import "./Ownable.sol";
import "./ERC20Detailed.sol";
import "./ERC20Mintable.sol";
import "./ERC20Pausable.sol";
contract CrowdliToken is ERC20Detailed, ERC20Mintable, ERC20Pausable, Ownable {
address[] public investors;
constructor (string memory _name, string memory _symbol, uint8 _decimals) ERC20Detailed(_name,_symbol,_decimals) public {
}
function mint(address account, uint256 amount) public onlyMinter returns (bool) {
if (balanceOf(account) == 0) {
investors.push(account);
}
return super.mint(account, amount);
}
function initToken(address _directorsBoard,address _crowdliSTO) external onlyOwner{
addMinter(_directorsBoard);
addMinter(_crowdliSTO);
addPauser(_directorsBoard);
addPauser(_crowdliSTO);
}
}
文件 7 的 22:ERC20.sol
pragma solidity ^0.5.0;
import "./IERC20.sol";
import "./SafeMath.sol";
contract ERC20 is IERC20 {
using SafeMath for uint256;
mapping (address => uint256) private _balances;
mapping (address => mapping (address => uint256)) private _allowances;
uint256 private _totalSupply;
function totalSupply() public view returns (uint256) {
return _totalSupply;
}
function balanceOf(address account) public view returns (uint256) {
return _balances[account];
}
function transfer(address recipient, uint256 amount) public returns (bool) {
_transfer(msg.sender, recipient, amount);
return true;
}
function allowance(address owner, address spender) public view returns (uint256) {
return _allowances[owner][spender];
}
function approve(address spender, uint256 value) public returns (bool) {
_approve(msg.sender, spender, value);
return true;
}
function transferFrom(address sender, address recipient, uint256 amount) public returns (bool) {
_transfer(sender, recipient, amount);
_approve(sender, msg.sender, _allowances[sender][msg.sender].sub(amount));
return true;
}
function increaseAllowance(address spender, uint256 addedValue) public returns (bool) {
_approve(msg.sender, spender, _allowances[msg.sender][spender].add(addedValue));
return true;
}
function decreaseAllowance(address spender, uint256 subtractedValue) public returns (bool) {
_approve(msg.sender, spender, _allowances[msg.sender][spender].sub(subtractedValue));
return true;
}
function _transfer(address sender, address recipient, uint256 amount) internal {
require(sender != address(0), "ERC20: transfer from the zero address");
require(recipient != address(0), "ERC20: transfer to the zero address");
_balances[sender] = _balances[sender].sub(amount);
_balances[recipient] = _balances[recipient].add(amount);
emit Transfer(sender, recipient, amount);
}
function _mint(address account, uint256 amount) internal {
require(account != address(0), "ERC20: mint to the zero address");
_totalSupply = _totalSupply.add(amount);
_balances[account] = _balances[account].add(amount);
emit Transfer(address(0), account, amount);
}
function _burn(address account, uint256 value) internal {
require(account != address(0), "ERC20: burn from the zero address");
_totalSupply = _totalSupply.sub(value);
_balances[account] = _balances[account].sub(value);
emit Transfer(account, address(0), value);
}
function _approve(address owner, address spender, uint256 value) internal {
require(owner != address(0), "ERC20: approve from the zero address");
require(spender != address(0), "ERC20: approve to the zero address");
_allowances[owner][spender] = value;
emit Approval(owner, spender, value);
}
function _burnFrom(address account, uint256 amount) internal {
_burn(account, amount);
_approve(account, msg.sender, _allowances[account][msg.sender].sub(amount));
}
}
文件 8 的 22:ERC20Capped.sol
pragma solidity ^0.5.0;
import "./ERC20Mintable.sol";
contract ERC20Capped is ERC20Mintable {
uint256 private _cap;
constructor (uint256 cap) public {
require(cap > 0, "ERC20Capped: cap is 0");
_cap = cap;
}
function cap() public view returns (uint256) {
return _cap;
}
function _mint(address account, uint256 value) internal {
require(totalSupply().add(value) <= _cap, "ERC20Capped: cap exceeded");
super._mint(account, value);
}
}
文件 9 的 22:ERC20Detailed.sol
pragma solidity ^0.5.0;
import "./IERC20.sol";
contract ERC20Detailed is IERC20 {
string private _name;
string private _symbol;
uint8 private _decimals;
constructor (string memory name, string memory symbol, uint8 decimals) public {
_name = name;
_symbol = symbol;
_decimals = decimals;
}
function name() public view returns (string memory) {
return _name;
}
function symbol() public view returns (string memory) {
return _symbol;
}
function decimals() public view returns (uint8) {
return _decimals;
}
}
文件 10 的 22:ERC20Mintable.sol
pragma solidity ^0.5.0;
import "./ERC20.sol";
import "./MinterRole.sol";
contract ERC20Mintable is ERC20, MinterRole {
function mint(address account, uint256 amount) public onlyMinter returns (bool) {
_mint(account, amount);
return true;
}
}
文件 11 的 22:ERC20Pausable.sol
pragma solidity ^0.5.0;
import "./ERC20.sol";
import "./Pausable.sol";
contract ERC20Pausable is ERC20, Pausable {
function transfer(address to, uint256 value) public whenNotPaused returns (bool) {
return super.transfer(to, value);
}
function transferFrom(address from, address to, uint256 value) public whenNotPaused returns (bool) {
return super.transferFrom(from, to, value);
}
function approve(address spender, uint256 value) public whenNotPaused returns (bool) {
return super.approve(spender, value);
}
function increaseAllowance(address spender, uint addedValue) public whenNotPaused returns (bool) {
return super.increaseAllowance(spender, addedValue);
}
function decreaseAllowance(address spender, uint subtractedValue) public whenNotPaused returns (bool) {
return super.decreaseAllowance(spender, subtractedValue);
}
}
文件 12 的 22:IERC20.sol
pragma solidity ^0.5.0;
interface IERC20 {
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address recipient, uint256 amount) external returns (bool);
function allowance(address owner, address spender) external view returns (uint256);
function approve(address spender, uint256 amount) external returns (bool);
function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
}
文件 13 的 22:MinterRole.sol
pragma solidity ^0.5.0;
import "./Roles.sol";
contract MinterRole {
using Roles for Roles.Role;
event MinterAdded(address indexed account);
event MinterRemoved(address indexed account);
Roles.Role private _minters;
constructor () internal {
_addMinter(msg.sender);
}
modifier onlyMinter() {
require(isMinter(msg.sender), "MinterRole: caller does not have the Minter role");
_;
}
function isMinter(address account) public view returns (bool) {
return _minters.has(account);
}
function addMinter(address account) public onlyMinter {
_addMinter(account);
}
function renounceMinter() public {
_removeMinter(msg.sender);
}
function _addMinter(address account) internal {
_minters.add(account);
emit MinterAdded(account);
}
function _removeMinter(address account) internal {
_minters.remove(account);
emit MinterRemoved(account);
}
}
文件 14 的 22:Ownable.sol
pragma solidity ^0.5.0;
contract Ownable {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
constructor () internal {
_owner = msg.sender;
emit OwnershipTransferred(address(0), _owner);
}
function owner() public view returns (address) {
return _owner;
}
modifier onlyOwner() {
require(isOwner(), "Ownable: caller is not the owner");
_;
}
function isOwner() public view returns (bool) {
return msg.sender == _owner;
}
function renounceOwnership() public onlyOwner {
emit OwnershipTransferred(_owner, address(0));
_owner = address(0);
}
function transferOwnership(address newOwner) public onlyOwner {
_transferOwnership(newOwner);
}
function _transferOwnership(address newOwner) internal {
require(newOwner != address(0), "Ownable: new owner is the zero address");
emit OwnershipTransferred(_owner, newOwner);
_owner = newOwner;
}
}
文件 15 的 22:Pausable.sol
pragma solidity ^0.5.0;
import "./PauserRole.sol";
contract Pausable is PauserRole {
event Paused(address account);
event Unpaused(address account);
bool private _paused;
constructor () internal {
_paused = false;
}
function paused() public view returns (bool) {
return _paused;
}
modifier whenNotPaused() {
require(!_paused, "Pausable: paused");
_;
}
modifier whenPaused() {
require(_paused, "Pausable: not paused");
_;
}
function pause() public onlyPauser whenNotPaused {
_paused = true;
emit Paused(msg.sender);
}
function unpause() public onlyPauser whenPaused {
_paused = false;
emit Unpaused(msg.sender);
}
}
文件 16 的 22:PauserRole.sol
pragma solidity ^0.5.0;
import "./Roles.sol";
contract PauserRole {
using Roles for Roles.Role;
event PauserAdded(address indexed account);
event PauserRemoved(address indexed account);
Roles.Role private _pausers;
constructor () internal {
_addPauser(msg.sender);
}
modifier onlyPauser() {
require(isPauser(msg.sender), "PauserRole: caller does not have the Pauser role");
_;
}
function isPauser(address account) public view returns (bool) {
return _pausers.has(account);
}
function addPauser(address account) public onlyPauser {
_addPauser(account);
}
function renouncePauser() public {
_removePauser(msg.sender);
}
function _addPauser(address account) internal {
_pausers.add(account);
emit PauserAdded(account);
}
function _removePauser(address account) internal {
_pausers.remove(account);
emit PauserRemoved(account);
}
}
文件 17 的 22:Roles.sol
pragma solidity ^0.5.0;
library Roles {
struct Role {
mapping (address => bool) bearer;
}
function add(Role storage role, address account) internal {
require(!has(role, account), "Roles: account already has role");
role.bearer[account] = true;
}
function remove(Role storage role, address account) internal {
require(has(role, account), "Roles: account does not have role");
role.bearer[account] = false;
}
function has(Role storage role, address account) internal view returns (bool) {
require(account != address(0), "Roles: account is the zero address");
return role.bearer[account];
}
}
文件 18 的 22:SafeERC20.sol
pragma solidity ^0.5.0;
import "./IERC20.sol";
import "./SafeMath.sol";
import "./Address.sol";
library SafeERC20 {
using SafeMath for uint256;
using Address for address;
function safeTransfer(IERC20 token, address to, uint256 value) internal {
callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
}
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
}
function safeApprove(IERC20 token, address spender, uint256 value) internal {
require((value == 0) || (token.allowance(address(this), spender) == 0),
"SafeERC20: approve from non-zero to non-zero allowance"
);
callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
}
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 newAllowance = token.allowance(address(this), spender).add(value);
callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 newAllowance = token.allowance(address(this), spender).sub(value);
callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
function callOptionalReturn(IERC20 token, bytes memory data) private {
require(address(token).isContract(), "SafeERC20: call to non-contract");
(bool success, bytes memory returndata) = address(token).call(data);
require(success, "SafeERC20: low-level call failed");
if (returndata.length > 0) {
require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
}
}
文件 19 的 22:SafeMath.sol
pragma solidity ^0.5.0;
library SafeMath {
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a, "SafeMath: addition overflow");
return c;
}
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
require(b <= a, "SafeMath: subtraction overflow");
uint256 c = a - b;
return c;
}
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
uint256 c = a * b;
require(c / a == b, "SafeMath: multiplication overflow");
return c;
}
function div(uint256 a, uint256 b) internal pure returns (uint256) {
require(b > 0, "SafeMath: division by zero");
uint256 c = a / b;
return c;
}
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
require(b != 0, "SafeMath: modulo by zero");
return a % b;
}
}
文件 20 的 22:SignerRole.sol
pragma solidity ^0.5.0;
import "./Roles.sol";
contract SignerRole {
using Roles for Roles.Role;
event SignerAdded(address indexed account);
event SignerRemoved(address indexed account);
Roles.Role private _signers;
constructor () internal {
_addSigner(msg.sender);
}
modifier onlySigner() {
require(isSigner(msg.sender), "SignerRole: caller does not have the Signer role");
_;
}
function isSigner(address account) public view returns (bool) {
return _signers.has(account);
}
function addSigner(address account) public onlySigner {
_addSigner(account);
}
function renounceSigner() public {
_removeSigner(msg.sender);
}
function _addSigner(address account) internal {
_signers.add(account);
emit SignerAdded(account);
}
function _removeSigner(address account) internal {
_signers.remove(account);
emit SignerRemoved(account);
}
}
文件 21 的 22:WhitelistAdminRole.sol
pragma solidity ^0.5.0;
import "./Roles.sol";
contract WhitelistAdminRole {
using Roles for Roles.Role;
event WhitelistAdminAdded(address indexed account);
event WhitelistAdminRemoved(address indexed account);
Roles.Role private _whitelistAdmins;
constructor () internal {
_addWhitelistAdmin(msg.sender);
}
modifier onlyWhitelistAdmin() {
require(isWhitelistAdmin(msg.sender), "WhitelistAdminRole: caller does not have the WhitelistAdmin role");
_;
}
function isWhitelistAdmin(address account) public view returns (bool) {
return _whitelistAdmins.has(account);
}
function addWhitelistAdmin(address account) public onlyWhitelistAdmin {
_addWhitelistAdmin(account);
}
function renounceWhitelistAdmin() public {
_removeWhitelistAdmin(msg.sender);
}
function _addWhitelistAdmin(address account) internal {
_whitelistAdmins.add(account);
emit WhitelistAdminAdded(account);
}
function _removeWhitelistAdmin(address account) internal {
_whitelistAdmins.remove(account);
emit WhitelistAdminRemoved(account);
}
}
文件 22 的 22:WhitelistedRole.sol
pragma solidity ^0.5.0;
import "./Roles.sol";
import "./WhitelistAdminRole.sol";
contract WhitelistedRole is WhitelistAdminRole {
using Roles for Roles.Role;
event WhitelistedAdded(address indexed account);
event WhitelistedRemoved(address indexed account);
Roles.Role private _whitelisteds;
modifier onlyWhitelisted() {
require(isWhitelisted(msg.sender), "WhitelistedRole: caller does not have the Whitelisted role");
_;
}
function isWhitelisted(address account) public view returns (bool) {
return _whitelisteds.has(account);
}
function addWhitelisted(address account) public onlyWhitelistAdmin {
_addWhitelisted(account);
}
function removeWhitelisted(address account) public onlyWhitelistAdmin {
_removeWhitelisted(account);
}
function renounceWhitelisted() public {
_removeWhitelisted(msg.sender);
}
function _addWhitelisted(address account) internal {
_whitelisteds.add(account);
emit WhitelistedAdded(account);
}
function _removeWhitelisted(address account) internal {
_whitelisteds.remove(account);
emit WhitelistedRemoved(account);
}
}
{
"compilationTarget": {
"CrowdliSTO.sol": "CrowdliSTO"
},
"evmVersion": "byzantium",
"libraries": {},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"weiPerInvestor","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_investor","type":"address"}],"name":"addPendingVoucher","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"extendEndDate","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_saleStartDate","type":"uint256"}],"name":"updateStartDate","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"numberOfInvestors","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"chfPerInvestor","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"crowdliExchangeVault","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"exchangeRateDecimals","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"investorsWithEarlyBird","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"weiInvested","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"crowdliKycProvider","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"exchangeRate","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"isHardCapWithinCapThresholdReached","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"unpause","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"validationCode","type":"uint8"}],"name":"resolvePaymentError","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"pure","type":"function"},{"constant":true,"inputs":[],"name":"directorsBoard","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"account","type":"address"}],"name":"isPauser","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_beneficiary","type":"address"},{"name":"_eurAmount","type":"uint256"},{"name":"_exchangeRate","type":"uint256"},{"name":"_exchangeRateDecimals","type":"uint256"},{"name":"_hasRequestedPayments","type":"bool"},{"name":"_paymentId","type":"uint256"}],"name":"processBankPaymentEUR","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"boardComment","outputs":[{"name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"paidForVideoVerification","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"voucherTokensAmount","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"updateTimedStates","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"currentEarlyBirdInvestorsCount","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"paused","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"isLastPhase","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_investor","type":"address"},{"name":"_currencyAmount","type":"uint256"},{"name":"_currency","type":"uint8"},{"name":"_exchangeRate","type":"uint256"},{"name":"_exchangeRateDecimals","type":"uint256"},{"name":"_hasRequestedBankPayments","type":"bool"},{"name":"investmentPhaseOffset","type":"uint256"}],"name":"calculateTokenStatementFiat","outputs":[{"name":"","type":"uint256[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"earlyBirdInvestmentPhase","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"tokenAgentWallet","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"hardCapThresholdAmount","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"isHardCapReached","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"validate","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"investmentPhaseIndex","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"renouncePauser","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_crowdliKycProvider","type":"address"}],"name":"updateCrowdliKYCProvider","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"renounceOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"eurPerInvestor","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"videoVerificationCostAmount","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"isCurrentPhaseManuallySwitchable","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"earlyBirdAdditionalTokensAmount","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"isSoftCapReached","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"tokenAllocations","outputs":[{"name":"label","type":"bytes32"},{"name":"actionType","type":"uint8"},{"name":"valueType","type":"uint8"},{"name":"valueAmount","type":"uint256"},{"name":"beneficiary","type":"address"},{"name":"isAllocated","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"account","type":"address"}],"name":"addPauser","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"arrayMaxEntryLimit","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"pause","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"endDateExtensionDecissionDate","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"saleStartDate","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"isManuallyClosable","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getInvestmentPhaseCount","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"eurOverallInvested","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"isOwner","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"isEndDateReached","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_message","type":"bytes32"}],"name":"finalize","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"evalTimedStates","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"investmentPhases","outputs":[{"name":"label","type":"bytes32"},{"name":"allowManualSwitch","type":"bool"},{"name":"discountBPS","type":"uint256"},{"name":"capAmount","type":"uint256"},{"name":"tokensSoldAmount","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_beneficiary","type":"address"},{"name":"_chfAmount","type":"uint256"},{"name":"_hasRequestedPayments","type":"bool"},{"name":"_paymentId","type":"uint256"}],"name":"processBankPaymentCHF","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_label","type":"bytes32"},{"name":"_valueType","type":"uint8"},{"name":"_beneficiary","type":"address"},{"name":"_value","type":"uint256"}],"name":"registerPostAllocation","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"closeManually","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"switchCurrentPhaseManually","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"tokensSoldAmount","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_label","type":"bytes32"},{"name":"allowManualSwitch","type":"bool"},{"name":"_discountBPS","type":"uint256"},{"name":"_cap","type":"uint256"}],"name":"registerInvestmentPhase","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"isEndDateExtendable","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"chfOverallInvested","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_chfAmount","type":"uint256"},{"name":"_paymentId","type":"uint256"}],"name":"processTokenAgentPaymentCHF","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"earlyBirdTokensThresholdAmount","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"start","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_beneficiary","type":"address"},{"name":"_weiAmount","type":"uint256"},{"name":"_paymentId","type":"uint256"}],"name":"processEtherPayment","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"state","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"endDate","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"hardCapAmount","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"earlyBirdInvestorsCount","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_state","type":"uint8"}],"name":"hasState","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"minChfAmountPerInvestment","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getStatisticsData","outputs":[{"name":"","type":"uint256[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"allInvestmentsInChf","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getTokenAllocationsCount","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_boardComment","type":"bytes32"}],"name":"setBoardComment","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"endDateExtensionOffset","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"isCurrentPhaseCapReached","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_investor","type":"address"},{"name":"_currencyAmount","type":"uint256"},{"name":"_currency","type":"uint8"},{"name":"_hasRequestedBankPayments","type":"bool"},{"name":"investmentPhaseOffset","type":"uint256"}],"name":"calculateTokenStatementEther","outputs":[{"name":"","type":"uint256[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"paymentConfirmer","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_saleStartDate","type":"uint256"},{"name":"_earlyBirdTokensThreshold","type":"uint256"},{"name":"_earlyBirdAdditionalTokens","type":"uint256"},{"name":"_earlyBirdInvestorsCount","type":"uint256"},{"name":"_earlyBirdInvestmentPhase","type":"uint256"},{"name":"_voucherTokensAmount","type":"uint256"},{"name":"_videoVerificationCost","type":"uint256"},{"name":"_softCap","type":"uint256"},{"name":"_endDate","type":"uint256"},{"name":"_endDateExtensionDecissionDate","type":"uint256"},{"name":"_endDateExtensionOffset","type":"uint256"},{"name":"_gasCost","type":"uint256"},{"name":"_hardCapThresholdAmount","type":"uint256"},{"name":"_minChfAmountPerInvestment","type":"uint256"}],"name":"initSTO","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"investorsWithPendingVouchers","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"softCapAmount","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"token","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_exchangeRate","type":"uint256"},{"name":"_exchangeRateDecimals","type":"uint256"}],"name":"updateExchangeRate","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"_token","type":"address"},{"name":"_crowdliKycProvider","type":"address"},{"name":"_crowdliExchangeVault","type":"address"},{"name":"_directorsBoard","type":"address"},{"name":"_paymentConfirmer","type":"address"},{"name":"_tokenAgentWallet","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"name":"beneficiary","type":"address"},{"indexed":true,"name":"investment","type":"uint256"},{"indexed":false,"name":"paymentDetails","type":"uint256[]"},{"indexed":false,"name":"id","type":"uint256"},{"indexed":false,"name":"currency","type":"uint8"},{"indexed":false,"name":"executionType","type":"uint8"}],"name":"LogPaymentConfirmation","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"sender","type":"address"},{"indexed":false,"name":"allowManualSwitch","type":"bool"},{"indexed":true,"name":"label","type":"bytes32"},{"indexed":true,"name":"discountBPS","type":"uint256"},{"indexed":false,"name":"cap","type":"uint256"}],"name":"LogRegisterInvestmentPhase","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"sender","type":"address"},{"indexed":true,"name":"saleStartDate","type":"uint256"}],"name":"LogSaleStartUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"sender","type":"address"},{"indexed":true,"name":"crowdliKycProvider","type":"address"}],"name":"LogKYCProviderUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"sender","type":"address"}],"name":"LogStarted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"phaseIndex","type":"uint256"}],"name":"LogInvestmentPhaseSwitched","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"sender","type":"address"},{"indexed":false,"name":"endDate","type":"uint256"}],"name":"LogEndDateExtended","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"sender","type":"address"},{"indexed":false,"name":"stoSucessfull","type":"bool"}],"name":"LogClosed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"sender","type":"address"}],"name":"LogFinalized","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"_states","type":"uint8"}],"name":"LogStateChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"_investor","type":"address"}],"name":"LogPendingVoucherAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"previousOwner","type":"address"},{"indexed":true,"name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"account","type":"address"}],"name":"PauserAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"account","type":"address"}],"name":"PauserRemoved","type":"event"}]