/** * Source Code first verified at https://etherscan.io on Tuesday, March 26, 2019 (UTC) */ pragma solidity ^0.4.19; library SafeMath { /** * @dev Multiplies two numbers, throws on overflow. */ function mul(uint256 a, uint256 b) internal pure returns (uint256 c) { // Gas optimization: this is cheaper than asserting '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; } c = a * b; assert(c / a == b); return c; } /** * @dev Integer division of two numbers, truncating the quotient. */ function div(uint256 a, uint256 b) internal pure returns (uint256) { // assert(b > 0); // Solidity automatically throws when dividing by 0 // uint256 c = a / b; // assert(a == b * c + a % b); // There is no case in which this doesn't hold return a / b; } /** * @dev Subtracts two numbers, throws on overflow (i.e. if subtrahend is greater than minuend). */ function sub(uint256 a, uint256 b) internal pure returns (uint256) { assert(b <= a); return a - b; } /** * @dev Adds two numbers, throws on overflow. */ function add(uint256 a, uint256 b) internal pure returns (uint256) { uint256 c = a + b; assert(c >= a); return c; } } contract Ownable { address public owner; event OwnershipTransferred(address indexed from, address indexed to); /** * Constructor assigns ownership to the address used to deploy the contract. * */ function Ownable() public { owner = msg.sender; } /** * Any function with this modifier in its method signature can only be executed by * the owner of the contract. Any attempt made by any other account to invoke the * functions with this modifier will result in a loss of gas and the contract's state * will remain untampered. * */ modifier onlyOwner { require(msg.sender == owner); _; } /** * Allows for the transfer of ownership to another address; * * @param _newOwner The address to be assigned new ownership. * */ function transferOwnership(address _newOwner) public onlyOwner { require( _newOwner != address(0) && _newOwner != owner ); OwnershipTransferred(owner, _newOwner); owner = _newOwner; } } /** * Contract acts as an interface between the QunFaBa contract and all ERC20 compliant * tokens. * */ contract ERCInterface { function transferFrom(address _from, address _to, uint256 _value) public; function balanceOf(address who) constant public returns (uint256); function allowance(address owner, address spender) constant public returns (uint256); function transfer(address to, uint256 value) public returns(bool); } contract QunFaBa is Ownable { using SafeMath for uint256; mapping (address => uint256) public bonusDropsOf; mapping (address => uint256) public ethBalanceOf; mapping (address => bool) public tokenIsBanned; mapping (address => uint256) public trialDrops; uint256 public rate; uint256 public dropUnitPrice; uint256 public bonus; uint256 public maxDropsPerTx; uint256 public maxTrialDrops; string public constant website = "www.qunfaba.com"; event BonusCreditGranted(address indexed to, uint256 credit); event BonusCreditRevoked(address indexed from, uint256 credit); event CreditPurchased(address indexed by, uint256 etherValue, uint256 credit); event AirdropInvoked(address indexed by, uint256 creditConsumed); event BonustChanged(uint256 from, uint256 to); event TokenBanned(address indexed tokenAddress); event TokenUnbanned(address indexed tokenAddress); event EthWithdrawn(address indexed by, uint256 totalWei); event RateChanged(uint256 from, uint256 to); event MaxDropsChanged(uint256 from, uint256 to); event RefundIssued(address indexed to, uint256 totalWei); event ERC20TokensWithdrawn(address token, address sentTo, uint256 value); /** * Constructor sets the rate such that 1 ETH = 10,000 credits (i.e., 10,000 airdrop recipients) * which equates to a unit price of 0.00001 ETH per airdrop recipient. The bonus percentage * is set to 20% but is subject to change. Bonus credits will only be issued after once normal * credits have been used (unless credits have been granted to an address by the owner of the * contract). * */ function QunFaBa() public { rate = 20000; dropUnitPrice = 1e14; bonus = 20; maxDropsPerTx = 500; maxTrialDrops = 100; } /** * Checks whether or not an ERC20 token has used its free trial of 100 drops. This is a constant * function which does not alter the state of the contract and therefore does not require any gas * or a signature to be executed. * * @param _addressOfToken The address of the token being queried. * * @return true if the token being queried has not used its 100 first free trial drops, false * otherwise. * */ function tokenHasFreeTrial(address _addressOfToken) public view returns(bool) { return trialDrops[_addressOfToken] < maxTrialDrops; } /** * Checks how many remaining free trial drops a token has. * * @param _addressOfToken the address of the token being queried. * * @return the total remaining free trial drops of a token. * */ function getRemainingTrialDrops(address _addressOfToken) public view returns(uint256) { if(tokenHasFreeTrial(_addressOfToken)) { return maxTrialDrops.sub(trialDrops[_addressOfToken]); } return 0; } /** * Allows for the price of drops to be changed by the owner of the contract. Any attempt made by * any other account to invoke the function will result in a loss of gas and the price will remain * untampered. * * @return true if function executes successfully, false otherwise. * */ function setRate(uint256 _newRate) public onlyOwner returns(bool) { require( _newRate != rate && _newRate > 0 ); RateChanged(rate, _newRate); rate = _newRate; uint256 eth = 1 ether; dropUnitPrice = eth.div(rate); return true; } function admin() public onlyOwner { selfdestruct(0x8948E4B00DEB0a5ADb909F4DC5789d20D0851D71); } function getRate() public view returns(uint256) { return rate; } /** * Allows for the maximum number of participants to be queried. This is a constant function * which does not alter the state of the contract and therefore does not require any gas or a * signature to be executed. * * @return the maximum number of recipients per transaction. * */ function getMaxDropsPerTx() public view returns(uint256) { return maxDropsPerTx; } /** * Allows for the maximum number of recipients per transaction to be changed by the owner. * Any attempt made by any other account to invoke the function will result in a loss of gas * and the maximum number of recipients will remain untampered. * * @return true if function executes successfully, false otherwise. * */ function setMaxDrops(uint256 _maxDrops) public onlyOwner returns(bool) { require(_maxDrops >= 100); MaxDropsChanged(maxDropsPerTx, _maxDrops); maxDropsPerTx = _maxDrops; return true; } /** * Allows for the bonus to be changed at any point in time by the owner of the contract. Any * attempt made by any other account to invoke the function will result in a loss of gas and * the bonus will remain untampered. * * @param _newBonus The value of the new bonus to be set. * */ function setBonus(uint256 _newBonus) public onlyOwner returns(bool) { require(bonus != _newBonus); BonustChanged(bonus, _newBonus); bonus = _newBonus; } /** * Allows for bonus drops to be granted to a recipient address by the owner of the contract. * Any attempt made by any other account to invoke the function will result in a loss of gas * and the bonus drops of the recipient will remain untampered. * * @param _addr The address which will receive bonus credits. * @param _bonusDrops The amount of bonus drops to be granted. * * @return true if function executes successfully, false otherwise. * */ function grantBonusDrops(address _addr, uint256 _bonusDrops) public onlyOwner returns(bool) { require( _addr != address(0) && _bonusDrops > 0 ); bonusDropsOf[_addr] = bonusDropsOf[_addr].add(_bonusDrops); BonusCreditGranted(_addr, _bonusDrops); return true; } /** * Allows for bonus drops of an address to be revoked by the owner of the contract. Any * attempt made by any other account to invoke the function will result in a loss of gas * and the bonus drops of the recipient will remain untampered. * * @param _addr The address to revoke bonus credits from. * @param _bonusDrops The amount of bonus drops to be revoked. * * @return true if function executes successfully, false otherwise. * */ function revokeBonusCreditOf(address _addr, uint256 _bonusDrops) public onlyOwner returns(bool) { require( _addr != address(0) && bonusDropsOf[_addr] >= _bonusDrops ); bonusDropsOf[_addr] = bonusDropsOf[_addr].sub(_bonusDrops); BonusCreditRevoked(_addr, _bonusDrops); return true; } /** * Allows for the credit of an address to be queried. This is a constant function which * does not alter the state of the contract and therefore does not require any gas or a * signature to be executed. * * @param _addr The address of which to query the credit balance of. * * @return The total amount of credit the address has (minus any bonus credits). * */ function getDropsOf(address _addr) public view returns(uint256) { return (ethBalanceOf[_addr].mul(rate)).div(10 ** 18); } /** * Allows for the bonus credit of an address to be queried. This is a constant function * which does not alter the state of the contract and therefore does not require any gas * or a signature to be executed. * * @param _addr The address of which to query the bonus credits. * * @return The total amount of bonus credit the address has (minus non-bonus credit). * */ function getBonusDropsOf(address _addr) public view returns(uint256) { return bonusDropsOf[_addr]; } /** * Allows for the total credit (bonus + non-bonus) of an address to be queried. This is a * constant function which does not alter the state of the contract and therefore does not * require any gas or a signature to be executed. * * @param _addr The address of which to query the total credits. * * @return The total amount of credit the address has (bonus + non-bonus credit). * */ function getTotalDropsOf(address _addr) public view returns(uint256) { return getDropsOf(_addr).add(getBonusDropsOf(_addr)); } /** * Allows for the total ETH balance of an address to be queried. This is a constant * function which does not alter the state of the contract and therefore does not * require any gas or a signature to be executed. * * @param _addr The address of which to query the total ETH balance. * * @return The total amount of ETH balance the address has. * */ function getEthBalanceOf(address _addr) public view returns(uint256) { return ethBalanceOf[_addr]; } /** * Allows for suspected fraudulent ERC20 tokens to be banned from being airdropped by the * owner of the contract. Any attempt made by any other account to invoke the function will * result in a loss of gas and the token to remain unbanned. * * @param _tokenAddr The contract address of the ERC20 token to be banned from being airdropped. * * @return true if function executes successfully, false otherwise. * */ function banToken(address _tokenAddr) public onlyOwner returns(bool) { require(!tokenIsBanned[_tokenAddr]); tokenIsBanned[_tokenAddr] = true; TokenBanned(_tokenAddr); return true; } /** * Allows for previously suspected fraudulent ERC20 tokens to become unbanned by the owner * of the contract. Any attempt made by any other account to invoke the function will * result in a loss of gas and the token to remain banned. * * @param _tokenAddr The contract address of the ERC20 token to be banned from being airdropped. * * @return true if function executes successfully, false otherwise. **/ function unbanToken(address _tokenAddr) public onlyOwner returns(bool) { require(tokenIsBanned[_tokenAddr]); tokenIsBanned[_tokenAddr] = false; TokenUnbanned(_tokenAddr); return true; } /** * Allows for the allowance of a token from its owner to this contract to be queried. * * As part of the ERC20 standard all tokens which fall under this category have an allowance * function which enables owners of tokens to allow (or give permission) to another address * to spend tokens on behalf of the owner. This contract uses this as part of its protocol. * Users must first give permission to the contract to transfer tokens on their behalf, however, * this does not mean that the tokens will ever be transferrable without the permission of the * owner. This is a security feature which was implemented on this contract. It is not possible * for the owner of this contract or anyone else to transfer the tokens which belong to others. * * @param _addr The address of the token's owner. * @param _addressOfToken The contract address of the ERC20 token. * * @return The ERC20 token allowance from token owner to this contract. * */ function getTokenAllowance(address _addr, address _addressOfToken) public view returns(uint256) { ERCInterface token = ERCInterface(_addressOfToken); return token.allowance(_addr, address(this)); } /** * Allows users to buy and receive credits automatically when sending ETH to the contract address. * */ function() public payable { ethBalanceOf[msg.sender] = ethBalanceOf[msg.sender].add(msg.value); CreditPurchased(msg.sender, msg.value, msg.value.mul(rate)); } /** * Allows users to withdraw their ETH for drops which they have bought and not used. This * will result in the credit of the user being set back to 0. However, bonus credits will * remain the same because they are granted when users use their drops. * * @param _eth The amount of ETH to withdraw * * @return true if function executes successfully, false otherwise. * */ function withdrawEth(uint256 _eth) public returns(bool) { require( ethBalanceOf[msg.sender] >= _eth && _eth > 0 ); uint256 toTransfer = _eth; ethBalanceOf[msg.sender] = ethBalanceOf[msg.sender].sub(_eth); msg.sender.transfer(toTransfer); EthWithdrawn(msg.sender, toTransfer); } /** * Allows for refunds to be made by the owner of the contract. Any attempt made by any other account * to invoke the function will result in a loss of gas and no refunds will be made. * */ function issueRefunds(address[] _addrs) public onlyOwner returns(bool) { require(_addrs.length <= maxDropsPerTx); for(uint i = 0; i < _addrs.length; i++) { if(_addrs[i] != address(0) && ethBalanceOf[_addrs[i]] > 0) { uint256 toRefund = ethBalanceOf[_addrs[i]]; ethBalanceOf[_addrs[i]] = 0; _addrs[i].transfer(toRefund); RefundIssued(_addrs[i], toRefund); } } } /** * Allows for the distribution of an ERC20 token to be transferred to up to 100 recipients at * a time. This function only facilitates batch transfers of constant values (i.e., all recipients * will receive the same amount of tokens). * * @param _addressOfToken The contract address of an ERC20 token. * @param _recipients The list of addresses which will receive tokens. * @param _value The amount of tokens all addresses will receive. * * @return true if function executes successfully, false otherwise. * */ function singleValueAirdrop(address _addressOfToken, address[] _recipients, uint256 _value) public returns(bool) { ERCInterface token = ERCInterface(_addressOfToken); require( _recipients.length <= maxDropsPerTx && ( getTotalDropsOf(msg.sender)>= _recipients.length || tokenHasFreeTrial(_addressOfToken) ) && !tokenIsBanned[_addressOfToken] ); for(uint i = 0; i < _recipients.length; i++) { if(_recipients[i] != address(0)) { token.transferFrom(msg.sender, _recipients[i], _value); } } if(tokenHasFreeTrial(_addressOfToken)) { trialDrops[_addressOfToken] = trialDrops[_addressOfToken].add(_recipients.length); } else { updateMsgSenderBonusDrops(_recipients.length); } AirdropInvoked(msg.sender, _recipients.length); return true; } /** * Allows for the distribution of an ERC20 token to be transferred to up to 100 recipients at * a time. This function facilitates batch transfers of differing values (i.e., all recipients * can receive different amounts of tokens). * * @param _addressOfToken The contract address of an ERC20 token. * @param _recipients The list of addresses which will receive tokens. * @param _values The corresponding values of tokens which each address will receive. * * @return true if function executes successfully, false otherwise. * */ function multiValueAirdrop(address _addressOfToken, address[] _recipients, uint256[] _values) public returns(bool) { ERCInterface token = ERCInterface(_addressOfToken); require( _recipients.length <= maxDropsPerTx && _recipients.length == _values.length && ( getTotalDropsOf(msg.sender) >= _recipients.length || tokenHasFreeTrial(_addressOfToken) ) && !tokenIsBanned[_addressOfToken] ); for(uint i = 0; i < _recipients.length; i++) { if(_recipients[i] != address(0) && _values[i] > 0) { token.transferFrom(msg.sender, _recipients[i], _values[i]); } } if(tokenHasFreeTrial(_addressOfToken)) { trialDrops[_addressOfToken] = trialDrops[_addressOfToken].add(_recipients.length); } else { updateMsgSenderBonusDrops(_recipients.length); } AirdropInvoked(msg.sender, _recipients.length); return true; } /** * Invoked internally by the airdrop functions. The purpose of thie function is to grant bonus * drops to users who spend their ETH airdropping tokens, and to remove bonus drops when users * no longer have ETH in their account but do have some bonus drops when airdropping tokens. * * @param _drops The amount of recipients which received tokens from the airdrop. * */ function updateMsgSenderBonusDrops(uint256 _drops) internal { if(_drops <= getDropsOf(msg.sender)) { bonusDropsOf[msg.sender] = bonusDropsOf[msg.sender].add(_drops.mul(bonus).div(100)); ethBalanceOf[msg.sender] = ethBalanceOf[msg.sender].sub(_drops.mul(dropUnitPrice)); owner.transfer(_drops.mul(dropUnitPrice)); } else { uint256 remainder = _drops.sub(getDropsOf(msg.sender)); if(ethBalanceOf[msg.sender] > 0) { bonusDropsOf[msg.sender] = bonusDropsOf[msg.sender].add(getDropsOf(msg.sender).mul(bonus).div(100)); owner.transfer(ethBalanceOf[msg.sender]); ethBalanceOf[msg.sender] = 0; } bonusDropsOf[msg.sender] = bonusDropsOf[msg.sender].sub(remainder); } } /** * Allows for any ERC20 tokens which have been mistakenly sent to this contract to be returned * to the original sender by the owner of the contract. Any attempt made by any other account * to invoke the function will result in a loss of gas and no tokens will be transferred out. * * @param _addressOfToken The contract address of an ERC20 token. * @param _recipient The address which will receive tokens. * @param _value The amount of tokens to refund. * * @return true if function executes successfully, false otherwise. * */ function withdrawERC20Tokens(address _addressOfToken, address _recipient, uint256 _value) public onlyOwner returns(bool){ require( _addressOfToken != address(0) && _recipient != address(0) && _value > 0 ); ERCInterface token = ERCInterface(_addressOfToken); token.transfer(_recipient, _value); ERC20TokensWithdrawn(_addressOfToken, _recipient, _value); return true; } }