Batch Minting vs Lazy Minting Directly to user wallet - ethereum

I'm writing a solidity smart contract and I'm trying to decide which
is more performant
saves gas fees
This is based on a NFT marketplace
Option 1:
Mint 10,000 NFTs to my wallet, and then transfer each NFT to a buyers wallet on purchase
Option 2:
Mint directly to the buyers wallet on purchase
Assumptions:
For now lets assume the tokenURI is constant and we dont need to rebuild it each time in option 2, because the time to upload to IPFS can be alot
Questions:
Does minting cost more than a transfer, or are they the same?
Is there a "quantity" where batch minting is more efficient?
Is there any scenario where batch minting can be better?
If we ignore the assumption and time to upload to IPFS is an added variable, would batch minting be more desirable?

Let me answer all the questions,
Does minting cost more than a transfer, or are they the same?
Answer : Minting cost will be higher than transfer. Because on minting function you will push a new value to the storage. mapping(uint256 => address) which you update address non-zero from zero, so it costs much more than non-zero to non-zero.
Is there a "quantity" where batch minting is more efficient?
Answer : With batch minting it can be efficient because some checks (value, amount etc.) done once at the beginning of transaction.
Is there any scenario where batch minting can be better?
Answer : You can use merkle-proofs for initial owners. But that approach usually used for whitelist, but i think it can be fit on that situation as well. There some other standarts like ERC721-A which is more gas efficient and ERC1155 which is more gas efficient on batch transactions. Maybe there is new standarts too...
If we ignore the assumption and time to upload to IPFS is an added variable, would batch minting be more desirable?
Answer : I dont think there is a connection between ipfs base uri and batch minting. The only thing you have to care about is wont let users see collection image order before minting. To hide this use a hidden NFT and assign that uri to all NFT's until all get minted and revealed.

Related

Estimate gas of minting erc20 token without deploying contract

I want to estimate gas of minting erc20 token without deploying contract. I am aware of contract.estimateGas but I dont have any contract address to test it. Is it possible to do it without providing contract address?
Each ERC20 contract can have different gas requirements, depending on its implementation.
Generally, token mint consists of one storage write, one event log, few memory operations, and usually one storage read (validating whether the user executing this function is authorized to mint tokens)... Which in total usually costs tens of thousands gas units.
But the actual cost is affected not only by the amount of operations but also number of functions in the contract, whether the mint function is external or public, and many other factors.
TLDR: "Usually tens of thousands of gas units" is the closest you can get without estimating against a specific implementation.

Solidity: deploy contract, mint 50 nfts, and send nft to 50 hard coded addresses all in one step possible?

Is it possible and or advisable to, in one step, deploy a smart contract, have it mint 50 nfts and send those 50 nfts to 50 different addresses all in one transaction?
Or would it be better practice to split these 3 different actions into two different transactions?
Would love any references to existing smart contracts that do anything similar!
Thank you!
It is not advisable to run Solidity functions with long running for loops that may exhaust the available gas. Because you are minting 50 NFTs it is likely you cannot fit all the execution function to a single Ethereum transaction and you need to do minting in smaller batches.

Why transaction gas used increased after London hardfork?

I am developing a smart contract and I recently updated Hardhat to 2.6.x, which has london hardfork as default network settings. My issue is that gas used by my transaction increased by more than 50% in most cases. When I switch back to berlin hardfork, the cost is "correct" again.
Can somebody explain me why? Is there some updated opcode gas cost list? Or did gas cost for storing / loading values from storage increased? I am failing in finding the source of the increase.
I read EIP-1559, but there is no much information about opcode updates, just gas price and model of a base fee burning, which should not increase transaction gas usage imo.
I use ethers and property gasUsed on transaction receipt to get used gas.
It looks like EIP-3529 is the reason of the increase. EIP-3529 is reducing gas refunds and for one NFT transfers its ~20k gas increase because while transferring NFT you set storage of previous owner and (in some cases) his balance to 0, which are refunded less after london hardfork. Unfortunate.

Storing gas inside a contract

Is it possible to store gas that is not used inside a contract, so it can be used at a later time (presuming it is possible to require a certain amount of gas calling a function)?
I am trying to write a contract that requires user input and does something based upon that input at a later moment in time that will require gas.
As it does not sound really attractive to pay that gas out of the contract owners pocket I am looking for a way to make the user of the contract pay for the gas it needs to complete the request.
In fact, it is really possible to store gas in a contract for later use.
There are some operations in EVM that can return some gas that was payed before:
SSTORE: changing storage value from non-zero to zero releases 15000 gas
SELFDESTRUCT: destroying contract releases 24000 gas
That means that e.g. storing some value requires 20k gas, but deleting it form storage requires only 5k gas and releases 15k gas for later usage in the same transaction. Actually this is a reward for clearing up blockchain storage.
You can get more details by searching "refund" in Yellowpaper.
There is GasToken project that uses this very feature to store gas in contract when it is cheap and release (and use) when it is expensive.
To be clear, I don't think this allows user to issue transactions without paying gas at all.
Seems like you have mixed up a little bit the meaning of gas.
The gas is the Ether you have to pay to have your transaction mined. The gas is always paid by the address that calls the function(Contract) and not from the Contract itself or the owner of the Contract so it doesn't come out of the owners pocket. Also gas is basically ether so "storing gas" is to store Ether in a contract but you cannot store the gas that is used to mine the transaction. If you want to store Ether you have to send Ether to the Contract or have the users send Ether when they call a function.

