-
Notifications
You must be signed in to change notification settings - Fork 0
/
UniversalFactory.sol
923 lines (823 loc) · 50.7 KB
/
UniversalFactory.sol
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
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
// SPDX-License-Identifier: MIT
/*
* Universal Factory Contract
* This standard defines an universal factory smart contract where any address (contract or regular account) can
* deploy and reserve deterministic contract addresses in any network.
*
* Written in 2024 by Lohann Paterno Coutinho Ferreira <[email protected]>.
*
* Universal Factory is derived from EIP-2470 and EIP-3171, with an additional feature that allows the contract
* constructor to read arguments without including it in the bytecode, this way custom arguments can be provided
* and immutables can be set without influencing the resulting `CREATE2` address.
* - EIP-2470: https://eips.ethereum.org/EIPS/eip-2470
* - EIP-3171: https://github.com/ethereum/EIPs/pull/3171
*
* This contract is intented to be deployed at the same address on all networks using keyless deployment method.
* - Keyless Deployment Method: https://weka.medium.com/how-to-send-ether-to-11-440-people-187e332566b7
*
* ██╗ ██╗███╗ ██╗██╗██╗ ██╗███████╗██████╗ ███████╗ █████╗ ██╗
* ██║ ██║████╗ ██║██║██║ ██║██╔════╝██╔══██╗██╔════╝██╔══██╗██║
* ██║ ██║██╔██╗ ██║██║██║ ██║█████╗ ██████╔╝███████╗███████║██║
* ██║ ██║██║╚██╗██║██║╚██╗ ██╔╝██╔══╝ ██╔══██╗╚════██║██╔══██║██║
* ╚██████╔╝██║ ╚████║██║ ╚████╔╝ ███████╗██║ ██║███████║██║ ██║███████╗
* ╚═════╝ ╚═╝ ╚═══╝╚═╝ ╚═══╝ ╚══════╝╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝╚══════╝
* ███████╗ █████╗ ██████╗████████╗ ██████╗ ██████╗ ██╗ ██╗
* ██╔════╝██╔══██╗██╔════╝╚══██╔══╝██╔═══██╗██╔══██╗╚██╗ ██╔╝
* █████╗ ███████║██║ ██║ ██║ ██║██████╔╝ ╚████╔╝
* ██╔══╝ ██╔══██║██║ ██║ ██║ ██║██╔══██╗ ╚██╔╝
* ██║ ██║ ██║╚██████╗ ██║ ╚██████╔╝██║ ██║ ██║
* ╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝
*
*/
pragma solidity ^0.8.0;
/**
* @title Universal Factory Contract
* @author Lohann Paterno Coutinho Ferreira
* @notice This contract is a factory that allows you to deploy contracts at deterministic addresses in any network.
*
* # How it works
* To pass arbitrary arguments to the contract without influecing resulting address, this contract caches the arguments
* locally, and provide it to the contract constructor when it calls the `context()` function.
* - For `cancun` it caches the arguments using the EIP-1153 Transient Storage (~100 gas per word + overhead).
* - For `shanghai` it caches the arguments using the regular storage (~2900 gas per word + overhead).
*
* # Predictable Gas Cost
* This contract uses many different Branchless Code techniques (most of them develop by the author), so this contract have an very
* predictable gas overhead in any network (the actual overhead may change depending on the EVM implementation).
* - For `shanghai` evms, to guarantee an predictable gas cost, it make sure all values stored are different than zero.
* This is accomplished by hashing the arguments, and use the resulting hash to XOR the bytes before store and after read it.
* - For `cancun` there's no diffence between storing zero or non-zero values, so the `XOR` step is skipped.
*/
contract UniversalFactory {
/**
* @notice The Constructor is payable due Frontier EVM compatibility, once that EVM have the concept
* of existential deposit (ED), in this evm if you send a balance to a contract without ED, then the
* ED will be discounted from the contract balance, as result `address(this).balance < msg.value`,
* which is impossible in standard EVM's clients.
* - https://github.com/polkadot-evm/frontier/blob/polkadot-v1.11.0/ts-tests/tests/test-balance.ts#L41
*
* This contract works correctly in Frontier and standard EVM's, because it forwards the minimum value
* between `address(this).balance` and `msg.value` to the created contract.
*/
constructor() payable {
assembly {
// Context Storage Layout
// | 32-bit | 22-bit | 32-bit | 160-bit | 7-bit | 3-bit |
// +-+-+-+-+-+-+-+-+-+-+-+--+-+-+-+-+-+-+-+-+-+-+-++-+-+-+-+-+-+-+-+
// | args | args.len | selector | contract | depth | flags | offset: 0
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | args[..12] (96-bit) | sender (160-bit) | offset: 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | salt | offset: 2
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | value | offset: 3
// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
// | keccak256(args)* | offset: 2**64 * depth
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | args[16..] | offset: 2**64 * depth + 1
// | ... | length: (args.length + 15) / 32
// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
//
// - For `cancun`, the context is stored in `transient storage` using the `TSTORE` and `TLOAD` opcodes.
// - For `shanghai`, the context is stored in `regular storage` using the `SSTORE` and `SLOAD` opcodes.
//
// If the EVM doesn't support EIP-1153 TRANSIENT STORAGE, then it initialize the storage,
// because the transition from empty to non-empty cost more gas than the transition
// from non-empty to any, example:
// - zero to non-zero 20000 gas
// - non-zero to non-zero: 2900 gas
// - non-zero to zero: 100 gas
//
// The original values are restored at the end of the execution, except for `args`, which don't need to be restored
// once they are stored in an unique offset per call depth.
//
// # Motivation behind `keccak256(args)`.
// Different than `EIP-1153` transient storage, the regular storage is persisted between transactions, so to
// guarantee an predictable gas cost, and prevent the previous calls to increase the gas cost of future calls, is
// necessary to guarantee that all values stored are different than zero. The way we enforce this is by computing
// the keccak256 of `args`, and use it to XOR the bytes before store and after read it, so is infeseable
// to create an `argument` that can have 256 consecutive zeros when stored.
let placeholder := not(0)
sstore(0, placeholder)
sstore(1, placeholder)
sstore(2, placeholder)
sstore(3, placeholder)
// Notice is possible to send funds to this contract address before it is deployed. To guarantee this contract
// doesn't hold any funds, we sent the whole balance to an `GiveAway` contract, which is deployed below.
// GiveAway contract bytecode
// 0x34600c573d3d3d3d47335af15b00:
// 0x00 0x34 CALLVALUE val
// 0x01 0x600c PUSH1 0x0c 12 val
// ,=<0x03 0x57 JUMPI
// | 0x04 0x3d RETURNDATASIZE 0
// | 0x05 0x3d RETURNDATASIZE 0 0
// | 0x06 0x3d RETURNDATASIZE 0 0 0
// | 0x07 0x3d RETURNDATASIZE 0 0 0 0
// | 0x08 0x47 SELFBALANCE balance 0 0 0 0
// | 0x09 0x33 CALLER addr balance 0 0 0 0
// | 0x0a 0x5a GAS gas addr balance 0 0 0 0
// | 0x0b 0xf1 CALL suc
// `=>0x0c 0x5b JUMPDEST suc
// 0x0d 0x00 STOP
mstore(0x00, 0x6d34600c573d3d3d3d47335af15b003d52600e6012f3)
pop(create(selfbalance(), 0x0a, 22))
}
}
/**
* @dev This contract must be compatible with `shanghai` and `cancun` hardforks, the `EIP-1153` opcodes must
* only be used when the EVM supports `cancun`, to accomplish that, this contract calls itself and check the
* `TLOAD` opcode support, this check is done dynamically in case the EVM supports it in the future.
*
* @notice inline assembly is used for 3 main reason:
* 1. Guarantee the opcode `MCOPY` isn't called, once it is only supported by `cancun`.
* 2. Guarantee an predictable gas cost, this contract uses various branchless code techniques develop by the author.
* 3. Optimized code, to reduce the gas cost of the contract.
*
* `Solidity` was chosen over pure `Yul` due portability and developer experience reasons, once currently you can't
* import Yul code in Solidity, also in most block explorers you can't verify/publish YUL code.
*/
fallback() external payable {
// ---------------- Static Memory Layout ---------------
// offset | description
// -------|---------------------------------------------
// 0x0000 | final contract address
// 0x0020 | `Create3Proxy` proxy contract address.
// 0x0040 | keccak256(creationCode)
// 0x0060 | keccak256(arguments)
// 0x0080 | keccak256(callback)
// 0x00a0 | current depth
// 0x00c0 | creationCode.offset
// 0x00e0 | creationCode.length
// 0x0100 | arguments.offset
// 0x0120 | arguments.length
// 0x0140 | callback.offset
// 0x0160 | callback.length
// 0x0180 | previous slot 0
// 0x01a0 | previous slot 1
// 0x01c0 | previous salt
// 0x01e0 | previous value
// 0x0200 | dynamic memory, ex: creationCode, arguments and callback.
//
// Obs: the memory layout above is ignored for revert messages.
assembly {
// If the contracts calls itself, then is for check whether EIP-1153
// transient storage is supported or not.
// This check is done dynamically for each call, because even if the EVM
// doesn't support this opcode right now, it may support it in the future.
if eq(caller(), address()) {
mstore(0x00, tload(0))
mstore(0x20, tload(1))
mstore(0x40, tload(2))
mstore(0x60, tload(3))
// obs: for debugging purposes, you can change this to `revert(0,0)`
// to disable EIP-1153 support.
return(0, 0x80)
}
// ------- BITFLAGS -------
// HAS_ARGUMENTS = 0x01
// HAS_CALLBACK = 0x02
// IS_CREATE3 = 0x04
// SUPPORT_EIP1153 = 0x08
// ------------------------
let bitflags
{
{
let selector := shr(0xe0, calldataload(0))
///////////////////////////////////////////////////////////////
// function context() external view returns (Context memory) //
///////////////////////////////////////////////////////////////
// This selector is checked first to reduce the gas overhead, once this is expected to
// be called more frequently.
if eq(selector, 0xd0496d6a) {
// No value can be sent once it is a view function. It also make sure the call has
// sufficient gas, to prevent false negatives when checking for EIP-1153 support.
if or(callvalue(), lt(gas(), 3000)) { revert(0, 0) }
//////////////////////////
// Load current context //
//////////////////////////
// First try to retrieve the context using EIP-1153 TRANSIENT STORAGE, the result
// is automatically stored in corresponding static memory.
// Obs: This call use all 2000 gas if the EVM doesn't support EIP-1153, and 472 gas
// if it supports. We provide an extra gas margin in case those opcodes change their
// gas cost in the future.
let support_eip1153 := staticcall(2000, address(), 0, 0, 0x0180, 0x80)
// if it doesn't support EIP-1153, then load the context from storage.
if iszero(support_eip1153) {
let slot0 := sload(0)
if xor(slot0, not(0)) {
mstore(0x0180, slot0)
mstore(0x01a0, sload(1))
// Once the `salt` zero is very common, we XOR it with the slot0 to reduce the likelihood
// of storing a zero value, otherwise using the salt zero ended up using more gas than
// using a non-zero salt, which is inconvenient but not an issue at all.
// Notice the previous salt is always restored at the end of the execution. So this value
// cannot influence any subsequent contract creation gas cost.
mstore(0x01c0, xor(sload(2), slot0))
// Only load the value if the `HAS_VALUE` flag is set.
// This flag is used to avoid storing a zero value in the storage, it also saves one storage
// write/read when no value is provided (saves ~2900 gas).
let has_value := and(slot0, 0x01)
if has_value { mstore(0x01e0, sload(3)) }
}
}
// Load context from static memory
let slot0 := mload(0x0180)
let slot1 := mload(0x01a0)
let salt := mload(0x01c0)
let value := mload(0x01e0)
// Decode `call_flags`.
// IS_CREATE3 := 0x04
// HAS_CALLBACK := 0x02
// HAS_VALUE := 0x01
let call_flags := and(slot0, 0x07)
let depth := and(shr(3, slot0), 0x7f)
let contract_addr := and(shr(10, slot0), 0xffffffffffffffffffffffffffffffffffffffff)
let callback_selector := shl(224, shr(170, slot0))
let args_len := and(shr(202, slot0), 0x3fffff)
let data := or(shl(160, shr(160, slot1)), shl(128, shr(224, slot0)))
let sender := and(slot1, 0xffffffffffffffffffffffffffffffffffffffff)
// discard `HAS_VALUE` flag
call_flags := shr(1, call_flags)
// Store `Context` in memory following Solidity ABI encoding
mstore(0x0000, 0x20) // offset
mstore(0x0020, contract_addr) // contract_address
mstore(0x0040, sender) // sender
mstore(0x0060, depth) // call depth
mstore(0x0080, shr(1, call_flags)) // kind (0 for CREATE2, 1 for CREATE3)
mstore(0x00a0, and(1, call_flags)) // hasCallback
mstore(0x00c0, callback_selector) // callbackSelector
mstore(0x00e0, value) // value
mstore(0x0100, salt) // salt
mstore(0x0120, 0x0120) // offset
mstore(0x0140, args_len) // data_len
mstore(0x0160, data) // arguments[..16]
// If the args.length > 16, then copy the rest of the arguments to memory.
switch support_eip1153
case 0 {
if gt(args_len, 16) {
// Copy `data[16..]` from storage to memory
for {
let end := add(0x0160, args_len)
let ptr := 0x170
let offset := shl(64, depth)
let args_hash := sload(offset)
offset := add(offset, 0x01)
} lt(ptr, end) {
ptr := add(ptr, 0x20)
offset := add(offset, 0x01)
} { mstore(ptr, xor(sload(offset), args_hash)) }
}
}
default {
// Copy `data[16..]` from transient storage to memory
for {
let end := add(0x0160, args_len)
let ptr := 0x170
let offset := shl(64, depth)
} lt(ptr, end) {
ptr := add(ptr, 0x20)
offset := add(offset, 0x01)
} { mstore(ptr, tload(offset)) }
}
// Remove any non-zero from padding bytes
mstore(add(0x0160, args_len), 0)
// Return an 32-byte aligned result
return(0x00, add(0x0160, and(add(args_len, 0x1f), 0xffffffffffffffe0)))
}
///////////////////////////
// Validate the selector //
///////////////////////////
// The 5 least significant bits of the selectors are unique, this allow an efficient selector
// verification using less than 100 gas.
// | FUNCTION | SELECTOR | suffix (5-bit) | index | bitflags |
// | create2(bytes32,bytes,bytes,bytes) | 0x8778391e | 0x8778391e & 0x1f == 30 | 26 | 011 |
// | create3(bytes32,bytes,bytes,bytes) | 0xac049de2 | 0xac049de2 & 0x1f == 2 | 27 | 111 |
// | create2(bytes32,bytes,bytes) | 0xce40d339 | 0xce40d339 & 0x1f == 25 | 28 | 001 |
// | create3(bytes32,bytes,bytes) | 0xd2a8169a | 0xd2a8169a & 0x1f == 26 | 29 | 101 |
// | create2(bytes32,bytes) | 0xb9aaf526 | 0xb9aaf526 & 0x1f == 6 | 30 | 000 |
// | create3(bytes32,bytes) | 0x2af25238 | 0x2af25238 & 0x1f == 24 | 31 | 100 |
// Convert the 5-bit suffix into an index using byte lookup.
let index
{
let suffix := and(selector, 0x1f)
index := byte(suffix, 0x00001b0000001e00000000000000000000000000000000001f1c1d0000001a00)
}
// Extract the selector at the expected index, where 0x2af25238b9aaf526d2a8169ace40d339ac049de28778391e
// is simply the selectors concatenated.
let expected_selector
{
let shift := byte(index, 0x20406080a0)
expected_selector := shr(shift, 0x2af25238b9aaf526d2a8169ace40d339ac049de28778391e)
expected_selector := and(expected_selector, 0xffffffff)
}
// Check if the `selector` matches the `expected_selector`
let valid := eq(selector, expected_selector)
// Validate the calldatasize against the minimal size
{
let min_calldata_size := byte(index, 0x838363634343)
valid := and(valid, gt(calldatasize(), min_calldata_size))
mstore(0x00, sub(min_calldata_size, 4))
}
// Revert if the selector is invalid
if iszero(valid) { revert(0, 0) }
///////////////////////////////
// Load the previous context //
///////////////////////////////
// First try to retrieve the context using EIP-1153 TRANSIENT STORAGE, the result is automatically
// stored in corresponding static memory.
// Obs: This call use all 2000 gas if the EVM doesn't support EIP-1153, and 472 gas if it supports.
// We provide an extra gas margin in case those opcodes change their gas cost in the future.
let support_eip1153 := staticcall(2000, address(), 0, 0, 0x0180, 0x80)
// if it doesn't support EIP-1153, then load the previous context from storage.
if iszero(support_eip1153) {
let slot0 := sload(0)
if xor(slot0, not(0)) {
mstore(0x0180, slot0)
mstore(0x01a0, sload(1))
mstore(0x01c0, sload(2))
// Only load the value if the `HAS_VALUE` flag is set.
// This flag is used to avoid storing a non-zero value in the storage.
let has_value := and(slot0, 0x01)
if has_value { mstore(0x01e0, sload(3)) }
}
}
// Set the bitflags using byte lookup.
// HAS_ARGUMENTS = 0x01
// HAS_CALLBACK = 0x02
// IS_CREATE3 = 0x04
// SUPPORT_EIP1153 = 0x08
bitflags := or(shl(3, support_eip1153), byte(index, 0x030701050004))
}
let valid
/////////////////////////////
// Validate `creationCode` //
/////////////////////////////
let min_calldata_size := mload(0x00)
{
// creationcode_ptr <= 0xffffffffffffffff
let creationcode_ptr := calldataload(0x24)
valid := lt(creationcode_ptr, 0x010000000000000000)
// creationcode_ptr > min_calldata_size
valid := and(valid, gt(creationcode_ptr, min_calldata_size))
// calldatasize > (creationcode_ptr + 0x1f)
creationcode_ptr := add(creationcode_ptr, 0x04)
valid := and(valid, slt(add(creationcode_ptr, 0x1f), calldatasize()))
// creationcode_len <= 0xffffffffffffffff
let creationcode_len := calldataload(creationcode_ptr)
valid := and(valid, lt(creationcode_len, 0x010000000000000000))
creationcode_ptr := add(creationcode_ptr, 0x20)
// (creationcode_ptr + creationcode_len + 0x20) <= calldatasize
valid := and(valid, iszero(gt(add(creationcode_ptr, creationcode_len), calldatasize())))
// creationcode_len > 0
valid := and(valid, gt(creationcode_len, 0))
// store the `creationcode_ptr` and `creationcode_len` at static memory 0xc0-0xe0
mstore(0xc0, creationcode_ptr)
mstore(0xe0, creationcode_len)
}
//////////////////////////
// Validate `arguments` //
//////////////////////////
{
// args_ptr <= 0xffffffffffffffff
let args_ptr := calldataload(0x44)
let has_args := and(bitflags, 0x01)
let valid_args := and(has_args, lt(args_ptr, 0x010000000000000000))
// args_ptr > min_calldata_size
valid_args := and(valid_args, gt(args_ptr, min_calldata_size))
// calldatasize > (args_ptr + 0x1f)
args_ptr := add(args_ptr, 0x04)
valid_args := and(valid_args, slt(add(args_ptr, 0x1f), calldatasize()))
// args_len <= 0x3fffff
let args_len := calldataload(args_ptr)
valid_args := and(valid_args, lt(args_len, 0x400000))
args_ptr := add(args_ptr, 0x20)
// (args_ptr + args_len + 0x20) >= calldatasize
valid_args := and(valid_args, iszero(gt(add(args_ptr, args_len), calldatasize())))
// Set args_ptr and args_len to zero if there's no arguments
valid_args := and(valid_args, has_args)
args_ptr := mul(args_ptr, valid_args)
args_len := mul(args_len, valid_args)
// store the `args_ptr` and `args_len` at static memory 0x0100-0x0120
mstore(0x0100, args_ptr)
mstore(0x0120, args_len)
// If has no arguments, it is always valid.
valid := and(valid, or(valid_args, iszero(has_args)))
}
/////////////////////////
// Validate `callback` //
/////////////////////////
{
// callback_ptr <= 0xffffffffffffffff
let callback_ptr := calldataload(0x64)
let valid_callback := lt(callback_ptr, 0x010000000000000000)
// callback_ptr > 0x7f
valid_callback := and(valid_callback, gt(callback_ptr, 0x7f))
// calldatasize > (callback_ptr + 0x1f)
callback_ptr := add(callback_ptr, 0x04)
valid_callback := and(valid_callback, slt(add(callback_ptr, 0x1f), calldatasize()))
// callback_len <= 0xffffffffffffffff
let callback_len := calldataload(callback_ptr)
valid_callback := and(valid_callback, lt(callback_len, 0x010000000000000000))
callback_ptr := add(callback_ptr, 0x20)
// (callback_ptr + callback_len + 0x20) >= calldatasize
valid_callback := and(valid_callback, iszero(gt(add(callback_ptr, callback_len), calldatasize())))
// Set callback_ptr and callback_len to zero if there's no callback
let has_callback := and(shr(1, bitflags), 1)
valid_callback := and(valid_callback, has_callback)
callback_ptr := mul(callback_ptr, valid_callback)
callback_len := mul(callback_len, valid_callback)
// store the `callback_ptr` and `callback_len` at static memory 0x0140-0x0160
mstore(0x0140, callback_ptr)
mstore(0x0160, callback_len)
// If the call has no callback, it is always valid.
valid_callback := or(valid_callback, iszero(has_callback))
valid := and(valid, valid_callback)
}
if iszero(valid) { revert(0, 0) }
}
///////////////////////////////////////////////////
// Check if the maximum depth of 127 was reached //
///////////////////////////////////////////////////
{
// Decode the previous `depth`.
let slot0 := mload(0x0180)
let depth := and(shr(3, slot0), 0x7f)
// The `depth` must be less than 127, which is the maximum number of nested calls before overflow.
// obs: probably impossible to reach this limit, due EIP-150 `all but one 64th`.
// - reference: https://eips.ethereum.org/EIPS/eip-150
if gt(depth, 0x7e) {
// revert CallStackOverflow()
mstore(0x00, 0x41f739de)
revert(0x1c, 0x04)
}
mstore(0xa0, add(depth, 1))
}
// Workaround for Frontier EVM chains, where `address(this).balance` can be less than `msg.value` if
// this contract has no previous existential deposit.
// - https://github.com/polkadot-evm/frontier/blob/polkadot-v1.11.0/ts-tests/tests/test-balance.ts#L41
//
// The following code is a branchless version of the ternary operator, equivalent to:
// ```solidity
// uint256 value = address(this).balance < msg.value ? address(this).balance : msg.value;
// ```
// also see: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/4976
let value := xor(callvalue(), mul(xor(selfbalance(), callvalue()), lt(selfbalance(), callvalue())))
////////////////////////////
// Compute Arguments Hash //
////////////////////////////
{
let arguments_ptr := mload(0x0100)
let arguments_len := mload(0x0120)
// Copy `arguments` to memory
calldatacopy(0x0200, arguments_ptr, arguments_len)
// Compute the keccak256 hash of the `arguments`
let arguments_hash := keccak256(0x0200, arguments_len)
// Set zero if there's no `arguments`.
arguments_hash := mul(arguments_hash, gt(arguments_ptr, 0))
// Save the `arguments_hash` in the static memory
mstore(0x60, arguments_hash)
}
{
let addr
{
// Copy `creationCode` to memory
let creationcode_len := mload(0xe0)
{
let creationcode_ptr := mload(0xc0)
calldatacopy(0x0200, creationcode_ptr, creationcode_len)
}
////////////////////////////////////////////////////////////////////
// Compute CREATE2 address //
////////////////////////////////////////////////////////////////////
addr := or(address(), 0xff0000000000000000000000000000000000000000)
mstore(0x00, addr)
mstore(0x20, calldataload(0x04))
let creationcode_hash := keccak256(0x0200, creationcode_len)
mstore(0x40, creationcode_hash)
let create2_addr := and(keccak256(11, 85), 0xffffffffffffffffffffffffffffffffffffffff)
////////////////////////////////////////////////////////////////////
// Compute CREATE3 address //
////////////////////////////////////////////////////////////////////
// Compute `CREATE3` salt, which is `keccak25(abi.encodePacked(msg.sender, salt))`
mstore(0x00, caller())
mstore(0x20, calldataload(0x04))
mstore(0x20, keccak256(12, 52))
// Compute `CREATE3` proxy address, which is `keccak256(abi.encodePacked(0xff, address(this), create3salt, proxyHash))`
mstore(0x00, addr)
let proxy_hash := 0x0281a97663cf81306691f0800b13a91c4d335e1d772539f127389adae654ffc6
mstore(0x40, proxy_hash)
let proxy_addr := and(keccak256(11, 85), 0xffffffffffffffffffffffffffffffffffffffff)
// Compute `CREATE3` contract address, which is `keccak256(abi.encodePacked(hex"d694", proxyAddr, hex"01"))`
mstore(0x00, or(0xd694000000000000000000000000000000000000000001, shl(8, proxy_addr)))
let create3_addr := and(keccak256(0x09, 23), 0xffffffffffffffffffffffffffffffffffffffff)
//////////////////////
// Validate //
//////////////////////
// Validate if the contract exists and if the `creationCode` is not the `Create3Proxy` contract.
//
// IMPORTANT: The `Create3Proxy` creationCode CANNOT be used in `create2(...)` functions, otherwise
// anyone can deploy an contract in a address that belongs to another `msg.sender`.
// If someone attempt it, this contract reverts with `ReservedInitCode` error.
{
let is_create3 := and(shr(2, bitflags), 1)
addr := xor(create2_addr, mul(xor(create3_addr, create2_addr), is_create3))
// The proxy creation code is reserved only for `create3` methods
let invalid_init_code := and(eq(creationcode_hash, proxy_hash), iszero(is_create3))
// The contract must not exist
let contract_exists := extcodesize(addr)
if or(contract_exists, invalid_init_code) {
// 0xb8bcb0c9 == bytes4(keccak256("ReservedInitCode()"))
// 0xc5644373 == bytes4(keccak256("ContractAlreadyExists(address)"))
// 0x7dd8f3ba == 0xb8bcb0c9 ^ 0xc5644373
let sig := xor(0xb8bcb0c9, mul(0x7dd8f3ba, invalid_init_code))
let len := add(0x04, shl(5, iszero(invalid_init_code)))
mstore(0x00, sig)
mstore(0x20, addr)
revert(0x1c, len)
}
}
// Store final contract address, proxy address and creationCode in
// their respective static memory slots.
mstore(0x00, addr)
mstore(0x20, proxy_addr)
mstore(0x40, creationcode_hash)
}
////////////////////////////////////////////////////////////////////
// UPDATE CONTEXT //
////////////////////////////////////////////////////////////////////
// Context Storage Layout
// | 32-bit | 22-bit | 32-bit | 160-bit | 7-bit | 3-bit |
// +-+-+-+-+-+-+-+-+-+-+-+--+-+-+-+-+-+-+-+-+-+-+-++-+-+-+-+-+-+-+-+
// | args | args len | selector | contract | depth | flags | offset: 0
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | args[..12] (96-bit) | sender (160-bit) | offset: 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | salt | offset: 2
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | value | offset: 3
// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
// | keccak256(args)* | offset: 2**64 * depth
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | args[16..] | offset: 2**64 * depth + 1
// | ... | length: (args.length + 15) / 32
// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
let arguments_ptr := mload(0x0100)
let arguments_len := mload(0x0120)
let arguments_data := mul(calldataload(arguments_ptr), gt(arguments_ptr, 0))
let slot0
// Encode `args[12..16]` (32 bits)
{
let has_args := and(bitflags, 0x01)
let args := shr(224, shl(96, arguments_data))
args := mul(args, has_args)
slot0 := args
}
// Encode args_len (22 bits)
// Obs: validated previously, so is always less than 2**22
slot0 := or(shl(22, slot0), arguments_len)
// Encode selector (32 bits)
{
let callback_ptr := mload(0x0140)
let callback_len := mload(0x0160)
let callback_selector := shr(224, calldataload(callback_ptr))
callback_selector := mul(callback_selector, gt(callback_len, 0))
slot0 := or(shl(32, slot0), callback_selector)
}
// Encode contractAddress (160 bits)
slot0 := or(shl(160, slot0), addr)
// Encode depth (7 bits)
let depth := mload(0xa0)
slot0 := or(shl(7, slot0), depth)
// Encode bitflags (3 bits)
slot0 := or(shl(3, slot0), or(and(bitflags, 0x06), gt(value, 0)))
// Encode `args[..12]` (96 bit) + sender (160 bit)
let slot1 := or(shl(160, shr(160, arguments_data)), caller())
// Encode salt (256 bits)
let salt := calldataload(0x04)
// Store the new Context in the transient storage or storage.
let support_eip1153 := and(bitflags, 0x08)
switch support_eip1153
case 0 {
// Store the context in the storage, skip `value` if zero.
sstore(0, slot0)
sstore(1, slot1)
// Once the `salt` zero is very common, we XOR it with the slot0 to reduce the likelihood
// of storing a zero value, otherwise using the salt zero ended up using more gas than
// using a non-zero salt, which is inconvenient but not an issue at all.
// Notice the previous salt is always restored at the end of the execution. So this value
// cannot influence any subsequent contract creation gas cost.
sstore(2, xor(salt, slot0))
// When `msg.value > 0`, then the first bit of `flags` is set, so no need to store this value (saves ~2900 gas).
if value { sstore(3, value) }
if gt(arguments_len, 16) {
// When `arguments.length > 16`, we also store the argument hash in the context.
let arguments_hash := mload(0x60)
/// If `args.length > 16`, then store the remaining bytes in the transient storage,
// starting at index `2**64 * depth`.
for {
let end := add(arguments_ptr, arguments_len)
let ptr := add(arguments_ptr, 16)
let offset := shl(64, depth)
sstore(offset, arguments_hash)
offset := add(offset, 0x01)
} lt(ptr, end) {
ptr := add(ptr, 0x20)
offset := add(offset, 0x01)
} { sstore(offset, xor(calldataload(ptr), arguments_hash)) }
}
}
default {
// Store the context in the EIP-1153 Transient Storage.
// obs: don't need to skip value once the gas cost is negligible (~100 gas).
tstore(0, slot0)
tstore(1, slot1)
tstore(2, salt)
tstore(3, value)
// If `data.length > 16`, then store the remaining bytes in the transient storage,
// starting at index `2**64 * depth`.
for {
let end := add(arguments_ptr, arguments_len)
let ptr := add(arguments_ptr, 16)
let offset := shl(64, depth)
} lt(ptr, end) {
ptr := add(ptr, 0x20)
offset := add(offset, 0x01)
} { tstore(offset, calldataload(ptr)) }
}
}
// Create contract using `create2` or `create3`
switch and(bitflags, 0x04)
case 0 {
/////////////////
// CREATE2 //
/////////////////
// Deploy contract or Proxy, depending if `is_create3` is enabled.
let creationcode_len := mload(0xe0)
let contract_addr :=
create2(mul(value, iszero(and(bitflags, 0x06))), 0x0200, creationcode_len, calldataload(0x04))
// Computed address and actual address must match
if or(iszero(contract_addr), xor(contract_addr, mload(0))) {
// 0x04a5b3ee -> Create2Failed()
mstore(0x00, 0x04a5b3ee)
revert(0x1c, 0x04)
}
}
default {
/////////////////
// CREATE3 //
/////////////////
// Create3Proxy creation code
// 0x763318602e57363d3d37363d47f080915215602e57f35bfd602b52336014526460203d3d733d526030601bf3:
// 0x00 0x763318602e.. PUSH23 0x3318.. 0x3318602e57363d3d37363d47f080915215602e57f35bfd
// 0x18 0x602b PUSH1 0x2b 43 0x3318602e57363d3d37363d47f080915215602e57f35bfd
// 0x1a 0x52 MSTORE
// 0x1b 0x33 CALLER addr
// 0x1c 0x6014 PUSH1 20 20 addr
// 0x1f 0x52 MSTORE
// 0x25 0x6460203d3d73 PUSH5 0x6020.. 0x60203d3d73
// 0x26 0x3d RETURNDATASIZE 0 0x60203d3d73
// 0x27 0x52 MSTORE
// 0x29 0x6030 PUSH1 48 48
// 0x2a 0x601b PUSH1 27 27 48
// 0x2b 0xf3 RETURN
// Create3Proxy runtime code, where `XXXX..` is the Universal Factory contract address.
// 0x60203d3d73XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX3318602e57363d3d37363d47f080915215602e57f35bfd
// 0x00 0x6020 PUSH1 32 32
// 0x02 0x3d RETURNDATASIZE 0 32
// 0x03 0x3d RETURNDATASIZE 0 0 32
// 0x04 0x74XXXXXX.. PUSH20 XXXXXXX factory 0 0 32
// 0x19 0x33 CALLER caller factory 0 0 32
// 0x1a 0x18 XOR invalid 0 0 32
// 0x1b 0x602e PUSH1 0x2e 46 invalid 0 0 32
// ,=< 0x1d 0x57 JUMPI 0 0 32
// | 0x1e 0x36 CALLDATASIZE cls 0 0 32
// | 0x1f 0x3d RETURNDATASIZE 0 cls 0 0 32
// | 0x20 0x3d RETURNDATASIZE 0 0 cls 0 0 32
// | 0x21 0x37 CALLDATACOPY 0 0 32
// | 0x22 0x36 CALLDATASIZE cls 0 0 32
// | 0x23 0x3d RETURNDATASIZE 0 cls 0 0 32
// | 0x24 0x47 SELFBALANCE val 0 cls 0 0 32
// | 0x25 0xf0 CREATE addr 0 0 32
// | 0x26 0x80 DUP1 addr addr 0 0 32
// | 0x27 0x91 SWAP2 0 addr addr 0 32
// | 0x28 0x52 MSTORE addr 0 32
// | 0x29 0x16 ISZERO fail 0 32
// | 0x2a 0x602e PUSH1 0x2e 46 fail 0 32
// |=< 0x2c 0x57 JUMPI 0 32
// | 0x2d 0xf3 RETURN
// `=> 0x2e 0x5b JUMPDEST
// 0x2f 0xfd REVERT
// Deploy the `Create3Proxy`
let proxy_addr
{
// Save the current memory state, to restore it after the proxy deployment.
let mem00 := mload(0x00)
let mem20 := mload(0x20)
{
// Compute `CREATE3` salt, which is `keccak25(abi.encodePacked(msg.sender, salt))`
mstore(0x00, caller())
mstore(0x20, calldataload(0x04))
let salt := keccak256(12, 52)
// Store `Create3Proxy` initcode in memory.
mstore(0x0c, 0x60203d3d733d526030601bf3)
mstore(0x00, 0x763318602e57363d3d37363d47f080915215602e57f35bfd602b523360145264)
proxy_addr := create2(0, 0x00, 44, salt)
}
// Restore the memory state
mstore(0x00, mem00)
mstore(0x20, mem20)
}
// return an error if failed to create `Create3Proxy`.
if iszero(and(eq(proxy_addr, mload(0x20)), eq(extcodesize(proxy_addr), 48))) {
// revert Create3Failed()
mstore(0x00, 0x08fde50a)
revert(0x1c, 0x04)
}
// Save the computed address in the stack
// once the `Create3Proxy` will override the 0x00 memory location.
let computed_addr := mload(0)
// Call the `Create3Proxy` to deploy the desired contract at deterministic address.
let creationcode_len := mload(0xe0)
// Only send funds if there's no callback
let no_callback := iszero(and(bitflags, 0x02))
// Deploy the contract using `Create3Proxy`
let success := call(gas(), proxy_addr, mul(value, no_callback), 0x0200, creationcode_len, 0x00, 0x20)
// Compare the computed address and actual address
if or(iszero(success), xor(computed_addr, mload(0))) {
// 0x08fde50a -> Create3Failed()
mstore(0x00, 0x08fde50a)
revert(0x1c, 0x04)
}
}
// Emit `ContractCreated` event and call `callback` if provided
{
let callback_ptr := mload(0x140)
let callback_len := mload(0x160)
{
// Compute `keccak256(callback)`
let callback_hash
// Copy `callback` to memory
calldatacopy(0x0200, callback_ptr, callback_len)
// Compute callback hash
callback_hash := keccak256(0x0200, callback_len)
// Set zero if there's no callback
callback_hash := mul(callback_hash, gt(callback_ptr, 0))
// emit ContractCreated(contractAddress, creationCodeHash, salt, dataHash, codeHash, callbackHash, depth, value)
let creation_code_hash := mload(0x40)
let contract_addr := mload(0)
let args_hash := mload(0x60)
let depth := mload(0xa0)
mstore(0x20, args_hash)
mstore(0x40, extcodehash(contract_addr))
mstore(0x60, callback_hash)
mstore(0x80, depth)
mstore(0xa0, value)
log4(0x20, 0xa0, contract_addr, creation_code_hash, calldataload(0x04), caller())
// Restore contract addr
mstore(0, contract_addr)
}
// Call `callback` if provided
if callback_ptr {
if iszero(call(gas(), mload(0), value, 0x0200, callback_len, 0, 0)) {
mstore(0x00, 0x30b9b6dd)
// error offset
mstore(0x20, 0x20)
// error length
mstore(0x40, returndatasize())
// cleanup padding bytes, in case it has non-zero bytes
mstore(add(0x60, returndatasize()), 0x00)
// Copy revert data to memory
returndatacopy(0x60, 0, returndatasize())
// revert(data + padding)
revert(0x1c, add(and(add(returndatasize(), 31), 0xffffffffffffffe0), 0x44))
}
}
}
// Restore previous ctx and salt
// Obs: the logic for restore the state is different for storage and transient storage,
// because for storage, the `zero to non-zero` transition use more gas than the `non-zero to non-zero`.
let prev_slot0 := mload(0x0180)
let prev_slot1 := mload(0x01a0)
let prev_salt := mload(0x01c0)
let prev_value := mload(0x01e0)
let support_eip1153 := and(bitflags, 0x08)
switch support_eip1153
case 0 {
let is_empty := iszero(prev_slot0)
let mask := sub(0, is_empty)
sstore(0, or(prev_slot0, mask))
sstore(1, or(prev_slot1, mask))
sstore(2, or(prev_salt, mask))
if value { sstore(3, or(prev_value, mask)) }
}
default {
tstore(0, prev_slot0)
tstore(1, prev_slot1)
tstore(2, prev_salt)
tstore(3, prev_value)
}
// return the created contract address
return(0, 0x20)
}
}
}