December’s Solidity Fridays with transmissions11 is really excellent, and I think a very high-leverage way to learn how good Solidity actually gets written. When the guest is especially good, as with transmissions, I put Solidity Fridays in the same sort of category as Destroy All Software – teaching by communicating models and patterns of thought, rather than regurgitating tutorial content.
Anyway, here are my notes in case somebody else finds them useful:
When emitting events, t11s emits instances of the contract rather than the address directly; the compiler will swap it out for an address anyway, but this approach gives you greater type safety
CREATE2to revert if the contract has already been deployed, by adding a salt of the underlying address to the deployment
Addresses are 20 bytes, salts have to be 32 bytes. So we call
fillLast12Bytesto add the remaining bytes (provided in Solmate’s
The gas cost of
>is equivalent to
!=when the comparator is
0, more expensive otherwise.
Illustration of his opinionated approach to smart contract development: “the performance of the code for users who aren’t stupid matters” - that’s why the [[ERC20]] implementation in [[Solmate]] doesn’t stop you from transferring into the contract’s address. “I’m not raising the cost for everyone else”
fdivis like division, but accounting for the bases. ‘Scale this down by the contract’s base’. Multiply the numerator by
baseUnitand then divide the
numerator * baseUnitby the denominator. Also checks for overflow on
numerator * baseUnit, since overflow isn’t protected in assembly calls.
In general, keep external calls all the way at the end of the function – including after any events are emitted – to make reentrancy more difficult.
More important with eg ERC777 since eg
safeTransferFrommight allow arbitrary code execution (https://eips.ethereum.org/EIPS/eip-777)
Removing things from the end of an array is significantly cheaper than from the beginning, since in the latter case you have to move everything over.
uint256 currentIndex = withdrawalQueue.length - 1; for (; ; currentIndex--)is better in this case than initialising the
uint56 i = withdrawalQueue.length - 1since we’re only doing effects (the tx will revert by underflow automatically), so we don’t need to check the length. Saves gas.
trustedboolean to the strategies, which is then checked on deposit and withdrawal. Makes it easier for EOAs to manage vaults without having to be wrapped in some other contract. Also makes it possible to disable withdrawal from strategies easily if they’re malicious in some way.
Two reads to the same struct from
getStrategyData[strategy]has no extra gas cost, since it gets optimised by the compiler into one single
SLOAD. & makes it clearer to read by a dev where it’s coming from.
uncheckedwhen you know you won’t underflow or overflow, and so can therefore do without the safety. Saves gas.
uncheckedisn’t leaky - it won’t uncheck in nested function calls
The implementation of Compound’s cToken is a little funky, since in a lot of places function calls return an error code rather than revert. So sometimes you need to
require(cToken.blah() == 0)to ensure it succeeded.