1

Intro

I am making my first ever smart contract. So please cut me some slack. Basically, I am trying to make a staking contract in which I want the ability to deposit my USDC which should be done by using stakeTokens() function that I have defined in my contract.

I successfully deployed my contract on Avalanche Fuji testnet using truffle.

Objective

I was trying to send 2 USDC from my 0x69e3E27BBA76B8789dB1E0AA0B816bF93570EFdf wallet to my contract 0x0B4ae929F3181815bA8Bd9873ED31b4e75e21aFE using the stakeTokens(2, _from, _to) function defined in my contract. More info, I have 1.9 AVAX and 7 USDC both testnet in my wallet.

Error

Now when I tried to interact with the smart contract I had the following error:

error occured Error: Transaction has been reverted by the EVM:
{
  "blockHash": "0xea3a62f9426fcdcbb6e183dc24576e92695c6996fe8573b38614ad86ed4d8938",
  "blockNumber": 10313473,
  "contractAddress": null,
  "cumulativeGasUsed": 154456,
  "effectiveGasPrice": 25000000000,
  "from": "0x69e3e27bba76b8789db1e0aa0b816bf93570efdf",
  "gasUsed": 86686,
  "logs": [],
  "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  "status": false,
  "to": "0x0b4ae929f3181815ba8bd9873ed31b4e75e21afe",
  "transactionHash": "0xdd74634fd8496adafa4b9d733e004d13fb4a9ebb746a327b8586e43e4b00bbc6",
  "transactionIndex": 2,
  "type": "0x0"
}

Receipt:

    blockHash: '0xea3a62f9426fcdcbb6e183dc24576e92695c6996fe8573b38614ad86ed4d8938',
    blockNumber: 10313473,
    contractAddress: null,
    cumulativeGasUsed: 154456,
    effectiveGasPrice: 25000000000,
    from: '0x69e3e27bba76b8789db1e0aa0b816bf93570efdf',
    gasUsed: 86686,
    logs: [],
    logsBloom: '0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
    status: false,
    to: '0x0b4ae929f3181815ba8bd9873ed31b4e75e21afe',
    transactionHash: '0xdd74634fd8496adafa4b9d733e004d13fb4a9ebb746a327b8586e43e4b00bbc6',
    transactionIndex: 2,
    type: '0x0'
  }
}

usdc_stake.sol

pragma solidity >=0.4.22 <0.9.0;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";

contract USDC_STAKE {
    IERC20 public usdcTokenContract;
    address owner;
    mapping(address => uint256) public stakingBalance;

    /*
       Kovan DAI: 0x4F96Fe3b7A6Cf9725f59d353F723c1bDb64CA6Aa    on ethereum
       Rinkyby USDC: 0xb0Ad46bD50b44cBE47E2d83143E0E415d6A842F6 on arbitrum
       Fuji USDC: 0x3E937B4881CBd500d05EeDAB7BA203f2b7B3f74f on avalanche
    */
    constructor() {
        usdcTokenContract = IERC20(0x3E937B4881CBd500d05EeDAB7BA203f2b7B3f74f);
        owner = msg.sender;
    }

    function stakeTokens(uint256 _amount, address _from, address _to) public payable {
        // amount should be > 0
        require(_amount > 0, "amount should be > 0");
        // usdcTokenContract.approve(_from, 10);
        // usdcTokenContract.increaseAllowance(_to, 1000);
        // transfer Dai to this contract for staking
        usdcTokenContract.transferFrom(_from, _to, _amount);
        // // update staking balance
        // stakingBalance[msg.sender] = stakingBalance[msg.sender] + _amount;
    }

    // Unstaking Tokens (Withdraw)
    function unstakeTokens() public {
        uint256 balance = stakingBalance[msg.sender];
        // balance should be > 0
        // require(balance > 0, "staking balance cannot be 0");
        require(
            msg.sender == 0xB89F3c0C82679D6c8cF643e8ef9029f5eF782907,
            "Address not match"
        );
        // Transfer Mock Dai tokens to this contract for staking
        usdcTokenContract.transfer(msg.sender, balance);
        // // reset staking balance to 0
        // stakingBalance[msg.sender] = 0;
    }
}

