2

I'm trying to deploy an ERC777 test contract ON REMIX IDE; but the VM is giving me the below error when I try to transact the contract

VM error: revert. revert The transaction has been reverted to the initial state. Note: The called function should be payable if you send value and the value you send should be less than your current balance. Debug the transaction to get more information.

Below are the parameters used for the deployment

Parameters-Empty

Parameters-Filled

Parameters: testing, TST, ["0xdD870fA1b7C4700F2BD7f44238821C26f7392148"], 10000

Can you please check my code and advise what was missed.

// File: contracts\open-zeppelin-contracts\token\ERC777\ERC777.sol

pragma solidity ^0.5.0;

contract ERC777 is IERC777, IERC20 {
using SafeMath for uint256;
using Address for address;

IERC1820Registry private _erc1820 = IERC1820Registry(0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24);

mapping(address => uint256) private _balances;

uint256 private _totalSupply;

string private _name;
string private _symbol;

// We inline the result of the following hashes because Solidity doesn't resolve them at compile time.
// See https://github.com/ethereum/solidity/issues/4024

// keccak256("ERC777TokensSender")
bytes32 constant private TOKENS_SENDER_INTERFACE_HASH =
    0x29ddb589b1fb5fc7cf394961c1adf5f8c6454761adf795e67fe149f658abe895;

// keccak256("ERC777TokensRecipient")
bytes32 constant private TOKENS_RECIPIENT_INTERFACE_HASH =
    0xb281fc8c12954d22544db45de3159a39272895b169a852b314f9cc762e44c53b;

// This isn't ever read from - it's only used to respond to the defaultOperators query.
address[] private _defaultOperatorsArray;

// Immutable, but accounts may revoke them (tracked in __revokedDefaultOperators).
mapping(address => bool) private _defaultOperators;

// For each account, a mapping of its operators and revoked default operators.
mapping(address => mapping(address => bool)) private _operators;
mapping(address => mapping(address => bool)) private _revokedDefaultOperators;

// ERC20-allowances
mapping (address => mapping (address => uint256)) private _allowances;

/**
 * @dev `defaultOperators` may be an empty array.
 */
constructor(
    string memory name,
    string memory symbol,
    address[] memory defaultOperators
) public {
    _name = name;
    _symbol = symbol;

    _defaultOperatorsArray = defaultOperators;
    for (uint256 i = 0; i < _defaultOperatorsArray.length; i++) {
        _defaultOperators[_defaultOperatorsArray[i]] = true;
    }

    // register interfaces
    _erc1820.setInterfaceImplementer(address(this), keccak256("ERC777Token"), address(this));
    _erc1820.setInterfaceImplementer(address(this), keccak256("ERC20Token"), address(this));
}

/**
 * @dev See `IERC777.name`.
 */
function name() public view returns (string memory) {
    return _name;
}

/**
 * @dev See `IERC777.symbol`.
 */
function symbol() public view returns (string memory) {
    return _symbol;
}

/**
 * @dev See `ERC20Detailed.decimals`.
 *
 * Always returns 18, as per the
 * [ERC777 EIP](https://eips.ethereum.org/EIPS/eip-777#backward-compatibility).
 */
function decimals() public pure returns (uint8) {
    return 18;
}

/**
 * @dev See `IERC777.granularity`.
 *
 * This implementation always returns `1`.
 */
function granularity() public view returns (uint256) {
    return 1;
}

/**
 * @dev See `IERC777.totalSupply`.
 */
function totalSupply() public view returns (uint256) {
    return _totalSupply;
}

/**
 * @dev Returns the amount of tokens owned by an account (`tokenHolder`).
 */
function balanceOf(address tokenHolder) public view returns (uint256) {
    return _balances[tokenHolder];
}

/**
 * @dev See `IERC777.send`.
 *
 * Also emits a `Transfer` event for ERC20 compatibility.
 */
function send(address recipient, uint256 amount, bytes calldata data) external {
    _send(msg.sender, msg.sender, recipient, amount, data, "", true);
}

/**
 * @dev See `IERC20.transfer`.
 *
 * Unlike `send`, `recipient` is _not_ required to implement the `tokensReceived`
 * interface if it is a contract.
 *
 * Also emits a `Sent` event.
 */
function transfer(address recipient, uint256 amount) external returns (bool) {
    require(recipient != address(0), "ERC777: transfer to the zero address");

    address from = msg.sender;

    _callTokensToSend(from, from, recipient, amount, "", "");

    _move(from, from, recipient, amount, "", "");

    _callTokensReceived(from, from, recipient, amount, "", "", false);

    return true;
}

/**
 * @dev See `IERC777.burn`.
 *
 * Also emits a `Transfer` event for ERC20 compatibility.
 */
function burn(uint256 amount, bytes calldata data) external {
    _burn(msg.sender, msg.sender, amount, data, "");
}

/**
 * @dev See `IERC777.isOperatorFor`.
 */
function isOperatorFor(
    address operator,
    address tokenHolder
) public view returns (bool) {
    return operator == tokenHolder ||
        (_defaultOperators[operator] && !_revokedDefaultOperators[tokenHolder][operator]) ||
        _operators[tokenHolder][operator];
}

/**
 * @dev See `IERC777.authorizeOperator`.
 */
function authorizeOperator(address operator) external {
    require(msg.sender != operator, "ERC777: authorizing self as operator");

    if (_defaultOperators[operator]) {
        delete _revokedDefaultOperators[msg.sender][operator];
    } else {
        _operators[msg.sender][operator] = true;
    }

    emit AuthorizedOperator(operator, msg.sender);
}

/**
 * @dev See `IERC777.revokeOperator`.
 */
function revokeOperator(address operator) external {
    require(operator != msg.sender, "ERC777: revoking self as operator");

    if (_defaultOperators[operator]) {
        _revokedDefaultOperators[msg.sender][operator] = true;
    } else {
        delete _operators[msg.sender][operator];
    }

    emit RevokedOperator(operator, msg.sender);
}

/**
 * @dev See `IERC777.defaultOperators`.
 */
function defaultOperators() public view returns (address[] memory) {
    return _defaultOperatorsArray;
}

/**
 * @dev See `IERC777.operatorSend`.
 *
 * Emits `Sent` and `Transfer` events.
 */
function operatorSend(
    address sender,
    address recipient,
    uint256 amount,
    bytes calldata data,
    bytes calldata operatorData
)
external
{
    require(isOperatorFor(msg.sender, sender), "ERC777: caller is not an operator for holder");
    _send(msg.sender, sender, recipient, amount, data, operatorData, true);
}

/**
 * @dev See `IERC777.operatorBurn`.
 *
 * Emits `Burned` and `Transfer` events.
 */
function operatorBurn(address account, uint256 amount, bytes calldata data, bytes calldata operatorData) external {
    require(isOperatorFor(msg.sender, account), "ERC777: caller is not an operator for holder");
    _burn(msg.sender, account, amount, data, operatorData);
}

/**
 * @dev See `IERC20.allowance`.
 *
 * Note that operator and allowance concepts are orthogonal: operators may
 * not have allowance, and accounts with allowance may not be operators
 * themselves.
 */
function allowance(address holder, address spender) public view returns (uint256) {
    return _allowances[holder][spender];
}

/**
 * @dev See `IERC20.approve`.
 *
 * Note that accounts cannot have allowance issued by their operators.
 */
function approve(address spender, uint256 value) external returns (bool) {
    address holder = msg.sender;
    _approve(holder, spender, value);
    return true;
}

/**
* @dev See `IERC20.transferFrom`.
*
* Note that operator and allowance concepts are orthogonal: operators cannot
* call `transferFrom` (unless they have allowance), and accounts with
* allowance cannot call `operatorSend` (unless they are operators).
*
* Emits `Sent`, `Transfer` and `Approval` events.
*/
function transferFrom(address holder, address recipient, uint256 amount) external returns (bool) {
    require(recipient != address(0), "ERC777: transfer to the zero address");
    require(holder != address(0), "ERC777: transfer from the zero address");

    address spender = msg.sender;

    _callTokensToSend(spender, holder, recipient, amount, "", "");

    _move(spender, holder, recipient, amount, "", "");
    _approve(holder, spender, _allowances[holder][spender].sub(amount));

    _callTokensReceived(spender, holder, recipient, amount, "", "", false);

    return true;
}

/**
 * @dev Creates `amount` tokens and assigns them to `account`, increasing
 * the total supply.
 *
 * If a send hook is registered for `account`, the corresponding function
 * will be called with `operator`, `data` and `operatorData`.
 *
 * See `IERC777Sender` and `IERC777Recipient`.
 *
 * Emits `Minted` and `Transfer` events.
 *
 * Requirements
 *
 * - `account` cannot be the zero address.
 * - if `account` is a contract, it must implement the `tokensReceived`
 * interface.
 */
function _mint(
    address operator,
    address account,
    uint256 amount,
    bytes memory userData,
    bytes memory operatorData
)
internal
{
    require(account != address(0), "ERC777: mint to the zero address");

    // Update state variables
    _totalSupply = _totalSupply.add(amount);
    _balances[account] = _balances[account].add(amount);

    _callTokensReceived(operator, address(0), account, amount, userData, operatorData, true);

    emit Minted(operator, account, amount, userData, operatorData);
    emit Transfer(address(0), account, amount);
}

/**
 * @dev Send tokens
 * @param operator address operator requesting the transfer
 * @param from address token holder address
 * @param to address recipient address
 * @param amount uint256 amount of tokens to transfer
 * @param userData bytes extra information provided by the token holder (if any)
 * @param operatorData bytes extra information provided by the operator (if any)
 * @param requireReceptionAck if true, contract recipients are required to implement ERC777TokensRecipient
 */
function _send(
    address operator,
    address from,
    address to,
    uint256 amount,
    bytes memory userData,
    bytes memory operatorData,
    bool requireReceptionAck
)
    private
{
    require(from != address(0), "ERC777: send from the zero address");
    require(to != address(0), "ERC777: send to the zero address");

    _callTokensToSend(operator, from, to, amount, userData, operatorData);

    _move(operator, from, to, amount, userData, operatorData);

    _callTokensReceived(operator, from, to, amount, userData, operatorData, requireReceptionAck);
}

/**
 * @dev Burn tokens
 * @param operator address operator requesting the operation
 * @param from address token holder address
 * @param amount uint256 amount of tokens to burn
 * @param data bytes extra information provided by the token holder
 * @param operatorData bytes extra information provided by the operator (if any)
 */
function _burn(
    address operator,
    address from,
    uint256 amount,
    bytes memory data,
    bytes memory operatorData
)
    private
{
    require(from != address(0), "ERC777: burn from the zero address");

    _callTokensToSend(operator, from, address(0), amount, data, operatorData);

    // Update state variables
    _totalSupply = _totalSupply.sub(amount);
    _balances[from] = _balances[from].sub(amount);

    emit Burned(operator, from, amount, data, operatorData);
    emit Transfer(from, address(0), amount);
}

function _move(
    address operator,
    address from,
    address to,
    uint256 amount,
    bytes memory userData,
    bytes memory operatorData
)
    private
{
    _balances[from] = _balances[from].sub(amount);
    _balances[to] = _balances[to].add(amount);

    emit Sent(operator, from, to, amount, userData, operatorData);
    emit Transfer(from, to, amount);
}

function _approve(address holder, address spender, uint256 value) private {
    // TODO: restore this require statement if this function becomes internal, or is called at a new callsite. It is
    // currently unnecessary.
    //require(holder != address(0), "ERC777: approve from the zero address");
    require(spender != address(0), "ERC777: approve to the zero address");

    _allowances[holder][spender] = value;
    emit Approval(holder, spender, value);
}

/**
 * @dev Call from.tokensToSend() if the interface is registered
 * @param operator address operator requesting the transfer
 * @param from address token holder address
 * @param to address recipient address
 * @param amount uint256 amount of tokens to transfer
 * @param userData bytes extra information provided by the token holder (if any)
 * @param operatorData bytes extra information provided by the operator (if any)
 */
function _callTokensToSend(
    address operator,
    address from,
    address to,
    uint256 amount,
    bytes memory userData,
    bytes memory operatorData
)
    private
{
    address implementer = _erc1820.getInterfaceImplementer(from, TOKENS_SENDER_INTERFACE_HASH);
    if (implementer != address(0)) {
        IERC777Sender(implementer).tokensToSend(operator, from, to, amount, userData, operatorData);
    }
}

/**
 * @dev Call to.tokensReceived() if the interface is registered. Reverts if the recipient is a contract but
 * tokensReceived() was not registered for the recipient
 * @param operator address operator requesting the transfer
 * @param from address token holder address
 * @param to address recipient address
 * @param amount uint256 amount of tokens to transfer
 * @param userData bytes extra information provided by the token holder (if any)
 * @param operatorData bytes extra information provided by the operator (if any)
 * @param requireReceptionAck if true, contract recipients are required to implement ERC777TokensRecipient
 */
function _callTokensReceived(
    address operator,
    address from,
    address to,
    uint256 amount,
    bytes memory userData,
    bytes memory operatorData,
    bool requireReceptionAck
)
    private
{
    address implementer = _erc1820.getInterfaceImplementer(to, TOKENS_RECIPIENT_INTERFACE_HASH);
    if (implementer != address(0)) {
        IERC777Recipient(implementer).tokensReceived(operator, from, to, amount, userData, operatorData);
    } else if (requireReceptionAck) {
        require(!to.isContract(), "ERC777: token recipient contract has no implementer for ERC777TokensRecipient");
    }
}
}

