├── LogicTests.sol
├── bool&&()Efficiency.sol
├── bytes20vsAddress.sol
├── bytesToAddress.sol
├── gasTester.sol
├── memory-reference.sol
├── paramBitPacking.sol
├── paramCanceling.sol
├── paramOrHardcode.sol
├── readme.md
├── ternaryIfsAndElses.sol
├── ternaryVsIf.sol
└── updatingArray.sol
/LogicTests.sol:
--------------------------------------------------------------------------------
1 | pragma solidity ^0.8.7;
2 |
3 | contract Test {
4 | function mod (uint input1, uint input2) pure external returns(uint result) {
5 | return (input1 % input2);
6 | }
7 |
8 | // decimals will round to the lowest whole number (i.e, 6.95 === 6)
9 | function div (uint input1, uint input2) pure external returns(uint result) {
10 | return (input1 / input2);
11 | }
12 |
13 | // when block.timestamp > futureTime, it will revert
14 | uint futureTime = block.timestamp + 5;
15 | function check() external view returns(uint) {
16 | return futureTime - block.timestamp;
17 | }
18 |
19 | uint[] array;
20 | function arrayChecker() external returns(uint length) {
21 | array.push(1);
22 | return array.length;
23 | }
24 |
25 | event GasUsed(uint gasUsed);
26 |
27 | // -----------------------------
28 | // For Loop, + vs -
29 | // - small gas diff, however starting from the top may be more gas efficient in some instances, i.e, minting from `i` + going down
30 | // -----------------------------
31 |
32 | // 1832 gas used
33 | function forUp() external {
34 | uint gasStart = gasleft();
35 | for (uint i; i < 10; i++) {}
36 | uint gasUsed = gasStart - gasleft();
37 | emit GasUsed(gasUsed);
38 | }
39 |
40 |
41 | // 1840 gas used
42 | function forDown() external {
43 | uint gasStart = gasleft();
44 | for (uint i = 10; i > 0; i--) {}
45 | uint gasUsed = gasStart - gasleft();
46 | emit GasUsed(gasUsed);
47 | }
48 |
49 |
50 | // -----------------------------
51 | // Optinal SSTORE
52 | // - Check if SSTORE is worth updating w/o of optinal if statement
53 | // -----------------------------
54 |
55 | uint ref0;
56 | uint ref1;
57 |
58 | // 4449 gas used
59 | function forceSSTORE() external {
60 | uint gasStart = gasleft();
61 |
62 | uint number = 10;
63 | if (number > 11) {}
64 | ref0 = number;
65 | ref1 = number;
66 |
67 | uint gasUsed = gasStart - gasleft();
68 | emit GasUsed(gasUsed);
69 | }
70 |
71 | // 4475 gas used
72 | function optinalSSTORE() external {
73 | uint gasStart = gasleft();
74 |
75 | uint number = 10;
76 | if (number > 11) {}
77 | if (number > 9) {
78 | ref0 = number;
79 | ref1 = number;
80 | }
81 |
82 | uint gasUsed = gasStart - gasleft();
83 | emit GasUsed(gasUsed);
84 | }
85 |
86 |
87 | // -----------------------------
88 | // != vs >
89 | // -----------------------------
90 |
91 | // 33 gas used
92 | function largerThan() external {
93 | uint number = 10;
94 | uint gasStart = gasleft();
95 |
96 | require(9 < 10, "");
97 |
98 | uint gasUsed = gasStart - gasleft();
99 | emit GasUsed(gasUsed);
100 | }
101 |
102 | // 36 gas used
103 | function notEqual() external {
104 | uint number = 10;
105 | uint gasStart = gasleft();
106 |
107 | require(9 != 10, "");
108 |
109 | uint gasUsed = gasStart - gasleft();
110 | emit GasUsed(gasUsed);
111 | }
112 |
113 |
114 | // -----------------------------
115 | // Array length
116 | // -----------------------------
117 |
118 | struct Array {
119 | uint256[] array0;
120 | }
121 |
122 | function checker() external pure returns(uint) {
123 | Array memory structTest;
124 |
125 | return structTest.array0.length;
126 | }
127 |
128 |
129 | // -----------------------------
130 | // Rounding
131 | // -----------------------------
132 |
133 | // daysPassed1 === 1
134 | // daysPassed2 === 1
135 | // daysPassed3 === 0
136 | function round() external pure returns(uint daysPassed1, uint daysPassed2, uint daysPassed3) {
137 | uint256 timePassed = 90000;
138 | daysPassed1 = timePassed < 1 days ? 0 : timePassed / 86400;
139 | daysPassed2 = timePassed / 86400;
140 | daysPassed3 = timePassed / 100000;
141 | }
142 |
143 |
144 | // -----------------------------
145 | // struct vs single line SSTORE
146 | // -----------------------------
147 |
148 | struct STRUCT {
149 | uint256 a;
150 | uint256 b;
151 | address c;
152 | }
153 |
154 | mapping(uint8 => STRUCT) private structo;
155 |
156 | // initialising: 46722 gas used
157 | // after initialising: 6922 gas used
158 | function structSSTORE() external {
159 | uint gasStart = gasleft();
160 |
161 | STRUCT memory s = STRUCT({
162 | a: 1,
163 | b: 1,
164 | c: address(0)
165 | });
166 |
167 | structo[0] = s;
168 |
169 | uint gasUsed = gasStart - gasleft();
170 | emit GasUsed(gasUsed);
171 | }
172 |
173 | // initialising: 46618 gas used
174 | // after initialising: 6818 gas used
175 | function singleLineSSTORE() external {
176 | uint gasStart = gasleft();
177 |
178 | STRUCT storage s = structo[1];
179 |
180 | s.a = 1;
181 | s.b = 2;
182 | s.c = address(0);
183 |
184 | uint gasUsed = gasStart - gasleft();
185 | emit GasUsed(gasUsed);
186 | }
187 | }
188 |
--------------------------------------------------------------------------------
/bool&&()Efficiency.sol:
--------------------------------------------------------------------------------
1 | pragma solidity 0.8.5;
2 |
3 | contract Test {
4 | bool public privs;
5 |
6 | event GasUsed(string topic, uint gasUsed);
7 |
8 | // testing what the most efficient way to do the `bool&&()` trick is.
9 |
10 | bool set1;
11 | bool set2;
12 | address user = 0x5B38Da6a701c568545dCfcB03FcB875f56beddC4;
13 |
14 | function test() public {
15 | uint gasStart = gasleft();
16 | user == msg.sender &&(set1 = true);
17 | user != msg.sender &&(set2 = true);
18 | uint gasUsed = gasStart = gasleft();
19 | emit GasUsed('test1', gasUsed);
20 | }
21 |
22 | function test2() public {
23 | uint gasStart = gasleft();
24 | user == msg.sender &&(set1 = true) ||
25 | user != msg.sender &&(set2 = true);
26 | uint gasUsed = gasStart = gasleft();
27 | emit GasUsed('test2', gasUsed);
28 | }
29 |
30 | }
31 |
--------------------------------------------------------------------------------
/bytes20vsAddress.sol:
--------------------------------------------------------------------------------
1 | pragma solidity 0.8.5;
2 |
3 | contract Test {
4 | event GasUsed(string topic, uint gasUsed);
5 |
6 | bool public set1;
7 | bool public set2;
8 | address public addy1;
9 | address public addy2;
10 |
11 | function test1(bytes20 addy) public {
12 | uint gasStart = gasleft();
13 | addy1 = address(addy);
14 | uint gasUsed = gasStart = gasleft();
15 | emit GasUsed('test1', gasUsed);
16 | }
17 |
18 | function test2(address addy) public {
19 | uint gasStart = gasleft();
20 | addy2 = addy;
21 | uint gasUsed = gasStart = gasleft();
22 | emit GasUsed('test1', gasUsed);
23 | }
24 |
25 | function encode(address addy) public pure returns(bytes20) {
26 | return bytes20(addy);
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/bytesToAddress.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: UNLICENSED
2 | pragma solidity 0.8.5;
3 |
4 | contract bytesTest {
5 |
6 | bytes20 public encoded1;
7 | bytes32 public encoded2;
8 | address public addy1;
9 | address public addy2;
10 |
11 |
12 | function encode(address user) public {
13 | encoded1 = bytes20(user);
14 | encoded2 = keccak256(abi.encode(user));
15 | }
16 |
17 | function decode(bytes20 a, bytes32 b) public {
18 | addy1 = address(a);
19 | addy2 = address(uint160(bytes20(b)));
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/gasTester.sol:
--------------------------------------------------------------------------------
1 | pragma solidity 0.8.5;
2 |
3 | contract Test {
4 | bool public privs;
5 |
6 | event GasUsed(string topic, uint gasUsed);
7 |
8 | function test(bool priv) public {
9 | uint gasStart = gasleft();
10 | priv && (privs = true);
11 | uint gasUsed = gasStart = gasleft();
12 | emit GasUsed('test1', gasUsed);
13 | }
14 |
15 | function test2(bool priv) public {
16 | uint gasStart = gasleft();
17 | if (priv) {
18 | privs = true;
19 | }
20 | uint gasUsed = gasStart = gasleft();
21 | emit GasUsed('test2', gasUsed);
22 | }
23 |
24 | }
--------------------------------------------------------------------------------
/memory-reference.sol:
--------------------------------------------------------------------------------
1 |
2 | // SPDX-License-Identifier: MIT
3 | // Created by DeGatchi (4/11/2021)
4 | pragma solidity 0.8.9;
5 |
6 | // Test 1 is slightly more gas efficient and also allows for storage manipulation.
7 |
8 | // test 1: 27724
9 | // test 2: 27757
10 |
11 | contract qek {
12 |
13 | struct X {
14 | uint256 a;
15 | address b;
16 | }
17 |
18 | mapping(uint256 => X) public x;
19 |
20 | event GasUsed(string topic, uint gasUsed);
21 |
22 | function test(uint256 id) public {
23 | uint gasStart = gasleft();
24 | X storage sX = x[id];
25 | X memory mX = sX;
26 | uint gasUsed = gasStart = gasleft();
27 | emit GasUsed('test1', gasUsed);
28 | }
29 |
30 | function test2(uint256 id) public {
31 | uint gasStart = gasleft();
32 | X memory mX = x[id];
33 | uint gasUsed = gasStart = gasleft();
34 | emit GasUsed('test2', gasUsed);
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/paramBitPacking.sol:
--------------------------------------------------------------------------------
1 | pragma solidity 0.8.5;
2 |
3 | contract Test {
4 | event GasUsed(string topic, uint gasUsed);
5 |
6 | address public addy1;
7 | uint24 public num1;
8 | uint256 largeuint1;
9 |
10 | // Testing: whether bit packing in param makes a difference.
11 | // Result: gas is saved when bit packing! (`test1`: 31,645, `test2`: 31,602)
12 |
13 | function test1(
14 | address addy_,
15 | uint256 uint256_,
16 | uint24 uint24_
17 | ) public {
18 | uint gasStart = gasleft();
19 | addy1 = addy_;
20 | largeuint1 = uint256_;
21 | num1 = uint24_;
22 | uint gasUsed = gasStart = gasleft();
23 | emit GasUsed('test1', gasUsed);
24 | }
25 |
26 | function test2(
27 | address addy_,
28 | uint24 uint24_,
29 | uint256 uint256_
30 | ) public {
31 | uint gasStart = gasleft();
32 | addy1 = addy_;
33 | largeuint1 = uint256_;
34 | num1 = uint24_;
35 | uint gasUsed = gasStart = gasleft();
36 | emit GasUsed('test1', gasUsed);
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/paramCanceling.sol:
--------------------------------------------------------------------------------
1 | pragma solidity 0.8.5;
2 |
3 | contract Test {
4 | bool public privs;
5 |
6 | event GasUsed(string topic, uint gasUsed);
7 |
8 | address user;
9 |
10 | // testing if having a param that isn't used uses a lot of gas
11 | // compared to if it didn't have that param.
12 |
13 | function test1(bool choice) public {
14 | uint gasStart = gasleft();
15 | if (choice) {}
16 | uint gasUsed = gasStart = gasleft();
17 | emit GasUsed('test1', gasUsed);
18 | }
19 |
20 | function test2(address to, bool choice) public {
21 | uint gasStart = gasleft();
22 | if (choice) {
23 | user = to;
24 | }
25 | uint gasUsed = gasStart = gasleft();
26 | emit GasUsed('test2', gasUsed);
27 | }
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/paramOrHardcode.sol:
--------------------------------------------------------------------------------
1 | pragma solidity 0.8.5;
2 |
3 | contract Test {
4 | event GasUsed(string topic, uint gasUsed);
5 |
6 | bool public set1;
7 | bool public set2;
8 |
9 | function test1(bool choice) public {
10 | uint gasStart = gasleft();
11 | set1 = true;
12 | uint gasUsed = gasStart = gasleft();
13 | emit GasUsed('test1', gasUsed);
14 | }
15 |
16 | function test2(bool choice) public {
17 | uint gasStart = gasleft();
18 | set2 = choice;
19 | uint gasUsed = gasStart = gasleft();
20 | emit GasUsed('test1', gasUsed);
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | Experimental contracts that try to break Solidity and find new techniques.
2 |
3 | ----
4 |
5 | ### `gasTester.sol`
6 | Go to remix, paste in `gasTestor.sol` and input your varaibles inbetween the `gasleft()`s
7 |
8 |
9 |
10 | # Useful Information
11 | - https://ethereum.stackexchange.com/questions/37549/array-or-mapping-which-costs-more-gas
12 | - https://ethereum.stackexchange.com/questions/77099/efficient-bit-packing
13 | - https://ethereum.stackexchange.com/questions/15166/difference-between-require-and-assert-and-the-difference-between-revert-and-thro
14 | - https://ethereum.stackexchange.com/questions/19380/external-vs-public-best-practices
15 |
16 |
17 |
18 | # General Notes
19 | - 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.
20 | - `calledFunctionExample{ value: msg.value } (param 1, param2)` is how you send ether to a specific function in a specific contract
21 | - `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.
22 | - `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).
23 | - 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.
24 | - 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.
25 |
26 | # Audit Notes
27 | - 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().
28 | - 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.
29 |
30 |
31 |
32 | # Bytes of Types
33 | To reduce gas costs, Solidity tightly packs variables where possible so that they are stored within the same 32 bytes.
34 |
35 | Key:
36 | `bool`: 1 byte (8 bits)
37 | `address`: 20 bytes
38 | `uint8`: 1 byte
39 | `uint256`: 32 bytes (uint8 * 32)
40 |
41 |
42 | `if`: approximately 45-50 gas
43 | `if` are cheaper than `else if`: approximately 33 gas
44 | `
45 |
46 | # Findings
47 | ### `bytesToAddress.sol`
48 | 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.
49 |
50 |
51 |
52 | ### `paramCanceling.sol`
53 | 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).
54 |
55 |
56 |
57 | ### `bool && ()` trick (found 20/06/2020)
58 | For example:
59 | `boolParam && (boolVariable = bool`
60 | is the same as
61 | `if (boolParam) {
62 | boolVariable = bool;
63 | }`
64 | however saves gas and is more compact.
65 |
66 |
67 |
68 | ### `bool&&()Efficiency.sol`
69 | Tested what the most efficient way to do the `bool&&()` trick is.
70 | `test`: 26471
71 | `test2`: 26190
72 |
73 |
74 |
75 | ### `ternaryIfsAndElses.sol`
76 | Tested whether `terinary operators (? :)` are worth using instead of `if, else` && `if, else if`
77 | Suprising gas results:
78 | Ternary: 26122
79 | if, else: 26201
80 | if, else if: 26157
81 |
82 |
83 |
84 | ### `paramOrHardcode.sol`
85 | Tested whether using the param to set a variable would be cheaper in gas compared to setting it manually. The results were staggering.
86 | Test1 (manual): 45,942
87 | Test2 (param): 28,970
88 |
89 |
90 |
91 | ### `bytes20vsAddress.sol`
92 | 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`.
93 |
94 | Small difference, but still suprising results:
95 | byes20 param: 26,360
96 | address param: 26,391
97 |
98 |
99 |
100 | ### `ternaryVsIf.sol`
101 | Testing whether: using a ternary is more efficient than a single if statement.
102 | Results: Ternary wins by being more compact and being ever so slightly less in gas (Ternary: 29,004 && If: 29,021)
103 |
104 |
105 |
106 | ### `bitPackingParam.sol`
107 | Testing: whether bit packing in param makes a difference.
108 | Results: gas is saved when bit packing! (`test1`: 31,645, `test2`: 31,602)
109 |
110 |
111 |
112 | ### `updatingArray.sol`
113 | 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.
114 | Results:
115 | Array[] = 29,915
116 | Array[0] = 29,627
117 | Single = 28,549
118 |
119 |
120 |
121 |
--------------------------------------------------------------------------------
/ternaryIfsAndElses.sol:
--------------------------------------------------------------------------------
1 | pragma solidity 0.8.5;
2 |
3 | contract Test {
4 | bool public privs;
5 |
6 | event GasUsed(string topic, uint gasUsed);
7 |
8 | // testing whether `terinary operators (? :)` are worth using instead of `if, else` && `if, else if`
9 | // Suprising gas results:
10 | // Terniary: 26122
11 | // if, else: 26201
12 | // if, else if: 26157
13 |
14 | uint8 num;
15 |
16 | function test(bool choice) public {
17 | uint gasStart = gasleft();
18 | choice ? num = 1 : num = 1;
19 | uint gasUsed = gasStart = gasleft();
20 | emit GasUsed('test1', gasUsed);
21 | }
22 |
23 | function test2(bool choice) public {
24 | uint gasStart = gasleft();
25 | if (choice) {
26 | num = 1;
27 | } else {
28 | num = 1;
29 | }
30 | uint gasUsed = gasStart = gasleft();
31 | emit GasUsed('test2', gasUsed);
32 | }
33 |
34 | function test3(bool choice) public {
35 | uint gasStart = gasleft();
36 | if (choice) {
37 | num = 1;
38 | } else if (!choice) {
39 | num = 1;
40 | }
41 | uint gasUsed = gasStart = gasleft();
42 | emit GasUsed('test2', gasUsed);
43 | }
44 |
45 | }
46 |
--------------------------------------------------------------------------------
/ternaryVsIf.sol:
--------------------------------------------------------------------------------
1 | pragma solidity 0.8.5;
2 |
3 | contract Test {
4 | event GasUsed(string topic, uint gasUsed);
5 |
6 | bool public set1;
7 | bool public set2;
8 | address public addy1;
9 | address public addy2;
10 | uint8 public num1;
11 | uint8 public num2;
12 |
13 | function test1(uint8 num) public {
14 | uint gasStart = gasleft();
15 | num != 0 ? num1 = num : num;
16 | uint gasUsed = gasStart = gasleft();
17 | emit GasUsed('test1', gasUsed);
18 | }
19 |
20 | function test2(uint8 num) public {
21 | uint gasStart = gasleft();
22 | if (num != 0) {
23 | num2 = num;
24 | }
25 | uint gasUsed = gasStart = gasleft();
26 | emit GasUsed('test1', gasUsed);
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/updatingArray.sol:
--------------------------------------------------------------------------------
1 | pragma solidity 0.8.5;
2 |
3 | contract Test {
4 | address[] addresses01;
5 |
6 | event GasUsed(string topic, uint gasUsed);
7 |
8 | function test(
9 | address[] memory addresses1
10 | ) public {
11 | uint gasStart = gasleft();
12 |
13 | addresses01 = addresses1;
14 |
15 | uint gasUsed = gasStart = gasleft();
16 | emit GasUsed('test1', gasUsed);
17 | }
18 |
19 | function test2(
20 | address[] memory addresses1
21 | ) public {
22 | uint gasStart = gasleft();
23 |
24 | addresses01[0] = addresses1[0];
25 |
26 | uint gasUsed = gasStart = gasleft();
27 | emit GasUsed('test2', gasUsed);
28 | }
29 |
30 | function test3(
31 | address addresses1
32 | ) public {
33 | uint gasStart = gasleft();
34 |
35 | addresses01[0] = addresses1;
36 |
37 | uint gasUsed = gasStart = gasleft();
38 | emit GasUsed('test2', gasUsed);
39 | }
40 |
41 | }
42 |
--------------------------------------------------------------------------------