I have a contract that holds all its tokens. Now I need to transfer tokens to an user that sends some BNB to its "receive" fallback.

So... in OpenZeppelin derived contracts we have no access to the balance array and the "transfer" function always use the sender (the actual user) as the one that will give tokens.

function transfer(address recipient, uint256 amount) public virtual override returns (bool) {
    _transfer(_msgSender(), recipient, amount);
    return true;

In the receive method I can't make the contract give tokens to the sender.

    receive() external payable {
        require( msg.value > 0 );
        transfer( <MUST_BE_THE_USER>, tokens );

When "transfer()" calls "_transfer()" it will make the user giving tokens to itself. I need to call "_transfer(<THE_CONTRACT_ADDRESS>, <THE_USER_ADDRESS>, amount);" but this function cannot be accessed by my contract that extends O.Z.'s ERC20.

As alternative we have "transferFrom(address sender, address recipient, uint256 amount) " but it have all that "approve" stuff ....

How can I make transfers using OZ base contracts when the spender is not the msg.sender ?

2 Answers 2


I think you need to call your contract by your contract so try "this.transfer"...

simplified -> the keyword "this" will call the transfer function as an external function and this makes the contract the new "msg.sender" . please understand "this" very good before using it in your contract.

receive() external payable {
    require( msg.value > 0 );
    this.transfer( <MUST_BE_THE_USER>, tokens );

Probably above is not the best or cleanest solution. Sob how about using the mint method instead and mint the token when you receive the ether? so it will probably look like this:

receive() external payable {
    require (msg.value > 0);
    _mint(msg.sender, <number of tokens>);
  • Good point. But I think "mint" will craft more tokens than the initial supply and it is not my intent. Don't want to print money.
    – Magno C
    Jun 20, 2021 at 17:55

You mint tokens and it isn't the contract that holds them, but the minter

contract NMEToken is Ownable, ERC20Pausable {
    address internal minter; 
    constructor(address _treasury) ERC20("NME","Name") {
        minter = msg.sender;
        _mint(minter, 500000);

Later, when someone comes along to buy them:

    function buyToken() public payable {
        // payable to treasury
        // transfer 5:1 token
        (bool sent, bytes memory data) = payable(treasury).call{value: msg.value}("");
        require(sent, "Failed to send Ether");
        ERC20._transfer(minter, msg.sender, msg.value * 5);

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.