Calling transfer function using interface for ERC20 - ethereum

I am trying to send ERC20 tokens from a contract to an account .
This is the code that I have
IERC20 testToken = IERC20(tokenAddress);
testToken.transfer(accountAddress,amount);
The function to intitate the trasfer should ideally be called by someone else, lets call him "C"
So
msg.sender != accountAddress
Is there a way I can make the msg.sender for the transfer function to
be the contract itself instead of "C" ?
Also this function somehow works if I i call it from the account
address ,i.e when msg.sender == accountAddress . How does that happen
,in this case the from and to fields should both be the same ,and no
change in balance should happen ?

You'll need the transferFrom function and not transfer. The transfer function automatically uses the msg.sender as a parameter. You will also have to have that address have allowance of the tokens to transfer. The approve method takes care of this. Assuming that is all set, here is the code.
IERC20 tcontract = IERC20(tokenAddress);
require(tcontract.transferFrom(address(this), msg.sender, amount), "Don't have enough balance");

Related

Gas Estimation Error when Transfer ERC721 token in Remix

I am currently implementing an ERC721 token staking function contract, but when I add the transfer code, a Gas Estimation Error occurs.
MarineBluesContract(_nftContract).transferFrom(msg.sender, address(this), _tokenId);
function stake(uint256 _tokenId, address _nftContract)
external
nonReentrant
{
require(ntfContractList[_nftContract], "Not allowed NFT contract");
require(msg.sender != address(0), "Invalid staker address");
require(_tokenId != 0, "Invalid token id");
require(MarineBluesContract(_nftContract).ownerOf(_tokenId) == msg.sender, "Not token owner");
// Staking start time
uint48 timestamp = uint48(block.timestamp);
// Staking to contract
MarineBluesContract(_nftContract).transferFrom(msg.sender, address(this), _tokenId);
// Save staking information
stakedTokens.push(
StakedToken(msg.sender, _tokenId, _nftContract, timestamp, false)
);
// Increase in staking count
totalStaked++;
emit Stake(msg.sender, _tokenId, _nftContract, timestamp);
}
enter image description here
Make sure I have enough Ethereum
Make sure staking contract has enough Ethereum
Authorize the staking contract using setApprovalForAll so that it can transfer my NFTs
Gas Estimation error came out even after taking the above steps. I'm not sure why, but if you can guess or if I'm doing something wrong, please tell me. thanks in advance!
Based on the fact that the require() condition validating _nftContract.ownerOf() doesn't fail, I'm assuming that your contract is correctly deployed on the same network as _nftContract.
transferFrom() can fail for several reasons. Most common causes might be:
The token sender (specified in the first argument) didn't approve the transaction sender (in this case your contract) to operate this specific _tokenId
The _tokenId does not belong to the token sender
The _tokenId does not exist
Mind that you're executing the transferFrom() function from your contract - so the msg.sender (user executing stake() function) needs to give approval directly to yourContract.

Is there a way to pass verified parameters to smart contract function in Solidity?

I want to call a function named "testFunction" which takes "claimDate" as a parameter. claimDate parameter comes from a server via api.
contract TestSmartContract {
testFunction(uint256 id, uint256 claimDate) public payable returns (uint) {
// I need a logic to verify if claimDate is coming from a valid source which will mean it is correct.
}
}
In short, you can use signature verification to check if the data comes from the verified source.
The idea is that;
Let the source sign the claimDate from off-chain (ex. backend server).
Send the claimDate together with the signature to the contract.
The contract which stores source address verifies if the signature is correct. If yes, it means the claimDate is really from the source.
⚠️ Note that signing claimDate alone might not be enough. Because once the signature is created, it can be visible and reused by exploiters.
Read more: https://blog.chainsafe.io/how-to-verify-a-signed-message-in-solidity-6b3100277424
You can use the wallet address to validate if the source is valid.
For example:
contract TestSmartContract {
address sourceAddress = "0x00...";
function testFunction(uint256 id, uint256 claimDate) public returns(uint) {
require(msg.sender == sourceAddress, "Not valid source");
//code to execute
}
}

Aave aaveLendingPool.withdraw() return value validation