// File: TestToken.sol

pragma solidity ^0.5.0;

contract TestToken is ERC777 {

constructor(
  string memory name,
  string memory symbol,
  address[] memory defaultOperators,
  uint256 totalSupply
)
public payable ERC777(name, symbol, defaultOperators)
{
  // mint new tokens to address that deploys contract
  _mint(msg.sender, msg.sender, totalSupply, "", "");
}
}
gussy_88
  • 23
  • 1
  • 3

1 Answers1

2

This line caused the error:

_erc1820.setInterfaceImplementer(address(this), keccak256("ERC777Token"), address(this));

And you initiate _erc1820 with this line:

IERC1820Registry private _erc1820 = IERC1820Registry(0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24);

Make sure you have deployed ERC1820Registry contract at 0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24 in the VM.

StillFantasy
  • 1,677
  • 9
  • 21
  • Thanks for your answer; i believe the ERC1820Registry is deployed. you can take a look of the whole contract here https://pastebin.com/54YjKvRz still, nothing changes @StillFantasy – gussy_88 Nov 12 '19 at 15:05
  • @gussy_88 Hi, it works perfectly at my side. Are you sure erc1820Registry is deployed **in the VM**? And if you have closed the browser before or have refreshed it, you will need to re-deploy the contract again, because remix gives you a new VM every time. – StillFantasy Nov 12 '19 at 15:18
  • what do you that its deployed; on its own its an abstract contract so it cant be deployed before the deployment of the main contract. can you elaborate please @StillFantasy – gussy_88 Nov 12 '19 at 18:12
  • 1
    @gussy_88 I found the code of `ERC1820Registry` on Github. I deployed it, then replaced `0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24` in your code with new contract address. After that, your contract can be deployed without error. – StillFantasy Nov 12 '19 at 18:24
  • 1
    I think you misunderstand something, your contract doesn't deploy `ERC1820Registry`. This line: `IERC1820Registry private _erc1820 = IERC1820Registry(0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24);` means load the registry contract at that address. – StillFantasy Nov 12 '19 at 18:27
  • can you elaborate how to deploy it? as its my first time to deal with ERC1820; should i separate it and deploy it first ? OR what should be done before i transact the main contract? @StillFantasy – gussy_88 Nov 12 '19 at 19:45
  • @gussy_88 copy and paste [this](https://github.com/0xjac/ERC1820/blob/master/contracts/ERC1820Registry.sol) code in another file, deploy it, get the address of it, replace `0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24` with that address in your main contract – StillFantasy Nov 12 '19 at 19:54
  • My bad; i totally missed your point; i already did it on my own; I just missed one of your comments that why i got confused. anyway; i did that and now im getting the below which doesnt make sense. "creation of TestToken errored: transaction execution failed" and on transaction status "status 0x0 Transaction mined but execution failed" @StillFantasy – gussy_88 Nov 12 '19 at 20:19
  • 1
    @gussy_88 Does it run out of gas? – StillFantasy Nov 12 '19 at 20:27
  • Apparently yes; thanks for the hint. Deployed! @StillFantasy – gussy_88 Nov 12 '19 at 20:29
  • @gussy_88 No problem. I would be grateful if you could accept my answer by clicking the check mark – StillFantasy Nov 12 '19 at 20:52
  • @StillFantasy how can we deploy erc1820Registry to VM, for example to deploy it on remix VM? – Blissful Oct 14 '20 at 21:07
  • @MarkoBlazekovic Just deploy https://github.com/0xjac/ERC1820/blob/master/contracts/ERC1820Registry.sol – StillFantasy Oct 15 '20 at 17:27
  • @StillFantasy but I need to deploy it to 0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24 address, otherwise I need to change also the other contract where this is hardcoded which should mean I should also deploy this https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC777/ERC777.sol with changed address to JS VM after deploying erc1820 and then import the one I deployed not the one from github. right? – Blissful Oct 16 '20 at 11:38
  • @MarkoBlazekovic Yeah, I think in VM the only way is to deploy a new one and replace the hardcoded address to a new one – StillFantasy Oct 16 '20 at 15:19