-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathControlFlow.sol
More file actions
644 lines (526 loc) · 19.3 KB
/
ControlFlow.sol
File metadata and controls
644 lines (526 loc) · 19.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;
// ============================================================================
// SOLIDITY CONTROL FLOW - Complete Reference
// From Zero to Senior Level
// ============================================================================
contract ControlFlow {
// ============================================================================
// 1. IF-ELSE STATEMENTS
// ============================================================================
// Basic conditional logic
// Syntax: if (condition) { } else if (condition) { } else { }
function ifElseBasic(uint256 x) public pure returns (string memory) {
if (x > 100) {
return "Greater than 100";
} else if (x > 50) {
return "Greater than 50";
} else if (x > 0) {
return "Greater than 0";
} else {
return "Zero or negative";
}
}
// Nested if statements
function nestedIf(
uint256 x,
uint256 y
) public pure returns (string memory) {
if (x > 10) {
if (y > 10) {
return "Both greater than 10";
} else {
return "Only x greater than 10";
}
} else {
return "x not greater than 10";
}
}
// If without else
function ifWithoutElse(uint256 x) public pure returns (uint256) {
uint256 result = x;
if (x > 100) {
result = 100;
}
return result;
}
// ============================================================================
// 2. TERNARY OPERATOR
// ============================================================================
// Compact conditional expression
// Syntax: condition ? valueIfTrue : valueIfFalse
// More gas-efficient than if-else for simple conditions
function ternaryBasic(uint256 x) public pure returns (string memory) {
return x > 10 ? "Greater" : "Not greater";
}
function ternaryWithCalculation(uint256 x) public pure returns (uint256) {
return x > 100 ? x * 2 : x / 2;
}
// Nested ternary (use sparingly for readability)
function nestedTernary(uint256 x) public pure returns (string memory) {
return
x > 100
? "High"
: x > 50
? "Medium"
: "Low";
}
// Finding min/max with ternary
function min(uint256 a, uint256 b) public pure returns (uint256) {
return a < b ? a : b;
}
function max(uint256 a, uint256 b) public pure returns (uint256) {
return a > b ? a : b;
}
// ============================================================================
// 3. FOR LOOPS
// ============================================================================
// Standard loop for iteration
// Syntax: for (initialization; condition; increment) { }
// Be careful with gas costs in loops
// Basic for loop
function forLoopBasic() public pure returns (uint256) {
uint256 sum = 0;
for (uint256 i = 0; i < 10; i++) {
sum += i;
}
return sum;
}
// Loop through array
function sumArray(uint256[] memory arr) public pure returns (uint256) {
uint256 total = 0;
for (uint256 i = 0; i < arr.length; i++) {
total += arr[i];
}
return total;
}
// Optimized loop (cache array length)
function sumArrayOptimized(
uint256[] memory arr
) public pure returns (uint256) {
uint256 total = 0;
uint256 length = arr.length; // Cache length to save gas
for (uint256 i = 0; i < length; i++) {
total += arr[i];
}
return total;
}
// Loop with step
function forLoopWithStep() public pure returns (uint256) {
uint256 sum = 0;
// Increment by 2
for (uint256 i = 0; i < 20; i += 2) {
sum += i;
}
return sum;
}
// Reverse loop
function reverseLoop() public pure returns (uint256) {
uint256 sum = 0;
for (uint256 i = 10; i > 0; i--) {
sum += i;
}
return sum;
}
// Nested loops
function nestedLoops() public pure returns (uint256) {
uint256 sum = 0;
for (uint256 i = 0; i < 5; i++) {
for (uint256 j = 0; j < 5; j++) {
sum += i * j;
}
}
return sum;
}
// ============================================================================
// 4. WHILE LOOPS
// ============================================================================
// Loop that continues while condition is true
// Syntax: while (condition) { }
// Less common than for loops in Solidity
function whileLoopBasic(uint256 x) public pure returns (uint256) {
uint256 result = 0;
uint256 counter = 0;
while (counter < x) {
result += counter;
counter++;
}
return result;
}
// While loop with complex condition
function whileLoopComplex(uint256 x) public pure returns (uint256) {
uint256 result = x;
while (result > 1 && result % 2 == 0) {
result = result / 2;
}
return result;
}
// ============================================================================
// 5. DO-WHILE LOOPS
// ============================================================================
// Loop that executes at least once before checking condition
// Syntax: do { } while (condition);
// Executes body first, then checks condition
function doWhileLoop(uint256 x) public pure returns (uint256) {
uint256 result = 0;
uint256 counter = 0;
do {
result += counter;
counter++;
} while (counter < x);
return result;
}
// Do-while guarantees at least one execution
function doWhileGuaranteedExecution() public pure returns (uint256) {
uint256 result = 0;
do {
result = 100; // This executes even though condition is false
} while (false);
return result; // Returns 100
}
// ============================================================================
// 6. BREAK STATEMENT
// ============================================================================
// Exit loop early
// Works in for, while, and do-while loops
function breakExample() public pure returns (uint256) {
uint256 sum = 0;
for (uint256 i = 0; i < 100; i++) {
if (i == 10) {
break; // Exit loop when i reaches 10
}
sum += i;
}
return sum; // Returns sum of 0-9
}
// Break in nested loop (only exits inner loop)
function breakNestedLoop() public pure returns (uint256) {
uint256 sum = 0;
for (uint256 i = 0; i < 5; i++) {
for (uint256 j = 0; j < 5; j++) {
if (j == 3) {
break; // Only exits inner loop
}
sum += i + j;
}
}
return sum;
}
// Find first occurrence
function findFirst(
uint256[] memory arr,
uint256 target
) public pure returns (int256) {
for (uint256 i = 0; i < arr.length; i++) {
if (arr[i] == target) {
return int256(i); // Found, return index
}
}
return -1; // Not found
}
// ============================================================================
// 7. CONTINUE STATEMENT
// ============================================================================
// Skip current iteration and continue with next
// Works in for, while, and do-while loops
function continueExample() public pure returns (uint256) {
uint256 sum = 0;
for (uint256 i = 0; i < 10; i++) {
if (i % 2 == 0) {
continue; // Skip even numbers
}
sum += i;
}
return sum; // Returns sum of odd numbers only
}
// Continue with multiple conditions
function continueMultiple() public pure returns (uint256) {
uint256 sum = 0;
for (uint256 i = 0; i < 100; i++) {
if (i % 2 == 0) continue; // Skip even
if (i % 3 == 0) continue; // Skip multiples of 3
if (i > 50) continue; // Skip numbers > 50
sum += i;
}
return sum;
}
// ============================================================================
// 8. RETURN STATEMENT
// ============================================================================
// Exit function and return value
// Can be used to exit early from function
function earlyReturn(uint256 x) public pure returns (string memory) {
if (x == 0) {
return "Zero"; // Early return
}
if (x < 10) {
return "Small";
}
return "Large";
}
// Multiple return points
function multipleReturns(uint256 x) public pure returns (uint256) {
if (x > 100) return 100;
if (x < 10) return 10;
return x;
}
// ============================================================================
// 9. REQUIRE - INPUT VALIDATION
// ============================================================================
// Validate conditions, revert if false
// Gas refund on failure
// Use for validating inputs and conditions
// Syntax: require(condition, "error message");
function requireBasic(uint256 x) public pure returns (uint256) {
require(x > 0, "Value must be positive");
require(x < 1000, "Value too large");
return x * 2;
}
// Require with complex conditions
function requireComplex(
uint256 x,
uint256 y,
address addr
) public pure returns (uint256) {
require(x > 0 && y > 0, "Both values must be positive");
require(addr != address(0), "Invalid address");
require(x != y, "Values must be different");
return x + y;
}
// Require in loops
function requireInLoop(uint256[] memory arr) public pure returns (uint256) {
uint256 sum = 0;
for (uint256 i = 0; i < arr.length; i++) {
require(arr[i] > 0, "All values must be positive");
sum += arr[i];
}
return sum;
}
// ============================================================================
// 10. ASSERT - INTERNAL ERROR CHECKING
// ============================================================================
// Check for internal errors and invariants
// Should never fail in correct code
// No gas refund on failure (uses all remaining gas)
// Use for testing invariants and preventing impossible states
function assertExample(uint256 x) public pure returns (uint256) {
uint256 result = x * 2;
// Assert invariant: result should always be even
assert(result % 2 == 0);
return result;
}
// Assert for overflow check (pre-0.8.0 pattern)
function assertOverflow(
uint256 a,
uint256 b
) public pure returns (uint256) {
uint256 c = a + b;
// In Solidity 0.8.0+, this is automatic, but shown for educational purposes
assert(c >= a); // Check no overflow occurred
return c;
}
// ============================================================================
// 11. REVERT - EXPLICIT REVERSION
// ============================================================================
// Explicitly revert transaction
// More flexible than require
// Can be used with custom errors (0.8.4+)
function revertBasic(uint256 x) public pure returns (uint256) {
if (x == 0) {
revert("Value cannot be zero");
}
return x * 2;
}
// Revert with conditional
function revertConditional(
uint256 x,
bool shouldRevert
) public pure returns (uint256) {
if (shouldRevert) {
revert("Intentional revert");
}
return x;
}
// Custom errors (more gas efficient)
error InvalidValue(uint256 value, string reason);
error InsufficientAmount(uint256 provided, uint256 required);
function revertWithCustomError(uint256 x) public pure returns (uint256) {
if (x == 0) {
revert InvalidValue(x, "Cannot be zero");
}
if (x < 10) {
revert InsufficientAmount(x, 10);
}
return x;
}
// ============================================================================
// 12. TRY-CATCH - ERROR HANDLING
// ============================================================================
// Handle errors from external calls
// Cannot be used for internal function calls
// Three types of catch blocks
// Helper contract for demonstration
function externalFunction(uint256 x) external pure returns (uint256) {
require(x > 0, "Value must be positive");
return x * 2;
}
// Basic try-catch
function tryCatchBasic(
uint256 x
) public returns (bool success, uint256 result) {
try this.externalFunction(x) returns (uint256 value) {
// Success path
return (true, value);
} catch {
// Any error
return (false, 0);
}
}
// Try-catch with error types
function tryCatchDetailed(
uint256 x
)
public
returns (bool success, uint256 result, string memory errorMessage)
{
try this.externalFunction(x) returns (uint256 value) {
// Success
return (true, value, "");
} catch Error(string memory reason) {
// Catch revert with reason string
return (false, 0, reason);
} catch Panic(uint256 errorCode) {
// Catch panic errors (assert, overflow, etc.)
return (false, 0, string(abi.encodePacked("Panic: ", errorCode)));
} catch (bytes memory lowLevelData) {
// Catch other errors
return (false, 0, "Unknown error");
}
}
// ============================================================================
// 13. UNCHECKED BLOCKS (Solidity 0.8.0+)
// ============================================================================
// Disable overflow/underflow checks for gas optimization
// Use only when overflow is impossible or desired
// Saves gas but removes safety checks
function uncheckedExample() public pure returns (uint256) {
uint256 x = 10;
// Normal arithmetic (with overflow checks)
uint256 y = x + 5;
// Unchecked arithmetic (no overflow checks)
unchecked {
uint256 z = x + 5; // Saves gas
return z;
}
}
// Unchecked in loops (common optimization)
function uncheckedLoop() public pure returns (uint256) {
uint256 sum = 0;
for (uint256 i = 0; i < 100; ) {
sum += i;
unchecked {
i++; // Save gas on increment (overflow impossible)
}
}
return sum;
}
// Unchecked for known safe operations
function uncheckedSafeOperation(uint256 x) public pure returns (uint256) {
require(x <= 100, "Value too large");
unchecked {
// Safe because we validated x <= 100
return x + 50;
}
}
// ============================================================================
// 14. SHORT-CIRCUIT EVALUATION
// ============================================================================
// Boolean operators && and || use short-circuit evaluation
// Can save gas by ordering conditions efficiently
function shortCircuitAnd(uint256 x) public pure returns (bool) {
// If first condition is false, second is not evaluated
return x > 0 && expensiveCheck(x);
}
function shortCircuitOr(uint256 x) public pure returns (bool) {
// If first condition is true, second is not evaluated
return x == 0 || expensiveCheck(x);
}
function expensiveCheck(uint256 x) private pure returns (bool) {
// Simulate expensive operation
uint256 result = 0;
for (uint256 i = 0; i < x; i++) {
result += i;
}
return result > 0;
}
// Optimal ordering: cheap checks first
function optimizedConditions(
address addr,
uint256 x
) public view returns (bool) {
// Cheap checks first
return
addr != address(0) && // Cheap
x > 0 && // Cheap
x < 1000 && // Cheap
expensiveCheck(x); // Expensive - evaluated last
}
// ============================================================================
// 15. COMPLEX CONTROL FLOW PATTERNS
// ============================================================================
// State machine pattern
enum State {
Pending,
Active,
Completed,
Cancelled
}
State public currentState = State.Pending;
function stateMachine(State newState) public returns (string memory) {
if (currentState == State.Pending) {
if (newState == State.Active) {
currentState = newState;
return "Activated";
}
revert("Invalid state transition");
}
if (currentState == State.Active) {
if (newState == State.Completed || newState == State.Cancelled) {
currentState = newState;
return "State updated";
}
revert("Invalid state transition");
}
revert("Cannot change completed or cancelled state");
}
// Guard clauses pattern (early returns)
function guardClausesPattern(
uint256 x,
address addr
) public pure returns (uint256) {
// Validate inputs with early returns
if (x == 0) revert("X cannot be zero");
if (addr == address(0)) revert("Invalid address");
if (x > 1000) revert("X too large");
// Main logic after validations
return x * 2;
}
// ============================================================================
// KEY TAKEAWAYS FOR PROFESSIONAL DEVELOPERS:
// ============================================================================
// 1. Use require() for input validation (gas refund on failure)
// 2. Use assert() for invariants (should never fail)
// 3. Use revert() for complex error handling, custom errors save gas
// 4. Minimize loops, especially nested loops (gas costs)
// 5. Cache array length in loops to save gas
// 6. Use unchecked for known safe operations (0.8.0+)
// 7. Break and continue can optimize loop execution
// 8. Try-catch only works for external calls
// 9. Short-circuit evaluation: order conditions by gas cost
// 10. Ternary operators are more gas-efficient than if-else
// 11. Guard clauses improve readability and security
// 12. State machines provide clear control flow
// 13. Early returns reduce nesting and improve readability
// 14. Custom errors (0.8.4+) are more gas-efficient than strings
// ============================================================================
}