Sending offline transactions to mainnet is "flaky."

This is the code that I'm using to send signed transactions to mainnet programmatically:
import Web3 from 'web3'
import EthereumTx from 'ethereumjs-tx'
const web3 = new Web3(new Web3.providers.HttpProvider(INFURA_URL))
const Contract = new web3.eth.Contract(ABI, CONTRACT_ADDRESS)
const createItem = (name, price, nonce, callback) => {
console.log(`Nonce: ${nonce}. Create an Item with Name: ${name}, Price: ${price}`)
const data = Contract.methods.createItem(name, price).encodeABI()
const tx = new EthereumTx({
nonce: nonce,
gasPrice: web3.utils.toHex(web3.utils.toWei('4', 'gwei')),
gasLimit: 400000,
to: CONTRACT_ADDRESS,
value: 0,
data: data,
})
tx.sign(new Buffer(MAINNET_PRIVATE_KEY, 'hex'))
const raw = '0x' + tx.serialize().toString('hex')
web3.eth.sendSignedTransaction(raw, callback)
}
export default createItem
I have to mass create (i.e. populate) items in my contract, and I want to do it programatically. However, while the code works well in ropsten, it fails to send all the transactions in mainnet; it only sends the first few transactions and doesn't send the rest. The errors are not helpful because this error is usually guaranteed to occur:
Unhandled rejection Error: Transaction was not mined within 50 blocks, please make sure your transaction was properly sent. Be aware that it might still be mined!
I wonder how other people do when they have to send a lot of transactions to Ethereum mainnet today. Is there anything I'm doing wrong?
You basically cannot reliably do what you guys are trying to do. Here's a set of problems that arises:
Transactions only succeed in nonce order. If an early nonce is pending (or worse, went missing), the other transaction will pend until the early nonce has been consumed (or it gets removed from the mempool).
There's no hard rule for when a transaction will drop from the mempool. This is scary when you've made a nonce error or an intermediary nonce has not reached the network for some reason because you don't know what is going to happen when you finally post that nonce.
Many transactions with the same nonce can be sent. They are very likely to be selected by gas price (because miners are incentivized to do exactly that). One useful trick when something strange has happened is to clear out your nonces by sending a bunch of high gas price, zero value transactions. You might call this an increment method. Remember this comes with a cost.
A lot of tools do one of two things to handle nonces: a live read from getTransactionCount() or a read from getTransactionCount() followed by incrementing for each additional transaction you send. Neither of these work reliably: for the first, transactions are sometimes pending but not visible in the pool yet. This seems to especially happen if gas price is below safemin, but I'm not entirely sure what is happening here. For the second, if any other system sends a transaction with the same address, it will not work.
So, how do we work around this?
A smart contract is a straightforward way to reduce a transaction from many sender nonces to few sender nonces. Write a contract that sends all your different transactions, then send the budget to that contract. This is a relatively high cost (in terms of time/effort/expertise) way to solve the problem.
Just do it anyway, batch style. When I've had to send many transactions manually, I've batched them in to sets of 10 or so and gone for it. Incrementing the nonce manually each time (because the transaction is usually not on the network yet) and then waiting sufficiently long for all the transactions to confirm. Don't rely on pending transactions on Etherscan or similar to determine whether this is working, as things often vanish unpredictably from this level. Never reuse a nonce for a different transaction that isn't a 0ing high gas transaction - you will screw it up and you'll end up sending the same transaction twice by mistake.
Serialize. One-by-one you post a transaction, wait for it to confirm, increment your nonce. This is probably the best of the easy-to-implement automated solutions. It will not fail. It might buffer forever if you have a constant stream of transactions. It also assures you can never have more than one transaction per block, limiting your throughput to 4 a minute or so.
Fire and retry. This is a little sketchy because it involves reusing nonces for different transactions. Send all (or some large batch) of your transactions to the network. If any fail, restart from the failure nonce and send again. There's possibly a more intelligent solution where you just try to swap out the missing nonce. You'll need to be very careful you never send a transaction that is secretly in the pending-but-not-visible pool.
New address for every transaction. A buffering step of distributing funds to your own addresses ensures you never screw it up for other people. It does double your transaction time and cost though.
I think some variant of fire and retry is what most of the big services (like pools and exchanges) do. Some of these can be improved by splitting the budget over a few addresses as available (reducing the collision frequency).