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

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).

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.

Uniswap v3 custom ERC20 token swap

I am trying to implement a token swap of my custom ERC20 token via UniswapV3
I use Rinkeby Ethereum network.
I deployed the token under address: 0x4646CB39EA04d4763BED770F80F0e0dE8efcdF0f
I added the liquidity to Uniswap for this token and ETH.
Now, I try to execute swap in my contract, but it doesn't work. I get the error:
Gas estimation errored with the following message (see below). The transaction execution will likely fail. Do you want to force sending?
execution reverted
My Swap.sol contract takes an address of the token to swap with ETH as a constructor parameter. When I deploy it using DAI token address, the swap works just fine.
I assume this is a Uniswap liquidity related problem, but I added liquidity manually and I can swap my token inside their app.
Contract code:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;
pragma abicoder v2;
import "#uniswap/v3-periphery/contracts/libraries/TransferHelper.sol";
import "#uniswap/v3-periphery/contracts/interfaces/ISwapRouter.sol";
import "#uniswap/v3-periphery/contracts/interfaces/IQuoter.sol";
contract Swap {
address private constant SWAP_ROUTER =
0xE592427A0AEce92De3Edee1F18E0157C05861564;
address private constant WETH = 0xc778417E063141139Fce010982780140Aa0cD5Ab;
address public tokenAddress;
address public immutable _owner;
ISwapRouter public immutable swapRouter;
constructor(address token) {
_owner = msg.sender;
swapRouter = ISwapRouter(SWAP_ROUTER);
tokenAddress = token;
}
function swapExactInputSingle() external payable {
require(msg.value > 0, "Must pass non 0 ETH amount");
ISwapRouter.ExactInputSingleParams memory params = ISwapRouter
.ExactInputSingleParams({
tokenIn: WETH,
tokenOut: tokenAddress,
fee: 3000,
recipient: msg.sender,
deadline: block.timestamp,
amountIn: msg.value,
amountOutMinimum: 1,
sqrtPriceLimitX96: 0
});
swapRouter.exactInputSingle{value: msg.value}(params);
}
receive() external payable {}
}
I had the same issue with the swapExactInputMultihop function on uniswap. For each pools/paths you're going through, you need to make sure you've got the correct pool fee set.
You can checkout the swap fees on the uniswap website: V3-overview/fees
or on a video tutorial, going through the whole process:
Blockchain With Wisdom on YouTube
Managed to fix it.
I set the fee: 3000 in contract, but I created liquidity with 1% fee, so I had to change it to fee: 10000 according to docs: fee The fee tier of the pool, used to determine the correct pool contract in which to execute the swap

How to transfer an ERC721 token

I'm trying to transfer an ERC721 token, but I'm getting the error ERC721: transfer caller is not owner nor approved for the transferToken method.
Main.sol
import "./ERC721.sol";
import "./Counters.sol";
contract Main is ERC721 {
using Counters for Counters.Counter;
Counters.Counter internal _tokenIds;
address payable internal admin;
constructor() ERC721("MyToken", "TOKEN") {
admin = payable(msg.sender);
}
}
Auction.sol
import "./Main.sol";
contract Auction is Main {
struct AuctionInfo {
uint256 tokenId;
address highestBidder;
uint highestBid;
}
mapping(string => AuctionInfo) private _auctionInfo;
function createAuction(string memory id) public {
_tokenIds.increment();
uint256 newTokenId = _tokenIds.current();
_mint(msg.sender, newTokenId);
_auctionInfo[id].tokenId = newTokenId;
}
function transferToken(string memory id) public {
require(msg.sender == _auctionInfo[id].highestBidder, "You are not the highest bidder");
safeTransferFrom(address(this), _auctionInfo[id].highestBidder, _auctionInfo[id].tokenId);
}
// other methods...
}
The minting contract is this and the owner of the token is the msg.sender of the minting method if I'm not mistaken. Am I to use the approve (or setApprovalForAll) for this each time before transferring? I've tried this, payable(this), and address(this) for the safeTransferFrom method, but none seem to be working.
For example, I tried the following, but get the same revert message:
approve(address(this), _auctionInfo[id].tokenId);
this.safeTransferFrom(address(this), _auctionInfo[id].highestBidder, _auctionInfo[id].tokenId);
The main principle behind any Blockchain is that nobody on the blockchain network should be trusted, and still the transactions should happen fool proof, with no possibility of any cheating being done (barring of course of some hacking).
If you invoke the approve method from the Auction contract, then the msg.sender for the approve function in the ERC721 token contract is your auction contract address. So, in other words, your Auction Contract is trying to approve itself to sell someone else's NFTs, which is not very trustworthy.
What should really happen is that owner of the NFT should invoke the approve method of the ERC721 contract - i.e. the transaction that you send for the approve function call, should be signed by the NFT owner wallet address. This way, the msg.sender for the approve function in the ERC721 contract will be the owner of the NFT. As per the ERC721 standards, the owner of the NFT can approve anyone they want, to sell their NFT(s), as the no-trust in the network is still maintained (At least I should be able to trust myself). The approve method should be invoked from within your DAPP, before the transferToken function is invoked from the DAPP.
Hope that explains why you are unable to transfer your ERC721 tokens.
Because of the internal visibility of the ERC721._approve() function, you can effectively perform the approval for the user.
Then you'll be able to execute the safeTransferFrom(tokenOwner, receiver, tokenId) from your contract, because your contract address is approved to operate this specific token even though it belongs to the tokenOwner.
This snippet mints the token, assigning the ownership to the msg.sender. But then it also calls the _approve() function that doesn't contain any validations and simply assigns the approval of the token to the Auction address.
pragma solidity ^0.8;
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC721/ERC721.sol";
contract Auction is ERC721 {
constructor() ERC721("CollectionName", "Symbol") {}
function createAuction() public {
uint256 newTokenId = 1;
_mint(msg.sender, newTokenId);
_approve(address(this), newTokenId);
}
}
You can see from the screenshot that the owner is 0x5B... (the user address) and that the token is approved for 0xd9... (the contract address).
Note: The _approve() function is internal - it can be called from the ERC721 contract and contracts deriving from it (in your case Main and Auction), but it can't be called from external contracts or end user addresses.