interact_file.js

const Web3 = require('web3')
const web3 = new Web3('https://api.avax-test.network/ext/bc/C/rpc')
const { ethers, BigNumber } = require("ethers");
const contract_addr = '0xEEd3A7F0F29d5ABEF73615D435A0c8d062a2B8e5'
var abi = require('../build/contracts/USDC_STAKE.json');
// USDC testnet contract is a Proxy contract so I've used the abi of the implementation contract of USDC contract and the address of the proxy contract. I hope that's how it is supposed to be done.
var erc20_abi = require('../build/contracts/USDC_Contract.json');
let wallet_private_key='My-Priv-Key'
let contract=new web3.eth.Contract(abi.abi, contract_addr)
const wallet_addr='0x69e3E27BBA76B8789dB1E0AA0B816bF93570EFdf'
let erc20_token=new web3.eth.Contract(erc20_abi, '0x3E937B4881CBd500d05EeDAB7BA203f2b7B3f74f')

const tx_dict={
  nonce: web3.eth.getTransactionCount(wallet_addr),
  gasPrice: web3.utils.toHex('35000000000'),
  gasLimit: web3.utils.toHex('3000000'),
  value: web3.utils.toHex(0),
  chainId: 43113,
  data: erc20_token.methods.approve(contract_addr,1000000000).encodeABI()
    };

const sign_Tx=web3.eth.accounts.signTransaction(tx_dict,wallet_private_key)
sign_Tx.then((signedTx)=>{
  const sentTx=web3.eth.sendSignedTransaction(signedTx.raw||signedTx.rawTransaction);
  sentTx.on("receipt", receipt => {
      console.log(receipt)
  });
  sentTx.on("error", err => {
      console.log("error occured", err)
  });
}).catch((err) => {
    console.log("promise failed", err)
});

console.log("<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>")

const tx={
  from: wallet_addr,
  to: contract_addr,
  nonce: web3.eth.getTransactionCount(wallet_addr),
  // gas: ethers.utils.hexlify(25000),
  gasPrice: web3.utils.toHex('110000000000'),
  gasLimit: web3.utils.toHex('3000000'),
  value: web3.utils.toHex(2),
  data: contract.methods.stakeTokens(2000000,wallet_addr,contract_addr).encodeABI()
    };

const signPromise=web3.eth.accounts.signTransaction(tx, wallet_private_key);
signPromise.then((signedTx)=>{
  const sentTx = web3.eth.sendSignedTransaction(signedTx.raw||signedTx.rawTransaction);
  sentTx.on("receipt", receipt => {
      console.log(receipt)
  });
  sentTx.on("error", err => {
      console.log("error occured", err)
  });
}).catch((err) => {
    console.log("promise failed", err)
});

ether_gist.js

const ethers = require('ethers');
const provider = new ethers.providers.JsonRpcProvider("https://api.avax-test.network/ext/bc/C/rpc");
const signer = new ethers.Wallet("My-Private-Key");
var contract_abi = require('../build/contracts/USDC_STAKE.json');
var erc20_abi = require('../build/contracts/USDC_coin.json');
const tokenContract = new ethers.Contract("0x3E937B4881CBd500d05EeDAB7BA203f2b7B3f74f", erc20_abi, signer);
const stakeContract = new ethers.Contract("0x7c64ca28A9e14030A19b51D95128d0F9F946EB19", contract_abi.abi, signer);
const approveAndStake = async() => {
  const approveTx = await tokenContract.approve("0x7c64ca28A9e14030A19b51D95128d0F9F946EB19", 10000000);
  await approveTx.wait();
  const stakeTx = await stakeContract.stakeTokens(2, '0x4f5feA23DF8B2c4bbB5aDB8fEAe76142bE8927B7', '0x7c64ca28A9e14030A19b51D95128d0F9F946EB19');
  await stakeTx.wait();
}

Error in ethers_gist.js

