underlying.transferFrom in compound docs for lending throws this error: ProviderError: Error: Transaction reverted without a reason string

I'm trying to lend to the compound protocol in an App I'm building.
Also following the compound doc & guide here: Compound Blog Post
After forking the mainnet locally and impersonating an address with large USDT, I'm now trying to lend to the compound protocol.

In my test, this line underlying.transferFrom always fails with this error: ProviderError: Error: Transaction reverted without a reason string
Any suggestions on what could be the reason.

I've asked on the compound discord, and gotten no reply.
Also done lots of research, couldn't find a similar issue... don't know what else to do.
Below is a sample of my code

Dev Environment: Hardhat
OS: Windows 10

pragma solidity ^0.8.0;
import "hardhat/console.sol";
import "../interfaces/ICompound.sol";

contract CompoundController {
    mapping(address => mapping(address => UserInvestedTokenDetails))
        public UserInvestments;

    struct UserInvestedTokenDetails {
        uint256 tokenAmount;
        bool isExists;
    }

    function _setUserInvestments(
        address userAddress,
        address tokenAddress,
        uint256 tokenAmount
    ) internal {
        if (UserInvestments[userAddress][tokenAddress].isExists) {
            UserInvestments[userAddress][tokenAddress]
                .tokenAmount += tokenAmount;
        } else {
            UserInvestments[userAddress][
                tokenAddress
            ] = UserInvestedTokenDetails(tokenAmount, true);
        }
    }

    function supplyErc20ToCompound(
        address _erc20,
        address _cErc20,
        uint256 tokenAmount
    ) public returns (bool) {
        console.log("contract!");
        // Token being supplied to compound
        Erc20 underlying = Erc20(_erc20);
        // Token sent from compound in return
        CErc20 cToken = CErc20(_cErc20);
        require(
            underlying.transferFrom(msg.sender, address(this), tokenAmount),
            "compound: transferring tokens from user failed!"
        );

        underlying.approve(_cErc20, tokenAmount);
        require(cToken.mint(tokenAmount) == 0, "compound: mint failed!");
        _setUserInvestments(msg.sender, _erc20, tokenAmount);
        return true;
    }

}

Hi, I think USDT is not a standard ERC20 token, that means, there is not a returning value for the function transferFrom(), so you can not add a requirement for this function, maybe you can use the safeTrasferFrom

Heyy Skyge, thanks for your time man.
i thought there is a way to view the contracts for these tokens on Etherscan.
Checked here in the contracts section, couldn't find it. https://etherscan.io/token/0xdAC17F958D2ee523a2206206994597C13D831ec7#readContract
:man_facepalming:

Find the source code at here: Tether: USDT Stablecoin | 0xdAC17F958D2ee523a2206206994597C13D831ec7

Just did, Skyge.
Found this section


Doesn't that mean they support the transferFrom function?

Couldnt find a safeTransferFrom() function in the contract though?

This is a function in the library of the OpenZeppelin repo rather than in the USDT contract.

If i replace underlying.transferFrom() with underlying.safeTransferFrom() wont that fail, if its not a function they have in their ERC20 contract?

I think you can have a look at here: EIP20 | transferFrom

For a standard ERC20 token, it should achieve all interface functions in the IERC20.sol that defined as above. For USDT, it is not a standard ERC20 token, cause for the function transferFrom(), it does not have a return value, so you can not write like this:

require(
    underlying.transferFrom(msg.sender, address(this), tokenAmount)   // <<<--- here!!!
)

So you can just write like:

underlying.transferFrom(msg.sender, address(this), tokenAmount);

For the function safeTrasferFrom, it is in the SafeERC20.sol, this is a library contract, I mentioned this function due to much people would like to use it.

So, after days of struggling with this i found these pointers.

  • The USDT contract seems for some reason not to comply with the eip20 standard.
    It doesn't return a bool, so my interface for all erc20 contracts didn't match with their function.
    Immediately i tested with DAI which was eip20 compliant, it worked.
  • Also at some point, while forking the mainnet, my signer account(account i was testing with) didnt have the token(enough USDT) i was trying to supply to compound.

I mean, the USDT contract issue is frustrating, hope someone else finds this useful.:relieved:

1 Like

Also, thanks for your help @Skyge