I have recently been delving into the code of a few contracts and have been commenting them myself to try and understand how they work as the entire field seems ironically black-box from a development perspective with most code copy and pasted from other contracts with 0 comments explaining what functions do.
As such I have seen a recurring function across multiple projects which I am struggling to get my head around. Namely shouldswapback() and swapback().
shouldSwapBack():
function shouldSwapBack() internal view returns (bool) {
return msg.sender != pair
&& !inSwap
&& swapEnabled
&& _balances[address(this)] >= swapThreshold;
}
My understanding of above is to return true only when the caller of the function is not the LP address, swapping is allowed and we are not in a swap currently and the balance of the contract is >= the predefined swap threshold.
2 questions regarding this:
1. In what context/circumstance is the msg.sender the LP contract?
2. Why is it important to only swap if the contract balance is greater than a certain percentage? What would be the effect if this was 0?
The actual swap function is longer:
function swapBack() internal swapping {
uint256 contractTokenBalance = balanceOf(address(this));
/* (contract balance * 3)/15/2 */
uint256 amountToLiquify = contractTokenBalance.mul(liquidityFee).div(totalFee).div(2);
uint256 amountToSwap = contractTokenBalance.sub(amountToLiquify);
/* set the address path for the BNB and Token addresses */
address[] memory path = new address[](2);
path[0] = address(this);
path[1] = WBNB;
/* note before balance of contract */
uint256 balanceBefore = address(this).balance;
/*swap tokens using PCS */
router.swapExactTokensForETHSupportingFeeOnTransferTokens(
amountToSwap, /*The amount of input tokens to send. */
0, /*The minimum amount of output tokens that must be received for the transaction not to revert. */
path, /*An array of token addresses. path.length must be >= 2. Pools for each consecutive pair of addresses must exist and have liquidity. */
address(this), /*Recipient of the BNB. */
block.timestamp /*Unix timestamp after which the transaction will revert. (Think set to one block?)*/
);
uint256 amountBNB = address(this).balance.sub(balanceBefore); /* get amount swapped by checking new contract balance vs beforeswap */
uint256 totalBNBFee = totalFee.sub(liquidityFee.div(2));
uint256 amountBNBLiquidity = amountBNB.mul(liquidityFee).div(totalBNBFee).div(2);
uint256 amountBNBMarketing = amountBNB.mul(marketingFee).div(totalBNBFee);
/* send marketing fee to marketing wallet */
(bool MarketingSuccess, /* bytes memory data */) = payable(marketingFeeReceiver).call{value: amountBNBMarketing, gas: 30000}("");
require(MarketingSuccess, "receiver rejected ETH transfer");
/* if we meet the liquidity threshold add to marketwallet */
if(amountToLiquify > 0){
router.addLiquidityETH{value: amountBNBLiquidity}(
address(this),
amountToLiquify,
0,
0,
marketingFeeReceiver,
block.timestamp
);
emit AutoLiquify(amountBNBLiquidity, amountToLiquify);
}
}
My understanding for this is that the contract tokens are swapped to BNB and then the fees are calculated and sent to the appropriate wallet. However with this I don't understand:
1.Why are the tokens going the contract and not the LP?
2.If all the tokens are swapped to ETH do none get swapped back?
3.What is the significance of adding to the LP, why would this be necessary? (if this wasn't done what would the affect be on the token? I have seen projects fail in the past because they didn't buyback liquidity but I'm struggling to understand what it would do apart from ease volatility?)
I understand that this a lengthy question and my issue seems to be less from a code perspective and more from a key concept one but if anyone could help me it would be great. I have looked online for courses but none seem to really delve into dex interactions, a lot just seem very basic token/NFT creation I haven't been able to find one tutorial on making a contract that has a working tax implementation so instead I've just been cross referencing a bunch of different contracts.
If anyone can point me in the right direction for a course or even better a tutor that would be great.
Thanks.
Related
I want to pay two transactions within one function.
The msg.sender should pay a fee to the contract first. The rest which is still in msg.value should be transferred to the seller. I always get an error. To clarify i tested both transactions by their own and it worked and i have implemented a receive() function that the contract can receive the funds. Here is the code:
function sendMoney() public payable {
address payable seller = payable(address(this));
address payable sellerino = payable(0x910DCE3971F71Ee82785FF86B47CaB938eBB9E68);
sellerino.transfer(10);
seller.transfer(msg.value);
}
Here the error:
https://blockscout.mantis.hexapod.network/tx/0x343817da970dce47c74094f4766f9c7f21ee258ea848e117893afa53c4768dac/internal-transactions
Additional Code:
function buyTicketFromAttendee(uint256 _ticketId)
external
payable
{
require(eventticket[_ticketId - 1].availableForResell = true,"ticket not for sale");
uint256 _priceToPay = eventticket[_ticketId - 1].ticketPrice;
//address owner = ownerOf(_ticketId);
require((msg.value >= _priceToPay + transferFee),"not enough money");
address seller = eventticket[_ticketId - 1].seller;
address owner = eventticket[_ticketId - 1].owner;
payable(owner).transfer(transferFee);
payable(seller).transfer(msg.value - transferFee);
_transfer(address(this), msg.sender, _ticketId);
//payable(seller).transfer(_priceToPay);
eventticket[_ticketId - 1].availableForResell = false;
}
Your snippet is trying to send more funds than the contract has - which causes the whole transaction to revert.
The contract has 0 MANTIS before the transaction invoking sendMoney().
The transaction sent along 0.1 MANTIS ( == 100000000000000000 wei), which is reflected in the msg.value global variable.
The first transfer() sends 10 wei, so the left available balance is now 99999999999999990 wei (10 less than 0.1 MANTIS). But the second transfer() is trying to send 100000000000000000 (the msg.value), which is 10 wei more than the contract has available at the moment.
Solution: Lower the second transfer by the already sent amount.
sellerino.transfer(10);
seller.transfer(msg.value - 10);
I'm creating a solidity contract for an NFT and in my mint function I'm not sure if a call to totalSupply() vs using a token counter and incrementing it is better practice. Does either variation cost more gas? Is one the more standard practice? I've seen examples of both being used.
Variation 1:
contract MyNFT is ERC721Enumerable, PaymentSplitter, Ownable {
using Counters for Counters.Counter;
Counters.Counter private currentTokenId;
...
function mint(uint256 _count)
public payable
{
uint256 tokenId = currentTokenId.current();
require(tokenId < MAX_SUPPLY, "Max supply reached");
for(uint i = 0; i < _count; ++i){
currentTokenId.increment();
uint256 newItemId = currentTokenId.current();
_safeMint(msg.sender, newItemId);
}
}
}
Variation 2:
function mint(uint256 _count)
public payable
{
uint supply = totalSupply();
require( supply + _count <= MAX_SUPPLY, "Exceeds max supply." );
for(uint i = 0; i < _count; ++i){
_safeMint(msg.sender, supply + i);
}
}
Both versions seem to work. I just want to be sure I'm using the most efficient / secure. Thanks for any advice!
First off all, you need to show us the underlying implementations. However, I can speculate that these are unmodified openzeppelin implementations for ERC721Enumerable and Counters.
For your case only, using Counter seems a little bit pointless to me.
It increases your deployment costs(just a little bit) because of redundant code coming from Counter library
You already know the length of your tokens array, why keep it twice? Counters is created for situations where you don't know the number of elements, like a mapping.
I am not guaranteeing correctness of the following analysis
Calling totalSupply (looking from opcode point of view) will:
jump to totalsupply (8 gas)
sload tokens.slot (200) gas
However, while using Counter, you sstore (>= 5000 gas) each time you decrement and sload (200 gas) each time you read.
As long as i am not mistaken about Counter using storage, and therefore sstore and sload opcodes, second variant will use much less gas.
Using scaffold-eth to implement this single swap example from the uniswap documentation.
Pretty much just copied and pasted the code and was able to deploy it successfully to rinkeby:
//SPDX-License-Identifier: MIT
pragma solidity >=0.8.0 <0.9.0;
pragma abicoder v2;
import "hardhat/console.sol";
import '#uniswap/v3-periphery/contracts/interfaces/ISwapRouter.sol';
import '#uniswap/v3-periphery/contracts/libraries/TransferHelper.sol';
contract YourContract {
// For the scope of these swap examples,
// we will detail the design considerations when using
// `exactInput`, `exactInputSingle`, `exactOutput`, and `exactOutputSingle`.
// It should be noted that for the sake of these examples, we purposefully pass in the swap router instead of inherit the swap router for simplicity.
// More advanced example contracts will detail how to inherit the swap router safely.
ISwapRouter public immutable swapRouter;
// This example swaps DAI/WETH9 for single path swaps and DAI/USDC/WETH9 for multi path swaps.
address public constant DAI = 0x6B175474E89094C44Da98b954EedeAC495271d0F;
address public constant WETH9 = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
address public constant USDC = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48;
// For this example, we will set the pool fee to 0.3%.
uint24 public constant poolFee = 3000;
constructor(ISwapRouter _swapRouter) {
swapRouter = _swapRouter;
}
/// #notice swapExactInputSingle swaps a fixed amount of DAI for a maximum possible amount of WETH9
/// using the DAI/WETH9 0.3% pool by calling `exactInputSingle` in the swap router.
/// #dev The calling address must approve this contract to spend at least `amountIn` worth of its DAI for this function to succeed.
/// #param amountIn The exact amount of DAI that will be swapped for WETH9.
/// #return amountOut The amount of WETH9 received.
function swapExactInputSingle(uint256 amountIn) external returns (uint256 amountOut) {
// msg.sender must approve this contract
// Transfer the specified amount of DAI to this contract.
TransferHelper.safeTransferFrom(DAI, msg.sender, address(this), amountIn);
// Approve the router to spend DAI.
TransferHelper.safeApprove(DAI, address(swapRouter), amountIn);
// Naively set amountOutMinimum to 0. In production, use an oracle or other data source to choose a safer value for amountOutMinimum.
// We also set the sqrtPriceLimitx96 to be 0 to ensure we swap our exact input amount.
ISwapRouter.ExactInputSingleParams memory params =
ISwapRouter.ExactInputSingleParams({
tokenIn: DAI,
tokenOut: WETH9,
fee: poolFee,
recipient: msg.sender,
deadline: block.timestamp,
amountIn: amountIn,
amountOutMinimum: 0,
sqrtPriceLimitX96: 0
});
// The call to `exactInputSingle` executes the swap.
amountOut = swapRouter.exactInputSingle(params);
}
/// #notice swapExactOutputSingle swaps a minimum possible amount of DAI for a fixed amount of WETH.
/// #dev The calling address must approve this contract to spend its DAI for this function to succeed. As the amount of input DAI is variable,
/// the calling address will need to approve for a slightly higher amount, anticipating some variance.
/// #param amountOut The exact amount of WETH9 to receive from the swap.
/// #param amountInMaximum The amount of DAI we are willing to spend to receive the specified amount of WETH9.
/// #return amountIn The amount of DAI actually spent in the swap.
function swapExactOutputSingle(uint256 amountOut, uint256 amountInMaximum) external returns (uint256 amountIn) {
// Transfer the specified amount of DAI to this contract.
TransferHelper.safeTransferFrom(DAI, msg.sender, address(this), amountInMaximum);
// Approve the router to spend the specifed `amountInMaximum` of DAI.
// In production, you should choose the maximum amount to spend based on oracles or other data sources to achieve a better swap.
TransferHelper.safeApprove(DAI, address(swapRouter), amountInMaximum);
ISwapRouter.ExactOutputSingleParams memory params =
ISwapRouter.ExactOutputSingleParams({
tokenIn: DAI,
tokenOut: WETH9,
fee: poolFee,
recipient: msg.sender,
deadline: block.timestamp,
amountOut: amountOut,
amountInMaximum: amountInMaximum,
sqrtPriceLimitX96: 0
});
// Executes the swap returning the amountIn needed to spend to receive the desired amountOut.
amountIn = swapRouter.exactOutputSingle(params);
// For exact output swaps, the amountInMaximum may not have all been spent.
// If the actual amount spent (amountIn) is less than the specified maximum amount, we must refund the msg.sender and approve the swapRouter to spend 0.
if (amountIn < amountInMaximum) {
TransferHelper.safeApprove(DAI, address(swapRouter), 0);
TransferHelper.safeTransfer(DAI, msg.sender, amountInMaximum - amountIn);
}
}
}
Also learned to edit my deployer to add in the args for the constructor, and made sure to pass the SwapRouter address from here:
await deploy("YourContract", {
// Learn more about args here: https://www.npmjs.com/package/hardhat-deploy#deploymentsdeploy
from: deployer,
args: [ '0xE592427A0AEce92De3Edee1F18E0157C05861564' ],
log: true,
waitConfirmations: 5,
});
This all worked, and I am able to see this in the debug console on my local scaffold-eth, running on rinkeby:
I also made sure to have enough funds in my wallet for gas, etc:
When I try to hit send in the swapExactInputSingle function for any amount of DAI, I get the following error every time:
{
"reason":"cannot estimate gas; transaction may fail or may require manual gas limit",
"code":"UNPREDICTABLE_GAS_LIMIT",
"error":{
"code":-32000,
"message":"execution reverted"
},
"method":"estimateGas",
"transaction":{
"from":"0xF59FBfd44C9e495542D46109F81416bd3fC38Ed7",
"to":"0xcFc57b48365133105F6877a02126673B7b906a55",
"data":"0x73bd43ad000000000000000000000000000000000000000000000000000000000000000a",
"accessList":null
}
}
I get this error as soon as I hit send, and am never prompted to approve my DAI or anything else. Other simple contracts have worked and I am able to send transactions on Rinkeby properly, so assume this is due to the added complexity of using the Uniswap API, etc.
The recipient is not msg.sender, it should be address(this). Also you have to take care of the functions TransferHelper, who is going to receive, who is going to send.
try setting your config to look like this. I believe the issue you are experiencing is caused by metamask improperly estimating gas
networks: {
hardhat: {
forking: {
url: `https://eth-mainnet.alchemyapi.io/v2/YOUR_KEY_HERE`,
// url: `https://mainnet.infura.io/v3/${process.env.INFURA_KEY}`,
// url: 'http://localhost:8545',
},
gasPrice: 0,
initialBaseFeePerGas: 0,
}
The token address of DAI, WETH9, USDC are of the Ethereum mainnet and you have deployed it on Rinkeby. You have to use the token contract address of Rinkeby.
Not sure about any other issues if present.
I wrote a Money-saving smart contract where users deposit ETH and define the amount of time they want to keep that ETH in the contract, EX: USER X deposits 2ETH for one year, they can only withdraw their ETH after that period.
But solidity linter keeps telling me that I should not rely on block.timestamp to make decisions.
This is the Saving struct I'm using to map every address to a balance and endTime:
struct Saving {
uint256 balance;
uint256 endTime;
}
Here is my function modifier where I require the withdrawal time to be greater than the endTime I stored at the deposit moment:
modifier onlyValidTimeWithdraw() {
require(
block.timestamp > balances[msg.sender].endTime,
"You cannot withdraw yet"
);
_;
}
This is the message I get through the linter.
After doing some research I found that I should not have time-dependent conditions in my contract since miners can manipulate timestamps, but I did not find any alternative to this.
Miners can manipulate block timestamp to an extent of approx. few seconds, which is enough to affect business logic depending on a second-level precision.
function betLottery() external {
// the block.timestamp can be affected by a miner
// and they can submit their own winning transaction
if (block.timestamp % 2 == 0) {
win();
}
}
But since your logic depends on much longer period, I'd simply ignore or suppress the warning.
Or if it fits your usecase, you can validate against the block number, which most linters allow.
struct Saving {
uint256 balance;
uint256 endBlock;
}
require(
block.number > balances[msg.sender].endBlock,
"You cannot withdraw yet"
);
I have a pretty boilerplate test token that I'm going to use to support a DApp project. Key functions I have questions regarding are as follows:
constructor() {
name = "Test Token";
symbol = "TTKN";
decimals = 18;
_totalSupply = 1000000000000000000000000000000;
//WITHOUT DECIMALS = 1,000,000,000,000; should be 1 trillion
balances[msg.sender] = _totalSupply;
emit Transfer(address(0), msg.sender, _totalSupply);
}
function totalSupply() public override view returns (uint256) {
return _totalSupply - balances[address(0)];
}
First, a quick question about decimals and supply: did I set this up correctly to create 1 trillion of the TTKN token? And do I really need so many decimal places?
Second, what exactly is address(0)? My understanding of the constructor is that address(0) first transfers all the tokens to msg.sender, which is me, the person who deploys this contract.
And finally, what are the best practices for initially distributing the tokens? What I want is basically as follows:
a) Myself and a few other devs each get 1% of the initial supply
b) Our DApp, a separate smart contract, will get 50% of the initial supply, and will use this to reward users for interacting with our website/project
c) To accomplish a) and b), me, the contract deployer, should manually transfer these tokens as planned?
d) The rest of the coins... available to go on an exchange somehow (maybe out of scope of question)
So now that I've deployed this test token on remix and am getting a feel for how to transfer around the tokens, I want to understand the above points in relation to our project. Is my plan generally acceptable and feasible, and is it the case that as the initial owner I'm just making a bunch of transfer calls on the ETH mainnet eventually when I deploy?
did I set this up correctly to create 1 trillion of the TTKN token?
This is one of the correct ways. More readable would be also:
_totalSupply = 1000000000000 * 1e18;
or
// 10 to the power of
_totalSupply = 1000000000000 * (10 ** decimals);
^^ mind that this snippet performs a storage read (of the decimals variable) so it's more expensive gas-wise
a well as
_totalSupply = 1000000000000 ether;
^^ using the ether unit, an alias for * 1e18
what exactly is address(0)
If it's in the first param of the Transfer event, it means the tokens are minted. If it's in the second param, it means a burn of the tokens.
A token contract which creates new tokens SHOULD trigger a Transfer event with the _from address set to 0x0 when tokens are created.
Source: https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md#transfer-1
initially distributing the tokens
You can perform the distribution in the constructor. For the sake of simplicity, my example shows the "exchange" as a regular address managed by your team that will send the tokens to the exchange manually. But it's possible to list a token on a DEX automatically as well.
_totalSupply = 1000000000000 * 1e18;
address[3] memory devs = [address(0x123), address(0x456), address(0x789)];
address dapp = address(0xabc);
address exchange = address(0xdef);
// helper variable to calculate the remaining balance for the exchange
uint256 totalSupplyRemaining = _totalSupply;
// 1% for each of the devs
uint256 devBalance = _totalSupply / 100;
for (uint i = 0; i < 3; i++) {
balances[devs[i]] = devBalance;
emit Transfer(address(0x0), devs[i], devBalance);
totalSupplyRemaining -= devBalance;
}
// 50% for the DApp
uint256 dappBalance = _totalSupply / 2;
balances[dapp] = dappBalance;
emit Transfer(address(0x0), dapp, dappBalance);
totalSupplyRemaining -= dappBalance;
// the rest for the exchange
balances[exchange] = totalSupplyRemaining;
emit Transfer(address(0x0), exchange, totalSupplyRemaining);