0

I am trying to troubleshoot a problem I'm having with a crowdsale contract (written in Vyper). The contract compiles fine, and I can deploy the contract to Rinkeby without any problems, however when I call the buyTokens function (below), MetaMask gives me the error "Transaction error: exception thrown in contract code". I looked online for some help, and some people suggested having a higher gas limit, which I tried through the ABI, to no avail.

Crowdsale contract:

# Contributors: Binod Nirvan
# This file is released under Apache 2.0 license.
# @dev Crowdsale with a limit for total contributions.
# Ported from Open Zeppelin
# https://github.com/OpenZeppelin
# 
# See https://github.com/OpenZeppelin
# Open Zeppelin tests ported: Crowdsale.test.js


#@dev ERC20/223 Features referenced by this contract
interface TokenContract:
    def transfer(_to: address, _value: uint256) -> bool: payable

# Event for token purchase logging
# @param _purchaser who paid for the tokens
# @param _beneficiary who got the tokens
# @param _value weis paid for purchase
# @param _amount amount of tokens purchased

event TokenPurchase: 
    _purchaser: address
    _beneficiary: address
    _value: uint256
    _amount: uint256

# The token being sold
token: public(address)

#Address where funds are collected
wallet: public(address)

# How many token units a buyer gets per wei.
# The rate is the conversion between wei and the smallest and indivisible token unit.
# So, if you are using a rate of 1 with a DetailedERC20 token with 3 decimals called TOK
# 1 wei will give you 1 unit, or 0.001 TOK.
rate: public(uint256)

#Amount of wei raised
weiRaised: public(uint256)

@external
def __init__(_rate: uint256, _wallet: address, _token: address):
    """
    @dev Initializes this contract
    @param _rate Number of token units a buyer gets per wei
    @param _wallet Address where collected funds will be forwarded to
    @param _token Address of the token being sold
    """

    assert _rate > 0, "Invalid value supplied for the parameter \"_rate\"."
    assert _wallet != ZERO_ADDRESS, "Invalid wallet address."
    assert _token != ZERO_ADDRESS, "Invalid token address."

    self.rate = _rate
    self.wallet = _wallet
    self.token = _token

@internal
def getTokenAmount(_weiAmount: uint256) -> uint256:
    return _weiAmount * self.rate


@internal
def processTransaction(_sender: address, _beneficiary: address, _weiAmount: uint256):
    #pre validate
    assert _beneficiary != ZERO_ADDRESS, "Invalid address."
    assert _weiAmount != 0, "Invalid amount received."

    #calculate the number of tokens for the Ether contribution.
    tokens: uint256 = self.getTokenAmount(_weiAmount)
    
    self.weiRaised += _weiAmount

    #process purchase
    assert TokenContract(self.token).transfer(_beneficiary, tokens), "Could not forward funds due to an unknown error."
    log TokenPurchase(_sender, _beneficiary, _weiAmount, tokens)

    #forward funds to the receiving wallet address.
    send(self.wallet, _weiAmount)

    #post validate

@external
@payable
def buyTokens(_beneficiary: address):
    self.processTransaction(msg.sender, _beneficiary, msg.value)

@external
@payable
def __default__():
    self.processTransaction(msg.sender, msg.sender, msg.value)

Token Contract:

event Transfer:
    _from: indexed(address)
    _to: indexed(address)
    _value: uint256

event Approval:
    _owner: indexed(address)
    _spender: indexed(address)
    _value: uint256

event Freeze:
    _account: indexed(address)
    _freeze: bool

name: public(String[10])
symbol: public(String[3])
totalSupply: public(uint256)
decimals: public(uint256)
balances: HashMap[address, uint256]
allowed: HashMap[address, HashMap[address, uint256]]
frozenBalances: public(HashMap[address, bool])
owner: public(address)

interface EcoMgr:
    def fulfill(_purchaser: address, _amount: uint256) -> uint256: payable

