"revert ERC20: transfer amount exceeds allowance" error when transferring from a smart contract - solidity

I have a smart contract named "Manager" with a "claimRewards" method, that will be called by users and should transfer to them some ERC20 tokens. The ERC20 is another smart contract named "rdk".
function claimRewards() public {
uint256 howMany = calculateRewards(msg.sender);
if (howMany > 0) {
// does the contract wallet have tokens enough?
uint256 walletBalance = rdk.balanceOf(address(this));
emit Log(walletBalance, howMany);
require (walletBalance >= howMany, "empty wallet");
// transfer tokens from contract wallet to player
rdk.transferFrom(address(this), msg.sender, howMany);
}
}
The ERC20 tokens are in the smart contract wallet. The event "Log" shows these values:
Manager.Log(
balance: 101000 (type: uint256),
howMany: 9 (type: uint256)
)
So there are 101000 tokens on the smart contract wallet, and it has to transfer 9 to msg.sender
Before calling the method "claimRewards" I approve the smart contract to spend 100000 of those tokens:
it("Should transfer tokens from operator for rewards ", async function () {
await token.approve(manager.address, 100000, { from: accounts[1] });
await token.transfer(manager.address, 100000, { from: accounts[1] });
});
But when running my tests on Truffle, I got the error:
Error: Returned error: VM Exception while processing transaction: revert ERC20: transfer amount exceeds allowance -- Reason given: ERC20: transfer amount exceeds allowance.
I cannot understand if the smart contract "Manager" is the owner of 100000 RDK tokens, and even has been approved for spending 100000 RDK tokens, why is showing that error when transferring 9 of those tokens to the caller of the method.
Anyone could help me with this issue?

await token.approve(manager.address, 100000, { from: accounts[1] });
This JS snippet approves the Manager to spend accounts[1]'s tokens.
rdk.transferFrom(address(this), msg.sender, howMany);
But this Solidity snippet checks for approval from the Manager address (effectively the msg.sender in the rdk contract) to the same Manager address (1st argument of address(this)).
If you want to transfer tokens from the Manager contract, you can simply invoke the token's transfer() function in the Manager contract.
// transfer `howMany` of `rdk` tokens
// from the `Manager` address
// to the user invoking the `claimRewards()` function
rdk.transfer(msg.sender, howMany);
This will not check the allowance but only the sender (Manager) balance.

Related

Allowance is returned zero even after approval