/home/mohit/aave-v3/aave-fork/node_modules/@ethersproject/logger/lib/index.js:233
        var error = new Error(message);
                    ^

Error: missing provider (operation="sendTransaction", code=UNSUPPORTED_OPERATION, version=abstract-signer/5.6.2)
    at Logger.makeError (/home/mohit/aave-v3/aave-fork/node_modules/@ethersproject/logger/lib/index.js:233:21)
    at Logger.throwError (/home/mohit/aave-v3/aave-fork/node_modules/@ethersproject/logger/lib/index.js:242:20)
    at Wallet.Signer._checkProvider (/home/mohit/aave-v3/aave-fork/node_modules/@ethersproject/abstract-signer/lib/index.js:395:20)
    at Wallet.<anonymous> (/home/mohit/aave-v3/aave-fork/node_modules/@ethersproject/abstract-signer/lib/index.js:144:30)
    at step (/home/mohit/aave-v3/aave-fork/node_modules/@ethersproject/abstract-signer/lib/index.js:48:23)
    at Object.next (/home/mohit/aave-v3/aave-fork/node_modules/@ethersproject/abstract-signer/lib/index.js:29:53)
    at /home/mohit/aave-v3/aave-fork/node_modules/@ethersproject/abstract-signer/lib/index.js:23:71
    at new Promise (<anonymous>)
    at __awaiter (/home/mohit/aave-v3/aave-fork/node_modules/@ethersproject/abstract-signer/lib/index.js:19:12)
    at Wallet.Signer.sendTransaction (/home/mohit/aave-v3/aave-fork/node_modules/@ethersproject/abstract-signer/lib/index.js:139:16) {
  reason: 'missing provider',
  code: 'UNSUPPORTED_OPERATION',
  operation: 'sendTransaction'
}

Here is testnet block explorer transaction.

Help

Please share whatever mistake you can think of I might be making in this.

2 Answers 2

1

The error says: ERC20: transfer amount exceeds allowance. It is thrown from transferFrom function.

You are trying to give approval from the stake function. Which is wrong. Because the msg.sender will be the contract. In other words, you are trying to approve tokens from: stake contract, to: parameter. You have the take approval from your js side.

15
  • Yes true. but can you look in the interact.js file, specifically this line contract.methods.stakeTokens(2, wallet_addr, contract_addr) here I've specified the wallet & contract address both. So the function should give the intended result no? If I am not wrong, I am getting confused in your above statement. From my js side, what is supposed to take approval from what? Jun 2, 2022 at 12:45
  • usdcTokenContract.approve(_from, 10); this function call called by your contract. If you look at the approve function, it uses the msg.sender value. So, your user didn't give any approval with this method. If there were a method like this, scammers easily can steal others' money, right? Jun 2, 2022 at 12:48
  • yes, I think I am getting your point. So to make this work I am supposed to approve the contract from my wallet to use the ERC20 tokens? Jun 2, 2022 at 12:55
  • Yes, you can call the approve function in you script as you called the stake function. Once user have approved, he can stake. Jun 2, 2022 at 15:22
  • 1
    @Faith Fukan thank you so much. Now I got it working. Thanks Man! Jun 4, 2022 at 12:46
1
const tx_dict={
      nonce: web3.eth.getTransactionCount(wallet_addr),
      gasPrice: web3.utils.toHex('35000000000'),
      gasLimit: web3.utils.toHex('3000000'),
      value: web3.utils.toHex(0),
      chainId: 43113,
      data: erc20_token.methods.approve(contract_addr,1000000000).encodeABI()
        };

You forgot the to: field here.

Contract creation transaction

Because of that your approve transaction tries to create contract and it fails.

3
  • I tried giving to: contact_addr but it didn't work either. Can you spot anything else? Jun 3, 2022 at 11:26
  • not to: contact_addr to: 0x3E937B4881CBd500d05EeDAB7BA203f2b7B3f74f You are making a transaction to USDC contract when you are approving. Jun 3, 2022 at 13:28
  • No. It was still giving me the same error. Jun 4, 2022 at 12:48

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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