pragma solidity ^0.4.18;
// File: contracts/zeppelin/math/SafeMath.sol
/**
* @title SafeMath
* @dev Math operations with safety checks that throw on error
*/
library SafeMath {
/**
* @dev Multiplies two numbers, throws on overflow.
*/
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
uint256 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 c;
}
/**
* @dev Substracts 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;
}
}
// File: contracts/CrowdsaleAuthorizer.sol
/**
* @title CrowdsaleAuthorizer
* @dev Crowd Sale Authorizer
*/
contract CrowdsaleAuthorizer {
mapping(address => uint256) public participated;
mapping(address => bool) public whitelistAddresses;
address public admin;
uint256 public saleStartTime;
uint256 public saleEndTime;
uint256 public increaseMaxContribTime;
uint256 public minContribution;
uint256 public maxContribution;
using SafeMath for uint256;
/**
* @dev Modifier for only admin
*/
modifier onlyAdmin() {
require(msg.sender == admin);
_;
}
/**
* @dev Modifier for valid address
*/
modifier validAddress(address _addr) {
require(_addr != address(0x0));
require(_addr != address(this));
_;
}
/**
* @dev Contract Constructor
* @param _saleStartTime - The Start Time of the Token Sale
* @param _saleEndTime - The End Time of the Token Sale
* @param _increaseMaxContribTime - Time to increase Max Contribution of the Token Sale
* @param _minContribution - Minimum ETH contribution per contributor
* @param _maxContribution - Maximum ETH contribution per contributor
*/
function CrowdsaleAuthorizer(
address _admin,
uint256 _saleStartTime,
uint256 _saleEndTime,
uint256 _increaseMaxContribTime,
uint256 _minContribution,
uint256 _maxContribution
)
validAddress(_admin)
public
{
require(_saleStartTime > now);
require(_saleEndTime > now);
require(_increaseMaxContribTime > now);
require(_saleStartTime < _saleEndTime);
require(_increaseMaxContribTime > _saleStartTime);
require(_maxContribution > 0);
require(_minContribution < _maxContribution);
admin = _admin;
saleStartTime = _saleStartTime;
saleEndTime = _saleEndTime;
increaseMaxContribTime = _increaseMaxContribTime;
minContribution = _minContribution;
maxContribution = _maxContribution;
}
event UpdateWhitelist(address _user, bool _allow, uint _time);
/**
* @dev Update Whitelist Address
* @param _user - Whitelist address
* @param _allow - eligibility
*/
function updateWhitelist(address _user, bool _allow)
public
onlyAdmin
{
whitelistAddresses[_user] = _allow;
UpdateWhitelist(_user, _allow, now);
}
/**
* @dev Batch Update Whitelist Address
* @param _users - Array of Whitelist addresses
* @param _allows - Array of eligibilities
*/
function updateWhitelists(address[] _users, bool[] _allows)
external
onlyAdmin
{
require(_users.length == _allows.length);
for (uint i = 0 ; i < _users.length ; i++) {
address _user = _users[i];
bool _allow = _allows[i];
whitelistAddresses[_user] = _allow;
UpdateWhitelist(_user, _allow, now);
}
}
/**
* @dev Get Eligible Amount
* @param _contributor - Contributor address
* @param _amount - Intended contribution amount
*/
function eligibleAmount(address _contributor, uint256 _amount)
public
view
returns(uint256)
{
// If sales has not started or sale ended, there's no allocation
if (!saleStarted() || saleEnded()) {
return 0;
}
// Amount lesser than minimum contribution will be rejected
if (_amount < minContribution) {
return 0;
}
uint256 userMaxContribution = maxContribution;
// If sale has past 24hrs, increase max cap
if (now >= increaseMaxContribTime) {
userMaxContribution = maxContribution.mul(10);
}
// Calculate remaining contribution for the contributor
uint256 remainingCap = userMaxContribution.sub(participated[_contributor]);
// Return either the amount contributed or cap whichever is lower
return (remainingCap > _amount) ? _amount : remainingCap;
}
/**
* @dev Get if sale has started
*/
function saleStarted() public view returns(bool) {
return now >= saleStartTime;
}
/**
* @dev Get if sale has ended
*/
function saleEnded() public view returns(bool) {
return now > saleEndTime;
}
/**
* @dev Check for eligible amount and modify participation map
* @param _contributor - Contributor address
* @param _amount - Intended contribution amount
*/
function eligibleAmountCheck(address _contributor, uint256 _amount)
internal
returns(uint256)
{
// Check if contributor is whitelisted
if (!whitelistAddresses[_contributor]) {
return 0;
}
uint256 result = eligibleAmount(_contributor, _amount);
participated[_contributor] = participated[_contributor].add(result);
return result;
}
}
// File: contracts/zeppelin/ownership/Ownable.sol
/**
* @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 public owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev The Ownable constructor sets the original `owner` of the contract to the sender
* account.
*/
function Ownable() public {
owner = msg.sender;
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(msg.sender == owner);
_;
}
/**
* @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 {
require(newOwner != address(0));
OwnershipTransferred(owner, newOwner);
owner = newOwner;
}
}
// File: contracts/zeppelin/token/ERC20Basic.sol
/**
* @title ERC20Basic
* @dev Simpler version of ERC20 interface
* @dev see https://github.com/ethereum/EIPs/issues/179
*/
contract ERC20Basic {
function totalSupply() public view returns (uint256);
function balanceOf(address who) public view returns (uint256);
function transfer(address to, uint256 value) public returns (bool);
event Transfer(address indexed from, address indexed to, uint256 value);
}
// File: contracts/zeppelin/token/BasicToken.sol
/**
* @title Basic token
* @dev Basic version of StandardToken, with no allowances.
*/
contract BasicToken is ERC20Basic {
using SafeMath for uint256;
mapping(address => uint256) balances;
uint256 totalSupply_;
/**
* @dev total number of tokens in existence
*/
function totalSupply() public view returns (uint256) {
return totalSupply_;
}
/**
* @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) {
require(_to != address(0));
require(_value <= balances[msg.sender]);
// SafeMath.sub will throw if there is not enough balance.
balances[msg.sender] = balances[msg.sender].sub(_value);
balances[_to] = balances[_to].add(_value);
Transfer(msg.sender, _to, _value);
return true;
}
/**
* @dev Gets the balance of the specified address.
* @param _owner The address to query the the balance of.
* @return An uint256 representing the amount owned by the passed address.
*/
function balanceOf(address _owner) public view returns (uint256 balance) {
return balances[_owner];
}
}
// File: contracts/zeppelin/token/BurnableToken.sol
/**
* @title Burnable Token
* @dev Token that can be irreversibly burned (destroyed).
*/
contract BurnableToken is BasicToken {
event Burn(address indexed burner, uint256 value);
/**
* @dev Burns a specific amount of tokens.
* @param _value The amount of token to be burned.
*/
function burn(uint256 _value) public {
require(_value <= balances[msg.sender]);
// no need to require value <= totalSupply, since that would imply the
// sender's balance is greater than the totalSupply, which *should* be an assertion failure
address burner = msg.sender;
balances[burner] = balances[burner].sub(_value);
totalSupply_ = totalSupply_.sub(_value);
Burn(burner, _value);
}
}
// File: contracts/zeppelin/token/ERC20.sol
/**
* @title ERC20 interface
* @dev see https://github.com/ethereum/EIPs/issues/20
*/
contract ERC20 is ERC20Basic {
function allowance(address owner, address spender) public view returns (uint256);
function transferFrom(address from, address to, uint256 value) public returns (bool);
function approve(address spender, uint256 value) public returns (bool);
event Approval(address indexed owner, address indexed spender, uint256 value);
}
// File: contracts/zeppelin/token/StandardToken.sol
/**
* @title Standard ERC20 token
*
* @dev Implementation of the basic standard token.
* @dev https://github.com/ethereum/EIPs/issues/20
* @dev Based on code by FirstBlood: https://github.com/Firstbloodio/token/blob/master/smart_contract/FirstBloodToken.sol
*/
contract StandardToken is ERC20, BasicToken {
mapping (address => mapping (address => uint256)) internal allowed;
/**
* @dev Transfer tokens from one address to another
* @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) {
require(_to != address(0));
require(_value <= balances[_from]);
require(_value <= allowed[_from][msg.sender]);
balances[_from] = balances[_from].sub(_value);
balances[_to] = balances[_to].add(_value);
allowed[_from][msg.sender] = allowed[_from][msg.sender].sub(_value);
Transfer(_from, _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) {
allowed[msg.sender][_spender] = _value;
Approval(msg.sender, _spender, _value);
return true;
}
/**
* @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 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
* @param _spender The address which will spend the funds.
* @param _addedValue The amount of tokens to increase the allowance by.
*/
function increaseApproval(address _spender, uint _addedValue) public returns (bool) {
allowed[msg.sender][_spender] = allowed[msg.sender][_spender].add(_addedValue);
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
* @param _spender The address which will spend the funds.
* @param _subtractedValue The amount of tokens to decrease the allowance by.
*/
function decreaseApproval(address _spender, uint _subtractedValue) public returns (bool) {
uint oldValue = allowed[msg.sender][_spender];
if (_subtractedValue > oldValue) {
allowed[msg.sender][_spender] = 0;
} else {
allowed[msg.sender][_spender] = oldValue.sub(_subtractedValue);
}
Approval(msg.sender, _spender, allowed[msg.sender][_spender]);
return true;
}
}
// File: contracts/PolicyPalNetworkToken.sol
/**
* @title PolicyPalNetwork Token
* @dev A standard ownable token
*/
contract PolicyPalNetworkToken is StandardToken, BurnableToken, Ownable {
/**
* @dev Token Contract Constants
*/
string public constant name = "PolicyPal Network Token";
string public constant symbol = "PAL";
uint8 public constant decimals = 18;
/**
* @dev Token Contract Public Variables
*/
address public tokenSaleContract;
bool public isTokenTransferable = false;
/**
* @dev Token Contract Modifier
*
* Check if a transfer is allowed
* Transfers are restricted to token creator & owner(admin) during token sale duration
* Transfers after token sale is limited by `isTokenTransferable` toggle
*
*/
modifier onlyWhenTransferAllowed() {
require(isTokenTransferable || msg.sender == owner || msg.sender == tokenSaleContract);
_;
}
/**
* @dev Token Contract Modifier
* @param _to - Address to check if valid
*
* Check if an address is valid
* A valid address is as follows,
* 1. Not zero address
* 2. Not token address
*
*/
modifier isValidDestination(address _to) {
require(_to != address(0x0));
require(_to != address(this));
_;
}
/**
* @dev Enable Transfers (Only Owner)
*/
function toggleTransferable(bool _toggle) external
onlyOwner
{
isTokenTransferable = _toggle;
}
/**
* @dev Token Contract Constructor
* @param _adminAddr - Address of the Admin
*/
function PolicyPalNetworkToken(
uint _tokenTotalAmount,
address _adminAddr
)
public
isValidDestination(_adminAddr)
{
require(_tokenTotalAmount > 0);
totalSupply_ = _tokenTotalAmount;
// Mint all token
balances[msg.sender] = _tokenTotalAmount;
Transfer(address(0x0), msg.sender, _tokenTotalAmount);
// Assign token sale contract to creator
tokenSaleContract = msg.sender;
// Transfer contract ownership to admin
transferOwnership(_adminAddr);
}
/**
* @dev Token Contract transfer
* @param _to - Address to transfer to
* @param _value - Value to transfer
* @return bool - Result of transfer
* "Overloaded" Function of ERC20Basic's transfer
*
*/
function transfer(address _to, uint256 _value) public
onlyWhenTransferAllowed
isValidDestination(_to)
returns (bool)
{
return super.transfer(_to, _value);
}
/**
* @dev Token Contract transferFrom
* @param _from - Address to transfer from
* @param _to - Address to transfer to
* @param _value - Value to transfer
* @return bool - Result of transferFrom
*
* "Overloaded" Function of ERC20's transferFrom
* Added with modifiers,
* 1. onlyWhenTransferAllowed
* 2. isValidDestination
*
*/
function transferFrom(address _from, address _to, uint256 _value) public
onlyWhenTransferAllowed
isValidDestination(_to)
returns (bool)
{
return super.transferFrom(_from, _to, _value);
}
/**
* @dev Token Contract burn
* @param _value - Value to burn
* "Overloaded" Function of BurnableToken's burn
*/
function burn(uint256 _value)
public
{
super.burn(_value);
Transfer(msg.sender, address(0x0), _value);
}
/**
* @dev Token Contract Emergency Drain
* @param _token - Token to drain
* @param _amount - Amount to drain
*/
function emergencyERC20Drain(ERC20 _token, uint256 _amount) public
onlyOwner
{
_token.transfer(owner, _amount);
}
}
// File: contracts/PolicyPalNetworkCrowdsale.sol
/**
* @title PPN Crowdsale
* @dev Crowd Sale Contract
*/
contract PolicyPalNetworkCrowdsale is CrowdsaleAuthorizer {
/**
* @dev Token Crowd Sale Contract Public Variables
*/
address public multiSigWallet;
PolicyPalNetworkToken public token;
uint256 public raisedWei;
bool public haltSale;
uint public rate;
/**
* @dev Modifier for valid sale
*/
modifier validSale() {
require(!haltSale);
require(saleStarted());
require(!saleEnded());
_;
}
/**
* @dev Buy Event
*/
event Buy(address _buyer, uint256 _tokens, uint256 _payedWei);
/**
* @dev Token Crowd Sale Contract Constructor
* @param _admin - Address of the Admin
* @param _multiSigWallet - Address of Multisig wallet
* @param _totalTokenSupply - Total Token Supply
* @param _premintedTokenSupply - Total preminted token supply
* @param _saleStartTime - The Start Time of the Token Sale
* @param _saleEndTime - The End Time of the Token Sale
* @param _increaseMaxContribTime - Time to increase max contribution
* @param _rate - Rate of ETH to PAL
* @param _minContribution - Minimum ETH contribution per contributor
* @param _maxContribution - Maximum ETH contribution per contributor
*/
function PolicyPalNetworkCrowdsale(
address _admin,
address _multiSigWallet,
uint256 _totalTokenSupply,
uint256 _premintedTokenSupply,
uint256 _presaleTokenSupply,
uint256 _saleStartTime,
uint256 _saleEndTime,
uint256 _increaseMaxContribTime,
uint _rate,
uint256 _minContribution,
uint256 _maxContribution
)
CrowdsaleAuthorizer(
_admin,
_saleStartTime,
_saleEndTime,
_increaseMaxContribTime,
_minContribution,
_maxContribution
)
validAddress(_multiSigWallet)
public
{
require(_totalTokenSupply > 0);
require(_premintedTokenSupply > 0);
require(_presaleTokenSupply > 0);
require(_rate > 0);
require(_premintedTokenSupply < _totalTokenSupply);
require(_presaleTokenSupply < _totalTokenSupply);
multiSigWallet = _multiSigWallet;
rate = _rate;
token = new PolicyPalNetworkToken(
_totalTokenSupply,
_admin
);
// transfer preminted tokens to company wallet
token.transfer(multiSigWallet, _premintedTokenSupply);
// transfer presale tokens to admin
token.transfer(_admin, _presaleTokenSupply);
}
/**
* @dev Token Crowd Sale Contract Halter
* @param _halt - Flag to halt sale
*/
function setHaltSale(bool _halt)
onlyAdmin
public
{
haltSale = _halt;
}
/**
* @dev Token Crowd Sale payable
*/
function() public payable {
buy(msg.sender);
}
/**
* @dev Token Crowd Sale Buy
* @param _recipient - Address of the recipient
*/
function buy(address _recipient) public payable
validSale
validAddress(_recipient)
returns(uint256)
{
// Get the contributor's eligible amount
uint256 weiContributionAllowed = eligibleAmountCheck(_recipient, msg.value);
require(weiContributionAllowed > 0);
// Get tokens remaining for sale
uint256 tokensRemaining = token.balanceOf(address(this));
require(tokensRemaining > 0);
// Get tokens that the contributor will receive
uint256 receivedTokens = weiContributionAllowed.mul(rate);
// Check remaining tokens
// If lesser, update tokens to be transfer and contribution allowed
if (receivedTokens > tokensRemaining) {
receivedTokens = tokensRemaining;
weiContributionAllowed = tokensRemaining.div(rate);
}
// Transfer tokens to contributor
assert(token.transfer(_recipient, receivedTokens));
// Send ETH payment to MultiSig Wallet
sendETHToMultiSig(weiContributionAllowed);
raisedWei = raisedWei.add(weiContributionAllowed);
// Check weiContributionAllowed is larger than value sent
// If larger, transfer the excess back to the contributor
if (msg.value > weiContributionAllowed) {
msg.sender.transfer(msg.value.sub(weiContributionAllowed));
}
// Broadcast event
Buy(_recipient, receivedTokens, weiContributionAllowed);
return weiContributionAllowed;
}
/**
* @dev Token Crowd Sale Emergency Drain
* In case something went wrong and ETH is stuck in contract
* @param _anyToken - Token to drain
*/
function emergencyDrain(ERC20 _anyToken) public
onlyAdmin
returns(bool)
{
if (this.balance > 0) {
sendETHToMultiSig(this.balance);
}
if (_anyToken != address(0x0)) {
assert(_anyToken.transfer(multiSigWallet, _anyToken.balanceOf(this)));
}
return true;
}
/**
* @dev Token Crowd Sale
* Transfer ETH to MultiSig Wallet
* @param _value - Value of ETH to send
*/
function sendETHToMultiSig(uint256 _value) internal {
multiSigWallet.transfer(_value);
}
}
{
"compilationTarget": {
"PolicyPalNetworkToken.sol": "PolicyPalNetworkToken"
},
"libraries": {},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_spender","type":"address"},{"name":"_value","type":"uint256"}],"name":"approve","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_from","type":"address"},{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transferFrom","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_value","type":"uint256"}],"name":"burn","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"tokenSaleContract","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_spender","type":"address"},{"name":"_subtractedValue","type":"uint256"}],"name":"decreaseApproval","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"}],"name":"balanceOf","outputs":[{"name":"balance","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":"isTokenTransferable","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transfer","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_spender","type":"address"},{"name":"_addedValue","type":"uint256"}],"name":"increaseApproval","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_toggle","type":"bool"}],"name":"toggleTransferable","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_token","type":"address"},{"name":"_amount","type":"uint256"}],"name":"emergencyERC20Drain","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"},{"name":"_spender","type":"address"}],"name":"allowance","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"_tokenTotalAmount","type":"uint256"},{"name":"_adminAddr","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"name":"previousOwner","type":"address"},{"indexed":true,"name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"burner","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Burn","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"},{"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"}]