Experimental contracts that try to break Solidity and find new techniques.
Go to remix, paste in gasTestor.sol
and input your varaibles inbetween the gasleft()
s
- https://ethereum.stackexchange.com/questions/37549/array-or-mapping-which-costs-more-gas
- https://ethereum.stackexchange.com/questions/77099/efficient-bit-packing
- https://ethereum.stackexchange.com/questions/15166/difference-between-require-and-assert-and-the-difference-between-revert-and-thro
- https://ethereum.stackexchange.com/questions/19380/external-vs-public-best-practices
- Clearing
address
variales in code: useaddress(bytes20(''));
oraddress(0)
, this will set the address to0x0000000000000000000000000000000000000000
. The reason we do this is because when you have anaddress
orbytes20
param, you are forced to input an address. However, you can set it withaddress(bytes20(''));
within the code. calledFunctionExample{ value: msg.value } (param 1, param2)
is how you send ether to a specific function in a specific contractrequire()
at the bottom of the tx will revert he whole tx and nothing with get passed & the error message costs more gas the longer it is.Stack too deep
is caused when a function is using more than 16 slots of storage. Each param, return variable andstorage
declaration is 1 variable (strings count as 2 variables).- Dynamic arrays with no assigned value will not return anything when uint256:
0
is called, but instead it will revert. When you push or assign a value, it will assign it to the uint256:0
position, making the length:1
. When no address is assigned to an empty array, not even0x000...
will be returned. - In public funcs, Solidity copies array arguments to memory, while external functions can read directly from calldata. Memory allocation is expensive, whereas reading from calldata is cheap. Public funcs may be called internally whereas external funcs don't. The gas difference is significant between the two, especailly when passing in large arrays.
- The "public" functions that are never called by the contract could be declared "external". When the inputsare arrays "external" functions are more efficient than "public" functions. Examples Functions like :quote(), getAmountIn(), getAmountOut(), getAmountsIn(), getAmountsOut().
- The “DOMAIN_SEPARATOR” defined in L45 is listed as “public” yet follows the naming convention of “constant” and “immutable” variables and the “PERMIT_TYPEHASH” is declared as “public” correctly following the UPPER_CASE_FORMAT but being illegible for off-chain applications via its compiler-generated getter.
To reduce gas costs, Solidity tightly packs variables where possible so that they are stored within the same 32 bytes.
Key:
bool
: 1 byte (8 bits)
address
: 20 bytes
uint8
: 1 byte
uint256
: 32 bytes (uint8 * 32)
`if`: approximately 45-50 gas
`if` are cheaper than `else if`: approximately 33 gas
`
To convert bytes to address, we used bytes20(example_address)
and to convert back we use address(bytes20_result)
. The reason why is because the address
type is 20 bytes, not 32 bytes. Even with a bytes32
conversion (address(uint160(bytes20(bytes32Address))
), it doesn't come out as the original.
Canceling a param with a bool doesn't spend too much gas! (in our test it was a 100 gas difference, in favour of the single param when compared to the double param).
For example:
boolParam && (boolVariable = bool
is the same as
if (boolParam) { boolVariable = bool; }
however saves gas and is more compact.
Tested what the most efficient way to do the bool&&()
trick is.
test
: 26471
test2
: 26190
Tested whether terinary operators (? :)
are worth using instead of if, else
&& if, else if
Suprising gas results:
Ternary: 26122
if, else: 26201
if, else if: 26157
Tested whether using the param to set a variable would be cheaper in gas compared to setting it manually. The results were staggering.
Test1 (manual): 45,942
Test2 (param): 28,970
Tested whether: converting a bytes20 param to an address and assigning with that value
is cheaper than: using an address param and assigning with that value
.
Small difference, but still suprising results:
byes20 param: 26,360
address param: 26,391
Testing whether: using a ternary is more efficient than a single if statement.
Results: Ternary wins by being more compact and being ever so slightly less in gas (Ternary: 29,004 && If: 29,021)
Testing: whether bit packing in param makes a difference.
Results: gas is saved when bit packing! (test1
: 31,645, test2
: 31,602)
Testing: The gas difference between assigning an address array dynamically, with an address array into a fixed slot assignment and with a assinging a single address into a fixed slot array.
Results:
Array[] = 29,915
Array[0] = 29,627
Single = 28,549