Description
https://github.com/ithacaxyz/odyssey-examples/tree/main/chapter1/eof
Primer
EOF disables a number of opcodes. Right now, if your contract contains any of those it will get compiled but will not be deployable.
The banned opcodes are:
- Code introspection opcodes:
CODESIZE
,CODECOPY
,EXTCODESIZE
,EXTCODECOPY
,EXTCODEHASH
. - Dynamic jumps:
JUMP
,JUMPI
,PC
. - Gas introspection opcodes:
GAS
,GASLIMIT
,GASPRICE
. - Legacy call instructions:
CREATE
,CALL
,DELEGATECALL
,CREATE2
,STATICCALL
. - Legacy opcodes:
SELFDESTRUCT
,CALLCODE
.
Plan
-
Test with
forge test --eof
. -
Tests
test__codesize()
to be removed. We cannot usecodesize
anymore. Not really a dealbreaker._brutalizeMemory()
. We will simply have to spamkeccak256
andmcopy
.- Have all the etch functions for factories and escape hatches.
-
Transpiling
- Identify a list of problematic contracts and easy contracts. Sort them from most problematic to least.
- Hit the most problematic contracts first.
Escape hatch
https://github.com/Vectorized/escape-hatch
To be deployed via Nick's factory:
-
EXTCODECOPY
,EXTCODESIZE
-
CREATE
- Reverts with returndata upon failure. Returns the address otherwise.
-
CREATE2
- Reverts with returndata upon failure. Returns the address otherwise.
- Since Nick's doesn't return the address, we will just have to make our own factory.
-
SELFDESTRUCT
to force send Ether- Takes in address.
- Reverts with empty data upon failure. Returns true otherwise.
-
Gas limited
CALL
- Forwards value.
- Returns/Reverts with all returndata.
-
Gas limited
STATICCALL
- Returns/Reverts with all returndata.
I think it's best that we make a separate repo while devving these escape hatches. Doesn't need npm.
Notes
-
All addresses passed in to
EXTCALL
,EXTDELEGATECALL
,EXTDELEGATECALL
must have upper 96 bits cleaned. Also, their behavior are different. -
Hand translation might not be that hard. We can use functions to encapsulate if it looks better.
Transpilation
We might define an eof/src
and eof/test
directory. These directories are intentionally not in src
or test
, so that when people install Solady for use in a regular Solidity project, they still can compile hassle free.
I'm thinking if it might be better if we keep the EOF stuff in Solady:
- Aggregate traffic into Solady.
- It keeps the diffing easier.
- Say if we put the stuff in Soledge, and someone who wants to use the
tstore
libs on a non EOF project (which we expect most users to), they might run into compilation hassles. - I'd also like to reserve Soledge for the worse case scenario where Solidity becomes a completely different language in the future (in a bad way), and Soledge will have to exist to add support for that.
I feel that some stuff might not be fit for automatic transpilation. We'll know it when we get there.
A lot of the code will be a one-time transpilation. There are also many devious edge cases. A hand-transpilation approach might be overall more effort efficient even in the long run. Additionally, hand-transpiling will allow us to perform EOF specific hand-optimizations if time permits.
Effort estimates
- 1 week to craft, mine vanity address, and deploy escape hatches.
- 1 week to refactor the test base contracts
TestPlus.sol
,Brutalizer.sol
,SoladyTest.sol
,Test.sol
. - 1 week to refactor the original test contracts to avoid using banned opcodes.
- 2 weeks of porting the contracts to EOF.
These will be full-time efforts.
In the long term, we will need to maintain two 1:1 copies of Solady, the legacy in src
and eof
.
Priorities
Our top priority is drop-in compatibility with EOF Solidity compiler.
We'll just go with full code-reuse style. We do NOT need inline anything.
In fact, I have a feel that contracts with more code reuse are better suited for EOF, with the subroutine stuff.
LibClone might actually have lesser SLOCs with rampant code reuse.
Regression
Going forward, Solady's legacy code will still be at the forefront of cutting edge features.
We will need to have some discipline on keeping the EOF quickly up to parity with the non-EOF code, but if resources are limited, we will just create a new backlog issue on porting it to EOF.
File analysis
[
{
"srcPath": "src/Milady.sol",
"lastModifiedGitTimestamp": 1730050433
},
{
"srcPath": "src/accounts/Receiver.sol",
"lastModifiedGitTimestamp": 1725453789
},
{
"srcPath": "src/auth/Ownable.sol",
"lastModifiedGitTimestamp": 1697754631
},
{
"srcPath": "src/auth/OwnableRoles.sol",
"lastModifiedGitTimestamp": 1720385905
},
{
"srcPath": "src/tokens/ERC2981.sol",
"lastModifiedGitTimestamp": 1686120478
},
{
"srcPath": "src/tokens/ERC6909.sol",
"lastModifiedGitTimestamp": 1703921789
},
{
"srcPath": "src/utils/Base64.sol",
"lastModifiedGitTimestamp": 1709232746
},
{
"srcPath": "src/utils/DateTimeLib.sol",
"lastModifiedGitTimestamp": 1705005025
},
{
"srcPath": "src/utils/DynamicBufferLib.sol",
"lastModifiedGitTimestamp": 1726471158
},
{
"srcPath": "src/utils/EIP712.sol",
"lastModifiedGitTimestamp": 1698938547
},
{
"srcPath": "src/utils/ERC1967FactoryConstants.sol",
"lastModifiedGitTimestamp": 1720878475
},
{
"srcPath": "src/utils/EfficientHashLib.sol",
"lastModifiedGitTimestamp": 1724696571
},
{
"srcPath": "src/utils/EnumerableSetLib.sol",
"lastModifiedGitTimestamp": 1728734415
},
{
"srcPath": "src/utils/FixedPointMathLib.sol",
"lastModifiedGitTimestamp": 1725075080
},
{
"srcPath": "src/utils/JSONParserLib.sol",
"lastModifiedGitTimestamp": 1705066803
},
{
"srcPath": "src/utils/LibBit.sol",
"lastModifiedGitTimestamp": 1717740645
},
{
"srcPath": "src/utils/LibBitmap.sol",
"lastModifiedGitTimestamp": 1721699660
},
{
"srcPath": "src/utils/LibBytes.sol",
"lastModifiedGitTimestamp": 1730857419
},
{
"srcPath": "src/utils/LibMap.sol",
"lastModifiedGitTimestamp": 1690778106
},
{
"srcPath": "src/utils/LibPRNG.sol",
"lastModifiedGitTimestamp": 1719111683
},
{
"srcPath": "src/utils/LibRLP.sol",
"lastModifiedGitTimestamp": 1726471715
},
{
"srcPath": "src/utils/LibSort.sol",
"lastModifiedGitTimestamp": 1727873015
},
{
"srcPath": "src/utils/LibString.sol",
"lastModifiedGitTimestamp": 1730857419
},
{
"srcPath": "src/utils/LibTransient.sol",
"lastModifiedGitTimestamp": 1730765339
},
{
"srcPath": "src/utils/MerkleProofLib.sol",
"lastModifiedGitTimestamp": 1697511114
},
{
"srcPath": "src/utils/MinHeapLib.sol",
"lastModifiedGitTimestamp": 1723492714
},
{
"srcPath": "src/utils/ReentrancyGuardTransient.sol",
"lastModifiedGitTimestamp": 1730497227
},
{
"srcPath": "src/utils/SafeCastLib.sol",
"lastModifiedGitTimestamp": 1715433563
},
{
"srcPath": "src/utils/Initializable.sol",
"scores": {
"extcodesize": 1
},
"lastModifiedGitTimestamp": 1705139261
},
{
"srcPath": "src/utils/RedBlackTreeLib.sol",
"scores": {
"codesize": 1
},
"lastModifiedGitTimestamp": 1717740645
},
{
"srcPath": "src/utils/ReentrancyGuard.sol",
"scores": {
"codesize": 1
},
"lastModifiedGitTimestamp": 1706263836
},
{
"srcPath": "src/tokens/ERC20.sol",
"scores": {
"staticcall": 1
},
"lastModifiedGitTimestamp": 1729135343
},
{
"srcPath": "src/tokens/ERC20Votes.sol",
"scores": {
"staticcall": 1
},
"lastModifiedGitTimestamp": 1729135343
},
{
"srcPath": "src/tokens/ERC4626.sol",
"scores": {
"staticcall": 1
},
"lastModifiedGitTimestamp": 1717740645
},
{
"srcPath": "src/utils/MetadataReaderLib.sol",
"scores": {
"staticcall": 2
},
"lastModifiedGitTimestamp": 1723462389
},
{
"srcPath": "src/accounts/ERC4337Factory.sol",
"scores": {
"codesize": 1,
"call": 1
},
"lastModifiedGitTimestamp": 1703276182
},
{
"srcPath": "src/accounts/ERC6551Proxy.sol",
"scores": {
"codesize": 1,
"delegatecall": 1
},
"lastModifiedGitTimestamp": 1710219546
},
{
"srcPath": "src/utils/Multicallable.sol",
"scores": {
"codesize": 1,
"delegatecall": 1
},
"lastModifiedGitTimestamp": 1725108562
},
{
"srcPath": "src/utils/UpgradeableBeacon.sol",
"scores": {
"codesize": 2,
"extcodesize": 1
},
"lastModifiedGitTimestamp": 1715045031
},
{
"srcPath": "src/auth/EnumerableRoles.sol",
"scores": {
"staticcall": 2
},
"lastModifiedGitTimestamp": 1728811386
},
{
"srcPath": "src/tokens/WETH.sol",
"scores": {
"codesize": 2,
"call": 1
},
"lastModifiedGitTimestamp": 1719095021
},
{
"srcPath": "src/utils/CREATE3.sol",
"scores": {
"extcodesize": 1,
"create2": 1,
"call": 1
},
"lastModifiedGitTimestamp": 1720878475
},
{
"srcPath": "src/utils/DynamicArrayLib.sol",
"scores": {
"codesize": 2,
"codecopy": 2
},
"lastModifiedGitTimestamp": 1728355500
},
{
"srcPath": "src/utils/GasBurnerLib.sol",
"scores": {
"codecopy": 2,
"staticcall": 1
},
"lastModifiedGitTimestamp": 1721303834
},
{
"srcPath": "src/utils/WebAuthn.sol",
"scores": {
"staticcall": 2
},
"lastModifiedGitTimestamp": 1730239265
},
{
"srcPath": "src/accounts/Pod.sol",
"scores": {
"codesize": 2,
"call": 2
},
"lastModifiedGitTimestamp": 1727862888
},
{
"srcPath": "src/utils/DeploylessPredeployQueryer.sol",
"scores": {
"codesize": 1,
"extcodesize": 1,
"call": 2
},
"lastModifiedGitTimestamp": 1713208829
},
{
"srcPath": "src/utils/ERC1967Factory.sol",
"scores": {
"create": 1,
"create2": 1,
"call": 2
},
"lastModifiedGitTimestamp": 1706770409
},
{
"srcPath": "src/utils/UUPSUpgradeable.sol",
"scores": {
"codesize": 2,
"staticcall": 1,
"delegatecall": 1
},
"lastModifiedGitTimestamp": 1715045031
},
{
"srcPath": "src/utils/P256.sol",
"scores": {
"staticcall": 4
},
"lastModifiedGitTimestamp": 1723742125
},
{
"srcPath": "src/utils/LibZip.sol",
"scores": {
"codesize": 4,
"codecopy": 3,
"delegatecall": 1
},
"lastModifiedGitTimestamp": 1707000199
},
{
"srcPath": "src/accounts/LibERC6551.sol",
"scores": {
"extcodesize": 2,
"extcodecopy": 7,
"call": 1
},
"lastModifiedGitTimestamp": 1709789868
},
{
"srcPath": "src/tokens/ERC721.sol",
"scores": {
"codesize": 6,
"extcodesize": 1,
"call": 1,
"staticcall": 1
},
"lastModifiedGitTimestamp": 1711338124
},
{
"srcPath": "src/utils/SSTORE2.sol",
"scores": {
"codesize": 1,
"extcodesize": 4,
"extcodecopy": 3,
"create": 1,
"create2": 2,
"call": 1
},
"lastModifiedGitTimestamp": 1728067022
},
{
"srcPath": "src/accounts/ERC1271.sol",
"scores": {
"codesize": 1,
"gaslimit": 2,
"gasprice": 8,
"staticcall": 2
},
"lastModifiedGitTimestamp": 1730050055
},
{
"srcPath": "src/accounts/ERC6551.sol",
"scores": {
"codesize": 2,
"extcodecopy": 3,
"call": 2,
"staticcall": 3
},
"lastModifiedGitTimestamp": 1717372305
},
{
"srcPath": "src/utils/ECDSA.sol",
"scores": {
"staticcall": 8
},
"lastModifiedGitTimestamp": 1730845513
},
{
"srcPath": "src/utils/Lifebuoy.sol",
"scores": {
"codesize": 4,
"extcodesize": 3,
"call": 5,
"staticcall": 1
},
"lastModifiedGitTimestamp": 1724378720
},
{
"srcPath": "src/accounts/ERC4337.sol",
"scores": {
"codesize": 11,
"extcodesize": 2,
"call": 5,
"staticcall": 1,
"delegatecall": 1
},
"lastModifiedGitTimestamp": 1713297028
},
{
"srcPath": "src/tokens/ERC1155.sol",
"scores": {
"extcodesize": 3,
"call": 4,
"staticcall": 10
},
"lastModifiedGitTimestamp": 1718114922
},
{
"srcPath": "src/utils/ext/delegatexyz/DelegateCheckerLib.sol",
"scores": {
"staticcall": 20
},
"lastModifiedGitTimestamp": 1720170364
},
{
"srcPath": "src/utils/SignatureCheckerLib.sol",
"scores": {
"codesize": 2,
"extcodesize": 2,
"call": 3,
"staticcall": 21
},
"lastModifiedGitTimestamp": 1730845513
},
{
"srcPath": "src/utils/SafeTransferLib.sol",
"scores": {
"codesize": 29,
"extcodesize": 11,
"create": 4,
"call": 21,
"staticcall": 6
},
"lastModifiedGitTimestamp": 1729206025
},
{
"srcPath": "src/utils/LibClone.sol",
"scores": {
"codesize": 23,
"codecopy": 3,
"extcodesize": 25,
"extcodecopy": 16,
"create": 11,
"create2": 21,
"call": 11,
"staticcall": 16
},
"lastModifiedGitTimestamp": 1730767406
}
]