DISCLAIMER: Everything provided in this thread is subject to my own interpretation of how things work.
Solmate's ERC20 implementation touts itself as a modern, gas-efficient ERC20 with integrated support for EIP-2612. Today, I will only touch the "stuff that caught my interest", so I don't bloat the thread.
The first thing we note is that its an abstract contract. Unlike OZ's implementation, this means that it cannot be deployed on its own; it must be inherited by your own implementation.
One variable that caught my attention in Solmate's ERC20 is `decimals`, which specifies the number of decimal places the token should have. It caught my interest because it was uint8 and immutable.
Why uint8?
Using uint8 as a data type is preferable when the value stored in a variable falls between 0 and 255. This is because smaller data types are more gas efficient. Decimals have a maximum value of 18, which is well within the `uint8` range.
Why immutable?
Upon first inspection of the contract, I believed that the decimals were defined as immutable since they could be frequently accessed by token consumers. This is more cost-effective than using regular state variables as immutables are not stored in storage.
However, I had some doubts and reached out to the Secureum #solidity channel for clarification.
@patrickd_de made a valid argument regarding the decimals of a token. Changing them would be similar to having the ability to arbitrarily multiply or divide the token value by 10. This could lead to issues with other protocols that depend on a fixed decimal precision.
But .. doesn't that apply to name/symbol as well? Then, another member chimed in with an excellent point - many contracts use a constant or a hardcoded fn for name/symbol (seaport), so state var is probably used to make the implementation reusable & strings cant be immutable yet.
Before we go further, we need to clarify one thing. What does "unchecked" mean in Solidity?
The "unchecked" keyword in Solidity disables runtime checks for specific operations, which can optimize performance and reduce gas costs. However, it also introduces risks as it permits operations to be executed without proper validation.
When "unchecked" is utilized, Solidity omits various checks that are normally performed at runtime to guarantee safe and correct code execution. For instance, integer overflow and underflow checks are skipped, which can lead to faster code execution with lower gas costs.
Lets check this fn transfer(address to, uint256 amount)
You might be YELLING atm - THERE'S NO ZERO ADDRESS CHECK!!11!!!11, IT DOESNT CHECK IF THE USER HAS BALANCE AT ALL, but hold on for a second.
Now, if you try subtracting 1 from 0 in Solidity >= 8.0.0 we know what happens, right?
That's a super smart way to do two checks (underflow and zero address) at once without having to spend additional gas. Smart, right?
Remember totalSupply is uint256? This means all the user balances < totalSupply. Now that we know this, you probably understand why the snippet of code in unchecked cannot overflow.
If you looked carefully at the start of the contract there's a note: Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it. Taking in account that, you might find that there's actually a possibility of overflow.
Here's why: The invariant is relying on the fact that a user only mint new tokens through the `_mint` function, but it's actually possible to update the user's balance in a contract that inherits Solmate's ERC20 and modify the balance, because balanceOf is a public variable.
The potential issue with relying on developers to not break the invariant instead of enforcing it through code is that it increases the risk of errors due to oversights, misunderstandings, or deliberate malicious intent.
By omitting over/underflow protection, gas costs can be reduced, but this can lead to unexpected and costly errors that could have been prevented.
However, the decision to rely on developers comes with potential benefits. It allows for the freedom to use cases that are not widely known or accepted yet.
Nevertheless, this approach also means that developers must exercise great care and responsibility to avoid breaking the invariant and ensure the secure and reliable execution of the contract.
function transferFrom(...)
The comment in the code snippet says that it "Saves gas for limited approvals` indicates that it saves gas by reducing the number of SSTORE operations.", but how does that happen?
By loading the value of allowed from memory, it avoids the need for a potentially expensive SSTORE operation. This reduces gas consumption and, thus the cost of executing the contract.
The rest of the function operates similarly to the `transfer` function discussed earlier.
Aaand, thats a wrap! Hopefully that helps you one way or another!
• • •
Missing some Tweet in this thread? You can try to
force a refresh
Imagine having an NFT builder, that simplifies NFT creation through a user-friendly interface. Although the initialization data (e.g., name, symbol, royalties) may vary, the code is mostly identical.
As an auditor, it's crucial to know the ERC20 approve vulnerability.
It's a sneaky exploit that can be used to manipulate transaction ordering and double spend allowance.
In this 🧵, I'll share tips on identifying and protecting against this exploit during your audits.
1/ First, let's start with some context.
Whenever you buy tokens or transfer them to other accounts, you interact with the token contract in a predefined way.
In general, the token contract is usually written according to a certain standard.
2/ ERCs (Ethereum Request for Comment) are documents that contain standards and notes for smart contracts development.
Standards describe what should be implemented in the smart contract code and how to interact with it.
You should learn Solidity first. There's no shortcut to that.
However, today I'm sharing with you MY step-by-step guide on how I PERSONALLY studied it, hopefully to help you get started and save you some time 👇
1/ Patrick Collin's course for Blockchain, Solidity, and Full Stack Web3 Development with JavaScript. This one doesn't need an explanation.
2/ cryptozombies.io
Superb resource inspired by the "learn by doing" philosophy. Their courses will take you through various scenarios to help you wet your feet with Solidity.