Skip to content

degatchi/solidity-experiments

Repository files navigation

Experimental contracts that try to break Solidity and find new techniques.


gasTester.sol

Go to remix, paste in gasTestor.sol and input your varaibles inbetween the gasleft()s


Useful Information


General Notes

  • Clearing address variales in code: use address(bytes20('')); or address(0), this will set the address to 0x0000000000000000000000000000000000000000. The reason we do this is because when you have an address or bytes20 param, you are forced to input an address. However, you can set it with address(bytes20('')); within the code.
  • calledFunctionExample{ value: msg.value } (param 1, param2) is how you send ether to a specific function in a specific contract
  • require() 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 and storage 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 even 0x000... 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.

Audit Notes

  • 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.

Bytes of Types

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
`

Findings

bytesToAddress.sol

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.


paramCanceling.sol

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).


bool && () trick (found 20/06/2020)

For example:
boolParam && (boolVariable = bool
is the same as
if (boolParam) { boolVariable = bool; }
however saves gas and is more compact.


bool&&()Efficiency.sol

Tested what the most efficient way to do the bool&&() trick is.
test: 26471
test2: 26190


ternaryIfsAndElses.sol

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


paramOrHardcode.sol

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


bytes20vsAddress.sol

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


ternaryVsIf.sol

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)


bitPackingParam.sol

Testing: whether bit packing in param makes a difference.
Results: gas is saved when bit packing! (test1: 31,645, test2: 31,602)


updatingArray.sol

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


About

Experimental contracts that test the details of Solidity, attempting to discover optimal use cases

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published