I was unit testing my contract and within the tests i approved that the smart contract should be able to spend the tokens of the msg.sender (the owner) and then tried to transferFrom function to transfer the tokens from the msg.sender to the contract. However, the first obstacle i faced was that the allowance is returned as zero when i called the transferFrom function. Now i was able to relearn that the transferFrom function should be another transaction for it to be able to work and i console loged all the variables making sure that the allowance is being changed when calling the approve function. However when i recall the transferFrom the error persists and shows me Insufficient balance!
`
This is my deposit function where the transferFrom is called only if the approval was a success
/**
* #notice addEth is a user function where the user chooses the pool and sends eth to it, funding it
* #param depositAmount is the eth being sent from the user to the pool
* #param poolId_ is the address of the pool being chosen by the user
*/
function deposit(uint256 depositAmount, uint256 poolId_) public{
// Check if the pool is either closed or paused or exists
Pool storage p = checkIfPoolExistsOrClosed(poolId_);
// Make sure the eth being sent is not equal to zero
if (depositAmount <= 0) revert WrongAmount();
// Check if the pool is empty, if it is the price of token is 1
if(p.valueInPool == 0) {
tokensForUser = depositAmount;
}else {
// Get the amount of tokens to be minted to the user
tokensForUser = (depositAmount /
(p.valueInPool/
IProperSubsetERC20(p.poolTokenAddress).totalSupply()));
}
// check if the approval was a success
if(!contractIsApproved[msg.sender]) revert ApprovalFailed();
// Send the USDC tokens to the fund contract
bool transfer = IProperSubsetUSDC(usdcAddress).transferFrom(msg.sender, address(this), depositAmount);
// Send the USDC tokens to the fund contract
// (bool success,)=usdcAddress.delegatecall(abi.encodeWithSignature('transfer(address,uint256)', address(this), depositAmount));
// Call the ERC20 contract to mint tokens to user
IProperSubsetERC20(p.poolTokenAddress).mint(msg.sender, tokensForUser);
// Update the amount of liquidity in the pool
p.valueInPool = p.valueInPool + depositAmount;
// Emit event after adding eth to pool
emit Deposit(msg.sender, poolId_, depositAmount);
}
This is my approval function where i call the approve function to add an allowance
/**
* #notice function to approve contract to spend the user's USDC tokens
* #param amount of usdt willing to give the contract approval for spending
*/
function approveUser(uint256 amount) public returns(bool){
// Approve spending the msg.sender tokens
(bool success,) = usdcAddress.delegatecall(abi.encodeWithSignature('approve(address,uint256)', address(this), amount));
// If the approve function is succesfull we update the map to show that this address is approved
if(success){
contractIsApproved[msg.sender] = true;
}
// Return if the function is successfull
return success;
}
`
Now these are the tests where the approve and the transferFrom function are called
it("Try approving for user 1 the transfer of their tokens from the contract", async function() {
await deployFunds.connect(signers[1]).approveUser(1000);
})
it("Try depositing and the price of the pool tokens should be equal to one since it is new", async function() {
const tx = await deployFunds.connect(signers[1]).deposit(1000, 2);
const receipt = await tx.wait();
filter = receipt.events?.filter((x) => {return x.event == "Deposit"});
poolId = filter.length > 0 ? filter[0].args[1] : '0x000';
tokensForUser = filter.length > 0 ? filter[0].args[2]: "0";
mintedTokens = await deployERC20.balanceOf(user1);
expect(filter.length).above(0);
expect(poolId).to.equal(2);
expect(tokensForUser).to.equal(1000);
})
I tried console logging all the variables being changed in the approve function and everything was checking out but when i console log the allowance when calling the transferFrom is is printed as zero. I then tried to put the approve function and the transferFrom in each transaction alone and still the error persists as Insufficient allowance
(bool success,) = usdcAddress.delegatecall(abi.encodeWithSignature('approve(address,uint256)', address(this), amount));
Delegatecall stores the state changes (updates a storage slot) in your contract - not in the usdcAddress contract.
In order to get approval from the user, they need to send a separate transaction executing the approve() function directly on the usdcAddress contract - not through any other contract.

How can I transfer eth from an account wallet to a smart contract

I'm creating a smart contract that allows people to pay for a monthly subscription
I got stacked in this :
how to transfer plan.amount from the user wallet to the smart contract?
function subscribe(uint planId) external {
Plan storage plan = plans[planId];
require(plan.merchant != address(0), 'address not valid');
bool sent = payable(address(this)).send(plan.amount);
require(sent, "tx failed");
emit PaymentSent(
msg.sender,
plan.merchant,
plan.amount, // the monthly amount for Subscription
planId,
block.timestamp
);
subscriptions[msg.sender][planId] = Subscription(
msg.sender,
block.timestamp,
block.timestamp + 4 weeks // next payement
);
emit SubscriptionCreated(msg.sender, planId, block.timestamp);
}
The subscribe() function needs to use the payable modifier in order to accept ETH. Then you can validate how much the user has sent while invoking your function with the msg.value global variable.
It's not possible to request a specific amount from the contract, as the contract code is executed after the user has sent the transaction. You always need to validate how much has the user sent along with the transaction invoking the function.
function subscribe(uint planId) external payable {
// revert if the sent value is not expected
require(msg.value == 1 ether, "You need to send 1 ETH");
}
However you can control the predefined value on the UI, while creating the transaction request to their MetaMask or other wallet.
await window.ethereum.request(
method: 'eth_sendTransaction',
[
from: userAddress,
to: yourContract,
data: <invoking the subscribe() function>,
value: <1 ETH in wei, in hex>
]
);
Docs: https://docs.metamask.io/guide/ethereum-provider.html#ethereum-request-args

How to use ERC20 token to transfer eth in solidity?

