Allowance is returned zero even after approval - solidity

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.

Related

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

Can a solidity smart contract execute the approve function in an erc20 contract on behalf of the msg.sender

Setting up a bank contract to perform deposits and withdraws. I was wondering if its possible for the bank contract can execute the approve function in the erc20 contract on behalf of the msg.sender for the tokens they are wanting to deposit.
Below is my attempt for the bank contract to call the erc20 token contracts approve function. However wouldn't the msg.sender be the bank contract address instead of the original msg.sender (second line of the depositToken function.) This sounds silly but is there a way for the contract to send the request passing in the msg.senders address? If not is there an integrated way for the msg.sender to approve the bank contract address and the amount to enable the bank contract to call the transferFrom function and be provided the allowance.
//best guess on what that would look like inside the function depositTokens
msg.sender = customer;
customer.IER20(usdt).approve.address(this), uint _amount;
address public usdt;
mapping(address => uint) public bankBalance;
constructor() public {
usdt = 0x77c24f0Af71257C0ee26e0E0a108F940D1698d53;
}
usdt = 0x77c24f0Af71257C0ee26e0E0a108F940D1698d53;
function depositTokens(uint _amount) public {
IER20(usdt).approve.address(this), uint _amount;
// Trasnfer usdt tokens to contract
IERC20(usdt).transferFrom(msg.sender, address(this), _amount);
// Update the bank balance in map
bankBalance[msg.sender] = bankBalance[msg.sender] + _amount;
}
//approve function in erc20
function approve(address delegate, uint256 numTokens) public override returns (bool) {
allowed[msg.sender][delegate] = numTokens;
emit Approval(msg.sender, delegate, numTokens);
return true;
The answer is no, the approve function takes the msg.sender as the one that gives allowance. You can see the most used OpenZeppelin implementation here.
However I can see what you are trying to do and I want to add that there is a way to automatically ask an EOA (externally owned account) or a user with a wallet to approve some tokens to the contract before sending a transaction.
I don't know if a wallet does it automatically or you have to code it in the UI but I think that is what you are looking for.
And then after the user already approved your bank, you can remove your first line in the deposit function

Solidity: transferFrom can't reach allowance value

Here are the steps I follow on Remix:
Deploy my ERC20Basic token and my DEX contract.
DEX.buy > Buy ERC20Basic tokens in exchange of ETH (works fine).
ERC20Basic.approve(contractAddress, tokenAmount) (works fine).
ERC20Basic.allowance > Works fine, function returns the amount of the allowance.
The problem comes when I try to sell tokens with this function from my DEX contract:
function sell(uint256 amount) public {
uint256 allowance = token.allowance(msg.sender, address(this));
require(allowance >= amount, "Check the token allowance");
token.transferFrom(msg.sender, address(this), amount);
payable(msg.sender).transfer(amount);
emit Sold(amount);
}
I still get "Check the token allowance".
When I log the allowance I get 0.
When I log msg.sender and address(this), I get the same addresses I used on the Remix interface to get the value of the allowance manually.
It feels like the allowance is reset to 0 when I call the sell function or that the sell function can't reach the value of the allowance. Maybe this is something about memory and storage?
My allowance function inside of ERC20Basic contract is:
function allowance(address owner, address delegate)
public
view
override
returns (uint256)
{
return allowed[owner][delegate];
}
Approval function:
function approve(address delegate, uint256 numTokens)
public
override
returns (bool)
{
allowed[msg.sender][delegate] = numTokens;
emit Approval(msg.sender, delegate, numTokens);
return true;
}
Allowance structure:
mapping(address => mapping(address => uint256)) allowed;
How are you calling the approve function?
In your code you aren't passing the require statement because your approval wasn't successful. If it was your allowance wouldn't be 0.
You are probably calling the approve function from the contract which won't work because then the msg.sender would be address of the contract not your accounts address.
You have to approve the amount manually outside of the contract for it to work.

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

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.

BEP20 Tokens are not being swapped for BNB and sent to charity wallet

I'm trying to create a bep20-token where a percentage of the transaction volume is converted into BNB, and subsequently sent to a designated charity wallet to store the BNB for future donations. Another feature of this token is that a another part of the transaction is also swapped for BNB and locked together with the token in the liquidity pool. This last feature is working, however, the wallet I set up as the charity wallet is not receiving BNB. I have already set up a liquidity pool with the test tokens and minor amount of BNB thats how I learned its not doing what it was designed for. This token is a fork of YUMMY and the complete contract can be viewed here: https://bscscan.com/address/0x05f2df7b3d612a23fe12162a6c996447dce728a5#code
function sendBNBToCharity(uint256 amount) private {
swapTokensForEth(amount);
_charityWalletAddress.transfer(address(this).balance);
}
function _setCharityWallet(address payable charityWalletAddress) external onlyOwner() {
_charityWalletAddress = charityWalletAddress;
}
function swapAndLiquify(uint256 contractTokenBalance) private lockTheSwap {
// split the contract balance into thirds
uint256 halfOfLiquify = contractTokenBalance.div(4);
uint256 otherHalfOfLiquify = contractTokenBalance.div(4);
uint256 portionForFees = contractTokenBalance.sub(halfOfLiquify).sub(otherHalfOfLiquify);
// capture the contract's current ETH balance.
// this is so that we can capture exactly the amount of ETH that the
// swap creates, and not make the liquidity event include any ETH that
// has been manually sent to the contract
uint256 initialBalance = address(this).balance;
// swap tokens for ETH
swapTokensForEth(halfOfLiquify); // <- this breaks the ETH -> HATE swap when swap+liquify is triggered
swapTokensForEth(portionForFees);
// how much ETH did we just swap into?
uint256 newBalance = address(this).balance.sub(initialBalance);
// add liquidity to uniswap
addLiquidity(otherHalfOfLiquify, newBalance);
sendBNBToCharity(portionForFees);
emit SwapAndLiquify(halfOfLiquify, newBalance, otherHalfOfLiquify);
}
function swapTokensForEth(uint256 tokenAmount) private {
// generate the uniswap pair path of token -> weth
address[] memory path = new address[](2);
path[0] = address(this);
path[1] = uniswapV2Router.WETH();
_approve(address(this), address(uniswapV2Router), tokenAmount);
// make the swap
uniswapV2Router.swapExactTokensForETHSupportingFeeOnTransferTokens(
tokenAmount,
0, // accept any amount of ETH
path,
address(this),
block.timestamp
);
}
Your swapTokensForEth function is swapping to WETH, not to ETH. Wrapped ETH is a coin that holds ETH and makes it act like an ERC20 token, so that it can be used on Uniswap and other place.
To get ETH out of your WETH, you'll need call WETH.withdraw(amount), which will convert your WETH back to ETH.