Smart Contract - BEP20: transfer amount exceeds allowance - solidity

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.

Related

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.

Burning Deployed ERC Tokens In an NFT Mint Function - Compiles, but Transaction Fails

I am very new to Solidity, and have recently been working on trying to learn the ropes. For reference, I have been using code from this video (https://www.youtube.com/watch?v=tBMk1iZa85Y) as a primer after having gone through the basic crypto zombies tutorial series.
I have been attempting to adapt the Solidity contract code presented in this video (which I had functioning just fine!) to require a Burn of a specified amount of an ERC-20 token before minting an NFT as an exercise for myself. I thought I had what should be a valid implementation which compiled in Remix, and then deployed to Rinkeby. I call the allowAccess function in Remix after deploying to Rinkeby, and that succeeds. But, when I call the mint function with the two parameters, I get: "gas estimation errored with the following message (see below). The transaction execution will likely fail. Do you want to force sending? execution reverted."
If I still send the transaction, metamask yields "Transaction xx failed! Transaction encountered an error.".
I'm positive it has to do with "require(paymentToken.transfer(burnwallet, amounttopay),"transfer Failed");", though I'm not sure what's wrong. Below is my entire contract code. I'm currently just interacting with the Chainlink contract on Rinkeby as my example, since they have a convenient token faucet.
pragma solidity ^0.8.0;
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC721/ERC721.sol";
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/IERC20.sol";
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/access/Ownable.sol";
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/Counters.sol";
contract myNFTwithBurn is ERC721, Ownable {
address externalTokenAddress = 0x01BE23585060835E02B77ef475b0Cc51aA1e0709; //Token Type to burn on minting
uint256 amounttopay = 5; //number of these tokens to burn
IERC20 paymentToken = IERC20(externalTokenAddress); //my code: create an interface of the external token
address burnwallet = 0x000000000000000000000000000000000000dEaD; //burn wallet
using Counters for Counters.Counter;
Counters.Counter private _tokenIds;
using Strings for uint256;
// Optional mapping for token URIs
mapping (uint256 => string) private _tokenURIs;
// Base URI
string private _baseURIextended;
constructor() ERC721("NFTsWithBurn","NWB") {
}
function setBaseURI(string memory baseURI_) external onlyOwner() {
_baseURIextended = baseURI_;
}
function _setTokenURI(uint256 tokenId, string memory _tokenURI) internal virtual {
require(_exists(tokenId), "ERC721Metadata: URI set of nonexistent token");
_tokenURIs[tokenId] = _tokenURI;
}
function _baseURI() internal view virtual override returns (string memory) {
return _baseURIextended;
}
function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token");
string memory _tokenURI = _tokenURIs[tokenId];
string memory base = _baseURI();
// If there is no base URI, return the token URI.
if (bytes(base).length == 0) {
return _tokenURI;
}
// If both are set, concatenate the baseURI and tokenURI (via abi.encodePacked).
if (bytes(_tokenURI).length > 0) {
return string(abi.encodePacked(base, _tokenURI));
}
// If there is a baseURI but no tokenURI, concatenate the tokenID to the baseURI.
return string(abi.encodePacked(base, tokenId.toString()));
}
function allowAccess() public
{
paymentToken.approve(address(this), 5000000); //This is my attempt to allow the contract access to the user's external tokens, in this case Chainlink (paymentToken)
}
function mintItem(address to, string memory tokenURI)
public
onlyOwner
returns (uint256)
{
require(paymentToken.transfer(burnwallet, amounttopay),"transfer Failed"); //Try to transfer 5 chainlink to the burn wallet
_tokenIds.increment();
uint256 id = _tokenIds.current();
_mint(to, id);
_setTokenURI(id, tokenURI);
return id;
}
}
If anybody can at least point me to what I'm doing completely wrong in the code that I've added, please do! TIA!
I'm not sure why are you trying to burn link in order to mint and nft but first check if the link code does not have a require that check if the destination address is the burn address if it has then burn the link is not possible and you should use any other erc20 maybe your own erc20, also your contract probably does not have any link and if you want to transfer the link from the user you should do this in the contract paymentToken.transferFrom(msg.sender,destinationAddress,amount) and if the user previously approve your contract you will able to send the tokens, and i suppose that the purpose of the allowAccess function is to make the user approve the contract to move the tokens that will never work, the approve function let's anyone that call it approve any address to move an amount of tokens, the thing is that to know who is approving to let other to move the tokens the function use msg.sender to explain how this work take a look at this example
let's say that your contract is the contract A and the link contract is the contract B
now a user call allowAccess in the contract A, so here the msg.sender is the user because they call the function
now internally this function call approve on contract B, here the contract A is the msg.sender, because the contract is who call the function
so what allowAccess is really doing is making the contract approving itself to move their own tokens that I assume it doesn't have

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.

How to withdraw all tokens form my bsc contract

I'm really really inexperienced with contracts and need your help. I created a contract with remix and sent some bnb to it. I want to retrieve it but I can't seem to make it happen.
pragma solidity ^0.8;
interface IERC20 {
function transfer(address _to, uint256 _amount) external returns (bool);
}
contract MyContract {
function withdrawToken(address _tokenContract, uint256 _amount) external {
IERC20 tokenContract = IERC20(_tokenContract);
// transfer the token from address of this contract
// to address of the user (executing the withdrawToken() function)
tokenContract.transfer(msg.sender, _amount);
}
}
This is the code that I'm using from another post but I don't understand it. Do I have to change the "_to" "and "_amount" with the numbers or do I just copy the code and compile it?
I'm really sorry but I have no idea what I did so I just want to take the tokens back.
Thanks
Sorry but you can't withdraw your bnb, bnb isn't a token, bnb is like the ether in ethereum, the native chain currency and the contract doesn't have a function to let you withdraw it, the only way is if you send wbnb, in that case you can look the address of the contract of the wbnb and call the function withdraw of the contract you made
As Jhonny said, BNB is not an actual token, and so your implemented logic won't work to withdraw BNB. But just for you to know, you could create a function which only allows withdrawing BNB (which is the native currency). It would be something like this:
function withdraw(uint _amount) external {
payable(msg.sender).transfer(_amount);
}
Hope you find this useful.