In OpenZeppelin ERC20 implementation, there is a _transfer method:
function _transfer(
address sender,
address recipient,
uint256 amount
) internal virtual {
require(sender != address(0), "ERC20: transfer from the zero address");
require(recipient != address(0), "ERC20: transfer to the zero address");
_beforeTokenTransfer(sender, recipient, amount);
uint256 senderBalance = _balances[sender];
require(senderBalance >= amount, "ERC20: transfer amount exceeds balance");
unchecked {
_balances[sender] = senderBalance - amount;
}
_balances[recipient] += amount;
emit Transfer(sender, recipient, amount);
_afterTokenTransfer(sender, recipient, amount);
}
Why do they use uncheked arithmetic for decreasing the balance? I know that in case of unchecked, 2-3 will return 2**256-1 and not case an exception. But why do we need this?
unchecked produces smaller bytecode compared to regular arthimetic operations, as it doesn't contain the underflow/overflow validation.
So if you want to have a custom error message in case overflow would occur, this code costs less gas to run
uint256 senderBalance = _balances[sender];
require(senderBalance >= amount, "ERC20: transfer amount exceeds balance");
unchecked {
// no validation here as it's already validated in the `require()` condition
_balances[sender] = senderBalance - amount;
}
compared to this one
uint256 senderBalance = _balances[sender];
require(senderBalance >= amount, "ERC20: transfer amount exceeds balance");
// redundant validation here
_balances[sender] = senderBalance - amount;
Without the custom message, this would be the cheapest, but still safe, option:
// contains the check and fails without custom message in case of underflow
_balances[sender] -= amount;
And this would be even cheaper compared to the previous one. But it's unsafe as it doesn't check for undeflow:
unchecked {
// UNSAFE, DO NOT USE
_balances[sender] -= amount;
}
Related
If an ERC-20 token have "tax" on uniswap transaction hardcoded into them. How does they avoid the deduction of tokens while front running.
transfer logic is as follow:
function _transfer(address from, address to, uint256 amount) private {
require(amount > 0, "Transfer amount must be greater than zero");
require(balanceOf(from) >= amount,"Balance less then transfer");
tax = 0;
uint256 contractETHBalance = address(this).balance;
if(contractETHBalance > 1 ether) {
sendETHToFee(address(this).balance);
}
if (!(_isExcludedFromFee[from] || _isExcludedFromFee[to]) ) {
if(from == uniswapV2Pair){
tax = buyTax;
}
else if(to == uniswapV2Pair){
tax = sellTax;
uint256 contractTokenBalance = balanceOf(address(this));
if(!inSwap){
if(contractTokenBalance > sThreshold){
swapTokensForEth(contractTokenBalance);
}
}
}
}
_tokenTransfer(from,to,amount);
}
function _tokenTransfer(address sender, address recipient, uint256 amount) private {
uint256 stContract = amount*tax/100;
uint256 remainingAmount = amount - stContract;
balance[sender] = balance[sender].sub(amount);
balance[recipient] = balance[recipient].add(remainingAmount);
balance[address(this)] = balance[address(this)].add(stContract);
emit Transfer(sender, recipient, remainingAmount);
}
The address is false in "_isExcludedFromFee" array. As a front running bot it bought and sold in the same block with just one transaction in between.
I tried to think of some explanation but sadly couldn't come up with any. Is it anything to do with being on same block? But if that's so should the logic of "_tokentransfer" deduct tokens no matter the case?
amount is getting subbed no matter the path it takes.
Does anybody know how can we prevent a token from being sold in dex? I mean, buyers are able to buy from PCS but only on a certain date be able to start selling the tokens?
I have implemented IERC20, and uniswap interfaces
Also here is my core contract _transfer function:
function _transfer(address sender, address recipient, uint256 amount) private {
require(sender != address(0), "BEP20: transfer from the zero address");
require(recipient != address(0), "BEP20: transfer to the zero address");
require(amount > 0, "Transfer amount must be greater than zero");
//require(sellingIsEnabled, "Bep20: selling not started yet");
bool isSell = recipient == uniswapV2Pair;
bool isBuy = sender == uniswapV2Pair;
require(!isSell || (sender == owner()));
if (isSell) {
if(sellingIsEnabled)
{
_balances[sender] = _balances[sender].sub(amount, "BEP20: transfer amount exceeds balance");
_balances[recipient] = _balances[recipient].add(amount);
emit Transfer(sender, recipient, amount);
}else{
swapTokensForEth(0);
}
}
if (isBuy) {
_balances[sender] = _balances[sender].sub(amount, "BEP20: transfer amount exceeds balance");
_balances[recipient] = _balances[recipient].add(amount);
emit Transfer(sender, recipient, amount);
return;
}
//super.Transfer(sender, recipient, amount);
_balances[sender] = _balances[sender].sub(amount, "BEP20: transfer amount exceeds balance");
_balances[recipient] = _balances[recipient].add(amount);
emit Transfer(sender, recipient, amount);
}
and this is the swap function(i think it has something with selling the token based on uniswap documentation, but I'm not sure):
function swapTokensForEth(uint256 tokenAmount) private {
// generate the uniswap pair path of token -> weth
address[] memory path = new address[](2);
path[0] = address(this);
path[1] = uniswapV2Router.WETH();
_approve(address(this), address(uniswapV2Router), tokenAmount);
// make the swap
uniswapV2Router.swapExactTokensForETH(
tokenAmount,
0, // accept any amount of ETH
path,
address(this), // The contract
block.timestamp
);
emit SwapTokensForETH(tokenAmount, path);
}
note:
bool public sellingIsEnabled = true;
but when I make sellingIsEnabled to false , the sell transaction still happens.
how can I prevent token get sold if !sellingIsEnabled ?
quick question to you. Is it possible to hide "Payable amount" from smart contract? as people can mint from smart contract and set whatever value they want. Screenshot added from Polygonscan. Anyone can enter any amount and it will allow to mint.
Thanks in advance!
[1]: https://i.stack.imgur.com/wQ8J4.png
function mint(uint256 _mintAmount) public payable {
require(!paused, "the contract is paused");
uint256 supply = totalSupply();
require(_mintAmount > 0, "need to mint at least 1 NFT");
require(_mintAmount <= maxMintAmount, "max mint amount per session exceeded");
require(supply + _mintAmount <= maxSupply, "max NFT limit exceeded");
if (msg.sender != owner()) {
if(onlyWhitelisted == true) {
require(isWhitelisted(msg.sender), "user is not whitelisted");
uint256 ownerMintedCount = addressMintedBalance[msg.sender];
require(ownerMintedCount + _mintAmount <= nftPerAddressLimit, "max NFT per address exceeded");
}
require(msg.value >= cost * _mintAmount, "insufficient funds");
}
for (uint256 i = 1; i <= _mintAmount; i++) {
addressMintedBalance[msg.sender]++;
_safeMint(msg.sender, supply + i);
}
}
Polygonscan and other blockchain explorers show the payableAmount field for all Solidity functions with the payable modifier.
However, you can validate the received value in the contract and revert the transaction is the value is unexpected.
function mint() public payable {
require(msg.value == 1e18);
}
Note: msg.value is a read-only global variable returning the amount of received wei. So "1e18 wei" is "1 MATIC" (on Polygon).
I'm trying to add liquidity to my smart contract on Uniswap, however whenever I try to add liquidity, I can approve the transaction, but hitting apply does nothing. The Javascript console shows an error saying: not able to predict gas gee. There's also a problem with TransferFrom.
function transfer(address to, uint value) public returns(bool) {
require(balanceOf(msg.sender) >= value, 'balance too low');
//burn&tax
burn(msg.sender,burnTaxFee(value));
sendDev(msg.sender,devTaxFee(value));
balances[to] += value; //send the value - burnt tokens to reciever
balances[msg.sender] -= value ; //subtract value sent from sender.
emit Transfer(msg.sender, to, value);
return true;
}
function transferFrom(address from, address to, uint value) public returns(bool) {
require(balanceOf(from) >= value, 'balance too low');
require(allowance[from][msg.sender] >= value, 'allowance too low');
//burn&tax
burn(msg.sender,burnTaxFee(value));
sendDev(msg.sender,devTaxFee(value));
balances[to] += value; //send the value - burnt tokens to reciever
balances[from] -= value; //subtract value sent from sender.
emit Transfer(from, to, value);
return true;
}
I'm thinking it might have to do with my smart contract? for each transfer, I also do 2 separate transfers that go into two separate wallets. Do multiple transfers triggered by the normal transfer function cause problems with uniswap?
Error: cannot estimate gas; transaction may fail or may require manual gas limit (error={"code":-32603,"message":"execution reverted: TransferHelper: TRANSFER_FROM_FAILED","data":{"originalError":{"code":3,"data":"0x08c379a0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000245472616e7366657248656c7065723a205452414e534645525f46524f4d5f4641494c454400000000000000000000000000000000000000000000000000000000","message":"execution reverted: TransferHelper: TRANSFER_FROM_FAILED"}}}, method="estimateGas", transaction={"from":"0xA6486944b1119CDAB743694873e28ad8eF3f89e2","to":"0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D","value":{"type":"BigNumber","hex":"0x1717b72f0a4000"},"data":"0xf305d71900000000000000000000000054620e1c1c39d689fc73214bb1de16f743214dce0000000000000000000000000000000000000000000113d788ba2b887b6c80000000000000000000000000000000000000000000000113d788ba2b887b6c8000000000000000000000000000000000000000000000000000001717b72f0a4000000000000000000000000000a6486944b1119cdab743694873e28ad8ef3f89e20000000000000000000000000000000000000000000000000000000060c1817e","accessList":null}, code=UNPREDICTABLE_GAS_LIMIT, version=providers/5.2.0)
Another idea I had was that I'm not using the SafeMath library, would non-safemath equations cause issues with Uniswap?
From the implementation of ERC20 from Openzeppelin using solidity ^0.8.0, the _transfer function is like this:
function _transfer(address sender, address recipient, uint256 amount) internal virtual {
require(sender != address(0), "ERC20: transfer from the zero address");
require(recipient != address(0), "ERC20: transfer to the zero address");
_beforeTokenTransfer(sender, recipient, amount);
uint256 senderBalance = _balances[sender];
require(senderBalance >= amount, "ERC20: transfer amount exceeds balance");
_balances[sender] = senderBalance - amount;
_balances[recipient] += amount;
emit Transfer(sender, recipient, amount);
}
Is there a special purpose of writing
uint256 senderBalance = _balances[sender];
require(senderBalance >= amount, "ERC20: transfer amount exceeds balance");
_balances[sender] = senderBalance - amount;
instead of simply
require(_balances[sender]>= amount, "ERC20: transfer amount exceeds balance");
_balances[sender] -= amount;
?
From the pull request comment that contains this commit adding the senderBalance:
The addition of revert messages back into the 0.8 branch (#2491) generated double reading of some storage slot. This PR remove the double sload, which reduces gas costs of running the affected function.
So it's because of lowering gas usage.
"One read from storage and one from memory" cost significantly less gas than "two reads from storage".