Worked on testnet but not on mainnet. Simple deposit/withdraw

Hi everyone,
I'm having some issues that I can't seem to get around. I wrote a simple contract that deposits erc20 usdt into the contract using deposit and another function to withdraw. I tested this contract on the testnet w testnet tokens and all worked well. I went to the token itself to approve my contract to use an allowance and all. The contract worked flawlessly on the testnet. Verified and all. But when I launched the contract on the mainnet, suddenly I get an error trying to call the deposit function. The gas fee is over 1k in eth and says it may fail. I tried in both solidity and connecting on etherscan w metamask. I looked at this over and over again and I'm not sure why it would work on the testnet but not the mainnet. Here is the contract.. again just a simple deposit / withdraw.

Any help or direction would be appreciated.

Thanks in advance.
J.Coffee

You called "approved" passing a low value. Remember that you need to add the decimals to that amount

I'm not sure what's going on here, but it looks like you've executed something like:

myContract.approve(myContract.address, 100)

At least according to what I'm seeing in the latest transaction on the contract linked in your question.

You can view it yourself by clicking the link above, scrolling down and then clicking 'Click to see More':

Function: approve(address spender,uint256 value)

MethodID: 0x095ea7b3
[0]:  0000000000000000000000006cfdda0a02e70299cbeccd4e6162be4b52b3782c
[1]:  0000000000000000000000000000000000000000000000000000000000000064

As mentioned on a previous answer here, the approved amount is extremely small - 100 (0x64) wei - so do not expect to be able to deposit more than that.

However, this seems like the minor problem here.

The major problem is that the spender's address (the one getting the permission to transfer from your account) seems to be the same as that of the contract whose function approve you are calling.

It might work out fine if this contract happens to be both an ERC20 token and a DEX pool.

But generally speaking, there should be two separate entities, and your transaction flow should look something like this (pseudocode):

  • tokenContract.approve(poolContract.address, amount, {from: yourWallet})
  • poolContract.deposit(amount, {from: yourWallet})

First off, thank you both for responding. This is very interesting. As far as the amount going in, I will just try a larger amount keeping in mind the decimal. <- but shouldn't that amount work anyway or does it need to match the decimal value?

Let me make sure i understand your psedocode correctly:

This (psedocode): Im going to put USDT , ownerWallet and thisContract for clarity.

