pragma solidity ^0.5.8;
/**
* @title Math
* @dev Assorted math operations
*/
library Math {
/**
* @dev Returns the largest of two numbers.
*/
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a >= b ? a : b;
}
/**
* @dev Returns the smallest of two numbers.
*/
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
/**
* @dev Calculates the average of two numbers. Since these are integers,
* averages of an even and odd number cannot be represented, and will be
* rounded down.
*/
function average(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b) / 2 can overflow, so we distribute
return (a / 2) + (b / 2) + ((a % 2 + b % 2) / 2);
}
}
/**
* @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;
}
}
/**
* @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.
* It will not be possible to call the functions with the `onlyOwner`
* modifier anymore.
* @notice Renouncing ownership will leave the contract without an owner,
* thereby removing any functionality that is only available to the owner.
*/
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;
}
}
/**
* @title ERC20 interface
* @dev see https://github.com/ethereum/EIPs/issues/20
*/
interface IERC20 {
function totalSupply() external view returns (uint256);
function balanceOf(address who) external view returns (uint256);
function allowance(address owner, address spender)
external view returns (uint256);
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);
event Transfer(
address indexed from,
address indexed to,
uint256 value
);
event Approval(
address indexed owner,
address indexed spender,
uint256 value
);
}
/**
* @title interface for unsafe ERC20
* @dev Unsafe ERC20 does not return when transfer, approve, transferFrom
*/
interface IUnsafeERC20 {
function totalSupply() external view returns (uint256);
function balanceOf(address who) external view returns (uint256);
function allowance(address owner, address spender) external view returns (uint256);
function transfer(address to, uint256 value) external;
function approve(address spender, uint256 value) external;
function transferFrom(address from, address to, uint256 value) external;
}
/**
* @title SafeERC20
* @dev Wrappers around ERC20 operations that throw on failure.
* To use this library you can add a `using SafeERC20 for ERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20 {
using SafeMath for uint256;
function safeTransfer(IERC20 token, address to, uint256 value) internal {
require(token.transfer(to, value));
}
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
require(token.transferFrom(from, to, value));
}
function safeApprove(IERC20 token, address spender, uint256 value) internal {
// safeApprove should only be called when setting an initial allowance,
// or when resetting it to zero. To increase and decrease it, use
// 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
require((value == 0) || (token.allowance(msg.sender, spender) == 0));
require(token.approve(spender, value));
}
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 newAllowance = token.allowance(address(this), spender).add(value);
require(token.approve(spender, newAllowance));
}
function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 newAllowance = token.allowance(address(this), spender).sub(value);
require(token.approve(spender, newAllowance));
}
}
/**
* @title TokenSale
*/
contract TokenSale is Ownable {
using SafeMath for uint256;
using SafeERC20 for IERC20;
// token for sale
IERC20 public saleToken;
// address where funds are collected
address public fundCollector;
// address where has tokens to sell
address public tokenWallet;
// use whitelist[user] to get whether the user was allowed to buy
mapping(address => bool) public whitelist;
// exchangeable token
struct ExToken {
bool safe;
bool accepted;
uint256 rate;
}
// exchangeable tokens
mapping(address => ExToken) private _exTokens;
// bonus threshold
uint256 public bonusThreshold;
// tier-1 bonus
uint256 public tierOneBonusTime;
uint256 public tierOneBonusRate;
// tier-2 bonus
uint256 public tierTwoBonusTime;
uint256 public tierTwoBonusRate;
/**
* @param _setter who set USDT
* @param _usdt address of USDT
*/
event USDTSet(
address indexed _setter,
address indexed _usdt
);
/**
* @param _setter who set fund collector
* @param _fundCollector address of fund collector
*/
event FundCollectorSet(
address indexed _setter,
address indexed _fundCollector
);
/**
* @param _setter who set sale token
* @param _saleToken address of sale token
*/
event SaleTokenSet(
address indexed _setter,
address indexed _saleToken
);
/**
* @param _setter who set token wallet
* @param _tokenWallet address of token wallet
*/
event TokenWalletSet(
address indexed _setter,
address indexed _tokenWallet
);
/**
* @param _setter who set bonus threshold
* @param _bonusThreshold exceed the threshold will get bonus
* @param _tierOneBonusTime tier one bonus timestamp
* @param _tierOneBonusRate tier one bonus rate
* @param _tierTwoBonusTime tier two bonus timestamp
* @param _tierTwoBonusRate tier two bonus rate
*/
event BonusConditionsSet(
address indexed _setter,
uint256 _bonusThreshold,
uint256 _tierOneBonusTime,
uint256 _tierOneBonusRate,
uint256 _tierTwoBonusTime,
uint256 _tierTwoBonusRate
);
/**
* @param _setter who set the whitelist
* @param _user address of the user
* @param _allowed whether the user allowed to buy
*/
event WhitelistSet(
address indexed _setter,
address indexed _user,
bool _allowed
);
/**
* event for logging exchangeable token updates
* @param _setter who set the exchangeable token
* @param _exToken the exchangeable token
* @param _safe whether the exchangeable token is a safe ERC20
* @param _accepted whether the exchangeable token was accepted
* @param _rate exchange rate of the exchangeable token
*/
event ExTokenSet(
address indexed _setter,
address indexed _exToken,
bool _safe,
bool _accepted,
uint256 _rate
);
/**
* event for token purchase logging
* @param _buyer address of token buyer
* @param _exToken address of the exchangeable token
* @param _exTokenAmount amount of the exchangeable tokens
* @param _amount amount of tokens purchased
*/
event TokensPurchased(
address indexed _buyer,
address indexed _exToken,
uint256 _exTokenAmount,
uint256 _amount
);
constructor (
address _fundCollector,
address _saleToken,
address _tokenWallet,
uint256 _bonusThreshold,
uint256 _tierOneBonusTime,
uint256 _tierOneBonusRate,
uint256 _tierTwoBonusTime,
uint256 _tierTwoBonusRate
)
public
{
_setFundCollector(_fundCollector);
_setSaleToken(_saleToken);
_setTokenWallet(_tokenWallet);
_setBonusConditions(
_bonusThreshold,
_tierOneBonusTime,
_tierOneBonusRate,
_tierTwoBonusTime,
_tierTwoBonusRate
);
}
/**
* @param _fundCollector address of the fund collector
*/
function setFundCollector(address _fundCollector) external onlyOwner {
_setFundCollector(_fundCollector);
}
/**
* @param _fundCollector address of the fund collector
*/
function _setFundCollector(address _fundCollector) private {
require(_fundCollector != address(0), "fund collector cannot be 0x0");
fundCollector = _fundCollector;
emit FundCollectorSet(msg.sender, _fundCollector);
}
/**
* @param _saleToken address of the sale token
*/
function setSaleToken(address _saleToken) external onlyOwner {
_setSaleToken(_saleToken);
}
/**
* @param _saleToken address of the sale token
*/
function _setSaleToken(address _saleToken) private {
require(_saleToken != address(0), "sale token cannot be 0x0");
saleToken = IERC20(_saleToken);
emit SaleTokenSet(msg.sender, _saleToken);
}
/**
* @param _tokenWallet address of the token wallet
*/
function setTokenWallet(address _tokenWallet) external onlyOwner {
_setTokenWallet(_tokenWallet);
}
/**
* @param _tokenWallet address of the token wallet
*/
function _setTokenWallet(address _tokenWallet) private {
require(_tokenWallet != address(0), "token wallet cannot be 0x0");
tokenWallet = _tokenWallet;
emit TokenWalletSet(msg.sender, _tokenWallet);
}
/**
* @param _bonusThreshold exceed the threshold will get bonus
* @param _tierOneBonusTime before t1 bonus timestamp will use t1 bonus rate
* @param _tierOneBonusRate tier-1 bonus rate
* @param _tierTwoBonusTime before t2 bonus timestamp will use t2 bonus rate
* @param _tierTwoBonusRate tier-2 bonus rate
*/
function setBonusConditions(
uint256 _bonusThreshold,
uint256 _tierOneBonusTime,
uint256 _tierOneBonusRate,
uint256 _tierTwoBonusTime,
uint256 _tierTwoBonusRate
)
external
onlyOwner
{
_setBonusConditions(
_bonusThreshold,
_tierOneBonusTime,
_tierOneBonusRate,
_tierTwoBonusTime,
_tierTwoBonusRate
);
}
function _setBonusConditions(
uint256 _bonusThreshold,
uint256 _tierOneBonusTime,
uint256 _tierOneBonusRate,
uint256 _tierTwoBonusTime,
uint256 _tierTwoBonusRate
)
private
onlyOwner
{
require(_bonusThreshold > 0," threshold cannot be zero.");
require(_tierOneBonusTime < _tierTwoBonusTime, "invalid bonus time");
require(_tierOneBonusRate >= _tierTwoBonusRate, "invalid bonus rate");
bonusThreshold = _bonusThreshold;
tierOneBonusTime = _tierOneBonusTime;
tierOneBonusRate = _tierOneBonusRate;
tierTwoBonusTime = _tierTwoBonusTime;
tierTwoBonusRate = _tierTwoBonusRate;
emit BonusConditionsSet(
msg.sender,
_bonusThreshold,
_tierOneBonusTime,
_tierOneBonusRate,
_tierTwoBonusTime,
_tierTwoBonusRate
);
}
/**
* @notice set allowed to ture to add the user into the whitelist
* @notice set allowed to false to remove the user from the whitelist
* @param _user address of user
* @param _allowed whether allow the user to deposit/withdraw or not
*/
function setWhitelist(address _user, bool _allowed) external onlyOwner {
whitelist[_user] = _allowed;
emit WhitelistSet(msg.sender, _user, _allowed);
}
/**
* @dev checks the amount of tokens left in the allowance.
* @return amount of tokens left in the allowance
*/
function remainingTokens() external view returns (uint256) {
return Math.min(
saleToken.balanceOf(tokenWallet),
saleToken.allowance(tokenWallet, address(this))
);
}
/**
* @param _exToken address of the exchangeable token
* @param _safe whether it is a safe ERC20
* @param _accepted true: accepted; false: rejected
* @param _rate exchange rate
*/
function setExToken(
address _exToken,
bool _safe,
bool _accepted,
uint256 _rate
)
external
onlyOwner
{
_exTokens[_exToken].safe = _safe;
_exTokens[_exToken].accepted = _accepted;
_exTokens[_exToken].rate = _rate;
emit ExTokenSet(msg.sender, _exToken, _safe, _accepted, _rate);
}
/**
* @param _exToken address of the exchangeable token
* @return whether the exchangeable token is a safe ERC20
*/
function safe(address _exToken) public view returns (bool) {
return _exTokens[_exToken].safe;
}
/**
* @param _exToken address of the exchangeable token
* @return whether the exchangeable token is accepted or not
*/
function accepted(address _exToken) public view returns (bool) {
return _exTokens[_exToken].accepted;
}
/**
* @param _exToken address of the exchangeale token
* @return amount of sale token a buyer gets per exchangeable token
*/
function rate(address _exToken) external view returns (uint256) {
return _exTokens[_exToken].rate;
}
/**
* @dev get exchangeable sale token amount
* @param _exToken address of the exchangeable token
* @param _amount amount of the exchangeable token (how much to pay)
* @return purchased sale token amount
*/
function exchangeableAmounts(
address _exToken,
uint256 _amount
)
external
view
returns (uint256)
{
return _getTokenAmount(_exToken, _amount);
}
/**
* @dev buy tokens
* @dev buyer must be in whitelist
* @param _exToken address of the exchangeable token
* @param _amount amount of the exchangeable token
*/
function buyTokens(
address _exToken,
uint256 _amount
)
external
{
require(_exTokens[_exToken].accepted, "token was not accepted");
require(_amount != 0, "amount cannot 0");
require(whitelist[msg.sender], "buyer must be in whitelist");
// calculate token amount to sell
uint256 _tokens = _getTokenAmount(_exToken, _amount);
require(_tokens >= 10**18, "at least buy 1 tokens per purchase");
_forwardFunds(_exToken, _amount);
_processPurchase(msg.sender, _tokens);
emit TokensPurchased(msg.sender, _exToken, _amount, _tokens);
}
/**
* @dev buyer transfers amount of the exchangeable token to fund collector
* @param _exToken address of the exchangeable token
* @param _amount amount of the exchangeable token will send to fund collector
*/
function _forwardFunds(address _exToken, uint256 _amount) private {
if (_exTokens[_exToken].safe) {
IERC20(_exToken).safeTransferFrom(msg.sender, fundCollector, _amount);
} else {
IUnsafeERC20(_exToken).transferFrom(msg.sender, fundCollector, _amount);
}
}
/**
* @dev calculated purchased sale token amount
* @param _exToken address of the exchangeable token
* @param _amount amount of the exchangeable token (how much to pay)
* @return amount of purchased sale token
*/
function _getTokenAmount(
address _exToken,
uint256 _amount
)
private
view
returns (uint256)
{
// round down value (v) by multiple (m) = (v / m) * m
uint256 _value = _amount
.mul(_exTokens[_exToken].rate)
.div(1000000000000000000)
.mul(1000000000000000000);
return _applyBonus(_value);
}
function _applyBonus(
uint256 _amount
)
private
view
returns (uint256)
{
if (_amount < bonusThreshold) {
return _amount;
}
if (block.timestamp <= tierOneBonusTime) {
return _amount.mul(tierOneBonusRate).div(100);
} else if (block.timestamp <= tierTwoBonusTime) {
return _amount.mul(tierTwoBonusRate).div(100);
} else {
return _amount;
}
}
/**
* @dev transfer sale token amounts from token wallet to beneficiary
* @param _beneficiary purchased tokens will transfer to this address
* @param _tokenAmount purchased token amount
*/
function _processPurchase(
address _beneficiary,
uint256 _tokenAmount
)
private
{
saleToken.safeTransferFrom(tokenWallet, _beneficiary, _tokenAmount);
}
}
{
"compilationTarget": {
"TokenSale.sol": "TokenSale"
},
"evmVersion": "petersburg",
"libraries": {},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"constant":false,"inputs":[{"name":"_exToken","type":"address"},{"name":"_amount","type":"uint256"}],"name":"buyTokens","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_exToken","type":"address"}],"name":"rate","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_bonusThreshold","type":"uint256"},{"name":"_tierOneBonusTime","type":"uint256"},{"name":"_tierOneBonusRate","type":"uint256"},{"name":"_tierTwoBonusTime","type":"uint256"},{"name":"_tierTwoBonusRate","type":"uint256"}],"name":"setBonusConditions","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_exToken","type":"address"}],"name":"accepted","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"tierTwoBonusTime","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"tierOneBonusTime","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"fundCollector","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_fundCollector","type":"address"}],"name":"setFundCollector","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_user","type":"address"},{"name":"_allowed","type":"bool"}],"name":"setWhitelist","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"renounceOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"tierTwoBonusRate","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"tierOneBonusRate","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":"","type":"address"}],"name":"whitelist","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_saleToken","type":"address"}],"name":"setSaleToken","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_tokenWallet","type":"address"}],"name":"setTokenWallet","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"bonusThreshold","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"remainingTokens","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"tokenWallet","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_exToken","type":"address"}],"name":"safe","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"saleToken","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_exToken","type":"address"},{"name":"_safe","type":"bool"},{"name":"_accepted","type":"bool"},{"name":"_rate","type":"uint256"}],"name":"setExToken","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_exToken","type":"address"},{"name":"_amount","type":"uint256"}],"name":"exchangeableAmounts","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[{"name":"_fundCollector","type":"address"},{"name":"_saleToken","type":"address"},{"name":"_tokenWallet","type":"address"},{"name":"_bonusThreshold","type":"uint256"},{"name":"_tierOneBonusTime","type":"uint256"},{"name":"_tierOneBonusRate","type":"uint256"},{"name":"_tierTwoBonusTime","type":"uint256"},{"name":"_tierTwoBonusRate","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_setter","type":"address"},{"indexed":true,"name":"_usdt","type":"address"}],"name":"USDTSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_setter","type":"address"},{"indexed":true,"name":"_fundCollector","type":"address"}],"name":"FundCollectorSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_setter","type":"address"},{"indexed":true,"name":"_saleToken","type":"address"}],"name":"SaleTokenSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_setter","type":"address"},{"indexed":true,"name":"_tokenWallet","type":"address"}],"name":"TokenWalletSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_setter","type":"address"},{"indexed":false,"name":"_bonusThreshold","type":"uint256"},{"indexed":false,"name":"_tierOneBonusTime","type":"uint256"},{"indexed":false,"name":"_tierOneBonusRate","type":"uint256"},{"indexed":false,"name":"_tierTwoBonusTime","type":"uint256"},{"indexed":false,"name":"_tierTwoBonusRate","type":"uint256"}],"name":"BonusConditionsSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_setter","type":"address"},{"indexed":true,"name":"_user","type":"address"},{"indexed":false,"name":"_allowed","type":"bool"}],"name":"WhitelistSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_setter","type":"address"},{"indexed":true,"name":"_exToken","type":"address"},{"indexed":false,"name":"_safe","type":"bool"},{"indexed":false,"name":"_accepted","type":"bool"},{"indexed":false,"name":"_rate","type":"uint256"}],"name":"ExTokenSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_buyer","type":"address"},{"indexed":true,"name":"_exToken","type":"address"},{"indexed":false,"name":"_exTokenAmount","type":"uint256"},{"indexed":false,"name":"_amount","type":"uint256"}],"name":"TokensPurchased","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"previousOwner","type":"address"},{"indexed":true,"name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"}]