I'm writing a defi project to transfer between an ERC20 token and eth. I want to develop a function that accept the ERC20 token from an address then send eth to that address.
What I deal with that ERC20 token was to generate the token in the smart contract and send the token to the user. The problem is that, if user A send some ERC20 token to user B, what should I do to allow user B use the token to ask for eth in my smart contract?
Another simple question is that, how to ask the user to use their wallet (e.g. metamask) to transfer ERC20 token to my smart contract?
Using the payable you would be able to transfer the native token (eth) from your contract into any user's wallet:
function transferEth(address payable _to, uint _amount) public {
(bool success, ) = _to.call{value: _amount}("");
require(success, "Failed to send Ether");
}
Notice that this function above is not safe as any one can call the transfer to your contract, please consider to use the modifier to prevent it.
In your case I can think of a function like:
function claimEth() public {
if (balanceOf(msg.sender) > 100) {
balances[msg.sender] = balances[msg.sender[.sub(100);
transferEth(msg.sender, 5);
}
}

send ERC-20 tokens with solidity to user

as you know, ERC-20 network has many tokens like USDT, SHIB, LINK, . . .
I want to create a website , when user need to buy USDT token I should send the USDT token in his wallet or use need to send USDT to another wallet on the blockchain and I want do all of these things into the blockchain, and the user could see the detail of his USDT transaction :
USDT Transactions
now I have a big question about transfer tokens in ERC-20 network.
i write this codes in remix and solidity :
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;
import 'https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/IERC20.sol';
contract DEX {
struct Token {
string token;
address contractAddress;
}
mapping(string => Token) public tokens;
string[] public tokenLis;
address public admin;
constructor() {
admin = msg.sender;
}
function addToken(string memory token, address contractAddress) external {
tokens[token] = Token(ticker,contractAddress);
tokenLis.push(ticker);
}
function getTokenAddress(string memory token) external view returns(address moemory) {
return tokens[token].contractAddress;
}
function sendToken(string memory token, address from , uint256 amount)
external
{
IERC20(tokens[token].contractAddress).transferFrom(
from,
address(this),
amount
);
}
}
I want to add dynamically tokens to my website and smart contract, for this write this codes :
struct Token {
string token;
address contractAddress;
}
mapping(string => Token) public tokens;
function addToken(string memory token, address contractAddress) external {
tokens[token] = Token(ticker,contractAddress);
tokenLis.push(ticker);
}
I called addToken with this info :
Token : USDT
contractAddress : 0xdac17f958d2ee523a2206206994597c13d831ec7 ( USDT mainnet contract address )
it's work and add success USDT in tokens .
now I want to send some USDT to the user with this function ( imported the openzepelin IERC20) :
function sendToken(string memory ticker , address from , uint256 amount)
external
{
IERC20(tokens[ticker].contractAddress).transferFrom(
from,
address(this),
amount
);
}
now when I want to transfer amount from remix address one to remixaddress to into the USDT contract address it show me this error :
What is the problem? how can I solve this error?
Your contract is on a different network (Remix VM emulator) than the USDT contract (Ethereum mainnet), so they cannot interact. You can fork the mainnet and connect to the forked network from your Remix IDE, see this answer for more details.
Then you're probably going to run into another issue within the sendToken() function (since you don't cover it in your question, I'm assuming you haven't performed this step). In order to successfully invoke transferFrom(), the function caller (in this case your contract address) needs to be approved by the token owner (in this case the from variable) to spend their tokens. You can find more info in the ERC-20 specification to the approve() function.
Summary: The from address needs to invoke the approve() method on the token contract directly (not through your or any other contract), passing it your contract address as the first param, so that your contract is able to pull their tokens.

Smart Contract - BEP20: transfer amount exceeds allowance

I'm new in solidity and I'm trying to swap tokens from "Address A" to "Address B".
I used the functions approve and transferFrom, but I'm still getting the error: "Error: VM Exception while processing transaction: reverted with reason string 'BEP20: transfer amount exceeds allowance'"
Could you please help me with this issue?
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity ^0.8.3;
import "./CryptoPlinkoBall.sol";
import "./CryptoPlinko.sol";
import "hardhat/console.sol";
contract TokenSwap {
address admin;
address public owner;
address private _token;
constructor(address token) {
admin = msg.sender;
_token = token;
}
function swapTokens(address recipient, uint256 amount) external {
BEP20(_token).approve(msg.sender, amount);
BEP20(_token).allowance(msg.sender, address(this));
BEP20(_token).transferFrom(msg.sender, recipient, amount);
}
}
When you call BEP20(_token).approve(msg.sender, amount); you are approving the user to move that amount of tokens that the contract owns if you want to transfer the tokens from the user, the user should have called the token contract and approved the amount before calling this function, if you are doing the frontend that will interact with the contract you will need to put the call to the token contract first then the call to this contract
The approve must be mined before the transferFrom gets called.You can't do both on the same call, meanning the approve should occur before going into the swapTokens function.