*** `tokenContract.approve(poolContract.address, amount, {from: yourWallet})`
     USDT.approve(address(this), amount, {from: ownerWallet}
*** `poolContract.deposit(amount, {from: yourWallet})`
     address(this).deposit(amount, {from: ownerWallet}

So the code update would look like this:

function adminDeposit(uint256 _value) external _onlyOwner{    
    require(_value > 0, "Deposit amount must be greater than 0");
    USDT.approve(address(this), _value, {from: ownerWallet};
    (bool sent, ) =  address(this).deposit(_value, {from: ownerWallet});
    require(sent, "Transfer Failed");
    ///require(IERC20(token).transferFrom(msg.sender, address(this), _value), "Transfer Failed");
    balances[address(this)] += _value;
    emit AdminDeposit(address(this), _value);
    } /// deposit

is this what you were referring to?

Last question, so I can understand this correctly. Why would this completely work on the testnet, but not on the mainnet. If my code was wrong, then I would except to the deposit / withdraw function to produce the same error that it would on the mainnet, NO?

This cannot be in executed from onchain context (i.e., from a contract function).
You need to execute it offchain, using your wallet.

Ok. i see. Is this any different then going to the USDT contract and approving it through their Etherscan write contract?

I approved the contract and through connecting my metamask to their contract - in the approve function i put myContract address and the amount. this was the first thing i did after launching the contract.

I now see what your code is saying.

Through web3 - first approve the (USDT) contract to allow this contract to spend this value from the owners wallet..
Then, run the adminDeposit.

thats what i see. Ok. as i said above, (sorry, processing this)... i launched the contract, went over to the usdt contract, connected to their approve function with the owners wallet, put myContract in there with the value and approved it.

then i tried to run the AdminDeposit function from solidity and etherscan, both had that error.


This is on the USDT Contract itself

this was my second step after launching contract -> this is the usdt contract. This is the same as you suggest with the tokenContract.approve(poolContract.address, amount, {from: yourWallet}) - am i correct?

Even with these things done -

  1. on the USDT CONTRACT: approve (this)Contract to spend (Owners) Tokens(USDT) (6 decimals 1000000) ie: Approve(contract, 1000000) using etherscan

  2. on this CONTRACT: I then run adminDeposit(1000000) with owners wallet that has the USDT.

not sure i see why this code is giving an error still? is it because i need to do it all through web3?


////// ADMIN DEPOSIT 

function adminDeposit(uint256 _value) external _onlyOwner{    
    require(_value > 0, "Deposit amount must be greater than 0");
    require(IERC20(token).transferFrom(msg.sender, address(this), _value), "Transfer Failed");
    balances[address(this)] += _value;
    emit AdminDeposit(address(this), _value);
    } /// deposit



///////  ADMINWITHDRAW

function adminWithdraw(uint _value) external _onlyOwner{
    require(IERC20(token).approve(address(this), _value), "Approval Failed");
    require(IERC20(token).transferFrom(address(this), owner, _value), "Transfer Failed");
    balances[address(this)] -= _value;
    emit Admin

emit adminWithdraw(owner, _value);
}

USDT does not adhere to the ERC20 standard. Try using the SafeERC20 which should handle the differences correctly

1 Like

Ooohhh.... now thats something new. I will explore this and get back to you. I left a space to change the token so Let me try to use an ERC20 token and see if that works, then i will update my code for safeTransfer. Lets see how it goes... "After these messages I will be right back..." :slight_smile:

Ok, im back - updating the token to an ERC20 token did not work. I will try to use safetransfer. again, I will be back to let you all know how it went.

before i go forward. Does safeTransfer and safeTransferFrom need extra information to use those functions?

I haven't worked with safeTransfer (as you can see im still learning)
What im thinking is that i need to import the SafeERC20 Library into the contract and then I can call safeTransfer? safeTransfer should do all the rest of the work or do i need to import something more?

Ok would love to get eyes on this before i try again. I did some research and found a video explaining why you should use safeTransfer instead of transfer. Nice to know why its used and what it corrects. Super helpful, thanks for directing me towards safeTransfer.

heres my updated contract - If you wouldn't mind, can you let me know if their might be something out of place or im using it incorrectly.

using SafeERC20 for IERC20;  ///<---  added SafeERC20 Library

address public owner;
IERC20 public token;
mapping(address => uint256) public balances;
mapping(address => mapping (address => uint256)) allowed;

constructor() public {
    owner = msg.sender;
    token = IERC20(0xdAC17F958D2ee523a2206206994597C13D831ec7);  ///  TETHER LIVE
}
////// ADMIN DEPOSIT 

function adminDeposit(uint256 _value) external _onlyOwner{    
    require(_value > 0, "Deposit amount must be greater than 0");
    uint256 allowance =  IERC20(token).allowance(msg.sender, address(this));
    require(allowance >= _value, "Do not have enough allowance");

    uint256 prevBalance = balances[address(this)]; //  added to for TX Failure check
    
    token.safeTransferFrom(msg.sender, address(this), _value); /// Updated
    
    balances[address(this)] += _value;
    require(balances[address(this)] - _value == prevBalance, "Tx Failed");  ///  Check if successful
   
    emit AdminDeposit(address(this), _value);
    } /// deposit
function adminWithdraw(uint _value) external _onlyOwner{
    require(IERC20(token).approve(address(this), _value), "Approval Failed");
    uint256 prevBalance = balances[address(this)];  ///  added for tx failure check

    token.safeTransfer(owner, _value);

    balances[address(this)] -= _value;
    require(balances[address(this)] + _value == prevBalance, "Tx Failed");  /// check if successful
    emit AdminWithdraw(owner, _value);
}

So very helpful - Thank you for the clue on this and direction.

For those that also want to understand the issue with transfer and safeTransfer, this guy explains it pretty well. https://youtu.be/SLbWWmJOF7Q

I want to thank everyone that posted here. I was able to get this contract to work using SafeTransfer. I did my search after all your suggestions and I appreciate the help. For anyone else having the same kind of issue:

I imported the SafeERC20 library into my contract and used it to wrap the transferFrom and approve functions. Check the Youtube video in the earlier post to see why SafeERC20 is used and what issue it solves:

token.safeTransferFrom(msg.sender, address(this), _value);

instead of transferFrom and safeApprove

token.safeApprove( _spender, _value);

Good stuff. Appreciate you all.