Getting a "revert" processing transaction on "transfer" call

Hi,

I’m working with solidity and ether contracts for the first time, so I’m going through the documentation and working through some simple tutorials. Although I’m learning quite a bit, I have no idea why I am unable to run a transfer of my ERC20 token. The exact error running my integration test is Error: Returned error: VM Exception while processing transaction: revert. It is clear that it is getting tripped up at the transfer line of the ArkICO contract, but to my knowledge, ‘transfer’ doesn’t need the approval ‘transferFrom’ needs? I also moved the transfer line directly into the test, and it is able to move the tokens to the userAddr, but obviously doesn’t subtract the value from the ICO contract. Or could it be because I’m initiating the transfer from a test?

Any guidance would be greatly appreciated.

:computer: Environment
openzeppelin/contracts: “^3.4.1”
Truffle v5.2.4
solidity 0.6.0

:memo:Details

:1234: Code to reproduce
ArkToken.sol

The following contract is the token itself, and I have added a function ‘fundICO’ to be run on migration to transfer tokens to the ICO contract.

    pragma solidity 0.6.0;

    import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
    import "./helpers/Owned.sol";

    contract ArkToken is ERC20, Owned {
      uint256 public FOR_ICO = 750000;
      uint256 public FOR_FOUNDER = 250000;

      constructor() public ERC20("Ark", "ARK") {
        _mint(msg.sender, FOR_ICO + FOR_FOUNDER);
      }

      function fundICO(address _icoAddr) public onlyOwner {
        transfer(_icoAddr, FOR_ICO);
      }
    }

ArkICO.sol

ICO contract from where I want to transfer tokens. Sets the rate of ETH to ARK, and executes the transfer.

pragma solidity 0.6.0;

import "@openzeppelin/contracts/math/SafeMath.sol";
import "./ArkToken.sol";

contract ArkICO {
  using SafeMath for uint256;
  ArkToken token;
  uint256 public RATE = 1000;

  function ArkIco(address _tokenAddr) public {
    token = ArkToken(_tokenAddr);
  }
  
  receive() external payable {
    uint256 _amount = _getTokenAmount(msg.value);
    token.transfer(msg.sender, _amount);
  }

  function _getTokenAmount(uint256 _weiAmount) internal view returns (uint256) {
    return _weiAmount.div(10 ** 18).mul(RATE);
  }
 
}

2_deploy_token.js

const ArkToken = artifacts.require("ArkToken")

module.exports = (deployer) => {
  deployer.deploy(ArkToken)
} 

3_deploy_ico.js

On ICO deploy, I pass the ArkToken address so ICO has access to the ERC20 features, then transfer the tokens to the ICO address.

const ArkToken = artifacts.require("ArkToken")
const ArkICO = artifacts.require("ArkICO")

module.exports = (deployer) => {
  
  deployer.deploy(ArkICO, ArkToken.address)
  .then(() => {
    return ArkToken.deployed()
  })
  .then(token => {
    return token.fundICO(ArkICO.address)
  })

}

test.js

Here we test the distribution which passes, however ‘can buy tokens’ throws the error at the sendTransaction line.

const ArkToken = artifacts.require("ArkToken")
const ArkICO = artifacts.require("ArkICO")
const web3 = require("web3")

const { 
  utils: { toWei },
} = web3

contract("token", (accounts) => {

  it("distributes token supply", async () => {
    const token = await ArkToken.deployed()
    const ico = await ArkICO.deployed()
    
    // accounts[0] is the same address that created the TweetherToken:
    const icoBalance = await token.balanceOf.call(ico.address)
    const founderBalance = await token.balanceOf.call(accounts[0])

    // Make sure it holds all of the supply:
    assert.equal(icoBalance.toString(), "750000")
    assert.equal(founderBalance.toString(), "250000")
  })

  it("can buy tokens", async () => {
    const token = await ArkToken.deployed()
    const ico = await ArkICO.deployed()

    const userAddr = accounts[1] // A random account that we control
    const wei = toWei("1", "ether") // We need to specify the value in wei

    await ico.sendTransaction({
      from: userAddr,
      value: wei,
    })

    const userBalance = await token.balanceOf.call(userAddr);
    assert.equal(userBalance.toString(), "1000")

    const icoBalance = await token.balanceOf.call(ico.address);
    assert.equal(icoBalance.toString(), "749000")
  })

})
1 Like

Hi @adamatronix,

Welcome to the community :wave:

When you deploy your ArkToken you are minting tokens to the deployer of the contract.
fundICO will fail as the ArkToken doesn’t actually hold any ArkTokens, only the deployer does, so then your ICO contract doesn’t hold any tokens.

I suggest having a look at the following:

Thanks for the reply. I’ll review those pages.

As for the fundIco not working, the two assertions pass:

assert.equal(icoBalance.toString(), "750000")
assert.equal(founderBalance.toString(), "250000")

So should this mean ArkICO has the funds to transfer?

1 Like

Hi @adamatronix,

I assume it is the amount of tokens and/or your rate. Meaning you don’t have enough tokens for what is being purchased.

https://docs.openzeppelin.com/contracts/2.x/crowdsales#crowdsale-rate
https://docs.openzeppelin.com/contracts/2.x/erc20#a-note-on-decimals