My function call is returning expensive gas cost on metamask

111 Views Asked by At

To context. I want to make a contract that receives USDC and send custom tokens to the buyer. After that, in the same call, the contract forwards those USDCs to another wallet. I have done tests, and calls. When I do it through Remix, I'm able to get it working nicely, Rate works, forward funds, everything. However, once I try to call it through Metamask, it returns me a absurd amound of gas required (10 matic) to call buyTokens(). Could someone help me find if something is wrong, or should it be this expensive?

ps: With buyTokens(), we are also calling approve() from the buyer, to approve Crowdsale to take the right amount of USDC

I have two contracts to demonstrate. First, the custom token:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "./ERC777P.sol";
import "./MinterRole.sol";

contract TokenMBP is ERC777P, MinterRole {
    constructor(
        string memory name,
        string memory symbol,
        address[] memory defaultOperators,
        uint256 initialSupply
    ) ERC777P(name, symbol, defaultOperators)
    {
        _mint(msg.sender, initialSupply, "", "", "");
    }

    function mint(address account, uint256 amount, bytes memory userData, bytes memory operatorData, bytes32 proof) public onlyMinter returns (bool) {
        _mint(account, amount, userData, operatorData, proof);
        return true;
    }

    function burn(uint256 amount, bytes memory userData, bytes memory operatorData) public returns (bool) {
        _burn(msg.sender, amount, userData, operatorData);
        return true;
    }
}

Then, the crowdsale:

pragma solidity ^0.5.0;

import "./Context.sol";
import "./IERC20.sol";
import "./StableCoin.sol";
import "./SafeMath.sol";
import "./SafeERC20.sol";
import "./ReentrancyGuard.sol";

/**
 * @title Crowdsale
 * @dev Crowdsale is a base contract for managing a token crowdsale,
 * allowing investors to purchase tokens with ether. This contract implements
 * such functionality in its most fundamental form and can be extended to provide additional
 * functionality and/or custom behavior.
 * The external interface represents the basic interface for purchasing tokens, and conforms
 * the base architecture for crowdsales. It is *not* intended to be modified / overridden.
 * The internal interface conforms the extensible and modifiable surface of crowdsales. Override
 * the methods to add functionality. Consider using 'super' where appropriate to concatenate
 * behavior.
 */


