Hi all, I am trying to develop a DAPP that accepts the staking of USDT; however, I seem to be running into an error.
I have accounted for the fact that USDT is a 6 decimal token so I am not sure what the error is being caused by.
My smart contract code:
// SPDX-License-Identifier: MIT
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
pragma solidity ^0.8.0;
contract TokenFarm is Ownable, ReentrancyGuard {
mapping(address => mapping(address => uint256)) public stakingBalance;
mapping(address => uint256) public uniqueTokensStaked;
mapping(address => address) public tokenPriceFeedMapping;
address[] public stakers;
address[] public allowedTokens;
IERC20 public timeToken;
uint256 public totalRaised;
uint256 public goal;
enum FUND_STATE {
OPEN,
REFUND,
CLOSED
}
FUND_STATE public fund_state;
constructor(address _timeTokenAddress) public {
timeToken = IERC20(_timeTokenAddress);
fund_state = FUND_STATE.OPEN;
goal = 175000000000000000000000;
}
function stateOpen() public onlyOwner {
fund_state = FUND_STATE.OPEN;
}
function stateRefund() public onlyOwner {
fund_state = FUND_STATE.REFUND;
}
function stateClosed() public onlyOwner {
fund_state = FUND_STATE.CLOSED;
}
function setPriceFeedContract(address _token, address _priceFeed)
public
onlyOwner
{
tokenPriceFeedMapping[_token] = _priceFeed;
}
function goalAmount() public onlyOwner returns (uint256) {
return goal;
}
function totalERCRaised() public onlyOwner returns (uint256) {
return totalRaised;
}
function getTokenValue(address _token)
public
view
returns (uint256, uint256)
{
address priceFeedAddress = tokenPriceFeedMapping[_token];
AggregatorV3Interface priceFeed = AggregatorV3Interface(
priceFeedAddress
);
(, int256 price, , , ) = priceFeed.latestRoundData();
uint256 decimals = uint256(priceFeed.decimals());
return (uint256(price), decimals);
}
function stakeTokens(uint256 _amount, address _token) public nonReentrant {
require(fund_state == FUND_STATE.OPEN);
require(_amount > 0, "Amount must be more than zero!");
require(tokenIsAllowed(_token), "Token is currently not allowed!");
(uint256 price, uint256 decimals) = getTokenValue(_token);
require(
totalRaised + ((_amount * price) / ((10**decimals))) < goal,
"Too much money"
);
uint256 _amount2 = ((_amount * (10**20)) / (10**decimals));
IERC20(_token).transferFrom(msg.sender, address(this), _amount);
updateUniqueTokensStaked(msg.sender, _token);
if (
address(_token) ==
address(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48)
) {
stakingBalance[_token][msg.sender] =
stakingBalance[_token][msg.sender] +
_amount2;
totalRaised = uint256(
totalRaised + (((_amount2 * price)) / ((10**decimals)))
);
}
if (
address(_token) ==
address(0xdAC17F958D2ee523a2206206994597C13D831ec7)
) {
stakingBalance[_token][msg.sender] =
stakingBalance[_token][msg.sender] +
_amount2;
totalRaised = uint256(
totalRaised + (((_amount2 * price)) / ((10**decimals)))
);
}
if (
address(_token) ==
address(0x6B175474E89094C44Da98b954EedeAC495271d0F)
) {
stakingBalance[_token][msg.sender] =
stakingBalance[_token][msg.sender] +
_amount;
totalRaised = uint256(
totalRaised + (((_amount * price)) / ((10**decimals)))
);
}
if (
address(_token) ==
address(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2)
) {
stakingBalance[_token][msg.sender] =
stakingBalance[_token][msg.sender] +
_amount;
totalRaised = uint256(
totalRaised + (((_amount * price)) / ((10**decimals)))
);
}
if (uniqueTokensStaked[msg.sender] == 1) {
stakers.push(msg.sender);
}
}
function issueTokens() public onlyOwner nonReentrant {
require(fund_state == FUND_STATE.OPEN);
for (
uint256 stakersIndex = 0;
stakersIndex < stakers.length;
stakersIndex++
) {
address recipient = stakers[stakersIndex];
uint256 userTotalValue = getUserTotalValue(recipient);
timeToken.transfer(recipient, userTotalValue);
}
}
function claimRefund(address _token) public nonReentrant {
require(fund_state == FUND_STATE.REFUND);
uint256 balance = stakingBalance[_token][msg.sender];
require(balance > 0, "Staking balance cannot be zero!");
IERC20(_token).transfer(msg.sender, balance);
stakingBalance[_token][msg.sender] = 0;
uniqueTokensStaked[msg.sender] = uniqueTokensStaked[msg.sender] - 1;
}
function getUserTotalValue(address _user) public view returns (uint256) {
uint256 totalValue = 0;
require(uniqueTokensStaked[_user] > 0, "No tokens staked!");
for (
uint256 allowedTokensIndex = 0;
allowedTokensIndex < allowedTokens.length;
allowedTokensIndex++
) {
totalValue =
totalValue +
getUserSingleTokenValue(
_user,
allowedTokens[allowedTokensIndex]
);
}
return totalValue;
}
function getUserSingleTokenValue(address _user, address _token)
public
view
returns (uint256)
{
if (uniqueTokensStaked[_user] <= 0) {
return 0;
}
(uint256 price, uint256 decimals) = getTokenValue(_token);
return ((stakingBalance[_token][_user] * price) / (10**decimals));
}
function withdrawToken(address _token, uint256 _amount)
external
onlyOwner
nonReentrant
{
IERC20 token = IERC20(_token);
token.transfer(msg.sender, _amount);
}
function updateUniqueTokensStaked(address _user, address _token) internal {
if (stakingBalance[_token][_user] <= 0) {
uniqueTokensStaked[_user] = uniqueTokensStaked[_user] + 1;
}
}
function addAllowedTokens(address _token) public onlyOwner {
allowedTokens.push(_token);
}
function tokenIsAllowed(address _token) public view returns (bool) {
for (
uint256 allowedTokensIndex = 0;
allowedTokensIndex < allowedTokens.length;
allowedTokensIndex++
) {
if (allowedTokens[allowedTokensIndex] == _token) {
return true;
}
}
return false;
}
}
So the error that I am getting on the front-end looks like this:
And my relevant front-end code is here:
import { useEffect, useState } from "react"
import { useEthers, useContractFunction, Kovan } from "@usedapp/core"
import { constants, utils } from "ethers"
import TokenFarm from "../utils/TokenFarm.json"
import ERC20 from "../utils/MockERC20.json"
import { Contract } from "@ethersproject/contracts"
import networkMapping from "../utils/constants.json"
export const useStakeTokens = (tokenAddress: string) => {
const { chainId } = useEthers()
console.log({ chainId })
const { abi } = TokenFarm
const tokenFarmAddress = chainId ? networkMapping[String(chainId)]["TokenFarm"][0] : constants.AddressZero
console.log(tokenFarmAddress)
const tokenFarmInterface = new utils.Interface(abi)
const tokenFarmContract = new Contract(tokenFarmAddress, tokenFarmInterface)
const erc20ABI = ERC20.abi
const erc20Interface = new utils.Interface(erc20ABI)
const erc20Contract = new Contract(tokenAddress, erc20Interface)
const { send: approveErc20Send, state: approveAndStakeErc20State } =
useContractFunction(erc20Contract, "approve", {
transactionName: "Approve ERC20 transfer",
})
const approveAndStake = (amount: string) => {
setAmountToStake(amount)
return approveErc20Send(tokenFarmAddress, amount)
}
const { send: stakeSend, state: stakeState } =
useContractFunction(tokenFarmContract, "stakeTokens", {
transactionName: "Stake Tokens",
})
const [amountToStake, setAmountToStake] = useState("0")
useEffect(() => {
if (approveAndStakeErc20State.status === "Success") {
stakeSend(amountToStake, tokenAddress);
}
}, [approveAndStakeErc20State])
const [state, setState] = useState(approveAndStakeErc20State)
useEffect(() => {
if (approveAndStakeErc20State.status === "Success") {
setState(stakeState)
} else {
setState(approveAndStakeErc20State)
}
}, [approveAndStakeErc20State, stakeState])
return { approveAndStake, approveErc20Send, approveAndStakeErc20State, state }
}
I would very much appreciate any and all help - really trying to get past this last step in my smart contract development for my project!
Cheers