Why can't I use this transferEther function to send Ether to the smart contract?

I have this code I have entered into Remix IDE, as ReceivedEther.sol, a standalone smart contract.
I've transferred 0.02 Ether to the smart contract, using MetaMask.
When I checked the smart contract's balance, it returns 200000000000000000, as expected.
If I try to use the transferEther function, however, and enter a number smaller than this - say, 0.005 ETH, or 50000000000000000 as the amount - it doesn't work using MetaMask.
When MetaMask prompts me it's never for that amount. It's for 0 ETH and 0.00322 gas fee (or whatever the gas is). Basically it always set the amount of ETH at 0 and only charges the fee.
Why can't I transfer an amount of ETH using this function in the Remix IDE with MetaMask?
pragma solidity ^0.8.0;
contract ReceivedEther {
function transferEther(address payable _recipient, uint _amount) external returns (bool) {
require(address(this).balance >= _amount, 'Not enough Ether in contract!');
_recipient.transfer(_amount);
return true;
}
/**
* #return contract balance
*/
function contractBalance() external view returns (uint) {
return address(this).balance;
}
}
Your code sends ETH (stated in the _amount variable) from the smart contract to the _recipient. So it doesn't require any ETH to be sent in order to execute the transferEther() function.
If you want your contract to accept ETH, the function that accepts it (or the general fallback() or receive() function) needs to be marked as payable.
Example:
pragma solidity ^0.8.0;
contract ReceivedEther {
receive() external payable {} // note the `payable` keyword
// rest of your implementation
}
Then you can send whathever amount of ETH to the smart contract address (without specifying any function to execute).
See more at https://docs.soliditylang.org/en/v0.8.5/contracts.html#receive-ether-function
If you want to prefill the amount in MetaMask from Remix IDE, you can use the "Value" input in the "Deploy & Run Transactions" tab.

Creating instance of contract inside another contract and calling it's methods results in thrown exception

I have 2 basic contracts: one is for token and the second is for sale.
Token сontract:
contract MyToken is StandardToken, Ownable {
string public constant name = "My Sample Token";
string public constant symbol = "MST";
uint32 public constant decimals = 18;
function MyToken(uint _totalSupply) {
require (_totalSupply > 0);
totalSupply = _totalSupply;
balances[msg.sender] = totalSupply;
}
}
Sale Contract
contract Sale {
address owner;
address public founderAddress;
uint256 public constant foundersAmount = 50;
MyToken public token = new MyToken(1000);
uint256 public issuedTokensAmount = 0;
function Sale() {
owner = msg.sender;
founderAddress = 0x14723a09acff6d2a60dcdf7aa4aff308fddc160c;
token.transfer(founderAddress, foundersAmount);
}
function() external payable {
token.transfer(msg.sender, 1);
owner.transfer(msg.value);
}
}
StandardToken and Ownable are all standard implementations from OpenZeppelin repository. Full contract source is available here.
So basically in my Sale Contract I create an instance of my token contract with fixed supply and assign all of the tokens to the caller. Then I transfer some amount of tokens to founder address. When I try to send some ethereum to Sale contract I'm attempting to transfer some of my tokens to the sender (Running all code in Remix browser, I create an instance of Sale contract and call "fallback" method specifying some ether amount). However, this fails with "Exception during execution. (invalid opcode). Please debug the transaction for more information." message. All that I can see when debugging is that code fails in payable method at line:
token.transfer(msg.sender, 1);
I can't see the exact reason for this as I'm not able to step into this method and see whats going on inside.
Interesting thing is that when I remove a call to transfer method on token instance in Sale Contract constructor - code seems to run fine without any exceptions.
What am I missing?
I debugged into the the contract using remix, and the invalid opcode is thrown by:
290 DUP8
291 DUP1
292 EXTCODESIZE
293 ISZERO
294 ISZERO
295 PUSH2 012f
298 JUMPI
299 PUSH1 00
301 DUP1
302 INVALID
I left out the rest, but essentially it loads the address of the token contract and calls EXTCODESIZE which retrieves the contracts code size, and checks that it's not equal to 0 (the token contract exists), unfortunately, it did equate to 0. At this point, I'm unsure whether this is a limitation in remix or I've misunderstood the setup.
I tried the identical contract setup on truffle + testrpc and it deployed, accepted the currency successfully. Do note however that testrpc indicated:
Gas usage: 59137
Meaning that this is above the default sendTransaction w/ no data default (21,000 gas). This means that in a live environment, ensure that you inform users to include extra gas, otherwise the fallback function would probably fail due to OOG errors.
The reason behind this is that you're using a fallback function. Try using a normal function and it should happen.