contract Crowdsale is Context, ReentrancyGuard {
    using SafeMath for uint256;
    using SafeERC20 for IERC20;

    // The token being sold
    IERC20 private _token;
    STABLE private _stableCoin = _stableCoin;

    // Address where funds are collected
    address payable private _wallet;

    // Contract owner address
    address _owner;
    // How many token units a buyer gets per usdc.
    // The rate is the conversion between usdc and the smallest and indivisible token unit.
    // So, if you are using a rate of 1 with a ERC20Detailed token with 3 decimals called TOK
    // 1 usdc will give you 1 unit, or 0.001 TOK.
    uint256 private _rate;

    // Amount of usdc raised
    uint256 private _usdcRaised;

    /**
     * Event for token purchase logging
     * @param purchaser who paid for the tokens
     * @param beneficiary who got the tokens
     * @param value usdcs paid for purchase
     * @param amount amount of tokens purchased
     */
    event TokensPurchased(address indexed purchaser, address indexed beneficiary, uint256 value, uint256 amount);

    /**
     * @param rate Number of token units a buyer gets per wei
     * @dev 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 ERC20Detailed token
     * with 3 decimals called TOK, 1 wei will give you 1 unit, or 0.001 TOK.
     * @param wallet Address where collected funds will be forwarded to
     * @param token Address of the token being sold
     */
    constructor (uint256 rate, address payable wallet, IERC20 token, address stableCoin) public {
        require(rate > 0, "Crowdsale: rate is 0");
        require(wallet != address(0), "Crowdsale: wallet is the zero address");
        require(address(token) != address(0), "Crowdsale: token is the zero address");
        require(address(stableCoin) != address(0), "Crowdsale: stable is the zero address");

        _owner = _msgSender();
        _rate = rate;
        _wallet = wallet;
        _token = token;
        _stableCoin = STABLE(stableCoin);
    }

    modifier onlyOwner() {
        require(_msgSender() == _owner, "OwnerRole: Only owner can make this change");
        _;
    }

    /**
     * @return the token being sold.
     */
    function token() public view returns (IERC20) {
        return _token;
    }

    /**
     * @return the stable token being required.
     */
    function stable() public view returns (STABLE) {
        return _stableCoin;
    }

    /**
     * @return the address where funds are collected.
     */
    function wallet() public view returns (address payable) {
        return _wallet;
    }

    /**
     * @return the number of token units a buyer gets per wei.
     */
    function rate() public view returns (uint256) {
        return _rate;
    }

    /**
     * @return the amount of wei raised.
     */
    function usdcRaised() public view returns (uint256) {
        return _usdcRaised;
    }

    /**
     * @dev Change contract rate
     * @param newRate New price for minimum amount of token
     * @return true if rate was modifie
     */

     function changeRate(uint256 newRate) public onlyOwner returns (bool) {
        _rate = newRate;
        return true;
     }

    /**
     * @dev low level token purchase ***DO NOT OVERRIDE***
     * This function has a non-reentrancy guard, so it shouldn't be called by
     * another `nonReentrant` function.
     * @param beneficiary Recipient of the token purchase
     * @param tokenAmount Stable coin quantity for the purchase
     */
    function buyTokens(address beneficiary, uint256 tokenAmount) public nonReentrant payable {
        // calculate token amount to be created
        // tokenAmount = tokenAmount * 10 ** 18;

        uint256 tokens = _getTokenAmount(tokenAmount);

        _preValidatePurchase(beneficiary, tokens);
        // update state
        _usdcRaised = _usdcRaised.add(tokenAmount);

        bool stableStatus = _stableCoin.transferFrom(beneficiary, address(this), tokenAmount);
        require(stableStatus, "Crowdsale: Stable token transfer failed");
        
        _processPurchase(beneficiary, tokens);
        emit TokensPurchased(_msgSender(), beneficiary, tokenAmount, tokens);

        _updatePurchasingState(beneficiary, tokenAmount);

        _forwardFunds(tokenAmount);
        _postValidatePurchase(beneficiary, tokenAmount);
    }
    /**
     * @dev Validation of an incoming purchase. Use require statements to revert state when conditions are not met.
     * Use `super` in contracts that inherit from Crowdsale to extend their validations.
     * Example from CappedCrowdsale.sol's _preValidatePurchase method:
     *     super._preValidatePurchase(beneficiary, tokenAmount);
     *     require(weiRaised().add(tokenAmount) <= cap);
     * @param beneficiary Address performing the token purchase
     * @param tokenAmount Value in wei involved in the purchase
     */
    function _preValidatePurchase(address beneficiary, uint256 tokenAmount) internal view {
        require(beneficiary != address(0), "Crowdsale: beneficiary is the zero address");
        require(tokenAmount != 0, "Crowdsale: tokenAmount is 0");
        require(!(_stableCoin.balanceOf(beneficiary) < tokenAmount), "Crowdsale: Not enough balance");
        this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
    }

    /**
     * @dev Validation of an executed purchase. Observe state and use revert statements to undo rollback when valid
     * conditions are not met.
     * @param beneficiary Address performing the token purchase
     * @param weiAmount Value in wei involved in the purchase
     */
    function _postValidatePurchase(address beneficiary, uint256 weiAmount) internal view {
        // solhint-disable-previous-line no-empty-blocks
    }

    /**
     * @dev Source of tokens. Override this method to modify the way in which the crowdsale ultimately gets and sends
     * its tokens.
     * @param beneficiary Address performing the token purchase
     * @param tokenAmount Number of tokens to be emitted
     */
    function _deliverTokens(address beneficiary, uint256 tokenAmount) internal {
        _token.safeTransfer(beneficiary, tokenAmount);
    }

    /**
     * @dev Executed when a purchase has been validated and is ready to be executed. Doesn't necessarily emit/send
     * tokens.
     * @param beneficiary Address receiving the tokens
     * @param tokenAmount Number of tokens to be purchased
     */
    function _processPurchase(address beneficiary, uint256 tokenAmount) internal {
        _deliverTokens(beneficiary, tokenAmount);
    }

    /**
     * @dev Override for extensions that require an internal state to check for validity (current user contributions,
     * etc.)
     * @param beneficiary Address receiving the tokens
     * @param weiAmount Value in wei involved in the purchase
     */
    function _updatePurchasingState(address beneficiary, uint256 weiAmount) internal {
        // solhint-disable-previous-line no-empty-blocks
    }

    /**
     * @dev Override to extend the way in which ether is converted to tokens.
     * @param stableCoinAmount Value in wei to be converted into tokens
     * @return Number of tokens that can be purchased with the specified _weiAmount
     */
    function _getTokenAmount(uint256 stableCoinAmount) internal view returns (uint256) {
        return stableCoinAmount.div(_rate);
    }

    /**
     * @dev Determines how ETH is stored/forwarded on purchases.
     */
    function _forwardFunds(uint256 tokenAmount) internal returns (bool){
        require(_stableCoin.transfer(_wallet, tokenAmount), "Crowdsale: Stable token transfer failed");
        return true;
    }

}

I used those contracts and with remix gas was as low as 0.01 matic.

0

There are 0 best solutions below