I am writing a pull payment function and, as with any external call it is good practice validate the result and check successful execution. I am using the following interface:
interface IAaveLendingPool {
function deposit(
address asset,
uint256 amount,
address onBehalfOf,
uint16 referralCode
) external;
function withdraw(
address asset,
uint256 amount,
address to
) external;
function getReservesList() external view returns (address[] memory);
}
aAveLendingPool= IAaveLendingPool(0x0543958349aAve_pooladdress)
The interface provided by Aave doesn't specify a return either. The withdraw function of the LendingPool.sol contract returns external override whenNotPaused returns (uint256).
The question: Can I use the returned uint to validate successful execution or aAveLendingPool.withdraw() returns a boolean? Will the following work as expected?
///#notice assigns dai to caller
require( aaveLendingPool.withdraw(
address(dai),
amount,
msg.sender), "Error, contract does not have enough DAI")
Please provide a solidity docs link to external function call return value, if any.
According to the Consensys Diligence Aave V2 audit:
ERC20 implementations are not always consistent. Some implementations of transfer and transferFrom could return ‘false’ on failure instead of reverting. It is safer to wrap such calls into require() statements to these failures.
Therefore, wrapping the transfer call in a require check is safe and sufficient.
require( aaveLendingPool.withdraw(
address(dai),
amount,
msg.sender), "Error, contract does not have enough DAI")

How to interact with the deployed ERC20 token with another smart-contract?

I have created a basic ERC20 token by implementing OpenZeppelin as follow in ERC20.sol file:
pragma solidity ^0.6.4;
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.4.0/contracts/token/ERC20/ERC20.sol";
contract Token is ERC20 {
constructor(string memory _name, string memory _symbol)
public
ERC20(_name, _symbol)
{
_mint(msg.sender, 10000000000000000000000000000);
}
}
Then implement another contract Contract.sol as follow:
import "./ERC20.sol";
pragma solidity ^0.6.4;
contract SimpleBank{
Token tokenContract;
constructor(Token _tokenContract) public {
tokenContract = _tokenContract;
}
function deposit(uint amt) public returns (bool) {
require(amt != 0 , "deposit amount cannot be zero");
tokenContract.transfer(address(this),amt);
return true;
}
}
As, I have deployed both contract from the address 0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2 so, it holds 10000000000000000000000000000 tokens.
But when I call deposit function from same address I got the following error:
transact to SimpleBank.deposit errored: VM error: revert. revert The
transaction has been reverted to the initial state. Reason provided by
the contract: "ERC20: transfer amount exceeds balance". Debug the
transaction to get more information.
So, what is the proper way to interact with the deployed ERC20 token so that the deploy function works.
The user address 0xAb8483... sends a transaction executing SimpleBank's function deposit(), which makes 0xAb8483... the value of msg.sender in SimpleBank.
But then SimpleBank sends an internal transaction executing Token's function transfer(). Which makes SimpleBank address (not the 0xAb8483...) the value of msg.sender in Token.
So the snippet tokenContract.transfer(address(this),amt); within SimpleBank is trying to send SimpleBank's tokens. Not the user's (0xAb8483...) tokens.
This transfer of tokens (from point 2) reverts, because SimpleBank doesn't own any tokens. Which makes the top-level transaction (from point 1) revert as well.
If you want SimpleBank to be able to transfer 0xAb8483...'s tokens, 0xAb8483... needs to approve() the tokens first to be spent by SimpleBank. Directly from their address, so that they are msg.sender in the Token contract.
Only then SimpleBank can execute transferFrom(0xAb8483..., address(this), amt) (from, to, amount).
TLDR: Your contract can't spend tokens that it doesn't own, unless the owner has manually approved your contract to spend them.
If it could spend someone else's tokens without approval, it would be very easy to steal from people who can't/don't verify your source code (by spending their USDT, WETH and other widely-used tokens).

How to prevent a token transfer in smart contract?

Is there any way to prevent token transfer from address A to address B in a smart contract? Let's say it needs some sort of approval from smart contract owner. Assuming it is not ERC-20 token.
There are many ways to implement such a restriction in your code logic.
For example, you could create a mapping with addresses that only the smart contract owner can add entries to. (Using an onlyOwner modifier). Then implement a require function that only permits the transfer if the address is in the mapping.
mapping (address=>bool) addr_mapping;
function transferToken(address sender, address receiver) public{
require(addr_mapping[sender] == true, "Sender not in mapping");
require(addr_mapping[receiver] == true, "Receiver not in mapping");
...
}
function addToMapping(address addr) onlyOwner {
...
}
PS. Not sure if the boolean comparison is possible, but you can create your own flag.