pragma solidity ^0.4.24;
import "./ERC20Interface.sol";
contract ERC20 is ERC20Interface {
string public name;
string public symbol;
uint8 public decimals;
uint256 public totalSupply;
// Function to access name of token .
function name() public view returns (string _name) {
return name;
}
// Function to access symbol of token .
function symbol() public view returns (string _symbol) {
return symbol;
}
// Function to access decimals of token .
function decimals() public view returns (uint8 _decimals) {
return decimals;
}
// Function to access total supply of tokens .
function totalSupply() public view returns (uint256 _totalSupply) {
return totalSupply;
}
mapping (address => uint256) balances;
mapping (address => mapping (address => uint256)) allowed;
function balanceOf(address participant) public constant returns (uint256 balance) {
return balances[participant];
}
function transfer(address _to, uint256 _value) public onlyPayloadSize(2) returns (bool success) {
require(_to != address(0));
// documentation says transfer of 0 must be treated as a transfer and fire the transfer event
require(balances[msg.sender] >= _value && _value > 0);
balances[msg.sender] = safeSub(balances[msg.sender], _value);
balances[_to] = safeAdd(balances[_to], _value);
emit Transfer(msg.sender, _to, _value);
return true;
}
function transferFrom(address _from, address _to, uint256 _value) onlyPayloadSize(3) public returns (bool success) {
require(_to != address(0));
require(balances[_from] >= _value && allowed[_from][msg.sender] >= _value && _value > 0);
balances[_from] = safeSub(balances[_from], _value);
balances[_to] = safeAdd(balances[_to], _value);
allowed[_from][msg.sender] = safeSub(allowed[_from][msg.sender], _value);
emit Transfer(_from, _to, _value);
return true;
}
// To change the approve amount you first have to reduce the addresses'
// allowance to zero by calling 'approve(_spender, 0)' if it is not
// already 0 to mitigate the race condition described here:
// https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
function approve(address _spender, uint256 _value) onlyPayloadSize(2) public returns (bool success) {
require((_value == 0) || (allowed[msg.sender][_spender] == 0));
allowed[msg.sender][_spender] = _value;
emit Approval(msg.sender, _spender, _value);
return true;
}
function changeApproval(address _spender, uint256 _oldValue, uint256 _newValue) onlyPayloadSize(3) public returns (bool success) {
require(allowed[msg.sender][_spender] == _oldValue);
allowed[msg.sender][_spender] = _newValue;
emit Approval(msg.sender, _spender, _newValue);
return true;
}
function allowance(address _owner, address _spender) constant public returns (uint256 remaining) {
return allowed[_owner][_spender];
}
// mitigate short address attack
// thanks to https://github.com/numerai/contract/blob/c182465f82e50ced8dacb3977ec374a892f5fa8c/contracts/Safe.sol#L30-L34.
// TODO: doublecheck implication of >= compared to ==
modifier onlyPayloadSize(uint numWords) {
assert(msg.data.length >= numWords * 32 + 4);
_;
}
function safeMul(uint a, uint b) internal pure returns (uint) {
uint c = a * b;
assert(a == 0 || c / a == b);
return c;
}
function safeSub(uint a, uint b) internal pure returns (uint) {
assert(b <= a);
return a - b;
}
function safeAdd(uint a, uint b) internal pure returns (uint) {
uint c = a + b;
assert(c>=a && c>=b);
return c;
}
}
pragma solidity ^0.4.24;
contract ERC20Interface {
function balanceOf(address _owner) public constant returns (uint256 balance);
function transfer(address _to, uint256 _value) public returns (bool success);
function transferFrom(address _from, address _to, uint256 _value) public returns (bool success);
function approve(address _spender, uint256 _value) public returns (bool success);
function allowance(address _owner, address _spender) public constant returns (uint256 remaining);
function totalSupply() public constant returns (uint256);
event Transfer(address indexed _from, address indexed _to, uint256 _value);
event Approval(address indexed _owner, address indexed _spender, uint256 _value);
}
pragma solidity ^0.4.24;
import "./ERC20.sol";
contract Isonex is ERC20 {
// 15M
uint256 public tokenCap = 15000000 * 10**18;
bool public tradeable = false;
address public primaryWallet;
address public secondaryWallet;
mapping (address => bool) public whitelist;
// Conversion rate from IX15 to ETH
struct Price { uint256 numerator; uint256 denominator; }
Price public currentPrice;
// The amount of time that the secondary wallet must wait between price updates
uint256 public priceUpdateInterval = 1 hours;
mapping (uint256 => Price) public priceHistory;
uint256 public currentPriceHistoryIndex = 0;
// time for each withdrawal is set to the currentPriceHistoryIndex
struct WithdrawalRequest { uint256 nummberOfTokens; uint256 time; }
mapping (address => WithdrawalRequest) withdrawalRequests;
mapping(uint8 => string) restrictionMap;
constructor (address newSecondaryWallet, uint256 newPriceNumerator) public {
require(newSecondaryWallet != address(0), "newSecondaryWallet != address(0)");
require(newPriceNumerator > 0, "newPriceNumerator > 0");
name = "Isonex";
symbol = "IX15";
decimals = 18;
primaryWallet = msg.sender;
secondaryWallet = newSecondaryWallet;
whitelist[primaryWallet] = true;
whitelist[secondaryWallet] = true;
currentPrice = Price(newPriceNumerator, 1000);
currentPriceHistoryIndex = now;
restrictionMap[1] = "Sender is not whitelisted";
restrictionMap[2] = "Receiver is not whitelisted";
restrictionMap[3] = "Trading is not enabled";
}
// Primary and Secondary wallets may updated the current price. Secondary wallet has time and change size constrainst
function updatePrice(uint256 newNumerator) external onlyPrimaryAndSecondaryWallets {
require(newNumerator > 0, "newNumerator > 0");
checkSecondaryWalletRestrictions(newNumerator);
currentPrice.numerator = newNumerator;
// After the token sale, map time to new Price
priceHistory[currentPriceHistoryIndex] = currentPrice;
currentPriceHistoryIndex = now;
emit PriceUpdated(newNumerator, currentPrice.denominator);
}
// secondaryWallet can only increase price by up to 20% and only every priceUpdateInterval
function checkSecondaryWalletRestrictions (uint256 newNumerator) view private
onlySecondaryWallet priceUpdateIntervalElapsed ifNewNumeratorGreater(newNumerator) {
uint256 percentageDiff = safeSub(safeMul(newNumerator, 100) / currentPrice.numerator, 100);
require(percentageDiff <= 20, "percentageDiff <= 20");
}
function updatePriceDenominator(uint256 newDenominator) external onlyPrimaryWallet {
require(newDenominator > 0, "newDenominator > 0");
currentPrice.denominator = newDenominator;
// map time to new Price
priceHistory[currentPriceHistoryIndex] = currentPrice;
currentPriceHistoryIndex = now;
emit PriceUpdated(currentPrice.numerator, newDenominator);
}
function processDeposit(address participant, uint numberOfTokens) external onlyPrimaryWallet {
require(participant != address(0), "participant != address(0)");
whitelist[participant] = true;
allocateTokens(participant, numberOfTokens);
emit Whitelisted(participant);
emit DepositProcessed(participant, numberOfTokens);
}
// When Ether is sent directly to the contract
function() public payable {
}
function allocateTokens(address participant, uint256 numberOfTokens) private {
// check that token cap is not exceeded
require(safeAdd(totalSupply, numberOfTokens) <= tokenCap, "Exceeds token cap");
// increase token supply, assign tokens to participant
totalSupply = safeAdd(totalSupply, numberOfTokens);
balances[participant] = safeAdd(balances[participant], numberOfTokens);
emit Transfer(address(0), participant, numberOfTokens);
}
function verifyParticipant(address participant) external onlyPrimaryAndSecondaryWallets {
whitelist[participant] = true;
emit Whitelisted(participant);
}
function removeFromWhitelist(address participant) external onlyPrimaryAndSecondaryWallets {
whitelist[participant] = false;
emit RemovedFromWhitelist(participant);
}
function requestWithdrawal(uint256 amountOfTokensToWithdraw) external isTradeable onlyWhitelist {
require(amountOfTokensToWithdraw > 0, "Amount must be greater than 0");
address participant = msg.sender;
require(balanceOf(participant) >= amountOfTokensToWithdraw, "Not enough balance");
require(withdrawalRequests[participant].nummberOfTokens == 0, "Outstanding withdrawal request must be processed");
balances[participant] = safeSub(balanceOf(participant), amountOfTokensToWithdraw);
withdrawalRequests[participant] = WithdrawalRequest({nummberOfTokens: amountOfTokensToWithdraw, time: currentPriceHistoryIndex});
emit WithdrawalRequested(participant, amountOfTokensToWithdraw);
}
function withdraw() external {
address participant = msg.sender;
uint256 nummberOfTokens = withdrawalRequests[participant].nummberOfTokens;
require(nummberOfTokens > 0, "Missing withdrawal request");
uint256 requestTime = withdrawalRequests[participant].time;
Price storage price = priceHistory[requestTime];
require(price.numerator > 0, 'Please wait for the next price update');
uint256 etherAmount = safeMul(nummberOfTokens, price.denominator) / price.numerator;
require(address(this).balance >= etherAmount, "Not enough Ether in the smart contract.");
withdrawalRequests[participant].nummberOfTokens = 0;
// Move the Isonex tokens to the primary wallet
balances[primaryWallet] = safeAdd(balances[primaryWallet], nummberOfTokens);
// Send ether from the contract wallet to the participant
participant.transfer(etherAmount);
emit Withdrew(participant, etherAmount, nummberOfTokens);
}
function checkWithdrawValue(uint256 amountTokensToWithdraw) public constant returns (uint256 etherValue) {
require(amountTokensToWithdraw > 0, "Amount must be greater than 0");
require(balanceOf(msg.sender) >= amountTokensToWithdraw, "Not enough balance");
uint256 withdrawValue = safeMul(amountTokensToWithdraw, currentPrice.denominator) / currentPrice.numerator;
require(address(this).balance >= withdrawValue, "Not enough balance in contract");
return withdrawValue;
}
// allow the primaryWallet or secondaryWallet to add Ether to the contract
function addLiquidity() external onlyPrimaryAndSecondaryWallets payable {
require(msg.value > 0, "Amount must be greater than 0");
emit LiquidityAdded(msg.value);
}
// allow the primaryWallet or secondaryWallet to remove Ether from contract
function removeLiquidity(uint256 amount) external onlyPrimaryAndSecondaryWallets {
require(amount <= address(this).balance, "amount <= address(this).balance");
primaryWallet.transfer(amount);
emit LiquidityRemoved(amount);
}
function changePrimaryWallet(address newPrimaryWallet) external onlyPrimaryWallet {
require(newPrimaryWallet != address(0), "newPrimaryWallet != address(0)");
primaryWallet = newPrimaryWallet;
}
function changeSecondaryWallet(address newSecondaryWallet) external onlyPrimaryWallet {
require(newSecondaryWallet != address(0), "newSecondaryWallet != address(0)");
secondaryWallet = newSecondaryWallet;
}
function changePriceUpdateInterval(uint256 newPriceUpdateInterval) external onlyPrimaryWallet {
priceUpdateInterval = newPriceUpdateInterval;
}
function enableTrading() external onlyPrimaryWallet {
tradeable = true;
}
function claimTokens(address _token) external onlyPrimaryWallet {
require(_token != address(0), "_token != address(0)");
ERC20Interface token = ERC20Interface(_token);
uint256 balance = token.balanceOf(this);
token.transfer(primaryWallet, balance);
}
// override transfer and transferFrom to add is tradeable modifier
function transfer(address _to, uint256 _value) public notRestricted(msg.sender, _to, _value) returns (bool success) {
return super.transfer(_to, _value);
}
function transferFrom(address _from, address _to, uint256 _value)
public notRestricted(_from, _to, _value) returns (bool success) {
return super.transferFrom(_from, _to, _value);
}
function pendingWithdrawalAmount() external constant returns (uint256) {
return withdrawalRequests[msg.sender].nummberOfTokens;
}
function pendingWithdrawalRateNumerator() external constant returns (uint256) {
return priceHistory[withdrawalRequests[msg.sender].time].numerator;
}
function isInWhitelist(address participant) external constant onlyPrimaryWallet returns (bool) {
return whitelist[participant];
}
function amIWhitelisted() external constant returns (bool) {
return whitelist[msg.sender];
}
// returns a restriction code,
// where 0 success
function detectTransferRestriction(address from, address to, uint256 value) public view returns (uint8) {
if (whitelist[from] == false) {
return 1;
}
if (whitelist[to] == false) {
return 2;
}
if (!(tradeable || msg.sender == primaryWallet)) {
return 3;
}
return 0;
}
function messageForTransferRestriction (uint8 restrictionCode) public view returns (string){
if (bytes(restrictionMap[restrictionCode]).length == 0 ){
return "Invalid restriction code";
}
return restrictionMap[restrictionCode];
}
// Events
event PriceUpdated(uint256 numerator, uint256 denominator);
event DepositProcessed(address indexed participant, uint256 numberOfTokens);
event Whitelisted(address indexed participant);
event RemovedFromWhitelist(address indexed participant);
event WithdrawalRequested(address indexed participant, uint256 numberOfTokens);
event Withdrew(address indexed participant, uint256 etherAmount, uint256 numberOfTokens);
event LiquidityAdded(uint256 ethAmount);
event LiquidityRemoved(uint256 ethAmount);
event UserDeposited(address indexed participant, address indexed beneficiary, uint256 ethValue, uint256 numberOfTokens);
// Modifiers
modifier onlyWhitelist {
require(whitelist[msg.sender], "Not whitelisted");
_;
}
modifier onlyPrimaryWallet {
require(msg.sender == primaryWallet, "Unauthorized");
_;
}
modifier onlySecondaryWallet {
if (msg.sender == secondaryWallet)
_;
}
modifier onlyPrimaryAndSecondaryWallets {
require(msg.sender == secondaryWallet || msg.sender == primaryWallet, "Unauthorized");
_;
}
modifier priceUpdateIntervalElapsed {
require(safeSub(now, priceUpdateInterval) >= currentPriceHistoryIndex, "Price update interval");
_;
}
modifier ifNewNumeratorGreater (uint256 newNumerator) {
if (newNumerator > currentPrice.numerator)
_;
}
modifier isTradeable { // exempt primaryWallet to allow dev allocations
require(tradeable || msg.sender == primaryWallet, "Trading is currently disabled");
_;
}
modifier notRestricted (address from, address to, uint256 value) {
uint8 restrictionCode = detectTransferRestriction(from, to, value);
require(restrictionCode == 0, messageForTransferRestriction(restrictionCode));
_;
}
}
{
"compilationTarget": {
"Isonex.sol": "Isonex"
},
"evmVersion": "byzantium",
"libraries": {},
"optimizer": {
"enabled": false,
"runs": 200
},
"remappings": []
}
[{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"_name","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_spender","type":"address"},{"name":"_value","type":"uint256"}],"name":"approve","outputs":[{"name":"success","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"participant","type":"address"}],"name":"isInWhitelist","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"_totalSupply","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"newPriceUpdateInterval","type":"uint256"}],"name":"changePriceUpdateInterval","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_from","type":"address"},{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transferFrom","outputs":[{"name":"success","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"newPrimaryWallet","type":"address"}],"name":"changePrimaryWallet","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"pendingWithdrawalAmount","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"name":"_decimals","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"withdraw","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"primaryWallet","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"priceUpdateInterval","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"participant","type":"address"}],"name":"verifyParticipant","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"participant","type":"address"}],"name":"balanceOf","outputs":[{"name":"balance","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"restrictionCode","type":"uint8"}],"name":"messageForTransferRestriction","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"enableTrading","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"participant","type":"address"}],"name":"removeFromWhitelist","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"newNumerator","type":"uint256"}],"name":"updatePrice","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"amountTokensToWithdraw","type":"uint256"}],"name":"checkWithdrawValue","outputs":[{"name":"etherValue","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_spender","type":"address"},{"name":"_oldValue","type":"uint256"},{"name":"_newValue","type":"uint256"}],"name":"changeApproval","outputs":[{"name":"success","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"_symbol","type":"string"}],"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":"amount","type":"uint256"}],"name":"removeLiquidity","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"currentPrice","outputs":[{"name":"numerator","type":"uint256"},{"name":"denominator","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"amountOfTokensToWithdraw","type":"uint256"}],"name":"requestWithdrawal","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"amIWhitelisted","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transfer","outputs":[{"name":"success","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"currentPriceHistoryIndex","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"priceHistory","outputs":[{"name":"numerator","type":"uint256"},{"name":"denominator","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"pendingWithdrawalRateNumerator","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"participant","type":"address"},{"name":"numberOfTokens","type":"uint256"}],"name":"processDeposit","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"newSecondaryWallet","type":"address"}],"name":"changeSecondaryWallet","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"from","type":"address"},{"name":"to","type":"address"},{"name":"value","type":"uint256"}],"name":"detectTransferRestriction","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"newDenominator","type":"uint256"}],"name":"updatePriceDenominator","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"tokenCap","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"},{"name":"_spender","type":"address"}],"name":"allowance","outputs":[{"name":"remaining","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_token","type":"address"}],"name":"claimTokens","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"addLiquidity","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":true,"inputs":[],"name":"tradeable","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"secondaryWallet","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[{"name":"newSecondaryWallet","type":"address"},{"name":"newPriceNumerator","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"payable":true,"stateMutability":"payable","type":"fallback"},{"anonymous":false,"inputs":[{"indexed":false,"name":"numerator","type":"uint256"},{"indexed":false,"name":"denominator","type":"uint256"}],"name":"PriceUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"participant","type":"address"},{"indexed":false,"name":"numberOfTokens","type":"uint256"}],"name":"DepositProcessed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"participant","type":"address"}],"name":"Whitelisted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"participant","type":"address"}],"name":"RemovedFromWhitelist","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"participant","type":"address"},{"indexed":false,"name":"numberOfTokens","type":"uint256"}],"name":"WithdrawalRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"participant","type":"address"},{"indexed":false,"name":"etherAmount","type":"uint256"},{"indexed":false,"name":"numberOfTokens","type":"uint256"}],"name":"Withdrew","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"ethAmount","type":"uint256"}],"name":"LiquidityAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"ethAmount","type":"uint256"}],"name":"LiquidityRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"participant","type":"address"},{"indexed":true,"name":"beneficiary","type":"address"},{"indexed":false,"name":"ethValue","type":"uint256"},{"indexed":false,"name":"numberOfTokens","type":"uint256"}],"name":"UserDeposited","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_from","type":"address"},{"indexed":true,"name":"_to","type":"address"},{"indexed":false,"name":"_value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_owner","type":"address"},{"indexed":true,"name":"_spender","type":"address"},{"indexed":false,"name":"_value","type":"uint256"}],"name":"Approval","type":"event"}]