2

There is an ERC20 token (contract 1 - I do not own this.) and the tokens are being used to pay for a service in my services contract (Contract 2).

I can easily send contract 1 tokens from the user owned account using metamask, to Contract 2 if the user triggers the send action from the wallet.

However, I want the user to click a button on my Dapp that will call a function in my services contract 2 that will then trigger the user to pay for the product in an ERC20 token of their choice (ie contract 1).

What is the best way to approach this knowing that msg.value from msg.sender and that address(this).balance is specifically for Ether and not tokens...

How do I make sure the smart contract is getting paid in the correct ERC20 token using a function?

I have tried this solution for an ERC223 but it won't work for this case as I need to remain in ERC20: Accepting an ERC20 Token as Payment

Is there anyway I can do this without changing the standard?

0

3 Answers 3

2

Example code,

function stake(address _token, uint256 amount)
        external
    {
        require(amount > 0, "Cannot stake 0");
        _totalSupply = _totalSupply.add(amount);
        _balances[msg.sender] = _balances[msg.sender].add(amount);
        // Before this you should have approved the amount 
        // This will transfer the amount of  _token from caller to contract
        IERC20(_token).transferFrom(msg.sender, address(this), amount);
        emit Staked(msg.sender, amount);
    }
3
  • thank you! This is where I was at and my main question here is whether or not it was possible to call: IERC20(_tokenContract1).methodWhatever() from inside my contract2? Basically, I can call IERC20(_tokenContract1) from inside any contract I own to invoke IERC20(_tokenContract) from a contract I do not own? I have to have the IERC spec implemented in my own contract of course correct on all of this??? I really appreciate it Ill test and give you the win! :) 🏆
    – lopezdp
    Jul 19, 2021 at 15:42
  • lastly, your comment on // Before this you should have approved the amount // --> this is executed from the dApp itself using ethers or web3 correct? Can you provide an example of how I can call approve using ethers for example please? if you dm me and send me a wallet address ill send you some ETH too if the answer gives me the result I want. ill set this up now to give you time to answer my follow up questions/comments also and I will send payment on successful test. Dont forget to DM me with wallet please.
    – lopezdp
    Jul 19, 2021 at 15:43
  • Code to call approve with ethersjs let contract = new ethers.Contract(contractAddress, contracObj.contractAbi, this.provider); let contractWithSigner = contract.connect(wallet); let rawTx = { gasLimit: 500000 }; await contractWithSigner.approve(toAccount, accountNum, rawTx); Jul 20, 2021 at 8:50
2

Here's a more complete example, with the caveat that:

DONT USE THIS AS IS, IT ALLOWS ANYONE TO TRANSFER MONEY OUT.

The trick here is that before you deposit tokens, you have to approve the contract first.

Example.sol:

// SPDX-License-Identifier: MIT                                                                                                                                                                                                                                                                                                
pragma solidity >0.8.5;
import "node_modules/@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "node_modules/@openzeppelin/contracts/token/ERC20/IERC20.sol";
contract SomeCoin is ERC20 {
    constructor() ERC20("SC", "Some Coin") {
        _mint(msg.sender, 1000000000);
    }
}
contract Example {
    // DONT USE THIS AS IS                                                                                                                                                                                                                                                                                                     
    // IT ALLOWS ANYONE TO TRANSFER TOKENS OUT                                                                                                                                                                                                                                                                                 
    // INSTEAD, KEEP TRACK OF THE USER BALANCES SEPERATELY                                                                                                                                                                                                                                                                     
    IERC20 public erc20coin;
    constructor(address erc20Address_){
        erc20coin = IERC20(erc20Address_);
    }
    function deposit(uint amount_)external{
        erc20coin.transferFrom(msg.sender, address(this), amount_);
    }
    function withdrawInto(address to_, uint amount_, address contractAddress_)external{
        IERC20 erc20 = IERC20(contractAddress_);
        erc20.transfer(to_, amount_);
    }
    function withdrawInto(address to_, uint amount_)external{
        erc20coin.transfer(to_, amount_);
    }
}

And for a quick example of how to run it (in Python, sorry JS users, but you can see what I'm doing here and replicate it i'm sure):

example.py:

#!/usr/bin/env python3                                                                                                                                                                                                                                                                                                         
from kista import *

w3 = w3_connect(0)

def status():
    print("-"*30)
    print("Example   balance", c.balanceOf(   e.address))
    print("address 0 balance", c.balanceOf(a[0].address))
    print("address 1 balance", c.balanceOf(a[1].address))
    print("address 2 balance", c.balanceOf(a[2].address))
    print("-"*30)
    pass

if __name__ == '__main__':
    a = [WrapAccount(a) for a in range(10)]

    c = WrapContract(force_load_contract_address(
        "SomeCoin", reload = True))

    e = WrapContract(force_load_contract_address(
        "Example", c.address, reload = True))

    status()
    print(c.approve(e.address, 150000000))
    print(e.deposit(100000000))
    status()
    print(e.withdrawInto(a[1].address, 2500))
    print(e.withdrawInto(a[2].address, 2500, c.address))
    status()
0

You cannot do such a function because the tokens work in another way. What you can do is make a two-step payment, the classical way in the DeFi space.

Firstly you should make a user approve his tokens so the contract can spend his tokens on the user's behalf. This is done through the call to the approve function of the ERC20 contract.

Then you should call transferFrom function of the contract 1 from your contract2's function that "accepts payment". And the user would just call this function to pay for a service with his tokens.

3
  • can you provide an example how I can call approve in contract 1 (that I do not own) from contract 2 (that I do own)... how would this transferFrom work inside the function I need in contract 2?
    – lopezdp
    Jul 18, 2021 at 23:06
  • For the first question - you can't. You should call the contract 1 and the user should sign it as a separate transaction because the approve function requires msg.sender. If you say that you do not own this contract then you should instantiate this contract from your frontend with the help of ERC20 or ABI and feed the call to the approve function to Metamask or whatever wallet you support. The second - you can see all functions including transferFrom in the OpenZeppelin's ERC20.sol Jul 18, 2021 at 23:44
  • I know how to read the spec, would you mind providing an example of how you would do it?
    – lopezdp
    Jul 19, 2021 at 1:23

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