1+ /**
2+ *Submitted for verification at Etherscan.io on 2020-10-14
3+ */
4+
5+ // ┏━━━┓━┏┓━┏┓━━┏━━━┓━━┏━━━┓━━━━┏━━━┓━━━━━━━━━━━━━━━━━━━┏┓━━━━━┏━━━┓━━━━━━━━━┏┓━━━━━━━━━━━━━━┏┓━
6+ // ┃┏━━┛┏┛┗┓┃┃━━┃┏━┓┃━━┃┏━┓┃━━━━┗┓┏┓┃━━━━━━━━━━━━━━━━━━┏┛┗┓━━━━┃┏━┓┃━━━━━━━━┏┛┗┓━━━━━━━━━━━━┏┛┗┓
7+ // ┃┗━━┓┗┓┏┛┃┗━┓┗┛┏┛┃━━┃┃━┃┃━━━━━┃┃┃┃┏━━┓┏━━┓┏━━┓┏━━┓┏┓┗┓┏┛━━━━┃┃━┗┛┏━━┓┏━┓━┗┓┏┛┏━┓┏━━┓━┏━━┓┗┓┏┛
8+ // ┃┏━━┛━┃┃━┃┏┓┃┏━┛┏┛━━┃┃━┃┃━━━━━┃┃┃┃┃┏┓┃┃┏┓┃┃┏┓┃┃━━┫┣┫━┃┃━━━━━┃┃━┏┓┃┏┓┃┃┏┓┓━┃┃━┃┏┛┗━┓┃━┃┏━┛━┃┃━
9+ // ┃┗━━┓━┃┗┓┃┃┃┃┃┃┗━┓┏┓┃┗━┛┃━━━━┏┛┗┛┃┃┃━┫┃┗┛┃┃┗┛┃┣━━┃┃┃━┃┗┓━━━━┃┗━┛┃┃┗┛┃┃┃┃┃━┃┗┓┃┃━┃┗┛┗┓┃┗━┓━┃┗┓
10+ // ┗━━━┛━┗━┛┗┛┗┛┗━━━┛┗┛┗━━━┛━━━━┗━━━┛┗━━┛┃┏━┛┗━━┛┗━━┛┗┛━┗━┛━━━━┗━━━┛┗━━┛┗┛┗┛━┗━┛┗┛━┗━━━┛┗━━┛━┗━┛
11+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┃┃━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
12+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┗┛━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
13+
14+ // SPDX-License-Identifier: CC0-1.0
15+
16+ pragma solidity 0.6.11 ;
17+
18+ // This interface is designed to be compatible with the Vyper version.
19+ /// @notice This is the Ethereum 2.0 deposit contract interface.
20+ /// For more information see the Phase 0 specification under https://github.com/ethereum/eth2.0-specs
21+ interface IDepositContract {
22+ /// @notice A processed deposit event.
23+ event DepositEvent (
24+ bytes pubkey ,
25+ bytes withdrawal_credentials ,
26+ bytes amount ,
27+ bytes signature ,
28+ bytes index
29+ );
30+
31+ /// @notice Submit a Phase 0 DepositData object.
32+ /// @param pubkey A BLS12-381 public key.
33+ /// @param withdrawal_credentials Commitment to a public key for withdrawals.
34+ /// @param signature A BLS12-381 signature.
35+ /// @param deposit_data_root The SHA-256 hash of the SSZ-encoded DepositData object.
36+ /// Used as a protection against malformed input.
37+ function deposit (
38+ bytes calldata pubkey ,
39+ bytes calldata withdrawal_credentials ,
40+ bytes calldata signature ,
41+ bytes32 deposit_data_root
42+ ) external payable ;
43+
44+ /// @notice Query the current deposit root hash.
45+ /// @return The deposit root hash.
46+ function get_deposit_root () external view returns (bytes32 );
47+
48+ /// @notice Query the current deposit count.
49+ /// @return The deposit count encoded as a little endian 64-bit number.
50+ function get_deposit_count () external view returns (bytes memory );
51+ }
52+
53+ // Based on official specification in https://eips.ethereum.org/EIPS/eip-165
54+ interface ERC165 {
55+ /// @notice Query if a contract implements an interface
56+ /// @param interfaceId The interface identifier, as specified in ERC-165
57+ /// @dev Interface identification is specified in ERC-165. This function
58+ /// uses less than 30,000 gas.
59+ /// @return `true` if the contract implements `interfaceId` and
60+ /// `interfaceId` is not 0xffffffff, `false` otherwise
61+ function supportsInterface (bytes4 interfaceId ) external pure returns (bool );
62+ }
63+
64+ // This is a rewrite of the Vyper Eth2.0 deposit contract in Solidity.
65+ // It tries to stay as close as possible to the original source code.
66+ /// @notice This is the Ethereum 2.0 deposit contract interface.
67+ /// For more information see the Phase 0 specification under https://github.com/ethereum/eth2.0-specs
68+ contract DepositContract is IDepositContract , ERC165 {
69+ uint constant DEPOSIT_CONTRACT_TREE_DEPTH = 32 ;
70+ // NOTE: this also ensures `deposit_count` will fit into 64-bits
71+ uint constant MAX_DEPOSIT_COUNT = 2 ** DEPOSIT_CONTRACT_TREE_DEPTH - 1 ;
72+
73+ bytes32 [DEPOSIT_CONTRACT_TREE_DEPTH] branch;
74+ uint256 deposit_count;
75+
76+ bytes32 [DEPOSIT_CONTRACT_TREE_DEPTH] zero_hashes;
77+
78+ constructor () public {
79+ // Compute hashes in empty sparse Merkle tree
80+ for (uint height = 0 ; height < DEPOSIT_CONTRACT_TREE_DEPTH - 1 ; height++ )
81+ zero_hashes[height + 1 ] = sha256 (abi.encodePacked (zero_hashes[height], zero_hashes[height]));
82+ }
83+
84+ function get_deposit_root () override external view returns (bytes32 ) {
85+ bytes32 node;
86+ uint size = deposit_count;
87+ for (uint height = 0 ; height < DEPOSIT_CONTRACT_TREE_DEPTH; height++ ) {
88+ if ((size & 1 ) == 1 )
89+ node = sha256 (abi.encodePacked (branch[height], node));
90+ else
91+ node = sha256 (abi.encodePacked (node, zero_hashes[height]));
92+ size /= 2 ;
93+ }
94+ return sha256 (abi.encodePacked (
95+ node,
96+ to_little_endian_64 (uint64 (deposit_count)),
97+ bytes24 (0 )
98+ ));
99+ }
100+
101+ function get_deposit_count () override external view returns (bytes memory ) {
102+ return to_little_endian_64 (uint64 (deposit_count));
103+ }
104+
105+ function deposit (
106+ bytes calldata pubkey ,
107+ bytes calldata withdrawal_credentials ,
108+ bytes calldata signature ,
109+ bytes32 deposit_data_root
110+ ) override external payable {
111+ // Extended ABI length checks since dynamic types are used.
112+ require (pubkey.length == 48 , "DepositContract: invalid pubkey length " );
113+ require (withdrawal_credentials.length == 32 , "DepositContract: invalid withdrawal_credentials length " );
114+ require (signature.length == 96 , "DepositContract: invalid signature length " );
115+
116+ // Check deposit amount
117+ require (msg .value >= 1 ether, "DepositContract: deposit value too low " );
118+ require (msg .value % 1 gwei == 0 , "DepositContract: deposit value not multiple of gwei " );
119+ uint deposit_amount = msg .value / 1 gwei ;
120+ require (deposit_amount <= type (uint64 ).max, "DepositContract: deposit value too high " );
121+
122+ // Emit `DepositEvent` log
123+ bytes memory amount = to_little_endian_64 (uint64 (deposit_amount));
124+ emit DepositEvent (
125+ pubkey,
126+ withdrawal_credentials,
127+ amount,
128+ signature,
129+ to_little_endian_64 (uint64 (deposit_count))
130+ );
131+
132+ // Compute deposit data root (`DepositData` hash tree root)
133+ bytes32 pubkey_root = sha256 (abi.encodePacked (pubkey, bytes16 (0 )));
134+ bytes32 signature_root = sha256 (abi.encodePacked (
135+ sha256 (abi.encodePacked (signature[:64 ])),
136+ sha256 (abi.encodePacked (signature[64 :], bytes32 (0 )))
137+ ));
138+ bytes32 node = sha256 (abi.encodePacked (
139+ sha256 (abi.encodePacked (pubkey_root, withdrawal_credentials)),
140+ sha256 (abi.encodePacked (amount, bytes24 (0 ), signature_root))
141+ ));
142+
143+ // Verify computed and expected deposit data roots match
144+ require (node == deposit_data_root, "DepositContract: reconstructed DepositData does not match supplied deposit_data_root " );
145+
146+ // Avoid overflowing the Merkle tree (and prevent edge case in computing `branch`)
147+ require (deposit_count < MAX_DEPOSIT_COUNT, "DepositContract: merkle tree full " );
148+
149+ // Add deposit data root to Merkle tree (update a single `branch` node)
150+ deposit_count += 1 ;
151+ uint size = deposit_count;
152+ for (uint height = 0 ; height < DEPOSIT_CONTRACT_TREE_DEPTH; height++ ) {
153+ if ((size & 1 ) == 1 ) {
154+ branch[height] = node;
155+ return ;
156+ }
157+ node = sha256 (abi.encodePacked (branch[height], node));
158+ size /= 2 ;
159+ }
160+ // As the loop should always end prematurely with the `return` statement,
161+ // this code should be unreachable. We assert `false` just to be safe.
162+ assert (false );
163+ }
164+
165+ function supportsInterface (bytes4 interfaceId ) override external pure returns (bool ) {
166+ return interfaceId == type (ERC165 ).interfaceId || interfaceId == type (IDepositContract).interfaceId;
167+ }
168+
169+ function to_little_endian_64 (uint64 value ) internal pure returns (bytes memory ret ) {
170+ ret = new bytes (8 );
171+ bytes8 bytesValue = bytes8 (value);
172+ // Byteswapping during copying to bytes.
173+ ret[0 ] = bytesValue[7 ];
174+ ret[1 ] = bytesValue[6 ];
175+ ret[2 ] = bytesValue[5 ];
176+ ret[3 ] = bytesValue[4 ];
177+ ret[4 ] = bytesValue[3 ];
178+ ret[5 ] = bytesValue[2 ];
179+ ret[6 ] = bytesValue[1 ];
180+ ret[7 ] = bytesValue[0 ];
181+ }
182+ }
0 commit comments