@external
def __init__():
    _initialSupply: uint256 = 6000000000
    _decimals: uint256 = 2
    self.totalSupply = _initialSupply * 10 ** _decimals
    self.balances[msg.sender] = self.totalSupply
    self.name = 'Honos Coin'
    self.symbol = 'HON'
    self.decimals = _decimals
    self.owner = msg.sender
    log Transfer(ZERO_ADDRESS, msg.sender, self.totalSupply)


@external
def freezeBalance(_target: address, _freeze: bool) -> bool:
    assert msg.sender == self.owner
    self.frozenBalances[_target] = _freeze
    log Freeze(_target, _freeze)
    return True

@external
def mintToken(_mintedAmount: uint256) -> bool:
    assert msg.sender == self.owner
    self.totalSupply += _mintedAmount
    self.balances[msg.sender] += _mintedAmount
    log Transfer(ZERO_ADDRESS, msg.sender, _mintedAmount)

    return True

@external
def burn(_burntAmount: uint256) -> bool:
    assert msg.sender == self.owner
    assert self.balances[msg.sender] >= _burntAmount
    self.totalSupply -= _burntAmount
    self.balances[msg.sender] -= _burntAmount
    log Transfer(msg.sender, ZERO_ADDRESS, _burntAmount)

    return True

@external
def balanceOf(_owner: address) -> uint256:
    return self.balances[_owner]

@external
def transfer(_to: address, _amount: uint256) -> bool:
    assert self.balances[msg.sender] >= _amount
    assert self.frozenBalances[msg.sender] == False
    self.balances[msg.sender] -= _amount
    self.balances[_to] += _amount
    log Transfer(msg.sender, _to, _amount)

    return True

@external
def transferFrom(_from: address, _to: address, _value: uint256) -> bool:
    assert _value <= self.allowed[_from][msg.sender]
    assert _value <= self.balances[_from]
    assert self.frozenBalances[msg.sender] == False

    self.balances[_from] -= _value
    self.allowed[_from][msg.sender] -= _value
    self.balances[_to] += _value
    log Transfer(_from, _to, _value)

    return True

@external
def approve(_spender: address, _amount: uint256) -> bool:
    self.allowed[msg.sender][_spender] = _amount
    log Approval(msg.sender, _spender, _amount)

    return True

@external
def allowance(_owner: address, _spender: address) -> uint256:
    return self.allowed[_owner][_spender]

Error message (through oneclickdapp) :

Transaction has been reverted by the EVM: { "blockHash": "0x7d615382ab34460051ba5ee57244209bd23cf8f80890a661f868b9dfba6a7767", "blockNumber": 7168509, "contractAddress": null, "cumulativeGasUsed": 511857, "from": "0xe0a965a9aaa31a7181eb03af09f5698d064f778e", "gasUsed": 48681, "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "status": false, "to": "0xfd22167d15fb4be4bfb9bad3e9a95994221e8d84", "transactionHash": "0x05511ffb472adafd283922dc71670b6d9d68de864818b5e1b97145a205f82b38", "transactionIndex": 7, "events": {} }
  • Which address have the initial supply of token? If you want to sell tokens through the Crowdsale contract you have to send the tokens it. – Ismael Sep 10 at 6:20
  • The Token contract’s address has the initialSupply. The crowd sale contract has an interface the pulls the transfer() function from the token contract. – phantalune Sep 10 at 15:50
  • The crowdsale transfer tokens from their own balance to the beneficiary address TokenContract(self.token).transfer(_beneficiary, tokens). For that to work it needs to have the required amount of tokens in their balance. Did you deposit tokens in the crowdsale contract? – Ismael Sep 10 at 19:15
  • Yes, I have deposited tokens into the crowdsale contract, the error still persists. – phantalune Sep 10 at 21:17
  • From the debug trace on rinkeby.etherscan.io it reverts inside the token transfer function. Inspecting the token's address it has a single holder which is not the crowdsale contract. It fails because the crowdsale cannot transfer tokens to the beneficiary because it doesn't have the funds. – Ismael Sep 10 at 23:11

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.