Skip to content

Instantly share code, notes, and snippets.

@lrgeoemtry
Created October 28, 2018 20:30
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lrgeoemtry/f32c615a1e39474bb3f906bdcd52707a to your computer and use it in GitHub Desktop.
Save lrgeoemtry/f32c615a1e39474bb3f906bdcd52707a to your computer and use it in GitHub Desktop.
herc777 gist for issues
/*
Copyright (c) 2018 Hercules SEZC
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.4.24;
/**
* @title SafeMath
* @dev Math operations with safety checks that revert on error
*/
library SafeMath {
/**
* @dev Multiplies two numbers, 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 numbers truncating the quotient, reverts on division by zero.
*/
function div(uint256 a, uint256 b) internal pure returns (uint256) {
require(b > 0); // Solidity only automatically asserts 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 Subtracts two numbers, 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 numbers, 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 numbers 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 OwnershipRenounced(address indexed previousOwner);
event OwnershipTransferred(
address indexed previousOwner,
address indexed newOwner
);
/**
* @dev The Ownable constructor sets the original `owner` of the contract to the sender
* account.
*/
constructor() public {
_owner = msg.sender;
}
/**
* @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 OwnershipRenounced(_owner);
_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;
}
}
/**
Begin Contract
*/
contract ERC20Token {
function name() public constant returns (string);
function symbol() public constant returns (string);
function decimals() public constant returns (uint8);
function totalSupply() public constant returns (uint256);
function balanceOf(address owner) public constant returns (uint256);
function transfer(address to, uint256 amount) public returns (bool);
function transferFrom(address from, address to, uint256 amount) public returns (bool);
function approve(address spender, uint256 amount) public returns (bool);
function allowance(address owner, address spender) public constant returns (uint256);
// solhint-disable-next-line no-simple-event-func-name
event Transfer(address indexed from, address indexed to, uint256 amount);
event Approval(address indexed owner, address indexed spender, uint256 amount);
}
/**
Begin Contract
*/
contract ERC820Registry {
function getManager(address _addr) public view returns(address);
function setManager(address _addr, address _newManager) external;
function getInterfaceImplementer(address _addr, bytes32 _interfaceHash) external view returns (address);
function setInterfaceImplementer(address _addr, bytes32 _interfaceHash, address _implementer) external;
}
/**
Begin Contract
*/
contract ERC820Implementer {
ERC820Registry erc820Registry = ERC820Registry(0xa691627805d5FAE718381ED95E04d00E20a1fea6);
function setInterfaceImplementation(string _interfaceLabel, address impl) internal {
bytes32 interfaceHash = keccak256(abi.encodePacked(_interfaceLabel));
erc820Registry.setInterfaceImplementer(this, interfaceHash, impl);
}
function interfaceAddr(address addr, string _interfaceLabel) internal constant returns(address) {
bytes32 interfaceHash = keccak256(abi.encodePacked(_interfaceLabel));
return erc820Registry.getInterfaceImplementer(addr, interfaceHash);
}
function delegateManagement(address _newManager) internal {
erc820Registry.setManager(this, _newManager);
}
}
contract ERC777TokensRecipient {
function tokensReceived(
address operator,
address from,
address to,
uint amount,
bytes userData,
bytes operatorData
) public;
}
contract ERC777TokensSender {
function tokensToSend(
address operator,
address from,
address to,
uint amount,
bytes userData,
bytes operatorData
) public;
}
contract ERC777Token {
function name() public view returns (string);
function symbol() public view returns (string);
function totalSupply() public view returns (uint256);
function balanceOf(address owner) public view returns (uint256);
function granularity() public view returns (uint256);
function defaultOperators() public view returns (address[]);
function isOperatorFor(address operator, address tokenHolder) public view returns (bool);
function authorizeOperator(address operator) public;
function revokeOperator(address operator) public;
function send(address to, uint256 amount, bytes holderData) public;
function operatorSend(address from, address to, uint256 amount, bytes holderData, bytes operatorData) public;
function burn(uint256 amount, bytes holderData) public;
function operatorBurn(address from, uint256 amount, bytes holderData, bytes operatorData) public;
event Sent(
address indexed operator,
address indexed from,
address indexed to,
uint256 amount,
bytes holderData,
bytes operatorData
); // solhint-disable-next-line separate-by-one-line-in-contract
event Minted(address indexed operator, address indexed to, uint256 amount, bytes operatorData);
event Burned(address indexed operator, address indexed from, uint256 amount, bytes holderData, bytes operatorData);
event AuthorizedOperator(address indexed operator, address indexed tokenHolder);
event RevokedOperator(address indexed operator, address indexed tokenHolder);
}
/**
Begin Contract
*/
contract ERC777BaseToken is ERC777Token, ERC820Implementer {
using SafeMath for uint256;
string internal mName;
string internal mSymbol;
uint256 internal mGranularity;
uint256 internal mTotalSupply;
mapping(address => uint) internal mBalances;
mapping(address => mapping(address => bool)) internal mAuthorized;
address[] internal mDefaultOperators;
mapping(address => bool) internal mIsDefaultOperator;
mapping(address => mapping(address => bool)) internal mRevokedDefaultOperator;
mapping(address=>bool) callRecipientMutex;
mapping(address=>bool) callSenderMutex;
/* -- Constructor -- */
//
/// @notice Constructor to create a ReferenceToken
/// @param _name Name of the new token
/// @param _symbol Symbol of the new token.
/// @param _granularity Minimum transferable chunk.
constructor(string _name, string _symbol, uint256 _granularity, address[] _defaultOperators) internal {
mName = _name;
mSymbol = _symbol;
mTotalSupply = 0;
require(_granularity >= 1);
mGranularity = _granularity;
mDefaultOperators = _defaultOperators;
for (uint i = 0; i < mDefaultOperators.length; i++) { mIsDefaultOperator[mDefaultOperators[i]] = true; }
setInterfaceImplementation("ERC777Token", this);
}
/* -- ERC777 Interface Implementation -- */
//
/// @return the name of the token
function name() public constant returns (string) { return mName; }
/// @return the symbol of the token
function symbol() public constant returns (string) { return mSymbol; }
/// @return the granularity of the token
function granularity() public constant returns (uint256) { return mGranularity; }
/// @return the total supply of the token
function totalSupply() public constant returns (uint256) { return mTotalSupply; }
/// @notice Return the account balance of some account
/// @param _tokenHolder Address for which the balance is returned
/// @return the balance of `_tokenAddress`.
function balanceOf(address _tokenHolder) public constant returns (uint256) { return mBalances[_tokenHolder]; }
/// @notice Return the list of default operators
/// @return the list of all the default operators
function defaultOperators() public view returns (address[]) { return mDefaultOperators; }
/// @notice Send `_amount` of tokens to address `_to` passing `_userData` to the recipient
/// @param _to The address of the recipient
/// @param _amount The number of tokens to be sent
function send(address _to, uint256 _amount, bytes _userData) public {
doSend(msg.sender, msg.sender, _to, _amount, _userData, "", true);
}
/// @notice Authorize a third party `_operator` to manage (send) `msg.sender`'s tokens.
/// @param _operator The operator that wants to be Authorized
function authorizeOperator(address _operator) public {
require(_operator != msg.sender);
if (mIsDefaultOperator[_operator]) {
mRevokedDefaultOperator[_operator][msg.sender] = false;
} else {
mAuthorized[_operator][msg.sender] = true;
}
emit AuthorizedOperator(_operator, msg.sender);
}
/// @notice Revoke a third party `_operator`'s rights to manage (send) `msg.sender`'s tokens.
/// @param _operator The operator that wants to be Revoked
function revokeOperator(address _operator) public {
require(_operator != msg.sender);
if (mIsDefaultOperator[_operator]) {
mRevokedDefaultOperator[_operator][msg.sender] = true;
} else {
mAuthorized[_operator][msg.sender] = false;
}
emit RevokedOperator(_operator, msg.sender);
}
/// @notice Check whether the `_operator` address is allowed to manage the tokens held by `_tokenHolder` address.
/// @param _operator address to check if it has the right to manage the tokens
/// @param _tokenHolder address which holds the tokens to be managed
/// @return `true` if `_operator` is authorized for `_tokenHolder`
function isOperatorFor(address _operator, address _tokenHolder) public constant returns (bool) {
return (_operator == _tokenHolder
|| mAuthorized[_operator][_tokenHolder]
|| (mIsDefaultOperator[_operator] && !mRevokedDefaultOperator[_operator][_tokenHolder]));
}
/// @notice Send `_amount` of tokens on behalf of the address `from` to the address `to`.
/// @param _from The address holding the tokens being sent
/// @param _to The address of the recipient
/// @param _amount The number of tokens to be sent
/// @param _userData Data generated by the user to be sent to the recipient
/// @param _operatorData Data generated by the operator to be sent to the recipient
function operatorSend(address _from, address _to, uint256 _amount, bytes _userData, bytes _operatorData) public {
require(isOperatorFor(msg.sender, _from));
doSend(msg.sender, _from, _to, _amount, _userData, _operatorData, true);
}
function burn(uint256 _amount, bytes _holderData) public {
doBurn(msg.sender, msg.sender, _amount, _holderData, "");
}
function operatorBurn(address _tokenHolder, uint256 _amount, bytes _holderData, bytes _operatorData) public {
require(isOperatorFor(msg.sender, _tokenHolder));
doBurn(msg.sender, _tokenHolder, _amount, _holderData, _operatorData);
}
/* -- Helper Functions -- */
//
/// @notice Internal function that ensures `_amount` is multiple of the granularity
/// @param _amount The quantity that want's to be checked
function requireMultiple(uint256 _amount) internal view {
require(_amount.div(mGranularity).mul(mGranularity) == _amount);
}
/// @notice Check whether an address is a regular address or not.
/// @param _addr Address of the contract that has to be checked
/// @return `true` if `_addr` is a regular address (not a contract)
function isRegularAddress(address _addr) internal constant returns(bool) {
if (_addr == 0) { return false; }
uint size;
assembly { size := extcodesize(_addr) } // solhint-disable-line no-inline-assembly
return size == 0;
}
/// @notice Helper function actually performing the sending of tokens.
/// @param _operator The address performing the send
/// @param _from The address holding the tokens being sent
/// @param _to The address of the recipient
/// @param _amount The number of tokens to be sent
/// @param _userData Data generated by the user to be passed to the recipient
/// @param _operatorData Data generated by the operator to be passed to the recipient
/// @param _preventLocking `true` if you want this function to throw when tokens are sent to a contract not
/// implementing `erc777_tokenHolder`.
/// ERC777 native Send functions MUST set this parameter to `true`, and backwards compatible ERC20 transfer
/// functions SHOULD set this parameter to `false`.
function doSend(
address _operator,
address _from,
address _to,
uint256 _amount,
bytes _userData,
bytes _operatorData,
bool _preventLocking
)
internal
{
requireMultiple(_amount);
callSender(_operator, _from, _to, _amount, _userData, _operatorData);
require(_to != address(0)); // forbid sending to 0x0 (=burning)
require(mBalances[_from] >= _amount); // ensure enough funds
mBalances[_from] = mBalances[_from].sub(_amount);
mBalances[_to] = mBalances[_to].add(_amount);
callRecipient(_operator, _from, _to, _amount, _userData, _operatorData, _preventLocking);
emit Sent(_operator, _from, _to, _amount, _userData, _operatorData);
}
/// @notice Helper function actually performing the burning of tokens.
/// @param _operator The address performing the burn
/// @param _tokenHolder The address holding the tokens being burn
/// @param _amount The number of tokens to be burnt
/// @param _holderData Data generated by the token holder
/// @param _operatorData Data generated by the operator
function doBurn(address _operator, address _tokenHolder, uint256 _amount, bytes _holderData, bytes _operatorData)
internal
{
requireMultiple(_amount);
require(balanceOf(_tokenHolder) >= _amount);
mBalances[_tokenHolder] = mBalances[_tokenHolder].sub(_amount);
mTotalSupply = mTotalSupply.sub(_amount);
callSender(_operator, _tokenHolder, 0x0, _amount, _holderData, _operatorData);
emit Burned(_operator, _tokenHolder, _amount, _holderData, _operatorData);
}
/// @notice Helper function that checks for ERC777TokensRecipient on the recipient and calls it.
/// May throw according to `_preventLocking`
/// @param _operator The address performing the send or mint
/// @param _from The address holding the tokens being sent
/// @param _to The address of the recipient
/// @param _amount The number of tokens to be sent
/// @param _userData Data generated by the user to be passed to the recipient
/// @param _operatorData Data generated by the operator to be passed to the recipient
/// @param _preventLocking `true` if you want this function to throw when tokens are sent to a contract not
/// implementing `ERC777TokensRecipient`.
/// ERC777 native Send functions MUST set this parameter to `true`, and backwards compatible ERC20 transfer
/// functions SHOULD set this parameter to `false`.
function callRecipient(
address _operator,
address _from,
address _to,
uint256 _amount,
bytes _userData,
bytes _operatorData,
bool _preventLocking
)
internal
{
// re-entrancy mutex lock
if (callRecipientMutex[msg.sender] == true) { revert() ;}
callRecipientMutex[msg.sender] = true;
address recipientImplementation = interfaceAddr(_to, "ERC777TokensRecipient");
if (recipientImplementation != 0) {
ERC777TokensRecipient(recipientImplementation).tokensReceived(
_operator, _from, _to, _amount, _userData, _operatorData);
} else if (_preventLocking) {
require(isRegularAddress(_to));
}
// re-entrancy mutex unlock
callRecipientMutex[msg.sender] = false;
}
/// @notice Helper function that checks for ERC777TokensSender on the sender and calls it.
/// May throw according to `_preventLocking`
/// @param _from The address holding the tokens being sent
/// @param _to The address of the recipient
/// @param _amount The amount of tokens to be sent
/// @param _userData Data generated by the user to be passed to the recipient
/// @param _operatorData Data generated by the operator to be passed to the recipient
/// implementing `ERC777TokensSender`.
/// ERC777 native Send functions MUST set this parameter to `true`, and backwards compatible ERC20 transfer
/// functions SHOULD set this parameter to `false`.
function callSender(
address _operator,
address _from,
address _to,
uint256 _amount,
bytes _userData,
bytes _operatorData
)
internal
{
// re-entrancy mutex lock
if (callSenderMutex[msg.sender] == true) { revert() ;}
callSenderMutex[msg.sender] = true;
address senderImplementation = interfaceAddr(_from, "ERC777TokensSender");
if (senderImplementation == 0) { return; }
ERC777TokensSender(senderImplementation).tokensToSend(_operator, _from, _to, _amount, _userData, _operatorData);
// re-entrancy mutex unlock
callSenderMutex[msg.sender] = false;
}
}
/**
Begin Contract
*/
contract ERC777ERC20BaseToken is ERC20Token, ERC777BaseToken {
bool internal mErc20compatible;
mapping(address => mapping(address => bool)) internal mAuthorized;
mapping(address => mapping(address => uint256)) internal mAllowed;
constructor(
string _name,
string _symbol,
uint256 _granularity,
address[] _defaultOperators
)
internal ERC777BaseToken(_name, _symbol, _granularity, _defaultOperators)
{
mErc20compatible = true;
setInterfaceImplementation("ERC20Token", this);
}
/// @notice This modifier is applied to erc20 obsolete methods that are
/// implemented only to maintain backwards compatibility. When the erc20
/// compatibility is disabled, this methods will fail.
modifier erc20 () {
require(mErc20compatible);
_;
}
/// @notice For Backwards compatibility
/// @return The decimls of the token. Forced to 18 in ERC777.
function decimals() public erc20 constant returns (uint8) { return uint8(18); }
/// @notice ERC20 backwards compatible transfer.
/// @param _to The address of the recipient
/// @param _amount The number of tokens to be transferred
/// @return `true`, if the transfer can't be done, it should fail.
function transfer(address _to, uint256 _amount) public erc20 returns (bool success) {
doSend(msg.sender, msg.sender, _to, _amount, "", "", false);
return true;
}
/// @notice ERC20 backwards compatible transferFrom.
/// @param _from The address holding the tokens being transferred
/// @param _to The address of the recipient
/// @param _amount The number of tokens to be transferred
/// @return `true`, if the transfer can't be done, it should fail.
function transferFrom(address _from, address _to, uint256 _amount) public erc20 returns (bool success) {
require(_amount <= mAllowed[_from][msg.sender]);
// Cannot be after doSend because of tokensReceived re-entry
mAllowed[_from][msg.sender] = mAllowed[_from][msg.sender].sub(_amount);
doSend(msg.sender, _from, _to, _amount, "", "", false);
return true;
}
/// @notice ERC20 backwards compatible approve.
/// `msg.sender` approves `_spender` to spend `_amount` tokens on its behalf.
/// @param _spender The address of the account able to transfer the tokens
/// @param _amount The number of tokens to be approved for transfer
/// @return `true`, if the approve can't be done, it should fail.
function approve(address _spender, uint256 _amount) public erc20 returns (bool success) {
mAllowed[msg.sender][_spender] = _amount;
emit Approval(msg.sender, _spender, _amount);
return true;
}
/// @notice ERC20 backwards compatible allowance.
/// This function makes it easy to read the `allowed[]` map
/// @param _owner The address of the account that owns the token
/// @param _spender The address of the account able to transfer the tokens
/// @return Amount of remaining tokens of _owner that _spender is allowed
/// to spend
function allowance(address _owner, address _spender) public erc20 constant returns (uint256 remaining) {
return mAllowed[_owner][_spender];
}
function doSend(
address _operator,
address _from,
address _to,
uint256 _amount,
bytes _userData,
bytes _operatorData,
bool _preventLocking
)
internal
{
super.doSend(_operator, _from, _to, _amount, _userData, _operatorData, _preventLocking);
if (mErc20compatible) { emit Transfer(_from, _to, _amount); }
}
function doBurn(address _operator, address _tokenHolder, uint256 _amount, bytes _holderData, bytes _operatorData)
internal
{
super.doBurn(_operator, _tokenHolder, _amount, _holderData, _operatorData);
if (mErc20compatible) { emit Transfer(_tokenHolder, 0x0, _amount); }
}
}
/** HERC Token ERC777
*/
contract HERCToken is ERC777ERC20BaseToken, Ownable {
address private mBurnOperator;
constructor(
address[] _defaultOperators,
address _burnOperator
)
ERC777ERC20BaseToken(
"Hercules", // name
"HERC", // symbol
1, // granularity
_defaultOperators
) public {
bytes memory operatorData = new bytes(0);
mint(msg.sender, 234259085000000000000000000, operatorData);
mBurnOperator = _burnOperator;
}
/// @notice Disables the ERC20 interface. This function can only be called
/// by the owner.
function disableERC20() public onlyOwner {
mErc20compatible = false;
setInterfaceImplementation("ERC20Token", 0x0);
}
/// @notice Re enables the ERC20 interface. This function can only be called
/// by the owner.
function enableERC20() public onlyOwner {
mErc20compatible = true;
setInterfaceImplementation("ERC20Token", this);
}
/* -- Mint And Burn Functions (not part of the ERC777 standard, only the Events/tokensReceived call are) -- */
//
/// @notice Generates `_amount` tokens to be assigned to `_tokenHolder`
/// Sample mint function to showcase the use of the `Minted` event and the logic to notify the recipient.
/// @param _tokenHolder The address that will be assigned the new tokens
/// @param _amount The quantity of tokens generated
/// @param _operatorData Data that will be passed to the recipient as a first transfer
function mint(address _tokenHolder, uint256 _amount, bytes _operatorData) public onlyOwner {
requireMultiple(_amount);
mTotalSupply = mTotalSupply.add(_amount);
mBalances[_tokenHolder] = mBalances[_tokenHolder].add(_amount);
callRecipient(msg.sender, 0x0, _tokenHolder, _amount, "", _operatorData, true);
emit Minted(msg.sender, _tokenHolder, _amount, _operatorData);
if (mErc20compatible) { emit Transfer(0x0, _tokenHolder, _amount); }
}
/// @notice Burns `_amount` tokens from `_tokenHolder`
/// Silly example of overriding the `burn` function to only let the owner burn its tokens.
/// Do not forget to override the `burn` function in your token contract if you want to prevent users from
/// burning their tokens.
/// @param _amount The quantity of tokens to burn
function burn(uint256 _amount, bytes _holderData) public onlyOwner {
super.burn(_amount, _holderData);
}
/// @notice Burns `_amount` tokens from `_tokenHolder` by `_operator`
/// Silly example of overriding the `operatorBurn` function to only let a specific operator burn tokens.
/// Do not forget to override the `operatorBurn` function in your token contract if you want to prevent users from
/// burning their tokens.
/// @param _tokenHolder The address that will lose the tokens
/// @param _amount The quantity of tokens to burn
function operatorBurn(address _tokenHolder, uint256 _amount, bytes _holderData, bytes _operatorData) public {
require(msg.sender == mBurnOperator);
super.operatorBurn(_tokenHolder, _amount, _holderData, _operatorData);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment