I am trying to implement a simple token transfer to a Vault but I'm having trouble approving the transaction and when I run tests using foundry, I receive this error:
[FAIL. Reason: ERC20: transfer amount exceeds allowance] testDeposit() (gas: 86770)
My code is for the deposit function is here:
function deposit(uint256 amount) external {
console.log("RANDOM inside deposit = ");
console.log(IERC20(underlyingToken).balanceOf(msg.sender));
console.log("msg sender =");
console.log(msg.sender);
console.log("approve = ");
console.log(IERC20(underlyingToken).approve(address(this), amount));
// IERC20(underlyingToken).approve(msg.sender, amount);
console.log("RANDOM inside deposit after approve = ");
console.log(IERC20(underlyingToken).allowance(msg.sender, address(this)));
IERC20(underlyingToken).transferFrom(msg.sender, address(this), amount);
// // totalDeposited += amount;
IPool(aavePool).supply(underlyingToken, amount, address(this), 0);
totalUnderlyingDeposited += amount;
}
Thank you for the help in advance
You can't have the vault give itself an allowance for the sender. That would defeat the whole point of the approval mechanism.
What your code IERC20(underlyingToken).approve(address(this), amount) actually does is give the vault permission to transfer any of its own tokens using transferFrom. Obviously this is a bit silly since the vault can just use transfer to do that.
Your commented-out code // IERC20(underlyingToken).approve(msg.sender, amount);, as you probably figured out, lets the sender transfer the vault's tokens.
The only way to let the vault do transferFrom(msg.sender, ..., ...) is if the sender interacts directly with the ERC20 by calling approve him/herself.
This means the user will need to do two transactions to do the first deposit into the vault: 1) approve the vault for an allowance sufficient to cover the deposit 2) do the deposit.
If the approval is for an "infinite" amount (max uint256), then subsequent deposits only require a single transaction each; however, this is not considered wise from a security standpoint.
Related
In below function, how the contract balance is updated by just assigning the msg.value into deposit parameterized variable?
function transferToContract(uint256 deposit) public payable {
require(msg.value == deposit);
deposit = msg.value; //how it works?
}
I'm trying to find answer about behind the mechanics of the above code.And I'm expecting the precise and to the point answer of this.
SInce your function is marked as payable you can send money when you call this function. If you develop an app on Remix, after you deploy the contract, you would have an inbox next to the function to enter the amount. Or if you are interacting with the contract on front-end, you might have a function like this
await contract.methods.contribute().send({
from: accounts[0],
value:1000000,
});
the value that you are sending to the contract will be available to the contract as msg.value.
so I created a token tokenA
contract tokenA is ERC20 {
address public deployer; //to save adress of the deployer
constructor() ERC20('tokenA', 'TA') { //called by the deployer (once)
_mint(msg.sender, 1000000000000 * 10 ** 10); //mint/create tokens - we have created 100000000000*10^18 tokens
deployer = msg.sender; //set the deployer
}
//total supply is fixed no more can be created ever
function burn (uint amount) external { //remove tokens by sending then to a zero address
_burn(msg.sender, amount);
}
}
and I have deployed them onto the Rinkeby (4) injected web3 environment.
now I have to use these tokens as price for purchasing an NFT (which I also have to make)
so how do I use them in my code?
I can use ether like
uint price = 0.005 ether;
and the compare the transferred value with this, but if I write
uint price = 0.005 tokenA;
it gives an error, even though I have deployed tokenA and it resides in my meta mask acc
uint price = 0.005 ether; this is correct.
But inside your function to buy an NFT you need:
Remove the payable keyword.
Add the transferFrom function of the ERC-20 token in order to transfer price (0.005e18) tokens to the recipient address you specify.
Keep in mind that in order to successfully call the transferFrom function the user first need to call the approve function.
For a more in dept explanation i suggest to read the OpenZeppelin ERC-20 docs/github repo
I came across this answer while researching creating a function for someone to buy an NFT:
https://stackoverflow.com/a/67384225/1414721
The relevant lines are
IERC20 tokenContract = IERC20(tokenAddress);
require(tokenContract.transferFrom(msg.sender, address(this), price),
"buy: payment failed");
I don't want people to be able to buy my NFTs in anything other than Ethereum, although the author here says:
prevent an edge case where if gas runs out during execution, the buyer could end up with their NFT for free
This caught my eye and from reading the code it seems the check that is being done here is to prevent this edge case.
What I'm not sure of is how the applies when the currency the NFT is being bought in is Ethereum.
I have adjusted my buy function to look like
function buy(uint256 _tokenId) external payable {
uint256 price = tokenIdToPrice[_tokenId];
require(price > 0, 'This token is not for sale');
require(msg.value == price, 'Incorrect value');
address seller = ownerOf(_tokenId);
IERC20 tokenContract = IERC20(address(0));
require(tokenContract.transferFrom(msg.sender, address(this), price), "buy: payment failed");
payable(seller).transfer(msg.value);
_transfer(seller, msg.sender, _tokenId);
tokenIdToPrice[_tokenId] = 0;
emit NftBought(seller, msg.sender, msg.value);
}
which I believe a) incorporates the token Contract from being the Ethereum token (IERC20(address(0)) - I understand address(0) is the Ethereum token address?) and b
require(tokenContract.transferFrom(msg.sender, address(this), price), "buy: payment failed");
makes sure the gas limit edge case mentioned is handled.
Is this correct, Googling this was quite hard.
One last question (unrelated, I hope that's OK) - when a market displays all the NFTs available for a collection, i assume that the way they are doing that is the contract has a function that returns the NFT IDs and the Token URIs? Is that correct, or does OpenZeppelin provide this functionality and I don't have to concern myself with adding this function?
i think the gas edge case was solved by changing the order of operations. first pay then transfer the token.
Exploring blockchain using Ethereum and solidity via Remix and I encounter this code:
pragma solidity ^0.8.1;
contract AccountsDemo{
address public whoDeposited;
uint public depositAmt;
uint public accountBalance;
function deposit() public payable{
whoDeposited = msg.sender;
depositAmt = msg.value;
accountBalance += address(this).balance
}
}
According to the book I'm using, calling deposit should result in accountBalance and depositAmt being increased. When I try, nothing happens. The transactions are recorded on the blockchain but no increase happens. The book uses an older version of solidity - 0.6.0 not sure if this is the cause of my frustration.
Any idea what I'm missing here?
What your current deposit function does:
whoDeposited = msg.sender;
Stores the sender address to property whoDeposited. Mind that the value of whoDeposited is overwritten every time you execute the deposit() function.
depositAmt = msg.value;
Stores the transaction value to property depositAmt. Mind that the value of depositAmt is overwritten every time you execute the deposit() function.
accountBalance += address(this).balance
Increments value of property accountBalance by current contract balance. The current contract balance (address(this).balance) already reflects the transaction value that was send while executing the deposit() function.
So if your contract already had balance of 10 wei, accountBalance was 10, and now you were sending 2 wei... During the execution, address(this).balance is now 12 (because of the 2 wei that you're sending), and then accountBalance += 12, which is a result you don't want.
By the way, your snippet has a syntax error and it wouldn't compile because of the missing semicolon at the end of line.
To achieve your goal - store the current contract balance in both depositAmt and accountBalance (so they are always going to have the same value, also same as address(this).balance), you can do this:
depositAmt += msg.value;
accountBalance = address(this).balance;
I switched the += and = operators. So now, when you send a value to your deposit() function:
whoDeposited = msg.sender;
The behavior stays the same as described above.
depositAmt += msg.value;
Every time you execute the deposit() function, the value of depositAmt increments by the value of the transaction. Which effectively reflects the current balance of your contract.
accountBalance = address(this).balance;
Every time you execute the deposit() function, the value of accountBalance is set to the current contract balance. The current contract balance already reflects the value you sent in the current transaction.
Note: I used the word "always" few times in this answer. It's not really always - there are some edge cases like funding your contract address from a selfdestruct function or prior to the contract deployment, where the address(this).balance is going to be different from your depositAmt and accountBalance. But most likely, you're not going to meet these edge cases in the beginning, so you can consider it "always" for now. It's just good to know that there are cases where this snippet won't work as expected.
it's my first ever solidity contract and i can't figure out why my withdraw function is consuming infinite gas. when i compile the contract it gives a warning.
Gas requirement of function Faucet.withdraw(uint256) high: infinite.
If the gas requirement of a function is higher than the block gas limit, it cannot be executed. Please avoid loops in your functions or actions that modify large areas of storage(this includes clearing or copying arrays in storage)"
pragma solidity ^0.5.11;
//Our First Contract is a Faucet
contract Faucet
{
//Deposits ethers
function deposit(uint256 amount) payable public {
require(msg.value == amount);
// nothing to do!
}
//Give out ether to anyone who asks
function withdraw(uint256 withdraw_amount) public
{
if(withdraw_amount <= address(this).balance)
{
//Send the amount to address which requested it
msg.sender.transfer(withdraw_amount);
}
}
}
Note: i have successfully deployed the contract but transactions are failing because they run out of gas. is it due to this warning ?
UPDATE
If you want to send ether to a contract without calling any of its functions, you need to have a fallback function in that contract.
Add this function in the contract:
function () external payable {}
The code looks fine.
I also didn't encounter any problems when actually running your code.
Sometimes error messages are not accurate. Maybe you call withdraw with value?
You can use remix to test it.