How does uniswap get the balance of eth or erc20 tokens? I can't find in the interface code with the multicall contract.
Every ERC20 token has a mapping to keep track of balances of addresses
mapping(address => uint) public balanceOf;
When Uniswap needs to check any balance of tokens, for example as in UniswapV2Pair.sol, it first needs to get the address of deployed tokens and then checks the mapping inside of each contract
// token0 is the contract address of token0
uint balance0 = IERC20(token0).balanceOf(address(this));
// token1 is the contract address of token1
uint balance1 = IERC20(token1).balanceOf(address(this));
Related
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
I'm writing a contract that involves transferring an ERC-721 token from one user to another. In order to test that this works with existing NFT collections, I'm using ganache-cli to fork mainnet and impersonate the holder of the ERC-721 token in question. I've confirmed on Etherscan that the address I'm unlocking is indeed the holder of the ERC-721 token that I'm trying to transfer.
First, I'm forking mainnet using ganache-cli:
ganache-cli -f <INFURA_MAINNET_ENDPOINT> -d -i 66 1 --unlock <HOLDER_ADDRESS>
My smart contract code includes:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface IERC721 {
function ownerOf(uint256 _tokenId) external returns (address);
function safeTransferFrom(address _from, address _to, uint256 _tokenId) external payable;
}
interface CryptopunkInterface {
function transferPunk(address _to, uint _tokenId) external;
}
and contains this function:
/// #dev Sells NFT into a bid (i.e., "hits" the bid)
/// #param _bidderAddress Address of the bidder
/// #param _nftAddress Address of collection to which the bid applies
/// #param _tokenId Token id of the NFT in question
/// #param _expectedWeiPriceEach Price (in wei) that seller expects to receive for each NFT
/// #return Proceeds remitted to seller
function hitBid(address _bidderAddress, address _nftAddress, uint256 _tokenId, uint256 _expectedWeiPriceEach) public returns (uint256) {
console.log("msg.sender of hitBid: ", msg.sender);
// Initialize bid
Bid memory bid = bids[_bidderAddress][_nftAddress];
// Require that bid exists
require(bid.quantity > 0, "This bid does not exist.");
// Require that bid amount is at least what the seller expects
require(bid.weiPriceEach >= _expectedWeiPriceEach, "Bid is insufficient.");
// Decrement bidder's bid quantity for this collection
bids[_bidderAddress][_nftAddress].quantity = bid.quantity - 1;
// Compute platform fee proceeds
uint256 platformFeeProceeds = bid.weiPriceEach * platformFee / 10000;
// Remit platform fee proceeds to owner
sendValue(OWNER, platformFeeProceeds);
// Transfer NFT to bidder
// Check whether _nftAddress is Cryptopunks address
if (_nftAddress == 0xb47e3cd837dDF8e4c57F05d70Ab865de6e193BBB) {
CryptopunkInterface(_nftAddress).transferPunk(_bidderAddress, _tokenId);
} else {
console.log("ownerOf NFT being sold: ", IERC721(_nftAddress).ownerOf(_tokenId));
IERC721(_nftAddress).safeTransferFrom(msg.sender, _bidderAddress, _tokenId);
}
// Compute seller proceeds
uint256 sellerProceeds = bid.weiPriceEach - platformFeeProceeds;
// Remit seller proceeds to seller
sendValue(payable(msg.sender), sellerProceeds);
// Emit new trade event
emit NewTrade(_bidderAddress, msg.sender, _nftAddress, bid.weiPriceEach, 1, _tokenId);
// Return seller proceeds
return sellerProceeds;
}
When I run truffle test, executing the function on behalf of the unlocked holder address, I get this error:
Error: Returned error: VM Exception while processing transaction: revert ERC721: transfer caller is not owner nor approved -- Reason given: ERC721: transfer caller is not owner nor approved.
UPDATE:
I switched from using ganache-cli to using Hardhat to fork mainnet. I'm impersonating the relevant addresses in my test.js file:
const BAYC_HOLDER_ADDRESS = "0x54BE3a794282C030b15E43aE2bB182E14c409C5e";
await hre.network.provider.request({
method: "hardhat_impersonateAccount",
params: [BAYC_HOLDER_ADDRESS],
});
I've also verified that msg.sender of hitBid is indeed the ownerOf the NFT in question with the console.log statements above.
msg.sender of hitBid: 0x54be3a794282c030b15e43ae2bb182e14c409c5e
ownerOf NFT being sold: 0x54be3a794282c030b15e43ae2bb182e14c409c5e
Nonetheless, I'm still getting the same error:
Error: VM Exception while processing transaction: reverted with reason string 'ERC721: transfer caller is not owner nor approved'
The reason you are getting this error is because msg.sender of hitBid() is not the same as msg.sender of IERC721(_nftAddress).safeTransferFrom().
The seller needs to sign two transactions:
The user needs to sign approve(yourContractAddress, tokenId)
The user needs to sign hitBid()
That will prevent hitBid() from reverting, since your contract address (msg.sender of safeTransferFrom()) will then be approved to make the transfer.
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.
Actually from our backend we are sending 2 transactions to the Ethereum Blockchain, first Tx we sent tokens, and second Tx we send Data as names, age, passport etc.
Can we send everything in only 1 Transaction? Data and Tokens?
Yes. You can send ETH with any smart contract transaction call.
If you are wanting to send some token other than ETH in one transaction, write a contract that handles this logic
contract OneTx {
mapping(address => string) names;
function constructor() {
ERC20 erc20 = ERC20("0x...")
}
function doStuff(address from, address to, uint amount, string name) {
erc20.transferFrom(from, to, amount);
names[from] = name;
}
}
Keep in mind you need the contract to be approved to move funds on behalf of from
I have few child wallets and a parent wallet. Whenever these child wallets get incoming ERC20 tokens or ETH. I want to send them automatically to the parent wallet.
I know I can do below to send the entire eth balance.
beneficiary.transfer(this.balance);
How can I do it for erc20 tokens.
Thanks in advance.
require(token.transfer(beneficiary, token.balanceOf(this)));
where token is the ERC20 token you're transferring. E.g.:
interface IERC20Token {
function transfer(address, uint256) external returns (bool);
function balanceOf(address) external returns (uint256);
}
contract Foo {
IERC20Token token = IERC20Token(0x123abc...);
function doit(address beneficiary) public {
require(token.transfer(beneficiary, token.balanceOf(this)));
}
}