/** * Source Code first verified at https://etherscan.io on Thursday, May 2, 2019 (UTC) */ /** * Invictus Capital - CRYPTO10 Hedged * https://invictuscapital.com * MIT License - https://github.com/invictuscapital/smartcontracts/ * Uses code from the OpenZeppelin project */ // File: contracts/openzeppelin-solidity/contracts/token/ERC20/IERC20.sol pragma solidity ^0.5.6; /** * @title ERC20 interface * @dev see https://github.com/ethereum/EIPs/issues/20 */ interface IERC20 { function transfer(address to, uint256 value) external returns (bool); function approve(address spender, uint256 value) external returns (bool); function transferFrom(address from, address to, uint256 value) external returns (bool); function totalSupply() external view returns (uint256); function balanceOf(address who) external view returns (uint256); function allowance(address owner, address spender) external view returns (uint256); event Transfer(address indexed from, address indexed to, uint256 value); event Approval(address indexed owner, address indexed spender, uint256 value); } // File: contracts/openzeppelin-solidity/contracts/token/ERC20/ERC20Detailed.sol pragma solidity ^0.5.6; /** * @title ERC20Detailed token * @dev The decimals are only for visualization purposes. * All the operations are done using the smallest and indivisible token unit, * just as on Ethereum all the operations are done in wei. */ 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; } /** * @return the name of the token. */ function name() public view returns (string memory) { return _name; } /** * @return the symbol of the token. */ function symbol() public view returns (string memory) { return _symbol; } /** * @return the number of decimals of the token. */ function decimals() public view returns (uint8) { return _decimals; } } // File: contracts/openzeppelin-solidity/contracts/math/SafeMath.sol pragma solidity ^0.5.6; /** * @title SafeMath * @dev Unsigned math operations with safety checks that revert on error */ library SafeMath { /** * @dev Multiplies two unsigned integers, reverts on overflow. */ function mul(uint256 a, uint256 b) internal pure returns (uint256) { // Gas optimization: this is cheaper than requiring 'a' not being zero, but the // benefit is lost if 'b' is also tested. // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522 if (a == 0) { return 0; } uint256 c = a * b; require(c / a == b); return c; } /** * @dev Integer division of two unsigned integers truncating the quotient, reverts on division by zero. */ function div(uint256 a, uint256 b) internal pure returns (uint256) { // Solidity only automatically asserts when dividing by 0 require(b > 0); uint256 c = a / b; // assert(a == b * c + a % b); // There is no case in which this doesn't hold return c; } /** * @dev Subtracts two unsigned integers, reverts on overflow (i.e. if subtrahend is greater than minuend). */ function sub(uint256 a, uint256 b) internal pure returns (uint256) { require(b <= a); uint256 c = a - b; return c; } /** * @dev Adds two unsigned integers, reverts on overflow. */ function add(uint256 a, uint256 b) internal pure returns (uint256) { uint256 c = a + b; require(c >= a); return c; } /** * @dev Divides two unsigned integers and returns the remainder (unsigned integer modulo), * reverts when dividing by zero. */ function mod(uint256 a, uint256 b) internal pure returns (uint256) { require(b != 0); return a % b; } } // File: contracts/openzeppelin-solidity/contracts/token/ERC20/ERC20.sol pragma solidity ^0.5.6; /** * @title Standard ERC20 token * * @dev Implementation of the basic standard token. * https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md * Originally based on code by FirstBlood: * https://github.com/Firstbloodio/token/blob/master/smart_contract/FirstBloodToken.sol * * This implementation emits additional Approval events, allowing applications to reconstruct the allowance status for * all accounts just by listening to said events. Note that this isn't required by the specification, and other * compliant implementations may not do it. */ contract ERC20 is IERC20 { using SafeMath for uint256; mapping (address => uint256) private _balances; mapping (address => mapping (address => uint256)) private _allowed; uint256 private _totalSupply; /** * @dev Total number of tokens in existence */ function totalSupply() public view returns (uint256) { return _totalSupply; } /** * @dev Gets the balance of the specified address. * @param owner The address to query the balance of. * @return An uint256 representing the amount owned by the passed address. */ function balanceOf(address owner) public view returns (uint256) { return _balances[owner]; } /** * @dev Function to check the amount of tokens that an owner allowed to a spender. * @param owner address The address which owns the funds. * @param spender address The address which will spend the funds. * @return A uint256 specifying the amount of tokens still available for the spender. */ function allowance(address owner, address spender) public view returns (uint256) { return _allowed[owner][spender]; } /** * @dev Transfer token for a specified address * @param to The address to transfer to. * @param value The amount to be transferred. */ function transfer(address to, uint256 value) public returns (bool) { _transfer(msg.sender, to, value); return true; } /** * @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender. * Beware that changing an allowance with this method brings the risk that someone may use both the old * and the new allowance by unfortunate transaction ordering. One possible solution to mitigate this * race condition is to first reduce the spender's allowance to 0 and set the desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * @param spender The address which will spend the funds. * @param value The amount of tokens to be spent. */ function approve(address spender, uint256 value) public returns (bool) { require(spender != address(0)); _allowed[msg.sender][spender] = value; emit Approval(msg.sender, spender, value); return true; } /** * @dev Transfer tokens from one address to another. * Note that while this function emits an Approval event, this is not required as per the specification, * and other compliant implementations may not emit the event. * @param from address The address which you want to send tokens from * @param to address The address which you want to transfer to * @param value uint256 the amount of tokens to be transferred */ function transferFrom(address from, address to, uint256 value) public returns (bool) { _allowed[from][msg.sender] = _allowed[from][msg.sender].sub(value); _transfer(from, to, value); emit Approval(from, msg.sender, _allowed[from][msg.sender]); return true; } /** * @dev Increase the amount of tokens that an owner allowed to a spender. * approve should be called when allowed_[_spender] == 0. To increment * allowed value is better to use this function to avoid 2 calls (and wait until * the first transaction is mined) * From MonolithDAO Token.sol * Emits an Approval event. * @param spender The address which will spend the funds. * @param addedValue The amount of tokens to increase the allowance by. */ function increaseAllowance(address spender, uint256 addedValue) public returns (bool) { require(spender != address(0)); _allowed[msg.sender][spender] = _allowed[msg.sender][spender].add(addedValue); emit Approval(msg.sender, spender, _allowed[msg.sender][spender]); return true; } /** * @dev Decrease the amount of tokens that an owner allowed to a spender. * approve should be called when allowed_[_spender] == 0. To decrement * allowed value is better to use this function to avoid 2 calls (and wait until * the first transaction is mined) * From MonolithDAO Token.sol * Emits an Approval event. * @param spender The address which will spend the funds. * @param subtractedValue The amount of tokens to decrease the allowance by. */ function decreaseAllowance(address spender, uint256 subtractedValue) public returns (bool) { require(spender != address(0)); _allowed[msg.sender][spender] = _allowed[msg.sender][spender].sub(subtractedValue); emit Approval(msg.sender, spender, _allowed[msg.sender][spender]); return true; } /** * @dev Transfer token for a specified addresses * @param from The address to transfer from. * @param to The address to transfer to. * @param value The amount to be transferred. */ function _transfer(address from, address to, uint256 value) internal { require(to != address(0)); _balances[from] = _balances[from].sub(value); _balances[to] = _balances[to].add(value); emit Transfer(from, to, value); } /** * @dev Internal function that mints an amount of the token and assigns it to * an account. This encapsulates the modification of balances such that the * proper events are emitted. * @param account The account that will receive the created tokens. * @param value The amount that will be created. */ function _mint(address account, uint256 value) internal { require(account != address(0)); _totalSupply = _totalSupply.add(value); _balances[account] = _balances[account].add(value); emit Transfer(address(0), account, value); } /** * @dev Internal function that burns an amount of the token of a given * account. * @param account The account whose tokens will be burnt. * @param value The amount that will be burnt. */ function _burn(address account, uint256 value) internal { require(account != address(0)); _totalSupply = _totalSupply.sub(value); _balances[account] = _balances[account].sub(value); emit Transfer(account, address(0), value); } /** * @dev Internal function that burns an amount of the token of a given * account, deducting from the sender's allowance for said account. Uses the * internal burn function. * Emits an Approval event (reflecting the reduced allowance). * @param account The account whose tokens will be burnt. * @param value The amount that will be burnt. */ function _burnFrom(address account, uint256 value) internal { _allowed[account][msg.sender] = _allowed[account][msg.sender].sub(value); _burn(account, value); emit Approval(account, msg.sender, _allowed[account][msg.sender]); } } // File: contracts/openzeppelin-solidity/contracts/token/ERC20/ERC20Burnable.sol pragma solidity ^0.5.6; /** * @title Burnable Token * @dev Token that can be irreversibly burned (destroyed). */ contract ERC20Burnable is ERC20 { /** * @dev Burns a specific amount of tokens. * @param value The amount of token to be burned. */ function burn(uint256 value) public { _burn(msg.sender, value); } /** * @dev Burns a specific amount of tokens from the target address and decrements allowance * @param from address The address which you want to send tokens from * @param value uint256 The amount of token to be burned */ function burnFrom(address from, uint256 value) public { _burnFrom(from, value); } } // File: contracts/openzeppelin-solidity/contracts/ownership/Ownable.sol pragma solidity ^0.5.6; /** * @title Ownable * @dev The Ownable contract has an owner address, and provides basic authorization control * functions, this simplifies the implementation of "user permissions". */ contract Ownable { address private _owner; event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); /** * @dev The Ownable constructor sets the original `owner` of the contract to the sender * account. */ constructor () internal { _owner = msg.sender; emit OwnershipTransferred(address(0), _owner); } /** * @return the address of the owner. */ function owner() public view returns (address) { return _owner; } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { require(isOwner()); _; } /** * @return true if `msg.sender` is the owner of the contract. */ function isOwner() public view returns (bool) { return msg.sender == _owner; } /** * @dev Allows the current owner to relinquish control of the contract. * @notice Renouncing to ownership will leave the contract without an owner. * It will not be possible to call the functions with the `onlyOwner` * modifier anymore. */ function renounceOwnership() public onlyOwner { emit OwnershipTransferred(_owner, address(0)); _owner = address(0); } /** * @dev Allows the current owner to transfer control of the contract to a newOwner. * @param newOwner The address to transfer ownership to. */ function transferOwnership(address newOwner) public onlyOwner { _transferOwnership(newOwner); } /** * @dev Transfers control of the contract to a newOwner. * @param newOwner The address to transfer ownership to. */ function _transferOwnership(address newOwner) internal { require(newOwner != address(0)); emit OwnershipTransferred(_owner, newOwner); _owner = newOwner; } } // File: contracts/openzeppelin-solidity/contracts/access/Roles.sol pragma solidity ^0.5.6; /** * @title Roles * @dev Library for managing addresses assigned to a Role. */ library Roles { struct Role { mapping (address => bool) bearer; } /** * @dev give an account access to this role */ function add(Role storage role, address account) internal { require(account != address(0)); require(!has(role, account)); role.bearer[account] = true; } /** * @dev remove an account's access to this role */ function remove(Role storage role, address account) internal { require(account != address(0)); require(has(role, account)); role.bearer[account] = false; } /** * @dev check if an account has this role * @return bool */ function has(Role storage role, address account) internal view returns (bool) { require(account != address(0)); return role.bearer[account]; } } // File: contracts/openzeppelin-solidity/contracts/access/roles/PauserRole.sol pragma solidity ^0.5.6; 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)); _; } 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); } } // File: contracts/openzeppelin-solidity/contracts/lifecycle/Pausable.sol pragma solidity ^0.5.6; /** * @title Pausable * @dev Base contract which allows children to implement an emergency stop mechanism. */ contract Pausable is PauserRole { event Paused(address account); event Unpaused(address account); bool private _paused; constructor () internal { _paused = false; } /** * @return true if the contract is paused, false otherwise. */ function paused() public view returns (bool) { return _paused; } /** * @dev Modifier to make a function callable only when the contract is not paused. */ modifier whenNotPaused() { require(!_paused); _; } /** * @dev Modifier to make a function callable only when the contract is paused. */ modifier whenPaused() { require(_paused); _; } /** * @dev called by the owner to pause, triggers stopped state */ function pause() public onlyPauser whenNotPaused { _paused = true; emit Paused(msg.sender); } /** * @dev called by the owner to unpause, returns to normal state */ function unpause() public onlyPauser whenPaused { _paused = false; emit Unpaused(msg.sender); } } // File: contracts/openzeppelin-solidity/contracts/access/roles/MinterRole.sol pragma solidity ^0.5.6; 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)); _; } 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); } } // File: contracts/openzeppelin-solidity/contracts/access/roles/WhitelistAdminRole.sol pragma solidity ^0.5.6; /** * @title WhitelistAdminRole * @dev WhitelistAdmins are responsible for assigning and removing Whitelisted accounts. */ 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)); _; } 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); } } // File: contracts/openzeppelin-solidity/contracts/access/roles/WhitelistedRole.sol pragma solidity ^0.5.6; /** * @title WhitelistedRole * @dev Whitelisted accounts have been approved by a WhitelistAdmin to perform certain actions (e.g. participate in a * crowdsale). This role is special in that the only accounts that can add it are WhitelistAdmins (who can also remove * it), and not Whitelisteds themselves. */ 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)); _; } 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); } } // File: contracts/InvictusWhitelist.sol pragma solidity ^0.5.6; /** * Manages whitelisted addresses. * */ contract InvictusWhitelist is Ownable, WhitelistedRole { constructor () WhitelistedRole() public { } /// @dev override to support legacy name function verifyParticipant(address participant) public onlyWhitelistAdmin { if (!isWhitelisted(participant)) { addWhitelisted(participant); } } /// Allow the owner to remove a whitelistAdmin function removeWhitelistAdmin(address account) public onlyOwner { require(account != msg.sender, "Use renounceWhitelistAdmin"); _removeWhitelistAdmin(account); } } // File: contracts/C10Token.sol pragma solidity ^0.5.6; /** * Contract for CRYPTO10 Hedged (C10) fund. * */ contract C10Token is ERC20Detailed, ERC20Burnable, Ownable, Pausable, MinterRole { // Maps participant addresses to the eth balance pending token issuance mapping(address => uint256) public pendingBuys; // The participant accounts waiting for token issuance address[] public participantAddresses; // Maps participant addresses to the withdrawal request mapping (address => uint256) public pendingWithdrawals; address payable[] public withdrawals; uint256 public minimumWei = 50 finney; uint256 public entryFee = 50; // 0.5% , or 50 bips uint256 public exitFee = 50; // 0.5% , or 50 bips uint256 public minTokenRedemption = 1 ether; uint256 public maxAllocationsPerTx = 50; uint256 public maxWithdrawalsPerTx = 50; Price public price; address public whitelistContract; struct Price { uint256 numerator; uint256 denominator; } event PriceUpdate(uint256 numerator, uint256 denominator); event AddLiquidity(uint256 value); event RemoveLiquidity(uint256 value); event DepositReceived(address indexed participant, uint256 value); event TokensIssued(address indexed participant, uint256 amountTokens, uint256 etherAmount); event WithdrawRequest(address indexed participant, uint256 amountTokens); event Withdraw(address indexed participant, uint256 amountTokens, uint256 etherAmount); event WithdrawInvalidAddress(address indexed participant, uint256 amountTokens); event WithdrawFailed(address indexed participant, uint256 amountTokens); event TokensClaimed(address indexed token, uint256 balance); constructor (uint256 priceNumeratorInput, address whitelistContractInput) ERC20Detailed("CRYPTO10 Hedged", "C10", 18) ERC20Burnable() Pausable() public { price = Price(priceNumeratorInput, 1000); require(priceNumeratorInput > 0, "Invalid price numerator"); require(whitelistContractInput != address(0), "Invalid whitelist address"); whitelistContract = whitelistContractInput; } /** * @dev fallback function that buys tokens if the sender is whitelisted. */ function () external payable { buyTokens(msg.sender); } /** * @dev Explicitly buy via contract. */ function buy() external payable { buyTokens(msg.sender); } /** * Sets the maximum number of allocations in a single transaction. * @dev Allows us to configure batch sizes and avoid running out of gas. */ function setMaxAllocationsPerTx(uint256 newMaxAllocationsPerTx) external onlyOwner { require(newMaxAllocationsPerTx > 0, "Must be greater than 0"); maxAllocationsPerTx = newMaxAllocationsPerTx; } /** * Sets the maximum number of withdrawals in a single transaction. * @dev Allows us to configure batch sizes and avoid running out of gas. */ function setMaxWithdrawalsPerTx(uint256 newMaxWithdrawalsPerTx) external onlyOwner { require(newMaxWithdrawalsPerTx > 0, "Must be greater than 0"); maxWithdrawalsPerTx = newMaxWithdrawalsPerTx; } function setEntryFee(uint256 newFee) external onlyOwner { require(newFee < 10000, "Must be less than 100 percent"); entryFee = newFee; } function setExitFee(uint256 newFee) external onlyOwner { require(newFee < 10000, "Must be less than 100 percent"); exitFee = newFee; } /// Sets the minimum wei when buying tokens. function setMinimumBuyValue(uint256 newMinimumWei) external onlyOwner { require(newMinimumWei > 0, "Minimum must be greater than 0"); minimumWei = newMinimumWei; } /// Sets the minimum number of tokens to redeem. function setMinimumTokenRedemption(uint256 newMinTokenRedemption) external onlyOwner { require(newMinTokenRedemption > 0, "Minimum must be greater than 0"); minTokenRedemption = newMinTokenRedemption; } /// Updates the price numerator. function updatePrice(uint256 newNumerator) external onlyMinter { require(newNumerator > 0, "Must be positive value"); price.numerator = newNumerator; allocateTokens(); processWithdrawals(); emit PriceUpdate(price.numerator, price.denominator); } /// Updates the price denominator. function updatePriceDenominator(uint256 newDenominator) external onlyMinter { require(newDenominator > 0, "Must be positive value"); price.denominator = newDenominator; } /** * Whitelisted token holders can request token redemption, and withdraw ETH. * @param amountTokensToWithdraw The number of tokens to withdraw. * @dev withdrawn tokens are burnt. */ function requestWithdrawal(uint256 amountTokensToWithdraw) external whenNotPaused onlyWhitelisted { address payable participant = msg.sender; require(balanceOf(participant) >= amountTokensToWithdraw, "Cannot withdraw more than balance held"); require(amountTokensToWithdraw >= minTokenRedemption, "Too few tokens"); burn(amountTokensToWithdraw); uint256 pendingAmount = pendingWithdrawals[participant]; if (pendingAmount == 0) { withdrawals.push(participant); } pendingWithdrawals[participant] = pendingAmount.add(amountTokensToWithdraw); emit WithdrawRequest(participant, amountTokensToWithdraw); } /// Allows owner to claim any ERC20 tokens. function claimTokens(ERC20 token) external onlyOwner { require(address(token) != address(0), "Invalid address"); uint256 balance = token.balanceOf(address(this)); token.transfer(owner(), token.balanceOf(address(this))); emit TokensClaimed(address(token), balance); } /** * @dev Allows the owner to burn a specific amount of tokens on a participant's behalf. * @param value The amount of tokens to be burned. */ function burnForParticipant(address account, uint256 value) external onlyOwner { _burn(account, value); } /// Adds liquidity to the contract, allowing anyone to deposit ETH function addLiquidity() external payable { require(msg.value > 0, "Must be positive value"); emit AddLiquidity(msg.value); } /// Removes liquidity, allowing owner to transfer eth to the owner. function removeLiquidity(uint256 amount) external onlyOwner { require(amount <= address(this).balance, "Insufficient balance"); msg.sender.transfer(amount); emit RemoveLiquidity(amount); } /// Allow the owner to remove a minter function removeMinter(address account) external onlyOwner { require(account != msg.sender, "Use renounceMinter"); _removeMinter(account); } /// Allow the owner to remove a pauser function removePauser(address account) external onlyOwner { require(account != msg.sender, "Use renouncePauser"); _removePauser(account); } /// returns the number of withdrawals pending. function numberWithdrawalsPending() external view returns (uint256) { return withdrawals.length; } /// returns the number of pending buys, waiting for token issuance. function numberBuysPending() external view returns (uint256) { return participantAddresses.length; } /** * @dev Function to mint tokens when not paused. * @param to The address that will receive the minted tokens. * @param value The amount of tokens to mint. * @return A boolean that indicates if the operation was successful. */ function mint(address to, uint256 value) public onlyMinter whenNotPaused returns (bool) { _mint(to, value); return true; } /** * First phase of the 2-part buy, the participant deposits eth and waits * for a price to be set so the tokens can be minted. * @param participant whitelisted buyer. */ function buyTokens(address participant) internal whenNotPaused onlyWhitelisted { assert(participant != address(0)); // Ensure minimum investment is met require(msg.value >= minimumWei, "Minimum wei not met"); uint256 pendingAmount = pendingBuys[participant]; if (pendingAmount == 0) { participantAddresses.push(participant); } // Increase the pending balance and wait for the price update pendingBuys[participant] = pendingAmount.add(msg.value); emit DepositReceived(participant, msg.value); } /// Internal function to allocate token. function allocateTokens() internal { uint256 numberOfAllocations = min(participantAddresses.length, maxAllocationsPerTx); uint256 startingIndex = participantAddresses.length; uint256 endingIndex = participantAddresses.length.sub(numberOfAllocations); for (uint256 i = startingIndex; i > endingIndex; i--) { handleAllocation(i - 1); } } function handleAllocation(uint256 index) internal { address participant = participantAddresses[index]; uint256 deposit = pendingBuys[participant]; uint256 feeAmount = deposit.mul(entryFee) / 10000; uint256 balance = deposit.sub(feeAmount); uint256 newTokens = balance.mul(price.numerator) / price.denominator; pendingBuys[participant] = 0; participantAddresses.pop(); if (feeAmount > 0) { address(uint160(owner())).transfer(feeAmount); } mint(participant, newTokens); emit TokensIssued(participant, newTokens, balance); } /// Internal function to process withdrawals. function processWithdrawals() internal { uint256 numberOfWithdrawals = min(withdrawals.length, maxWithdrawalsPerTx); uint256 startingIndex = withdrawals.length; uint256 endingIndex = withdrawals.length.sub(numberOfWithdrawals); for (uint256 i = startingIndex; i > endingIndex; i--) { handleWithdrawal(i - 1); } } function handleWithdrawal(uint256 index) internal { address payable participant = withdrawals[index]; uint256 tokens = pendingWithdrawals[participant]; uint256 withdrawValue = tokens.mul(price.denominator) / price.numerator; pendingWithdrawals[participant] = 0; withdrawals.pop(); if (address(this).balance < withdrawValue) { mint(participant, tokens); emit WithdrawFailed(participant, tokens); return; } uint256 feeAmount = withdrawValue.mul(exitFee) / 10000; uint256 balance = withdrawValue.sub(feeAmount); if (participant.send(balance)) { if (feeAmount > 0) { address(uint160(owner())).transfer(feeAmount); } emit Withdraw(participant, tokens, balance); } else { mint(participant, tokens); emit WithdrawInvalidAddress(participant, tokens); } } function min(uint256 a, uint256 b) internal pure returns (uint256) { return a < b ? a : b; } modifier onlyWhitelisted() { require(InvictusWhitelist(whitelistContract).isWhitelisted(msg.sender), "Must be whitelisted"); _; } }