Gas used by transaction: 1161031 WARN skipping invalid gasCost value -90403 for op RETURN WARN skipping invalid gasCost value -90403 for op RETURN WARN skipping invalid gasCost value -91882 for op RETURN WARN skipping invalid gasCost value -91882 for op RETURN WARN skipping invalid gasCost value -87378 for op RETURN WARN skipping invalid gasCost value -87378 for op RETURN WARN skipping invalid gasCost value -88810 for op RETURN WARN skipping invalid gasCost value -88810 for op RETURN WARN skipping invalid gasCost value -81700 for op RETURN WARN skipping invalid gasCost value -81700 for op RETURN WARN skipping invalid gasCost value -83041 for op RETURN WARN skipping invalid gasCost value -83041 for op RETURN WARN skipping invalid gasCost value -82942 for op RETURN WARN skipping invalid gasCost value -82942 for op RETURN WARN skipping invalid gasCost value -84415 for op RETURN WARN skipping invalid gasCost value -84415 for op RETURN WARN skipping invalid gasCost value -85823 for op RETURN WARN skipping invalid gasCost value -85823 for op RETURN WARN skipping invalid gasCost value -87230 for op RETURN WARN skipping invalid gasCost value -87230 for op RETURN WARN skipping invalid gasCost value -84074 for op RETURN WARN skipping invalid gasCost value -84074 for op RETURN WARN skipping invalid gasCost value -85453 for op RETURN WARN skipping invalid gasCost value -85453 for op RETURN WARN skipping invalid gasCost value -85355 for op RETURN WARN skipping invalid gasCost value -85355 for op RETURN WARN skipping invalid gasCost value -86866 for op RETURN WARN skipping invalid gasCost value -86866 for op RETURN WARN skipping invalid gasCost value -88711 for op STOP WARN skipping invalid gasCost value -88711 for op STOP WARN skipping invalid gasCost value -90276 for op RETURN WARN skipping invalid gasCost value -90276 for op RETURN WARN skipping invalid gasCost value -85398 for op RETURN WARN skipping invalid gasCost value -85398 for op RETURN WARN skipping invalid gasCost value -86798 for op RETURN WARN skipping invalid gasCost value -86798 for op RETURN WARN skipping invalid gasCost value -86700 for op RETURN WARN skipping invalid gasCost value -86700 for op RETURN WARN skipping invalid gasCost value -88232 for op RETURN WARN skipping invalid gasCost value -88232 for op RETURN WARN skipping invalid gasCost value -85147 for op RETURN WARN skipping invalid gasCost value -85147 for op RETURN WARN skipping invalid gasCost value -86543 for op RETURN WARN skipping invalid gasCost value -86543 for op RETURN WARN skipping invalid gasCost value -86444 for op RETURN WARN skipping invalid gasCost value -86444 for op RETURN WARN skipping invalid gasCost value -87972 for op RETURN WARN skipping invalid gasCost value -87972 for op RETURN WARN skipping invalid gasCost value -84952 for op RETURN WARN skipping invalid gasCost value -84952 for op RETURN WARN skipping invalid gasCost value -86344 for op RETURN WARN skipping invalid gasCost value -86344 for op RETURN WARN skipping invalid gasCost value -86246 for op RETURN WARN skipping invalid gasCost value -86246 for op RETURN WARN skipping invalid gasCost value -87771 for op RETURN WARN skipping invalid gasCost value -87771 for op RETURN WARN skipping invalid gasCost value -84687 for op RETURN WARN skipping invalid gasCost value -84687 for op RETURN WARN skipping invalid gasCost value -86076 for op RETURN WARN skipping invalid gasCost value -86076 for op RETURN WARN skipping invalid gasCost value -85977 for op RETURN WARN skipping invalid gasCost value -85977 for op RETURN WARN skipping invalid gasCost value -87498 for op RETURN WARN skipping invalid gasCost value -87498 for op RETURN WARN skipping invalid gasCost value -84422 for op RETURN WARN skipping invalid gasCost value -84422 for op RETURN WARN skipping invalid gasCost value -85807 for op RETURN WARN skipping invalid gasCost value -85807 for op RETURN WARN skipping invalid gasCost value -85708 for op RETURN WARN skipping invalid gasCost value -85708 for op RETURN WARN skipping invalid gasCost value -87225 for op RETURN WARN skipping invalid gasCost value -87225 for op RETURN WARN skipping invalid gasCost value -84158 for op RETURN WARN skipping invalid gasCost value -84158 for op RETURN WARN skipping invalid gasCost value -85538 for op RETURN WARN skipping invalid gasCost value -85538 for op RETURN WARN skipping invalid gasCost value -85439 for op RETURN WARN skipping invalid gasCost value -85439 for op RETURN WARN skipping invalid gasCost value -86952 for op RETURN WARN skipping invalid gasCost value -86952 for op RETURN WARN skipping invalid gasCost value -83893 for op RETURN WARN skipping invalid gasCost value -83893 for op RETURN WARN skipping invalid gasCost value -85269 for op RETURN WARN skipping invalid gasCost value -85269 for op RETURN WARN skipping invalid gasCost value -85170 for op RETURN WARN skipping invalid gasCost value -85170 for op RETURN WARN skipping invalid gasCost value -86679 for op RETURN WARN skipping invalid gasCost value -86679 for op RETURN WARN skipping invalid gasCost value -83629 for op RETURN WARN skipping invalid gasCost value -83629 for op RETURN WARN skipping invalid gasCost value -85000 for op RETURN WARN skipping invalid gasCost value -85000 for op RETURN WARN skipping invalid gasCost value -84902 for op RETURN WARN skipping invalid gasCost value -84902 for op RETURN WARN skipping invalid gasCost value -86406 for op RETURN WARN skipping invalid gasCost value -86406 for op RETURN WARN skipping invalid gasCost value -83364 for op RETURN WARN skipping invalid gasCost value -83364 for op RETURN WARN skipping invalid gasCost value -84732 for op RETURN WARN skipping invalid gasCost value -84732 for op RETURN WARN skipping invalid gasCost value -84633 for op RETURN WARN skipping invalid gasCost value -84633 for op RETURN WARN skipping invalid gasCost value -86133 for op RETURN WARN skipping invalid gasCost value -86133 for op RETURN WARN skipping invalid gasCost value -83099 for op RETURN WARN skipping invalid gasCost value -83099 for op RETURN WARN skipping invalid gasCost value -84463 for op RETURN WARN skipping invalid gasCost value -84463 for op RETURN WARN skipping invalid gasCost value -84364 for op RETURN WARN skipping invalid gasCost value -84364 for op RETURN WARN skipping invalid gasCost value -85860 for op RETURN WARN skipping invalid gasCost value -85860 for op RETURN WARN skipping invalid gasCost value -82835 for op RETURN WARN skipping invalid gasCost value -82835 for op RETURN WARN skipping invalid gasCost value -84194 for op RETURN WARN skipping invalid gasCost value -84194 for op RETURN WARN skipping invalid gasCost value -84095 for op RETURN WARN skipping invalid gasCost value -84095 for op RETURN WARN skipping invalid gasCost value -85586 for op RETURN WARN skipping invalid gasCost value -85586 for op RETURN WARN skipping invalid gasCost value -82570 for op RETURN WARN skipping invalid gasCost value -82570 for op RETURN WARN skipping invalid gasCost value -83925 for op RETURN WARN skipping invalid gasCost value -83925 for op RETURN WARN skipping invalid gasCost value -83826 for op RETURN WARN skipping invalid gasCost value -83826 for op RETURN WARN skipping invalid gasCost value -85313 for op RETURN WARN skipping invalid gasCost value -85313 for op RETURN WARN skipping invalid gasCost value -82306 for op RETURN WARN skipping invalid gasCost value -82306 for op RETURN WARN skipping invalid gasCost value -83656 for op RETURN WARN skipping invalid gasCost value -83656 for op RETURN WARN skipping invalid gasCost value -83557 for op RETURN WARN skipping invalid gasCost value -83557 for op RETURN WARN skipping invalid gasCost value -85040 for op RETURN WARN skipping invalid gasCost value -85040 for op RETURN WARN skipping invalid gasCost value -82041 for op RETURN WARN skipping invalid gasCost value -82041 for op RETURN WARN skipping invalid gasCost value -83387 for op RETURN WARN skipping invalid gasCost value -83387 for op RETURN WARN skipping invalid gasCost value -83289 for op RETURN WARN skipping invalid gasCost value -83289 for op RETURN WARN skipping invalid gasCost value -84767 for op RETURN WARN skipping invalid gasCost value -84767 for op RETURN WARN skipping invalid gasCost value -81776 for op RETURN WARN skipping invalid gasCost value -81776 for op RETURN WARN skipping invalid gasCost value -83118 for op RETURN WARN skipping invalid gasCost value -83118 for op RETURN WARN skipping invalid gasCost value -83020 for op RETURN WARN skipping invalid gasCost value -83020 for op RETURN WARN skipping invalid gasCost value -84494 for op RETURN WARN skipping invalid gasCost value -84494 for op RETURN WARN skipping invalid gasCost value -81512 for op RETURN WARN skipping invalid gasCost value -81512 for op RETURN WARN skipping invalid gasCost value -82850 for op RETURN WARN skipping invalid gasCost value -82850 for op RETURN WARN skipping invalid gasCost value -82751 for op RETURN WARN skipping invalid gasCost value -82751 for op RETURN WARN skipping invalid gasCost value -84221 for op RETURN WARN skipping invalid gasCost value -84221 for op RETURN WARN skipping invalid gasCost value -81247 for op RETURN WARN skipping invalid gasCost value -81247 for op RETURN WARN skipping invalid gasCost value -82581 for op RETURN WARN skipping invalid gasCost value -82581 for op RETURN WARN skipping invalid gasCost value -82482 for op RETURN WARN skipping invalid gasCost value -82482 for op RETURN WARN skipping invalid gasCost value -83948 for op RETURN WARN skipping invalid gasCost value -83948 for op RETURN WARN skipping invalid gasCost value -80982 for op RETURN WARN skipping invalid gasCost value -80982 for op RETURN WARN skipping invalid gasCost value -82312 for op RETURN WARN skipping invalid gasCost value -82312 for op RETURN WARN skipping invalid gasCost value -82213 for op RETURN WARN skipping invalid gasCost value -82213 for op RETURN WARN skipping invalid gasCost value -83675 for op RETURN WARN skipping invalid gasCost value -83675 for op RETURN WARN skipping invalid gasCost value -80563 for op RETURN WARN skipping invalid gasCost value -80563 for op RETURN WARN skipping invalid gasCost value -81886 for op RETURN WARN skipping invalid gasCost value -81886 for op RETURN WARN skipping invalid gasCost value -81788 for op RETURN WARN skipping invalid gasCost value -81788 for op RETURN WARN skipping invalid gasCost value -83242 for op RETURN WARN skipping invalid gasCost value -83242 for op RETURN WARN skipping invalid gasCost value -82413 for op STOP WARN skipping invalid gasCost value -82413 for op STOP WARN skipping invalid gasCost value -75899 for op RETURN WARN skipping invalid gasCost value -75899 for op RETURN WARN skipping invalid gasCost value -77148 for op RETURN WARN skipping invalid gasCost value -77148 for op RETURN WARN skipping invalid gasCost value -77049 for op RETURN WARN skipping invalid gasCost value -77049 for op RETURN WARN skipping invalid gasCost value -78429 for op RETURN WARN skipping invalid gasCost value -78429 for op RETURN WARN skipping invalid gasCost value -77599 for op STOP WARN skipping invalid gasCost value -77599 for op STOP WARN skipping invalid gasCost value -71192 for op RETURN WARN skipping invalid gasCost value -71192 for op RETURN WARN skipping invalid gasCost value -72366 for op RETURN WARN skipping invalid gasCost value -72366 for op RETURN WARN skipping invalid gasCost value -72267 for op STOP WARN skipping invalid gasCost value -72267 for op STOP WARN skipping invalid gasCost value -73571 for op RETURN WARN skipping invalid gasCost value -73571 for op RETURN WARN skipping invalid gasCost value -91783 for op RETURN WARN skipping invalid gasCost value -91783 for op RETURN Contract AppProxyUpgradeable at 0x832cf7014b3d9999a4df4f90c88d52e5ea1aa7ef defined in: @aragon/os/contracts/apps/AppProxyBase.sol, @aragon/os/contracts/apps/AppProxyUpgradeable.sol, @aragon/os/contracts/apps/AppStorage.sol, @aragon/os/contracts/common/DelegateProxy.sol, @aragon/os/contracts/common/DepositableDelegateProxy.sol, @aragon/os/contracts/common/DepositableStorage.sol, @aragon/os/contracts/common/IsContract.sol, @aragon/os/contracts/common/UnstructuredStorage.sol, @aragon/os/contracts/kernel/KernelConstants.sol synthetic instruction gas: 168 total gas spent in the contract: 1139839 Contract KernelProxy at 0x2701d8469d656ec6f87108b02c35b1e7cd840c70 defined in: @aragon/os/contracts/common/DelegateProxy.sol, @aragon/os/contracts/common/DepositableDelegateProxy.sol, @aragon/os/contracts/common/DepositableStorage.sol, @aragon/os/contracts/common/IsContract.sol, @aragon/os/contracts/kernel/KernelConstants.sol, @aragon/os/contracts/kernel/KernelProxy.sol synthetic instruction gas: 312 total gas spent in the contract: 132917 Contract Kernel at 0xe520c4a81ea63760f61d3f127a15886c9afd094d defined in: @aragon/os/contracts/common/Initializable.sol, @aragon/os/contracts/kernel/Kernel.sol synthetic instruction gas: 4806 total gas spent in the contract: 59021 Contract TestDePool at 0xc1b19389920ad2c990abd0d624f868d41489304f defined in: @aragon/os/contracts/apps/AragonApp.sol, @aragon/os/contracts/common/Initializable.sol, @aragon/os/contracts/common/UnstructuredStorage.sol, @aragon/os/contracts/lib/math/SafeMath.sol, contracts/0.4.24/DePool.sol, contracts/0.4.24/lib/Pausable.sol, contracts/0.4.24/test_helpers/TestDePool.sol, solidity-bytes-utils/contracts/BytesLib.sol synthetic instruction gas: 16324 total gas spent in the contract: 1135807 Contract AppProxyUpgradeable at 0x5708e58a72babc89a352663c9b5e33436d5cdfcb defined in: @aragon/os/contracts/apps/AppProxyBase.sol, @aragon/os/contracts/apps/AppProxyUpgradeable.sol, @aragon/os/contracts/apps/AppStorage.sol, @aragon/os/contracts/common/DelegateProxy.sol, @aragon/os/contracts/common/DepositableDelegateProxy.sol, @aragon/os/contracts/common/DepositableStorage.sol, @aragon/os/contracts/common/IsContract.sol, @aragon/os/contracts/common/UnstructuredStorage.sol, @aragon/os/contracts/kernel/KernelConstants.sol synthetic instruction gas: 84 total gas spent in the contract: 99249 Contract StETH at 0x97eb41c89a284dff4bb3f7a37c0a1f0b5df826fa defined in: @aragon/os/contracts/acl/ACLSyntaxSugar.sol, @aragon/os/contracts/apps/AppStorage.sol, @aragon/os/contracts/apps/AragonApp.sol, @aragon/os/contracts/common/ConversionHelpers.sol, @aragon/os/contracts/common/Initializable.sol, @aragon/os/contracts/common/TimeHelpers.sol, @aragon/os/contracts/common/UnstructuredStorage.sol, @aragon/os/contracts/lib/math/SafeMath.sol, contracts/0.4.24/StETH.sol, contracts/0.4.24/lib/Pausable.sol synthetic instruction gas: 1167 total gas spent in the contract: 89337 Contract AppProxyUpgradeable at 0xea5ee23c342a1023bdb16fddc315499f7aa9efcb defined in: @aragon/os/contracts/apps/AppProxyBase.sol, @aragon/os/contracts/apps/AppProxyUpgradeable.sol, @aragon/os/contracts/apps/AppStorage.sol, @aragon/os/contracts/common/DelegateProxy.sol, @aragon/os/contracts/common/DepositableDelegateProxy.sol, @aragon/os/contracts/common/DepositableStorage.sol, @aragon/os/contracts/common/IsContract.sol, @aragon/os/contracts/common/UnstructuredStorage.sol, @aragon/os/contracts/kernel/KernelConstants.sol synthetic instruction gas: 84 total gas spent in the contract: 13226 Contract ACL at 0xbeafff3726103888e3dedca6a67bf584755820a6 defined in: @aragon/os/contracts/acl/ACL.sol, @aragon/os/contracts/common/ConversionHelpers.sol, @aragon/os/contracts/common/Initializable.sol synthetic instruction gas: 598 total gas spent in the contract: 3278 Contract AppProxyUpgradeable at 0xede0f69c59983ba0619cd1f8bc6941a467d9a8ea defined in: @aragon/os/contracts/apps/AppProxyBase.sol, @aragon/os/contracts/apps/AppProxyUpgradeable.sol, @aragon/os/contracts/apps/AppStorage.sol, @aragon/os/contracts/common/DelegateProxy.sol, @aragon/os/contracts/common/DepositableDelegateProxy.sol, @aragon/os/contracts/common/DepositableStorage.sol, @aragon/os/contracts/common/IsContract.sol, @aragon/os/contracts/common/UnstructuredStorage.sol, @aragon/os/contracts/kernel/KernelConstants.sol synthetic instruction gas: 1764 total gas spent in the contract: 334118 Contract StakingProvidersRegistry at 0xc31f8d14e627d7546b85a7955ba3aaec5e0490ca defined in: contracts/0.4.24/sps/StakingProvidersRegistry.sol, solidity-bytes-utils/contracts/BytesLib.sol synthetic instruction gas: 9860 total gas spent in the contract: 125054 Contract ValidatorRegistrationMock at 0x9f3b7fb4b34f99795261fb5413be7dcce95cc716 defined in: contracts/0.4.24/test_helpers/ValidatorRegistrationMock.sol synthetic instruction gas: 22528 total gas spent in the contract: 472182 File contracts/0.4.24/test_helpers/TestDePool.sol ┌───┬────────┐ │ C │ GAS │ ├───┼────────┤ │ │ 0 │ pragma solidity 0.4.24; │ │ 0 │ │ │ 0 │ import "../DePool.sol"; │ │ 0 │ import "./VaultMock.sol"; │ │ 0 │ │ │ 0 │ │ │ 0 │ /** │ │ 0 │ * @dev Only for testing purposes! DePool version with some functions exposed. │ │ 0 │ */ │ │ 4510 │ contract TestDePool is DePool { │ │ 0 │ address private treasury; │ │ 0 │ address private insurance; │ │ 0 │ │ │ 0 │ function initialize( │ │ 0 │ ISTETH _token, │ │ 0 │ IValidatorRegistration validatorRegistration, │ │ 0 │ address _oracle, │ │ 0 │ IStakingProvidersRegistry _sps, │ │ 0 │ uint256 _depositIterationLimit │ │ 0 │ ) │ │ 0 │ public │ │ 0 │ { │ │ 0 │ super.initialize(_token, validatorRegistration, _oracle, _sps, _depositIterationLimit); │ │ 0 │ treasury = address(new VaultMock()); │ │ 0 │ insurance = address(new VaultMock()); │ │ 0 │ } │ │ 0 │ │ │ 0 │ /** │ │ 0 │ * @dev Returns the treasury address │ │ 0 │ */ │ │ 0 │ function getTreasury() public view returns (address) { │ │ 0 │ return treasury; │ │ 0 │ } │ │ 0 │ │ │ 0 │ /** │ │ 0 │ * @dev Returns the insurance fund address │ │ 0 │ */ │ │ 0 │ function getInsuranceFund() public view returns (address) { │ │ 0 │ return insurance; │ │ 0 │ } │ │ 0 │ │ │ 0 │ /** │ │ 0 │ * @dev Gets unaccounted (excess) Ether on this contract balance │ │ 0 │ */ │ │ 0 │ function getUnaccountedEther() public view returns (uint256) { │ │ 0 │ return _getUnaccountedEther(); │ │ 0 │ } │ │ 0 │ │ │ 0 │ /** │ │ 0 │ * @dev Padding memory array with zeroes up to 64 bytes on the right │ │ 0 │ * @param _b Memory array of size 32 .. 64 │ │ 0 │ */ │ │ 0 │ function pad64(bytes memory _b) public pure returns (bytes memory) { │ │ 0 │ return _pad64(_b); │ │ 0 │ } │ │ 0 │ │ │ 0 │ /** │ │ 0 │ * @dev Converting value to little endian bytes and padding up to 32 bytes on the right │ │ 0 │ * @param _value Number less than `2**64` for compatibility reasons │ │ 0 │ */ │ │ 11 │ function toLittleEndian64(uint256 _value) public pure returns (uint256 result) { │ │ 4 │ return _toLittleEndian64(_value); │ │ 0 │ } │ │ 0 │ } │ │ 0 │ └───┴────────┘ File contracts/0.4.24/DePool.sol ┌───┬────────┐ │ C │ GAS │ ├───┼────────┤ │ │ 0 │ /* See contracts/COMPILERS.md */ │ │ 0 │ pragma solidity 0.4.24; │ │ 0 │ │ │ 0 │ import "@aragon/os/contracts/apps/AragonApp.sol"; │ │ 0 │ import "@aragon/os/contracts/lib/math/SafeMath.sol"; │ │ 0 │ import "@aragon/os/contracts/lib/math/SafeMath64.sol"; │ │ 0 │ import "@aragon/os/contracts/common/IsContract.sol"; │ │ 0 │ import "solidity-bytes-utils/contracts/BytesLib.sol"; │ │ 0 │ │ │ 0 │ import "./interfaces/IDePool.sol"; │ │ 0 │ import "./interfaces/ISTETH.sol"; │ │ 0 │ import "./interfaces/IStakingProvidersRegistry.sol"; │ │ 0 │ import "./interfaces/IValidatorRegistration.sol"; │ │ 0 │ │ │ 0 │ import "./lib/Pausable.sol"; │ │ 0 │ │ │ 0 │ │ │ 0 │ /** │ │ 0 │ * @title Liquid staking pool implementation │ │ 0 │ * │ │ 0 │ * See the comment of `IDePool`. │ │ 0 │ * │ │ 0 │ * NOTE: the code below assumes moderate amount of staking providers, e.g. up to 50. │ │ 0 │ */ │ │ 0 │ contract DePool is IDePool, IsContract, Pausable, AragonApp { │ │ 0 │ using SafeMath for uint256; │ │ 0 │ using SafeMath64 for uint64; │ │ 0 │ using UnstructuredStorage for bytes32; │ │ 0 │ │ │ 0 │ /// ACL │ │ 0 │ bytes32 constant public PAUSE_ROLE = keccak256("PAUSE_ROLE"); │ │ 0 │ bytes32 constant public MANAGE_FEE = keccak256("MANAGE_FEE"); │ │ 0 │ bytes32 constant public MANAGE_WITHDRAWAL_KEY = keccak256("MANAGE_WITHDRAWAL_KEY"); │ │ 0 │ bytes32 constant public SET_ORACLE = keccak256("SET_ORACLE"); │ │ 89 │ bytes32 constant public SET_DEPOSIT_ITERATION_LIMIT = keccak256("SET_DEPOSIT_ITERATION_LIMIT"); │ │ 0 │ │ │ 0 │ uint256 constant public PUBKEY_LENGTH = 48; │ │ 0 │ uint256 constant public WITHDRAWAL_CREDENTIALS_LENGTH = 32; │ │ 6 │ uint256 constant public SIGNATURE_LENGTH = 96; │ │ 0 │ │ │ 36 │ uint256 constant public DEPOSIT_SIZE = 32 ether; │ │ 0 │ │ │ 3 │ uint256 internal constant MIN_DEPOSIT_AMOUNT = 1 ether; // validator_registration.vy │ │ 12 │ uint256 internal constant DEPOSIT_AMOUNT_UNIT = 1000000000 wei; // validator_registration.vy │ │ 0 │ │ │ 0 │ bytes32 internal constant FEE_VALUE_POSITION = keccak256("depools.DePool.fee"); │ │ 0 │ bytes32 internal constant TREASURY_FEE_VALUE_POSITION = keccak256("depools.DePool.treasuryFee"); │ │ 0 │ bytes32 internal constant INSURANCE_FEE_VALUE_POSITION = keccak256("depools.DePool.insuranceFee"); │ │ 0 │ bytes32 internal constant SP_FEE_VALUE_POSITION = keccak256("depools.DePool.SPFee"); │ │ 0 │ │ │ 81 │ bytes32 internal constant TOKEN_VALUE_POSITION = keccak256("depools.DePool.token"); │ │ 204 │ bytes32 internal constant VALIDATOR_REGISTRATION_VALUE_POSITION = keccak256("depools.DePool.validatorRegistration"); │ │ 0 │ bytes32 internal constant ORACLE_VALUE_POSITION = keccak256("depools.DePool.oracle"); │ │ 337 │ bytes32 internal constant SP_REGISTRY_VALUE_POSITION = keccak256("depools.DePool.spRegistry"); │ │ 0 │ │ │ 0 │ /// @dev A base value for tracking earned rewards │ │ 159 │ bytes32 internal constant REWARD_BASE_VALUE_POSITION = keccak256("depools.DePool.rewardBase"); │ │ 0 │ │ │ 0 │ /// @dev amount of Ether (on the current Ethereum side) buffered on this smart contract balance │ │ 654 │ bytes32 internal constant BUFFERED_ETHER_VALUE_POSITION = keccak256("depools.DePool.bufferedEther"); │ │ 0 │ /// @dev amount of Ether (on the current Ethereum side) deposited to the validator_registration.vy contract │ │ 237 │ bytes32 internal constant DEPOSITED_ETHER_VALUE_POSITION = keccak256("depools.DePool.depositedEther"); │ │ 0 │ /// @dev amount of Ether (on the Ethereum 2.0 side) managed by the system │ │ 87 │ bytes32 internal constant REMOTE_ETHER2_VALUE_POSITION = keccak256("depools.DePool.remoteEther2"); │ │ 0 │ │ │ 0 │ /// @dev last epoch reported by the oracle │ │ 81 │ bytes32 internal constant LAST_ORACLE_EPOCH_VALUE_POSITION = keccak256("depools.DePool.lastOracleEpoch"); │ │ 0 │ │ │ 0 │ /// @dev maximum number of Ethereum 2.0 validators registered in a single transaction │ │ 110 │ bytes32 internal constant DEPOSIT_ITERATION_LIMIT_VALUE_POSITION = keccak256("depools.DePool.depositIterationLimit"); │ │ 0 │ │ │ 0 │ │ │ 0 │ /// @dev Credentials which allows the DAO to withdraw Ether on the 2.0 side │ │ 0 │ bytes private withdrawalCredentials; │ │ 0 │ │ │ 0 │ │ │ 0 │ // Memory cache entry used in the _ETH2Deposit function │ │ 0 │ struct DepositLookupCacheEntry { │ │ 0 │ // Makes no sense to pack types since reading memory is as fast as any op │ │ 0 │ uint256 id; │ │ 0 │ uint256 stakingLimit; │ │ 0 │ uint256 stoppedValidators; │ │ 0 │ uint256 totalSigningKeys; │ │ 0 │ uint256 usedSigningKeys; │ │ 0 │ uint256 initialUsedSigningKeys; // for write-back control │ │ 0 │ } │ │ 0 │ │ │ 0 │ function initialize( │ │ 0 │ ISTETH _token, │ │ 0 │ IValidatorRegistration validatorRegistration, │ │ 0 │ address _oracle, │ │ 0 │ IStakingProvidersRegistry _sps, │ │ 0 │ uint256 _depositIterationLimit │ │ 0 │ ) │ │ 0 │ public onlyInit │ │ 0 │ { │ │ 0 │ _setToken(_token); │ │ 0 │ _setValidatorRegistrationContract(validatorRegistration); │ │ 0 │ _setOracle(_oracle); │ │ 0 │ _setSPs(_sps); │ │ 0 │ _setDepositIterationLimit(_depositIterationLimit); │ │ 0 │ │ │ 0 │ initialized(); │ │ 0 │ } │ │ 0 │ │ │ 0 │ /** │ │ 0 │ * @notice Adds eth to the pool │ │ 0 │ */ │ │ 0 │ function() external payable { │ │ 0 │ _submit(0); │ │ 0 │ } │ │ 0 │ │ │ 0 │ /** │ │ 0 │ * @notice Adds eth to the pool with optional _referral parameter │ │ 0 │ * @return StETH Amount of StETH generated │ │ 0 │ */ │ │ 28 │ function submit(address _referral) external payable returns (uint256 StETH) { │ │ 17 │ return _submit(_referral); │ │ 0 │ } │ │ 0 │ │ │ 0 │ /** │ │ 0 │ * @notice Stop pool routine operations │ │ 0 │ */ │ │ 0 │ function stop() external auth(PAUSE_ROLE) { │ │ 0 │ _stop(); │ │ 0 │ } │ │ 0 │ │ │ 0 │ /** │ │ 0 │ * @notice Resume pool routine operations │ │ 0 │ */ │ │ 0 │ function resume() external auth(PAUSE_ROLE) { │ │ 0 │ _resume(); │ │ 0 │ } │ │ 0 │ │ │ 0 │ /** │ │ 0 │ * @notice Set fee rate to `_feeBasisPoints` basis points. The fees are accrued when oracles report staking results │ │ 0 │ * @param _feeBasisPoints Fee rate, in basis points │ │ 0 │ */ │ │ 0 │ function setFee(uint16 _feeBasisPoints) external auth(MANAGE_FEE) { │ │ 0 │ _setBPValue(FEE_VALUE_POSITION, _feeBasisPoints); │ │ 0 │ emit FeeSet(_feeBasisPoints); │ │ 0 │ } │ │ 0 │ │ │ 0 │ /** │ │ 0 │ * @notice Set fee distribution: `_treasuryFeeBasisPoints` basis points go to the treasury, `_insuranceFeeBasisPoints` basis points go to the insurance fund, `_SPFeeBasisPoints` basis points go to staking providers. The sum has to be 10 000. │ │ 0 │ */ │ │ 0 │ function setFeeDistribution( │ │ 0 │ uint16 _treasuryFeeBasisPoints, │ │ 0 │ uint16 _insuranceFeeBasisPoints, │ │ 0 │ uint16 _SPFeeBasisPoints │ │ 0 │ ) │ │ 0 │ external auth(MANAGE_FEE) │ │ 0 │ { │ │ 0 │ require( │ │ 84 │ 10000 == uint256(_treasuryFeeBasisPoints) │ │ 0 │ .add(uint256(_insuranceFeeBasisPoints)) │ │ 0 │ .add(uint256(_SPFeeBasisPoints)), │ │ 0 │ "FEES_DONT_ADD_UP" │ │ 0 │ ); │ │ 0 │ │ │ 0 │ _setBPValue(TREASURY_FEE_VALUE_POSITION, _treasuryFeeBasisPoints); │ │ 0 │ _setBPValue(INSURANCE_FEE_VALUE_POSITION, _insuranceFeeBasisPoints); │ │ 0 │ _setBPValue(SP_FEE_VALUE_POSITION, _SPFeeBasisPoints); │ │ 0 │ │ │ 0 │ emit FeeDistributionSet(_treasuryFeeBasisPoints, _insuranceFeeBasisPoints, _SPFeeBasisPoints); │ │ 0 │ } │ │ 0 │ │ │ 0 │ /** │ │ 0 │ * @notice Set authorized oracle contract address to `_oracle` │ │ 0 │ * @dev Contract specified here must periodically make `reportEther2` calls. │ │ 0 │ */ │ │ 0 │ function setOracle(address _oracle) external auth(SET_ORACLE) { │ │ 0 │ _setOracle(_oracle); │ │ 0 │ } │ │ 0 │ │ │ 0 │ /** │ │ 0 │ * @notice Set maximum number of Ethereum 2.0 validators registered in a single transaction. │ │ 0 │ */ │ │ 0 │ function setDepositIterationLimit(uint256 _limit) external auth(SET_DEPOSIT_ITERATION_LIMIT) { │ │ 0 │ _setDepositIterationLimit(_limit); │ │ 0 │ } │ │ 0 │ │ │ 0 │ /** │ │ 0 │ * @notice Set credentials to withdraw ETH on ETH 2.0 side after the phase 2 is launched to `_withdrawalCredentials` │ │ 0 │ * @dev Note that setWithdrawalCredentials discards all unused signing keys as the signatures are invalidated. │ │ 0 │ * @param _withdrawalCredentials hash of withdrawal multisignature key as accepted by │ │ 0 │ * the validator_registration.deposit function │ │ 0 │ */ │ │ 0 │ function setWithdrawalCredentials(bytes _withdrawalCredentials) external auth(MANAGE_WITHDRAWAL_KEY) { │ │ 0 │ require(_withdrawalCredentials.length == WITHDRAWAL_CREDENTIALS_LENGTH, "INVALID_LENGTH"); │ │ 0 │ │ │ 0 │ withdrawalCredentials = _withdrawalCredentials; │ │ 0 │ getSPs().trimUnusedKeys(); │ │ 0 │ │ │ 0 │ emit WithdrawalCredentialsSet(_withdrawalCredentials); │ │ 0 │ } │ │ 0 │ │ │ 0 │ /** │ │ 0 │ * @notice Issues withdrawal request. Large withdrawals will be processed only after the phase 2 launch. WIP. │ │ 0 │ * @param _amount Amount of StETH to burn │ │ 0 │ * @param _pubkeyHash Receiving address │ │ 0 │ */ │ │ 0 │ function withdraw(uint256 _amount, bytes32 _pubkeyHash) external whenNotStopped { /* solhint-disable-line no-unused-vars */ │ │ 0 │ revert("NOT_IMPLEMENTED_YET"); │ │ 0 │ } │ │ 0 │ │ │ 0 │ /** │ │ 0 │ * @notice Ether on the ETH 2.0 side reported by the oracle │ │ 0 │ * @param _eth2balance Balance in wei on the ETH 2.0 side │ │ 0 │ */ │ │ 0 │ function reportEther2(uint256 _epoch, uint256 _eth2balance) external { │ │ 0 │ require(msg.sender == getOracle(), "APP_AUTH_FAILED"); │ │ 0 │ require(0 != _epoch, "ZERO_EPOCH"); │ │ 0 │ │ │ 0 │ if (_epoch <= LAST_ORACLE_EPOCH_VALUE_POSITION.getStorageUint256()) │ │ 0 │ return; // ignore old data │ │ 0 │ │ │ 0 │ LAST_ORACLE_EPOCH_VALUE_POSITION.setStorageUint256(_epoch); │ │ 0 │ REMOTE_ETHER2_VALUE_POSITION.setStorageUint256(_eth2balance); │ │ 0 │ │ │ 0 │ // Calculating real amount of rewards │ │ 0 │ uint256 rewardBase = REWARD_BASE_VALUE_POSITION.getStorageUint256(); │ │ 0 │ if (_eth2balance > rewardBase) { │ │ 0 │ uint256 rewards = _eth2balance.sub(rewardBase); │ │ 0 │ REWARD_BASE_VALUE_POSITION.setStorageUint256(_eth2balance); │ │ 0 │ distributeRewards(rewards); │ │ 0 │ } │ │ 0 │ } │ │ 0 │ │ │ 0 │ /** │ │ 0 │ * @notice Send funds to recovery Vault. Overrides default AragonApp behaviour. │ │ 0 │ * @param _token Token to be sent to recovery vault. │ │ 0 │ */ │ │ 0 │ function transferToVault(address _token) external { │ │ 0 │ require(allowRecoverability(_token), "RECOVER_DISALLOWED"); │ │ 0 │ address vault = getRecoveryVault(); │ │ 0 │ require(isContract(vault), "RECOVER_VAULT_NOT_CONTRACT"); │ │ 0 │ │ │ 0 │ uint256 balance; │ │ 0 │ if (_token == ETH) { │ │ 0 │ balance = _getUnaccountedEther(); │ │ 0 │ vault.transfer(balance); │ │ 0 │ } else { │ │ 0 │ ERC20 token = ERC20(_token); │ │ 0 │ balance = token.staticBalanceOf(this); │ │ 0 │ require(token.safeTransfer(vault, balance), "RECOVER_TOKEN_TRANSFER_FAILED"); │ │ 0 │ } │ │ 0 │ │ │ 0 │ emit RecoverToVault(vault, _token, balance); │ │ 0 │ } │ │ 0 │ │ │ 0 │ /** │ │ 0 │ * @notice Returns staking rewards fee rate │ │ 0 │ */ │ │ 0 │ function getFee() external view returns (uint16 feeBasisPoints) { │ │ 0 │ return _getFee(); │ │ 0 │ } │ │ 0 │ │ │ 0 │ /** │ │ 0 │ * @notice Returns fee distribution proportion │ │ 0 │ */ │ │ 0 │ function getFeeDistribution() │ │ 0 │ external │ │ 0 │ view │ │ 0 │ returns ( │ │ 0 │ uint16 treasuryFeeBasisPoints, │ │ 0 │ uint16 insuranceFeeBasisPoints, │ │ 0 │ uint16 SPFeeBasisPoints │ │ 0 │ ) │ │ 0 │ { │ │ 0 │ return _getFeeDistribution(); │ │ 0 │ } │ │ 0 │ │ │ 0 │ /** │ │ 0 │ * @notice Returns current credentials to withdraw ETH on ETH 2.0 side after the phase 2 is launched │ │ 0 │ */ │ │ 0 │ function getWithdrawalCredentials() external view returns (bytes) { │ │ 0 │ return withdrawalCredentials; │ │ 0 │ } │ │ 0 │ │ │ 0 │ /** │ │ 0 │ * @notice Gets the amount of Ether temporary buffered on this contract balance │ │ 0 │ */ │ │ 0 │ function getBufferedEther() external view returns (uint256) { │ │ 0 │ return _getBufferedEther(); │ │ 0 │ } │ │ 0 │ │ │ 0 │ /** │ │ 0 │ * @notice Gets the amount of Ether controlled by the system │ │ 0 │ */ │ │ 23 │ function getTotalControlledEther() external view returns (uint256) { │ │ 14 │ return _getTotalControlledEther(); │ │ 0 │ } │ │ 0 │ │ │ 0 │ /** │ │ 0 │ * @notice Gets liquid token interface handle │ │ 0 │ */ │ │ 7 │ function getToken() public view returns (ISTETH) { │ │ 17 │ return ISTETH(TOKEN_VALUE_POSITION.getStorageAddress()); │ │ 0 │ } │ │ 0 │ │ │ 0 │ /** │ │ 0 │ * @notice Gets validator registration contract handle │ │ 0 │ */ │ │ 146 │ function getValidatorRegistrationContract() public view returns (IValidatorRegistration) { │ │ 100 │ return IValidatorRegistration(VALIDATOR_REGISTRATION_VALUE_POSITION.getStorageAddress()); │ │ 0 │ } │ │ 0 │ │ │ 0 │ /** │ │ 0 │ * @notice Gets authorized oracle address │ │ 0 │ */ │ │ 0 │ function getOracle() public view returns (address) { │ │ 0 │ return ORACLE_VALUE_POSITION.getStorageAddress(); │ │ 0 │ } │ │ 0 │ │ │ 0 │ /** │ │ 0 │ * @notice Gets maximum number of Ethereum 2.0 validators registered in a single transaction │ │ 0 │ */ │ │ 7 │ function getDepositIterationLimit() public view returns (uint256) { │ │ 17 │ return DEPOSIT_ITERATION_LIMIT_VALUE_POSITION.getStorageUint256(); │ │ 0 │ } │ │ 0 │ │ │ 0 │ /** │ │ 0 │ * @notice Returns the value against which the next reward will be calculated │ │ 0 │ * This method can be discarded in the future │ │ 0 │ */ │ │ 0 │ function getRewardBase() public view returns (uint256) { │ │ 0 │ return REWARD_BASE_VALUE_POSITION.getStorageUint256(); │ │ 0 │ } │ │ 0 │ │ │ 0 │ /** │ │ 0 │ * @notice Gets staking providers registry interface handle │ │ 0 │ */ │ │ 28 │ function getSPs() public view returns (IStakingProvidersRegistry) { │ │ 68 │ return IStakingProvidersRegistry(SP_REGISTRY_VALUE_POSITION.getStorageAddress()); │ │ 0 │ } │ │ 0 │ │ │ 0 │ /** │ │ 0 │ * @notice Returns the treasury address │ │ 0 │ */ │ │ 0 │ function getTreasury() public view returns (address) { │ │ 0 │ address vault = getRecoveryVault(); │ │ 0 │ require(isContract(vault), "RECOVER_VAULT_NOT_CONTRACT"); │ │ 0 │ return vault; │ │ 0 │ } │ │ 0 │ │ │ 0 │ /** │ │ 0 │ * @notice Returns the insurance fund address │ │ 0 │ */ │ │ 0 │ function getInsuranceFund() public view returns (address) { │ │ 0 │ // TODO a separate vault │ │ 0 │ return getTreasury(); │ │ 0 │ } │ │ 0 │ │ │ 0 │ /** │ │ 0 │ * @notice Gets the stat of the system's Ether on the Ethereum 2 side │ │ 0 │ * @return deposited Amount of Ether deposited from the current Ethereum │ │ 0 │ * @return remote Amount of Ether currently present on the Ethereum 2 side (can be 0 if the Ethereum 2 is yet to be launch │ │ 0 │ */ │ │ 0 │ function getEther2Stat() public view returns (uint256 deposited, uint256 remote) { │ │ 0 │ deposited = DEPOSITED_ETHER_VALUE_POSITION.getStorageUint256(); │ │ 0 │ remote = REMOTE_ETHER2_VALUE_POSITION.getStorageUint256(); │ │ 0 │ } │ │ 0 │ │ │ 0 │ /** │ │ 0 │ * @dev Sets liquid token interface handle │ │ 0 │ */ │ │ 0 │ function _setToken(ISTETH _token) internal { │ │ 0 │ require(isContract(address(_token)), "NOT_A_CONTRACT"); │ │ 0 │ TOKEN_VALUE_POSITION.setStorageAddress(address(_token)); │ │ 0 │ } │ │ 0 │ │ │ 0 │ /** │ │ 0 │ * @dev Sets validator registration contract handle │ │ 0 │ */ │ │ 0 │ function _setValidatorRegistrationContract(IValidatorRegistration _contract) internal { │ │ 0 │ require(isContract(address(_contract)), "NOT_A_CONTRACT"); │ │ 0 │ VALIDATOR_REGISTRATION_VALUE_POSITION.setStorageAddress(address(_contract)); │ │ 0 │ } │ │ 0 │ │ │ 0 │ /** │ │ 0 │ * @dev Internal function to set authorized oracle address │ │ 0 │ */ │ │ 0 │ function _setOracle(address _oracle) internal { │ │ 0 │ require(isContract(_oracle), "NOT_A_CONTRACT"); │ │ 0 │ ORACLE_VALUE_POSITION.setStorageAddress(_oracle); │ │ 0 │ } │ │ 0 │ │ │ 0 │ /** │ │ 0 │ * @dev Internal function to set staking provider registry address │ │ 0 │ */ │ │ 0 │ function _setSPs(IStakingProvidersRegistry _r) internal { │ │ 0 │ require(isContract(_r), "NOT_A_CONTRACT"); │ │ 0 │ SP_REGISTRY_VALUE_POSITION.setStorageAddress(_r); │ │ 0 │ } │ │ 0 │ │ │ 0 │ /** │ │ 0 │ * @notice Internal function to set deposit loop iteration limit │ │ 0 │ */ │ │ 0 │ function _setDepositIterationLimit(uint256 _limit) internal { │ │ 0 │ require(0 != _limit, "ZERO_LIMIT"); │ │ 0 │ DEPOSIT_ITERATION_LIMIT_VALUE_POSITION.setStorageUint256(_limit); │ │ 0 │ } │ │ 0 │ │ │ 0 │ /** │ │ 0 │ * @dev Processes user deposit │ │ 0 │ */ │ │ 30 │ function _submit(address _referral) internal whenNotStopped returns (uint256 StETH) { │ │ 8 │ address sender = msg.sender; │ │ 8 │ uint256 deposit = msg.value; │ │ 0 │ │ │ 19 │ if (0 != deposit) { │ │ 0 │ │ + │ 100811 │ getToken().mint(sender, deposit); │ │ 0 │ │ │ 24 │ _submitted(sender, deposit, _referral); │ │ 0 │ } │ │ 0 │ │ │ 0 │ // Buffer management │ │ 21 │ uint256 buffered = _getBufferedEther(); │ │ 19 │ if (buffered >= DEPOSIT_SIZE) { │ │ 21 │ uint256 unaccounted = _getUnaccountedEther(); │ │ 0 │ │ │ 56 │ uint256 toUnbuffer = buffered.div(DEPOSIT_SIZE).mul(DEPOSIT_SIZE); │ │ 61 │ assert(toUnbuffer <= buffered && toUnbuffer != 0); │ │ 0 │ │ │ 33 │ _markAsUnbuffered(_ETH2Deposit(toUnbuffer)); │ │ 0 │ │ │ 35 │ assert(_getUnaccountedEther() == unaccounted); │ │ 0 │ } │ │ 0 │ } │ │ 0 │ │ │ 0 │ /** │ │ 0 │ * @dev Makes a deposit to the ETH 2.0 side │ │ 0 │ * @param _amount Total amount to deposit to the ETH 2.0 side │ │ 0 │ * @return actually deposited amount │ │ 0 │ */ │ │ 47 │ function _ETH2Deposit(uint256 _amount) internal returns (uint256) { │ │ 23 │ assert(_amount >= MIN_DEPOSIT_AMOUNT); │ │ 0 │ │ │ 0 │ // Memory is very cheap, although you don't want to grow it too much. │ │ 23 │ DepositLookupCacheEntry[] memory cache = _load_SP_cache(); │ │ 29 │ if (0 == cache.length) │ │ 0 │ return 0; │ │ 0 │ │ │ 11 │ uint256 totalDepositCalls = 0; │ │ 23 │ uint256 maxDepositCalls = getDepositIterationLimit(); │ │ 11 │ uint256 depositAmount = _amount; │ │ 183 │ while (depositAmount != 0 && totalDepositCalls < maxDepositCalls) { │ │ 0 │ // Finding the best suitable SP │ │ 25 │ uint256 bestSPidx = cache.length; // 'not found' flag │ │ 3 │ uint256 smallestStake; │ │ 0 │ // The loop is ligthweight comparing to an ether transfer and .deposit invocation │ │ 1807 │ for (uint256 idx = 0; idx < cache.length; ++idx) { │ │ 2223 │ DepositLookupCacheEntry memory entry = cache[idx]; │ │ 0 │ │ │ 1600 │ assert(entry.usedSigningKeys <= entry.totalSigningKeys); │ │ 1408 │ if (entry.usedSigningKeys == entry.totalSigningKeys) │ │ 0 │ continue; │ │ 0 │ │ │ 1705 │ uint256 stake = entry.usedSigningKeys.sub(entry.stoppedValidators); │ │ 1312 │ if (stake + 1 > entry.stakingLimit) │ │ 0 │ continue; │ │ 0 │ │ │ 1802 │ if (bestSPidx == cache.length || stake < smallestStake) { │ │ 24 │ bestSPidx = idx; │ │ 24 │ smallestStake = stake; │ │ 0 │ } │ │ 0 │ } │ │ 0 │ │ │ 58 │ if (bestSPidx == cache.length) // not found │ │ 0 │ break; │ │ 0 │ │ │ 0 │ // Invoking deposit for the best SP │ │ 58 │ depositAmount = depositAmount.sub(DEPOSIT_SIZE); │ │ 28 │ ++totalDepositCalls; │ │ 0 │ │ │ 18 │ (bytes memory key, bytes memory signature, bool used) = /* solium-disable-line */ │ + │ 45627 │ getSPs().getSigningKey(cache[bestSPidx].id, cache[bestSPidx].usedSigningKeys++); │ │ 46 │ assert(!used); │ │ 0 │ │ │ 0 │ // finally, stake the notch for the assigned validator │ │ 42 │ _stake(key, signature); │ │ 0 │ } │ │ 0 │ │ │ 27 │ uint256 deposited = totalDepositCalls.mul(DEPOSIT_SIZE); │ │ 19 │ if (0 != deposited) { │ │ 54 │ REWARD_BASE_VALUE_POSITION.setStorageUint256(REWARD_BASE_VALUE_POSITION.getStorageUint256().add(deposited)); │ │ 18 │ _write_back_SP_cache(cache); │ │ 0 │ } │ │ 0 │ │ │ 8 │ return deposited; │ │ 0 │ } │ │ 0 │ │ │ 0 │ /** │ │ 0 │ * @dev Invokes a validator_registration.deposit call │ │ 0 │ * @param _pubkey Validator to stake for │ │ 0 │ * @param _signature Signature of the deposit call │ │ 0 │ */ │ │ 50 │ function _stake(bytes memory _pubkey, bytes memory _signature) internal { │ │ 1760 │ require(withdrawalCredentials.length != 0, "EMPTY_WITHDRAWAL_CREDENTIALS"); │ │ 0 │ │ │ 6 │ uint256 value = DEPOSIT_SIZE; │ │ 0 │ │ │ 0 │ // The following computations and Merkle tree-ization will make validator_registration.vy happy │ │ 60 │ uint256 depositAmount = value.div(DEPOSIT_AMOUNT_UNIT); │ │ 82 │ assert(depositAmount.mul(DEPOSIT_AMOUNT_UNIT) == value); // properly rounded │ │ 0 │ │ │ 0 │ // Compute deposit data root (`DepositData` hash tree root) according to validator_registration.vy │ │ 1816 │ bytes32 pubkeyRoot = sha256(_pad64(_pubkey)); │ │ 1762 │ bytes32 signatureRoot = sha256( │ │ 204 │ abi.encodePacked( │ │ 1810 │ sha256(BytesLib.slice(_signature, 0, 64)), │ │ 1870 │ sha256(_pad64(BytesLib.slice(_signature, 64, SIGNATURE_LENGTH.sub(64)))) │ │ 0 │ ) │ │ 0 │ ); │ │ 0 │ │ │ 1762 │ bytes32 depositDataRoot = sha256( │ │ 204 │ abi.encodePacked( │ │ 5518 │ sha256(abi.encodePacked(pubkeyRoot, withdrawalCredentials)), │ │ 1972 │ sha256(abi.encodePacked(_toLittleEndian64(depositAmount), signatureRoot)) │ │ 0 │ ) │ │ 0 │ ); │ │ 0 │ │ │ 1468 │ uint256 targetBalance = address(this).balance.sub(value); │ │ 0 │ │ + │ 493182 │ getValidatorRegistrationContract().deposit.value(value)( │ │ 24 │ _pubkey, withdrawalCredentials, _signature, depositDataRoot); │ │ 1450 │ require(address(this).balance == targetBalance, "EXPECTING_DEPOSIT_TO_HAPPEN"); │ │ 0 │ } │ │ 0 │ │ │ 0 │ /** │ │ 0 │ * @dev Distributes rewards and fees. │ │ 0 │ * @param _totalRewards Total rewards accrued on the Ethereum 2.0 side. │ │ 0 │ */ │ │ 0 │ function distributeRewards(uint256 _totalRewards) internal { │ │ 0 │ // Amount of the rewards in Ether │ │ 0 │ uint256 tokens2mint = _totalRewards.mul(_getFee()).div(10000); │ │ 0 │ │ │ 0 │ assert(0 != getToken().totalSupply()); │ │ 0 │ │ │ 0 │ (uint16 treasuryFeeBasisPoints, uint16 insuranceFeeBasisPoints, ) = _getFeeDistribution(); │ │ 0 │ uint256 toTreasury = tokens2mint.mul(treasuryFeeBasisPoints).div(10000); │ │ 0 │ uint256 toInsuranceFund = tokens2mint.mul(insuranceFeeBasisPoints).div(10000); │ │ 0 │ uint256 toSP = tokens2mint.sub(toTreasury).sub(toInsuranceFund); │ │ 0 │ │ │ 0 │ getToken().mint(getTreasury(), toTreasury); │ │ 0 │ getToken().mint(getInsuranceFund(), toInsuranceFund); │ │ 0 │ getToken().mint(address(getSPs()), toSP); │ │ 0 │ getSPs().distributeRewards( │ │ 0 │ address(getToken()), │ │ 0 │ getToken().balanceOf(address(getSPs())) │ │ 0 │ ); │ │ 0 │ } │ │ 0 │ │ │ 0 │ /** │ │ 0 │ * @dev Records a deposit made by a user with optional referral │ │ 0 │ * @param _value Deposit value in wei │ │ 0 │ */ │ │ 15 │ function _submitted(address _sender, uint256 _value, address _referral) internal { │ │ 60 │ BUFFERED_ETHER_VALUE_POSITION.setStorageUint256(_getBufferedEther().add(_value)); │ │ 0 │ │ │ 1718 │ emit Submitted(_sender, _value, _referral); │ │ 0 │ } │ │ 0 │ │ │ 0 │ /** │ │ 0 │ * @dev Records a deposit to the validator_registration.deposit function. │ │ 0 │ * @param _amount Total amount deposited to the ETH 2.0 side │ │ 0 │ */ │ │ 11 │ function _markAsUnbuffered(uint256 _amount) internal { │ │ 24 │ DEPOSITED_ETHER_VALUE_POSITION.setStorageUint256( │ │ 30 │ DEPOSITED_ETHER_VALUE_POSITION.getStorageUint256().add(_amount)); │ │ 7 │ BUFFERED_ETHER_VALUE_POSITION.setStorageUint256( │ │ 50 │ BUFFERED_ETHER_VALUE_POSITION.getStorageUint256().sub(_amount)); │ │ 0 │ │ │ 1054 │ emit Unbuffered(_amount); │ │ 0 │ } │ │ 0 │ │ │ 0 │ /** │ │ 0 │ * @dev Write a value nominated in basis points │ │ 0 │ */ │ │ 0 │ function _setBPValue(bytes32 _slot, uint16 _value) internal { │ │ 0 │ require(_value <= 10000, "VALUE_OVER_100_PERCENT"); │ │ 0 │ _slot.setStorageUint256(uint256(_value)); │ │ 0 │ } │ │ 0 │ │ │ 0 │ /** │ │ 0 │ * @dev Write back updated usedSigningKeys SP values │ │ 0 │ */ │ │ 22 │ function _write_back_SP_cache(DepositLookupCacheEntry[] memory cache) internal { │ │ 9 │ uint256 updateSize; │ │ 894 │ for (uint256 idx = 0; idx < cache.length; ++idx) { │ │ 2656 │ if (cache[idx].usedSigningKeys > cache[idx].initialUsedSigningKeys) │ │ 30 │ updateSize++; │ │ 0 │ } │ │ 23 │ if (0 == updateSize) │ │ 0 │ return; │ │ 0 │ │ │ 92 │ uint256[] memory ids = new uint256[](updateSize); │ │ 86 │ uint64[] memory usedSigningKeys = new uint64[](updateSize); │ │ 0 │ uint256 i; │ │ 902 │ for (idx = 0; idx < cache.length; ++idx) { │ │ 2656 │ if (cache[idx].usedSigningKeys > cache[idx].initialUsedSigningKeys) { │ │ 274 │ ids[i] = cache[idx].id; │ │ 352 │ usedSigningKeys[i] = to64(cache[idx].usedSigningKeys); │ │ 12 │ i++; │ │ 0 │ } │ │ 0 │ } │ │ 23 │ assert(i == updateSize); │ │ 0 │ │ + │ 30815 │ getSPs().updateUsedKeys(ids, usedSigningKeys); │ │ 0 │ } │ │ 0 │ │ │ 0 │ /** │ │ 0 │ * @dev Returns staking rewards fee rate │ │ 0 │ */ │ │ 0 │ function _getFee() internal view returns (uint16) { │ │ 0 │ return _readBPValue(FEE_VALUE_POSITION); │ │ 0 │ } │ │ 0 │ │ │ 0 │ /** │ │ 0 │ * @dev Returns fee distribution proportion │ │ 0 │ */ │ │ 0 │ function _getFeeDistribution() internal view │ │ 0 │ returns (uint16 treasuryFeeBasisPoints, uint16 insuranceFeeBasisPoints, uint16 SPFeeBasisPoints) │ │ 0 │ { │ │ 0 │ treasuryFeeBasisPoints = _readBPValue(TREASURY_FEE_VALUE_POSITION); │ │ 0 │ insuranceFeeBasisPoints = _readBPValue(INSURANCE_FEE_VALUE_POSITION); │ │ 0 │ SPFeeBasisPoints = _readBPValue(SP_FEE_VALUE_POSITION); │ │ 0 │ } │ │ 0 │ │ │ 0 │ /** │ │ 0 │ * @dev Read a value nominated in basis points │ │ 0 │ */ │ │ 0 │ function _readBPValue(bytes32 _slot) internal view returns (uint16) { │ │ 0 │ uint256 v = _slot.getStorageUint256(); │ │ 0 │ assert(v <= 10000); │ │ 0 │ return uint16(v); │ │ 0 │ } │ │ 0 │ │ │ 0 │ /** │ │ 0 │ * @dev Gets the amount of Ether temporary buffered on this contract balance │ │ 0 │ */ │ │ 120 │ function _getBufferedEther() internal view returns (uint256) { │ │ 105 │ uint256 buffered = BUFFERED_ETHER_VALUE_POSITION.getStorageUint256(); │ │ 3610 │ assert(address(this).balance >= buffered); │ │ 0 │ │ │ 15 │ return buffered; │ │ 0 │ } │ │ 0 │ │ │ 0 │ /** │ │ 0 │ * @dev Gets unaccounted (excess) Ether on this contract balance │ │ 0 │ */ │ │ 8 │ function _getUnaccountedEther() internal view returns (uint256) { │ │ 1480 │ return address(this).balance.sub(_getBufferedEther()); │ │ 0 │ } │ │ 0 │ │ │ 0 │ /** │ │ 0 │ * @dev Returns true if the oracle ever provided data │ │ 0 │ */ │ │ 18 │ function _hasOracleData() internal view returns (bool) { │ │ 27 │ return 0 != LAST_ORACLE_EPOCH_VALUE_POSITION.getStorageUint256(); │ │ 0 │ } │ │ 0 │ │ │ 0 │ /** │ │ 0 │ * @dev Gets the amount of Ether controlled by the system │ │ 0 │ */ │ │ 36 │ function _getTotalControlledEther() internal view returns (uint256) { │ │ 24 │ uint256 remote = REMOTE_ETHER2_VALUE_POSITION.getStorageUint256(); │ │ 0 │ // Until the oracle provides data, we assume that all staked ether is intact. │ │ 23 │ uint256 deposited = DEPOSITED_ETHER_VALUE_POSITION.getStorageUint256(); │ │ 64 │ uint256 assets = _getBufferedEther().add(_hasOracleData() ? remote : deposited); │ │ 0 │ │ │ 0 │ return assets; │ │ 0 │ } │ │ 0 │ │ │ 33 │ function _load_SP_cache() internal view returns (DepositLookupCacheEntry[] memory cache) { │ │ 23 │ IStakingProvidersRegistry sps = getSPs(); │ + │ 14065 │ cache = new DepositLookupCacheEntry[](sps.getActiveStakingProvidersCount()); │ │ 29 │ if (0 == cache.length) │ │ 0 │ return cache; │ │ 0 │ │ │ 11 │ uint256 idx = 0; │ + │ 13244 │ for (uint256 SP_id = sps.getStakingProvidersCount().sub(1); ; SP_id = SP_id.sub(1)) { │ │ 96 │ ( │ │ 3 │ bool active, , , │ │ 3 │ uint64 stakingLimit, │ │ 3 │ uint64 stoppedValidators, │ │ 3 │ uint64 totalSigningKeys, │ │ 3 │ uint64 usedSigningKeys │ + │ 267129 │ ) = sps.getStakingProvider(SP_id, false); │ │ 368 │ if (!active) │ │ 0 │ continue; │ │ 0 │ │ │ 1279 │ DepositLookupCacheEntry memory cached = cache[idx++]; │ │ 144 │ cached.id = SP_id; │ │ 528 │ cached.stakingLimit = stakingLimit; │ │ 336 │ cached.stoppedValidators = stoppedValidators; │ │ 336 │ cached.totalSigningKeys = totalSigningKeys; │ │ 432 │ cached.usedSigningKeys = usedSigningKeys; │ │ 192 │ cached.initialUsedSigningKeys = usedSigningKeys; │ │ 0 │ │ │ 367 │ if (0 == SP_id) │ │ 11 │ break; │ │ 0 │ } │ │ 26 │ require(idx == cache.length, "SP_REGISTRY_INCOSISTENCY"); │ │ 0 │ } │ │ 0 │ │ │ 0 │ /** │ │ 0 │ * @dev Padding memory array with zeroes up to 64 bytes on the right │ │ 0 │ * @param _b Memory array of size 32 .. 64 │ │ 0 │ */ │ │ 88 │ function _pad64(bytes memory _b) internal pure returns (bytes memory) { │ │ 288 │ assert(_b.length >= 32 && _b.length <= 64); │ │ 116 │ if (64 == _b.length) │ │ 0 │ return _b; │ │ 0 │ │ │ 265 │ bytes memory zero32 = new bytes(32); │ │ 60 │ assembly { mstore(add(zero32, 0x20), 0) } │ │ 0 │ │ │ 118 │ if (32 == _b.length) │ │ 108 │ return BytesLib.concat(_b, zero32); │ │ 0 │ else │ │ 178 │ return BytesLib.concat(_b, BytesLib.slice(zero32, 0, uint256(64).sub(_b.length))); │ │ 0 │ } │ │ 0 │ │ │ 0 │ /** │ │ 0 │ * @dev Converting value to little endian bytes and padding up to 32 bytes on the right │ │ 0 │ * @param _value Number less than `2**64` for compatibility reasons │ │ 0 │ */ │ │ 36 │ function _toLittleEndian64(uint256 _value) internal pure returns (uint256 result) { │ │ 0 │ result = 0; │ │ 6 │ uint256 temp_value = _value; │ │ 742 │ for (uint256 i = 0; i < 8; ++i) { │ │ 432 │ result = (result << 8) | (temp_value & 0xFF); │ │ 240 │ temp_value >>= 8; │ │ 0 │ } │ │ 0 │ │ │ 40 │ assert(0 == temp_value); // fully converted │ │ 18 │ result <<= (24 * 8); │ │ 0 │ } │ │ 0 │ │ │ 8 │ function to64(uint256 v) internal pure returns (uint64) { │ │ 50 │ assert(v <= uint256(uint64(-1))); │ │ 0 │ return uint64(v); │ │ 0 │ } │ │ 0 │ } │ │ 0 │ └───┴────────┘ File contracts/0.4.24/lib/Pausable.sol ┌───┬────────┐ │ C │ GAS │ ├───┼────────┤ │ │ 0 │ pragma solidity 0.4.24; │ │ 0 │ │ │ 0 │ import "@aragon/os/contracts/common/UnstructuredStorage.sol"; │ │ 0 │ │ │ 0 │ │ │ 0 │ contract Pausable { │ │ 0 │ using UnstructuredStorage for bytes32; │ │ 0 │ │ │ 0 │ event Stopped(); │ │ 0 │ event Resumed(); │ │ 0 │ │ │ 175 │ bytes32 internal constant STOPPED_FLAG_POSITION = keccak256("depools.Pausable.stopped"); │ │ 0 │ │ │ 0 │ modifier whenNotStopped() { │ │ 76 │ require(!STOPPED_FLAG_POSITION.getStorageBool(), "CONTRACT_IS_STOPPED"); │ │ 4 │ _; │ │ 0 │ } │ │ 0 │ │ │ 0 │ modifier whenStopped() { │ │ 0 │ require(STOPPED_FLAG_POSITION.getStorageBool()); │ │ 0 │ _; │ │ 0 │ } │ │ 0 │ │ │ 22 │ function isStopped() external view returns (bool) { │ │ 12 │ return STOPPED_FLAG_POSITION.getStorageBool(); │ │ 0 │ } │ │ 0 │ │ │ 0 │ function _stop() internal whenNotStopped { │ │ 0 │ STOPPED_FLAG_POSITION.setStorageBool(true); │ │ 0 │ emit Stopped(); │ │ 0 │ } │ │ 0 │ │ │ 0 │ function _resume() internal whenStopped { │ │ 0 │ STOPPED_FLAG_POSITION.setStorageBool(false); │ │ 0 │ emit Resumed(); │ │ 0 │ } │ │ 0 │ } │ │ 0 │ └───┴────────┘ File contracts/0.4.24/StETH.sol ┌───┬────────┐ │ C │ GAS │ ├───┼────────┤ │ │ 0 │ /* See contracts/COMPILERS.md */ │ │ 0 │ pragma solidity 0.4.24; │ │ 0 │ │ │ 0 │ import "@aragon/os/contracts/apps/AragonApp.sol"; │ │ 0 │ import "@aragon/os/contracts/lib/math/SafeMath.sol"; │ │ 0 │ import {ERC20 as OZERC20} from "openzeppelin-solidity/contracts/token/ERC20/ERC20.sol"; │ │ 0 │ │ │ 0 │ import "./interfaces/ISTETH.sol"; │ │ 0 │ │ │ 0 │ import "./lib/Pausable.sol"; │ │ 0 │ │ │ 0 │ import "./interfaces/IDePool.sol"; │ │ 0 │ │ │ 0 │ │ │ 0 │ /** │ │ 0 │ * @title Implementation of a liquid version of ETH 2.0 native token │ │ 0 │ * │ │ 0 │ * ERC20 token which supports stop/resume, mint/burn mechanics. The token is operated by `IDePool`. │ │ 0 │ */ │ │ 409 │ contract StETH is ISTETH, Pausable, AragonApp { │ │ 0 │ using SafeMath for uint256; │ │ 0 │ │ │ 0 │ /// ACL │ │ 0 │ bytes32 constant public PAUSE_ROLE = keccak256("PAUSE_ROLE"); │ │ 81 │ bytes32 constant public MINT_ROLE = keccak256("MINT_ROLE"); │ │ 0 │ bytes32 constant public BURN_ROLE = keccak256("BURN_ROLE"); │ │ 0 │ │ │ 0 │ // DePool contract serves as a source of information on the amount of pooled funds │ │ 0 │ // and acts as the 'minter' of the new shares when staker submits his funds │ │ 0 │ IDePool public dePool; │ │ 0 │ │ │ 0 │ // Shares are the amounts of pooled Ether 'discounted' to the volume of ETH1.0 Ether deposited on the first day │ │ 0 │ // or, more precisely, to Ethers deposited from start until the first oracle report. │ │ 0 │ // Shares represent how much of first-day ether are worth all-time deposits of the given user. │ │ 0 │ // In this implementation token stores relative shares, not fixed balances. │ │ 0 │ mapping (address => uint256) private _shares; │ │ 0 │ uint256 private _totalShares; │ │ 0 │ │ │ 0 │ mapping (address => mapping (address => uint256)) private _allowed; │ │ 0 │ │ │ 0 │ event Transfer( │ │ 0 │ address indexed from, │ │ 0 │ address indexed to, │ │ 0 │ uint256 value │ │ 0 │ ); │ │ 0 │ │ │ 0 │ event Approval( │ │ 0 │ address indexed owner, │ │ 0 │ address indexed spender, │ │ 0 │ uint256 value │ │ 0 │ ); │ │ 0 │ │ │ 0 │ function initialize(IDePool _dePool) public onlyInit { │ │ 0 │ dePool = _dePool; │ │ 0 │ initialized(); │ │ 0 │ } │ │ 0 │ │ │ 0 │ /** │ │ 0 │ * @notice Stop transfers │ │ 0 │ */ │ │ 0 │ function stop() external auth(PAUSE_ROLE) { │ │ 0 │ _stop(); │ │ 0 │ } │ │ 0 │ │ │ 0 │ /** │ │ 0 │ * @notice Resume transfers │ │ 0 │ */ │ │ 1 │ function resume() external auth(PAUSE_ROLE) { │ │ 0 │ _resume(); │ │ 0 │ } │ │ 0 │ │ │ 0 │ /** │ │ 0 │ * @notice Mint is called by dePool contract when user submits the ETH1.0 deposit. │ │ 0 │ * It calculates share difference to preserve ratio of shares to the increased │ │ 0 │ * amount of pooledEthers so that all the previously created shares still correspond │ │ 0 │ * to the same amount of pooled ethers. │ │ 0 │ * Then adds the calculated difference to the user's share and to the totalShares │ │ 0 │ * similarly as traditional mint() function does with balances. │ │ 0 │ * @param _to Receiver of new shares │ │ 0 │ * @param _value Amount of pooledEthers (then gets converted to shares) │ │ 0 │ */ │ │ 70 │ function mint(address _to, uint256 _value) external whenNotStopped authP(MINT_ROLE, arr(_to, _value)) { │ │ 26 │ require(_to != 0); │ │ 12 │ uint256 sharesDifference; │ + │ 18123 │ uint256 totalControlledEthBefore = dePool.getTotalControlledEther(); │ │ 34 │ if ( totalControlledEthBefore == 0) { │ │ 8 │ sharesDifference = _value; │ │ 0 │ } else { │ │ 0 │ sharesDifference = getSharesByPooledEth(_value); │ │ 0 │ } │ │ 20833 │ _totalShares = _totalShares.add(sharesDifference); │ │ 21007 │ _shares[_to] = _shares[_to].add(sharesDifference); │ │ 1804 │ emit Transfer(address(0), _to, _value); │ │ 0 │ } │ │ 0 │ │ │ 0 │ /** │ │ 0 │ * @notice Burn `@tokenAmount(this, _value)` tokens from `_account` │ │ 0 │ * @param _account Account which tokens are to be burnt │ │ 0 │ * @param _value Amount of tokens to burn │ │ 0 │ */ │ │ 0 │ function burn(address _account, uint256 _value) external whenNotStopped authP(BURN_ROLE, arr(_account, _value)) { │ │ 0 │ if (0 == _value) │ │ 0 │ return; │ │ 0 │ │ │ 0 │ _burn(_account, _value); │ │ 0 │ } │ │ 0 │ │ │ 0 │ /** │ │ 0 │ * @dev Total number of tokens in existence │ │ 0 │ */ │ │ 0 │ function totalSupply() public view returns (uint256) { │ │ 0 │ return dePool.getTotalControlledEther(); │ │ 0 │ } │ │ 0 │ │ │ 0 │ /** │ │ 0 │ * @dev Gets the balance of the specified address. │ │ 0 │ * @param owner The address to query the balance of. │ │ 0 │ * @return An uint256 representing the amount owned by the passed address. │ │ 0 │ */ │ │ 0 │ function balanceOf(address owner) public view returns (uint256) { │ │ 0 │ return getPooledEthByHolder(owner); │ │ 0 │ } │ │ 0 │ │ │ 0 │ /** │ │ 0 │ * @dev Function to check the amount of tokens that an owner allowed to a spender. │ │ 0 │ * @param owner address The address which owns the funds. │ │ 0 │ * @param spender address The address which will spend the funds. │ │ 0 │ * @return A uint256 specifying the amount of tokens still available for the spender. │ │ 0 │ */ │ │ 0 │ function allowance( │ │ 0 │ address owner, │ │ 0 │ address spender │ │ 0 │ ) │ │ 0 │ public │ │ 0 │ view │ │ 0 │ returns (uint256) │ │ 0 │ { │ │ 0 │ return _allowed[owner][spender]; │ │ 0 │ } │ │ 0 │ │ │ 0 │ /** │ │ 0 │ * @dev Transfer token for a specified address │ │ 0 │ * @param to The address to transfer to. │ │ 0 │ * @param value The amount to be transferred. │ │ 0 │ */ │ │ 0 │ function transfer(address to, uint256 value) public whenNotStopped returns (bool) { │ │ 0 │ _transfer(msg.sender, to, value); │ │ 0 │ return true; │ │ 0 │ } │ │ 0 │ │ │ 0 │ /** │ │ 0 │ * @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender. │ │ 0 │ * Beware that changing an allowance with this method brings the risk that someone may use both the old │ │ 0 │ * and the new allowance by unfortunate transaction ordering. One possible solution to mitigate this │ │ 0 │ * race condition is to first reduce the spender's allowance to 0 and set the desired value afterwards: │ │ 0 │ * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 │ │ 0 │ * @param spender The address which will spend the funds. │ │ 0 │ * @param value The amount of tokens to be spent. │ │ 0 │ */ │ │ 0 │ function approve(address spender, uint256 value) public whenNotStopped returns (bool) { │ │ 0 │ require(spender != address(0)); │ │ 0 │ │ │ 0 │ _allowed[msg.sender][spender] = value; │ │ 0 │ emit Approval(msg.sender, spender, value); │ │ 0 │ return true; │ │ 0 │ } │ │ 0 │ │ │ 0 │ /** │ │ 0 │ * @dev Transfer tokens from one address to another │ │ 0 │ * @param from address The address which you want to send tokens from │ │ 0 │ * @param to address The address which you want to transfer to │ │ 0 │ * @param value uint256 the amount of tokens to be transferred │ │ 0 │ */ │ │ 0 │ function transferFrom( │ │ 0 │ address from, │ │ 0 │ address to, │ │ 0 │ uint256 value │ │ 0 │ ) │ │ 0 │ public │ │ 0 │ whenNotStopped │ │ 0 │ returns (bool) │ │ 0 │ { │ │ 0 │ require(value <= _allowed[from][msg.sender]); │ │ 0 │ │ │ 0 │ _allowed[from][msg.sender] = _allowed[from][msg.sender].sub(value); │ │ 0 │ _transfer(from, to, value); │ │ 0 │ return true; │ │ 0 │ } │ │ 0 │ │ │ 0 │ /** │ │ 0 │ * @dev Increase the amount of tokens that an owner allowed to a spender. │ │ 0 │ * approve should be called when allowed_[_spender] == 0. To increment │ │ 0 │ * allowed value is better to use this function to avoid 2 calls (and wait until │ │ 0 │ * the first transaction is mined) │ │ 0 │ * From MonolithDAO Token.sol │ │ 0 │ * @param spender The address which will spend the funds. │ │ 0 │ * @param addedValue The amount of tokens to increase the allowance by. │ │ 0 │ */ │ │ 0 │ function increaseAllowance( │ │ 0 │ address spender, │ │ 0 │ uint256 addedValue │ │ 0 │ ) │ │ 0 │ public │ │ 0 │ whenNotStopped │ │ 0 │ returns (bool) │ │ 0 │ { │ │ 0 │ require(spender != address(0)); │ │ 0 │ │ │ 0 │ _allowed[msg.sender][spender] = ( │ │ 0 │ _allowed[msg.sender][spender].add(addedValue)); │ │ 0 │ emit Approval(msg.sender, spender, _allowed[msg.sender][spender]); │ │ 0 │ return true; │ │ 0 │ } │ │ 0 │ │ │ 0 │ /** │ │ 0 │ * @dev Decrease the amount of tokens that an owner allowed to a spender. │ │ 0 │ * approve should be called when allowed_[_spender] == 0. To decrement │ │ 0 │ * allowed value is better to use this function to avoid 2 calls (and wait until │ │ 0 │ * the first transaction is mined) │ │ 0 │ * From MonolithDAO Token.sol │ │ 0 │ * @param spender The address which will spend the funds. │ │ 0 │ * @param subtractedValue The amount of tokens to decrease the allowance by. │ │ 0 │ */ │ │ 0 │ function decreaseAllowance( │ │ 0 │ address spender, │ │ 0 │ uint256 subtractedValue │ │ 0 │ ) │ │ 0 │ public │ │ 0 │ whenNotStopped │ │ 0 │ returns (bool) │ │ 0 │ { │ │ 0 │ require(spender != address(0)); │ │ 0 │ │ │ 0 │ _allowed[msg.sender][spender] = ( │ │ 0 │ _allowed[msg.sender][spender].sub(subtractedValue)); │ │ 0 │ emit Approval(msg.sender, spender, _allowed[msg.sender][spender]); │ │ 0 │ return true; │ │ 0 │ } │ │ 0 │ │ │ 0 │ /** │ │ 0 │ * @notice Returns the name of the token. │ │ 0 │ */ │ │ 0 │ function name() public pure returns (string) { │ │ 0 │ return "Liquid staked Ether 2.0"; │ │ 0 │ } │ │ 0 │ │ │ 0 │ /** │ │ 0 │ * @notice Returns the symbol of the token. │ │ 0 │ */ │ │ 0 │ function symbol() public pure returns (string) { │ │ 0 │ return "StETH"; │ │ 0 │ } │ │ 0 │ │ │ 0 │ /** │ │ 0 │ * @notice Returns the number of decimals of the token. │ │ 0 │ */ │ │ 0 │ function decimals() public pure returns (uint8) { │ │ 0 │ return 18; │ │ 0 │ } │ │ 0 │ │ │ 0 │ /** │ │ 0 │ * @dev Return the amount of shares that given holder has. │ │ 0 │ * @param _holder The address of the holder │ │ 0 │ */ │ │ 0 │ function getSharesByHolder(address _holder) public view returns (uint256) { │ │ 0 │ return _shares[_holder]; │ │ 0 │ } │ │ 0 │ │ │ 0 │ /** │ │ 0 │ * @dev Return the amount of pooled ethers for given amount of shares │ │ 0 │ * @param _sharesAmount The amount of shares │ │ 0 │ */ │ │ 0 │ function getPooledEthByShares(uint256 _sharesAmount) public view returns (uint256) { │ │ 0 │ if (_totalShares == 0) { │ │ 0 │ return 0; │ │ 0 │ } │ │ 0 │ return _sharesAmount.mul(dePool.getTotalControlledEther()).div(_totalShares); │ │ 0 │ } │ │ 0 │ │ │ 0 │ /** │ │ 0 │ * @dev Return the amount of pooled ethers for given holder │ │ 0 │ * @param _holder The address of the holder │ │ 0 │ */ │ │ 0 │ function getPooledEthByHolder(address _holder) public view returns (uint256) { │ │ 0 │ uint256 holderShares = getSharesByHolder(_holder); │ │ 0 │ uint256 holderPooledEth = getPooledEthByShares(holderShares); │ │ 0 │ return holderPooledEth; │ │ 0 │ } │ │ 0 │ │ │ 0 │ /** │ │ 0 │ * @dev Return the amount of shares backed by given amount of pooled Eth │ │ 0 │ * @param _pooledEthAmount The amount of pooled Eth │ │ 0 │ */ │ │ 0 │ function getSharesByPooledEth(uint256 _pooledEthAmount) public view returns (uint256) { │ │ 0 │ if (dePool.getTotalControlledEther() == 0) { │ │ 0 │ return 0; │ │ 0 │ } │ │ 0 │ return _pooledEthAmount.mul(_totalShares).div(dePool.getTotalControlledEther()); │ │ 0 │ } │ │ 0 │ │ │ 0 │ /** │ │ 0 │ * @dev Return the sum of the shares of all holders for better external visibility │ │ 0 │ */ │ │ 0 │ function getTotalShares() public view returns (uint256) { │ │ 0 │ return _totalShares; │ │ 0 │ } │ │ 0 │ │ │ 0 │ /** │ │ 0 │ * @dev Transfer token for a specified addresses │ │ 0 │ * @param from The address to transfer from. │ │ 0 │ * @param to The address to transfer to. │ │ 0 │ * @param value The amount to be transferred. │ │ 0 │ */ │ │ 0 │ function _transfer(address from, address to, uint256 value) internal { │ │ 0 │ require(to != address(0)); │ │ 0 │ uint256 sharesToTransfer = getSharesByPooledEth(value); │ │ 0 │ require(sharesToTransfer <= _shares[from]); │ │ 0 │ _shares[from] = _shares[from].sub(sharesToTransfer); │ │ 0 │ _shares[to] = _shares[to].add(sharesToTransfer); │ │ 0 │ emit Transfer(from, to, value); │ │ 0 │ } │ │ 0 │ │ │ 0 │ /** │ │ 0 │ * @dev Internal function that burns an amount of the token of a given │ │ 0 │ * account. │ │ 0 │ * @param account The account whose tokens will be burnt. │ │ 0 │ * @param value The amount that will be burnt. │ │ 0 │ */ │ │ 0 │ function _burn(address account, uint256 value) internal { │ │ 0 │ require(account != 0); │ │ 0 │ require(value != 0); │ │ 0 │ uint256 totalBalances = totalSupply(); │ │ 0 │ uint256 sharesToBurn = ( │ │ 0 │ _totalShares │ │ 0 │ .sub( │ │ 0 │ (totalBalances) │ │ 0 │ .mul(_totalShares.sub(_shares[account])) │ │ 0 │ .div(totalBalances - balanceOf(account) + value) │ │ 0 │ ) │ │ 0 │ ); │ │ 0 │ _totalShares = _totalShares.sub(sharesToBurn); │ │ 0 │ _shares[account] = _shares[account].sub(sharesToBurn); │ │ 0 │ emit Transfer(account, address(0), value); │ │ 0 │ } │ │ 0 │ │ │ 0 │ /** │ │ 0 │ * @dev Internal function that burns an amount of the token of a given │ │ 0 │ * account, deducting from the sender's allowance for said account. Uses the │ │ 0 │ * internal burn function. │ │ 0 │ * @param account The account whose tokens will be burnt. │ │ 0 │ * @param value The amount that will be burnt. │ │ 0 │ */ │ │ 0 │ function _burnFrom(address account, uint256 value) internal { │ │ 0 │ require(value <= _allowed[account][msg.sender]); │ │ 0 │ │ │ 0 │ // Should https://github.com/OpenZeppelin/zeppelin-solidity/issues/707 be accepted, │ │ 0 │ // this function needs to emit an event with the updated approval. │ │ 0 │ _allowed[account][msg.sender] = _allowed[account][msg.sender].sub( │ │ 0 │ value); │ │ 0 │ _burn(account, value); │ │ 0 │ } │ │ 0 │ } │ │ 0 │ └───┴────────┘ File contracts/0.4.24/sps/StakingProvidersRegistry.sol ┌───┬────────┐ │ C │ GAS │ ├───┼────────┤ │ │ 0 │ /* See contracts/COMPILERS.md */ │ │ 0 │ pragma solidity 0.4.24; │ │ 0 │ │ │ 0 │ import "@aragon/os/contracts/apps/AragonApp.sol"; │ │ 0 │ import "@aragon/os/contracts/common/IsContract.sol"; │ │ 0 │ import "@aragon/os/contracts/lib/math/SafeMath.sol"; │ │ 0 │ import "@aragon/os/contracts/lib/math/SafeMath64.sol"; │ │ 0 │ import "openzeppelin-solidity/contracts/token/ERC20/IERC20.sol"; │ │ 0 │ import "solidity-bytes-utils/contracts/BytesLib.sol"; │ │ 0 │ │ │ 0 │ import "../interfaces/IStakingProvidersRegistry.sol"; │ │ 0 │ │ │ 0 │ │ │ 0 │ /** │ │ 0 │ * @title Staking provider registry implementation │ │ 0 │ * │ │ 0 │ * See the comment of `IStakingProvidersRegistry`. │ │ 0 │ * │ │ 0 │ * NOTE: the code below assumes moderate amount of staking providers, e.g. up to 50. │ │ 0 │ */ │ │ 15453 │ contract StakingProvidersRegistry is IStakingProvidersRegistry, IsContract, AragonApp { │ │ 0 │ using SafeMath for uint256; │ │ 0 │ using SafeMath64 for uint64; │ │ 0 │ using UnstructuredStorage for bytes32; │ │ 0 │ │ │ 0 │ /// ACL │ │ 98 │ bytes32 constant public SET_POOL = keccak256("SET_POOL"); │ │ 0 │ bytes32 constant public MANAGE_SIGNING_KEYS = keccak256("MANAGE_SIGNING_KEYS"); │ │ 0 │ bytes32 constant public ADD_STAKING_PROVIDER_ROLE = keccak256("ADD_STAKING_PROVIDER_ROLE"); │ │ 0 │ bytes32 constant public SET_STAKING_PROVIDER_ACTIVE_ROLE = keccak256("SET_STAKING_PROVIDER_ACTIVE_ROLE"); │ │ 0 │ bytes32 constant public SET_STAKING_PROVIDER_NAME_ROLE = keccak256("SET_STAKING_PROVIDER_NAME_ROLE"); │ │ 0 │ bytes32 constant public SET_STAKING_PROVIDER_ADDRESS_ROLE = keccak256("SET_STAKING_PROVIDER_ADDRESS_ROLE"); │ │ 0 │ bytes32 constant public SET_STAKING_PROVIDER_LIMIT_ROLE = keccak256("SET_STAKING_PROVIDER_LIMIT_ROLE"); │ │ 0 │ bytes32 constant public REPORT_STOPPED_VALIDATORS_ROLE = keccak256("REPORT_STOPPED_VALIDATORS_ROLE"); │ │ 0 │ │ │ 6 │ uint256 constant public PUBKEY_LENGTH = 48; │ │ 30 │ uint256 constant public SIGNATURE_LENGTH = 96; │ │ 0 │ │ │ 174 │ bytes32 internal constant SIGNING_KEYS_MAPPING_NAME = keccak256("depools.DePool.signingKeys"); │ │ 0 │ │ │ 0 │ │ │ 0 │ /// @dev Staking provider parameters and internal state │ │ 0 │ struct StakingProvider { │ │ 0 │ bool active; // a flag indicating if the SP can participate in further staking and reward distribution │ │ 0 │ address rewardAddress; // Ethereum 1 address which receives steth rewards for this SP │ │ 0 │ string name; // human-readable name │ │ 0 │ uint64 stakingLimit; // the maximum number of validators to stake for this SP │ │ 0 │ uint64 stoppedValidators; // number of signing keys which stopped validation (e.g. were slashed) │ │ 0 │ │ │ 0 │ uint64 totalSigningKeys; // total amount of signing keys of this SP │ │ 0 │ uint64 usedSigningKeys; // number of signing keys of this SP which were used in deposits to the Ethereum 2 │ │ 0 │ } │ │ 0 │ │ │ 0 │ /// @dev Mapping of all staking providers. Mapping is used to be able to extend the struct. │ │ 0 │ mapping(uint256 => StakingProvider) internal sps; │ │ 0 │ │ │ 0 │ // @dev Total number of SPs │ │ 0 │ uint256 internal totalSPCount; │ │ 0 │ │ │ 0 │ // @dev Cached number of active SPs │ │ 0 │ uint256 internal activeSPCount; │ │ 0 │ │ │ 0 │ /// @dev link to the pool │ │ 0 │ address public pool; │ │ 0 │ │ │ 0 │ │ │ 0 │ modifier onlyPool() { │ │ 825 │ require(msg.sender == pool, "APP_AUTH_FAILED"); │ │ 0 │ _; │ │ 0 │ } │ │ 0 │ │ │ 0 │ modifier validAddress(address _a) { │ │ 0 │ require(_a != address(0), "EMPTY_ADDRESS"); │ │ 0 │ _; │ │ 0 │ } │ │ 0 │ │ │ 0 │ modifier SPExists(uint256 _id) { │ │ 14922 │ require(_id < totalSPCount, "STAKING_PROVIDER_NOT_FOUND"); │ │ 0 │ _; │ │ 0 │ } │ │ 0 │ │ │ 0 │ function initialize() public onlyInit { │ │ 0 │ totalSPCount = 0; │ │ 0 │ activeSPCount = 0; │ │ 0 │ initialized(); │ │ 0 │ } │ │ 0 │ │ │ 0 │ /** │ │ 0 │ * @notice Set the pool address to `_pool` │ │ 0 │ */ │ │ 0 │ function setPool(address _pool) external auth(SET_POOL) { │ │ 0 │ require(isContract(_pool), "POOL_NOT_CONTRACT"); │ │ 0 │ pool = _pool; │ │ 0 │ } │ │ 0 │ │ │ 0 │ /** │ │ 0 │ * @notice Add staking provider named `name` with reward address `rewardAddress` and staking limit `stakingLimit` validators │ │ 0 │ * @param _name Human-readable name │ │ 0 │ * @param _rewardAddress Ethereum 1 address which receives stETH rewards for this SP │ │ 0 │ * @param _stakingLimit the maximum number of validators to stake for this SP │ │ 0 │ * @return a unique key of the added SP │ │ 0 │ */ │ │ 0 │ function addStakingProvider(string _name, address _rewardAddress, uint64 _stakingLimit) external │ │ 0 │ auth(ADD_STAKING_PROVIDER_ROLE) │ │ 0 │ validAddress(_rewardAddress) │ │ 0 │ returns (uint256 id) │ │ 0 │ { │ │ 0 │ id = totalSPCount++; │ │ 0 │ StakingProvider storage sp = sps[id]; │ │ 0 │ │ │ 0 │ activeSPCount++; │ │ 0 │ sp.active = true; │ │ 0 │ sp.name = _name; │ │ 0 │ sp.rewardAddress = _rewardAddress; │ │ 0 │ sp.stakingLimit = _stakingLimit; │ │ 0 │ │ │ 0 │ emit StakingProviderAdded(id, _name, _rewardAddress, _stakingLimit); │ │ 0 │ } │ │ 0 │ │ │ 0 │ /** │ │ 0 │ * @notice `_active ? 'Enable' : 'Disable'` the staking provider #`_id` │ │ 0 │ */ │ │ 0 │ function setStakingProviderActive(uint256 _id, bool _active) external │ │ 0 │ authP(SET_STAKING_PROVIDER_ACTIVE_ROLE, arr(_id, _active ? uint256(1) : uint256(0))) │ │ 0 │ SPExists(_id) │ │ 0 │ { │ │ 0 │ if (sps[_id].active != _active) { │ │ 0 │ if (_active) │ │ 0 │ activeSPCount++; │ │ 0 │ else │ │ 0 │ activeSPCount = activeSPCount.sub(1); │ │ 0 │ } │ │ 0 │ │ │ 0 │ sps[_id].active = _active; │ │ 0 │ │ │ 0 │ emit StakingProviderActiveSet(_id, _active); │ │ 0 │ } │ │ 0 │ │ │ 0 │ /** │ │ 0 │ * @notice Change human-readable name of the staking provider #`_id` to `_name` │ │ 0 │ */ │ │ 0 │ function setStakingProviderName(uint256 _id, string _name) external │ │ 0 │ authP(SET_STAKING_PROVIDER_NAME_ROLE, arr(_id)) │ │ 0 │ SPExists(_id) │ │ 0 │ { │ │ 0 │ sps[_id].name = _name; │ │ 0 │ emit StakingProviderNameSet(_id, _name); │ │ 0 │ } │ │ 0 │ │ │ 0 │ /** │ │ 0 │ * @notice Change reward address of the staking provider #`_id` to `_rewardAddress` │ │ 0 │ */ │ │ 0 │ function setStakingProviderRewardAddress(uint256 _id, address _rewardAddress) external │ │ 0 │ authP(SET_STAKING_PROVIDER_ADDRESS_ROLE, arr(_id, uint256(_rewardAddress))) │ │ 0 │ SPExists(_id) │ │ 0 │ validAddress(_rewardAddress) │ │ 0 │ { │ │ 0 │ sps[_id].rewardAddress = _rewardAddress; │ │ 0 │ emit StakingProviderRewardAddressSet(_id, _rewardAddress); │ │ 0 │ } │ │ 0 │ │ │ 0 │ /** │ │ 0 │ * @notice Set the maximum number of validators to stake for the staking provider #`_id` to `_stakingLimit` │ │ 0 │ */ │ │ 0 │ function setStakingProviderStakingLimit(uint256 _id, uint64 _stakingLimit) external │ │ 0 │ authP(SET_STAKING_PROVIDER_LIMIT_ROLE, arr(_id, uint256(_stakingLimit))) │ │ 0 │ SPExists(_id) │ │ 0 │ { │ │ 0 │ sps[_id].stakingLimit = _stakingLimit; │ │ 0 │ emit StakingProviderStakingLimitSet(_id, _stakingLimit); │ │ 0 │ } │ │ 0 │ │ │ 0 │ /** │ │ 0 │ * @notice Report `_stoppedIncrement` more stopped validators of the staking provider #`_id` │ │ 0 │ */ │ │ 0 │ function reportStoppedValidators(uint256 _id, uint64 _stoppedIncrement) external │ │ 0 │ authP(REPORT_STOPPED_VALIDATORS_ROLE, arr(_id, uint256(_stoppedIncrement))) │ │ 0 │ SPExists(_id) │ │ 0 │ { │ │ 0 │ require(0 != _stoppedIncrement, "EMPTY_VALUE"); │ │ 0 │ sps[_id].stoppedValidators = sps[_id].stoppedValidators.add(_stoppedIncrement); │ │ 0 │ require(sps[_id].stoppedValidators <= sps[_id].usedSigningKeys, "STOPPED_MORE_THAN_LAUNCHED"); │ │ 0 │ │ │ 0 │ emit StakingProviderTotalStoppedValidatorsReported(_id, sps[_id].stoppedValidators); │ │ 0 │ } │ │ 0 │ │ │ 0 │ /** │ │ 0 │ * @notice Update used key counts │ │ 0 │ * @dev Function is used by the pool │ │ 0 │ * @param _ids Array of staking provider ids │ │ 0 │ * @param _usedSigningKeys Array of corresponding used key counts (the same length as _ids) │ │ 0 │ */ │ │ 81 │ function updateUsedKeys(uint256[] _ids, uint64[] _usedSigningKeys) external onlyPool { │ │ 23 │ require(_ids.length == _usedSigningKeys.length, "BAD_LENGTH"); │ │ 162 │ for (uint256 i = 0; i < _ids.length; ++i) { │ │ 1754 │ require(_ids[i] < totalSPCount, "STAKING_PROVIDER_NOT_FOUND"); │ │ 268 │ StakingProvider storage sp = sps[_ids[i]]; │ │ 0 │ │ │ 1800 │ uint64 current = sp.usedSigningKeys; │ │ 136 │ uint64 new_ = _usedSigningKeys[i]; │ │ 0 │ │ │ 88 │ require(current <= new_, "USED_KEYS_DECREASED"); │ │ 76 │ if (current == new_) │ │ 0 │ continue; │ │ 0 │ │ │ 1716 │ require(new_ <= sp.totalSigningKeys, "INCONSISTENCY"); │ │ 0 │ │ │ 11676 │ sp.usedSigningKeys = new_; │ │ 0 │ } │ │ 0 │ } │ │ 0 │ │ │ 0 │ /** │ │ 0 │ * @notice Remove unused signing keys │ │ 0 │ * @dev Function is used by the pool │ │ 0 │ */ │ │ 0 │ function trimUnusedKeys() external onlyPool { │ │ 0 │ uint256 length = totalSPCount; │ │ 0 │ for (uint256 SP_id = 0; SP_id < length; ++SP_id) { │ │ 0 │ if (sps[SP_id].totalSigningKeys != sps[SP_id].usedSigningKeys) // write only if update is needed │ │ 0 │ sps[SP_id].totalSigningKeys = sps[SP_id].usedSigningKeys; // discard unused keys │ │ 0 │ } │ │ 0 │ } │ │ 0 │ │ │ 0 │ /** │ │ 0 │ * @notice Add `_quantity` validator signing keys to the set of usable keys. Concatenated keys are: `_pubkeys`. Can be done by the DAO in question by using the designated rewards address. │ │ 0 │ * @dev Along with each key the DAO has to provide a signatures for the │ │ 0 │ * (pubkey, withdrawal_credentials, 32000000000) message. │ │ 0 │ * Given that information, the contract'll be able to call │ │ 0 │ * validator_registration.deposit on-chain. │ │ 0 │ * @param _SP_id Staking provider id │ │ 0 │ * @param _quantity Number of signing keys provided │ │ 0 │ * @param _pubkeys Several concatenated validator signing keys │ │ 0 │ * @param _signatures Several concatenated signatures for (pubkey, withdrawal_credentials, 32000000000) messages │ │ 0 │ */ │ │ 25 │ function addSigningKeys(uint256 _SP_id, uint256 _quantity, bytes _pubkeys, bytes _signatures) external │ │ 0 │ authP(MANAGE_SIGNING_KEYS, arr(_SP_id)) │ │ 0 │ { │ │ 1 │ _addSigningKeys(_SP_id, _quantity, _pubkeys, _signatures); │ │ 0 │ } │ │ 0 │ │ │ 0 │ /** │ │ 0 │ * @notice Add `_quantity` validator signing keys to the set of usable keys. Concatenated keys are: `_pubkeys`. Can be done by staking provider in question by using the designated rewards address. │ │ 0 │ * @dev Along with each key the DAO has to provide a signatures for the │ │ 0 │ * (pubkey, withdrawal_credentials, 32000000000) message. │ │ 0 │ * Given that information, the contract'll be able to call │ │ 0 │ * validator_registration.deposit on-chain. │ │ 0 │ * @param _SP_id Staking provider id │ │ 0 │ * @param _quantity Number of signing keys provided │ │ 0 │ * @param _pubkeys Several concatenated validator signing keys │ │ 0 │ * @param _signatures Several concatenated signatures for (pubkey, withdrawal_credentials, 32000000000) messages │ │ 0 │ */ │ │ 0 │ function addSigningKeysSP( │ │ 0 │ uint256 _SP_id, │ │ 0 │ uint256 _quantity, │ │ 0 │ bytes _pubkeys, │ │ 0 │ bytes _signatures │ │ 0 │ ) │ │ 0 │ external │ │ 0 │ { │ │ 0 │ require(msg.sender == sps[_SP_id].rewardAddress, "APP_AUTH_FAILED"); │ │ 0 │ _addSigningKeys(_SP_id, _quantity, _pubkeys, _signatures); │ │ 0 │ } │ │ 0 │ │ │ 0 │ /** │ │ 0 │ * @notice Removes a validator signing key #`_index` from the set of usable keys. Executed on behalf of DAO. │ │ 0 │ * @param _SP_id Staking provider id │ │ 0 │ * @param _index Index of the key, starting with 0 │ │ 0 │ */ │ │ 0 │ function removeSigningKey(uint256 _SP_id, uint256 _index) │ │ 0 │ external │ │ 0 │ authP(MANAGE_SIGNING_KEYS, arr(_SP_id)) │ │ 0 │ { │ │ 0 │ _removeSigningKey(_SP_id, _index); │ │ 0 │ } │ │ 0 │ │ │ 0 │ /** │ │ 0 │ * @notice Removes a validator signing key #`_index` from the set of usable keys. Executed on behalf of Staking Provider. │ │ 0 │ * @param _SP_id Staking provider id │ │ 0 │ * @param _index Index of the key, starting with 0 │ │ 0 │ */ │ │ 0 │ function removeSigningKeySP(uint256 _SP_id, uint256 _index) external { │ │ 0 │ require(msg.sender == sps[_SP_id].rewardAddress, "APP_AUTH_FAILED"); │ │ 0 │ _removeSigningKey(_SP_id, _index); │ │ 0 │ } │ │ 0 │ │ │ 0 │ /** │ │ 0 │ * @notice Distributes rewards among staking providers. │ │ 0 │ * @dev Function is used by the pool │ │ 0 │ * @param _token Reward token (must be ERC20-compatible) │ │ 0 │ * @param _totalReward Total amount to distribute (must be transferred to this contract beforehand) │ │ 0 │ */ │ │ 0 │ function distributeRewards(address _token, uint256 _totalReward) external onlyPool { │ │ 0 │ uint256 length = totalSPCount; │ │ 0 │ uint64 effectiveStakeTotal; │ │ 0 │ for (uint256 SP_id = 0; SP_id < length; ++SP_id) { │ │ 0 │ StakingProvider storage sp = sps[SP_id]; │ │ 0 │ if (!sp.active) │ │ 0 │ continue; │ │ 0 │ │ │ 0 │ uint64 effectiveStake = sp.usedSigningKeys.sub(sp.stoppedValidators); │ │ 0 │ effectiveStakeTotal = effectiveStakeTotal.add(effectiveStake); │ │ 0 │ } │ │ 0 │ │ │ 0 │ if (0 == effectiveStakeTotal) │ │ 0 │ revert("NO_STAKE"); │ │ 0 │ │ │ 0 │ for (SP_id = 0; SP_id < length; ++SP_id) { │ │ 0 │ sp = sps[SP_id]; │ │ 0 │ if (!sp.active) │ │ 0 │ continue; │ │ 0 │ │ │ 0 │ effectiveStake = sp.usedSigningKeys.sub(sp.stoppedValidators); │ │ 0 │ uint256 reward = uint256(effectiveStake).mul(_totalReward).div(uint256(effectiveStakeTotal)); │ │ 0 │ require(IERC20(_token).transfer(sp.rewardAddress, reward), "TRANSFER_FAILED"); │ │ 0 │ } │ │ 0 │ } │ │ 0 │ │ │ 0 │ /** │ │ 0 │ * @notice Returns total number of staking providers │ │ 0 │ */ │ │ 31 │ function getStakingProvidersCount() external view returns (uint256) { │ │ 803 │ return totalSPCount; │ │ 0 │ } │ │ 0 │ │ │ 0 │ /** │ │ 0 │ * @notice Returns number of active staking providers │ │ 0 │ */ │ │ 31 │ function getActiveStakingProvidersCount() external view returns (uint256) { │ │ 803 │ return activeSPCount; │ │ 0 │ } │ │ 0 │ │ │ 0 │ /** │ │ 0 │ * @notice Returns the n-th staking provider │ │ 0 │ * @param _id Staking provider id │ │ 0 │ * @param _fullInfo If true, name will be returned as well │ │ 0 │ */ │ │ 6240 │ function getStakingProvider(uint256 _id, bool _fullInfo) external view │ │ 48 │ SPExists(_id) │ │ 0 │ returns │ │ 0 │ ( │ │ 48 │ bool active, │ │ 48 │ string name, │ │ 48 │ address rewardAddress, │ │ 48 │ uint64 stakingLimit, │ │ 48 │ uint64 stoppedValidators, │ │ 48 │ uint64 totalSigningKeys, │ │ 48 │ uint64 usedSigningKeys │ │ 0 │ ) │ │ 0 │ { │ │ 1248 │ StakingProvider storage sp = sps[_id]; │ │ 0 │ │ │ 12992 │ active = sp.active; │ │ 1168 │ name = _fullInfo ? sp.name : ""; // reading name is 2+ SLOADs │ │ 13168 │ rewardAddress = sp.rewardAddress; │ │ 13232 │ stakingLimit = sp.stakingLimit; │ │ 320 │ stoppedValidators = sp.stoppedValidators; │ │ 272 │ totalSigningKeys = sp.totalSigningKeys; │ │ 224 │ usedSigningKeys = sp.usedSigningKeys; │ │ 0 │ } │ │ 0 │ │ │ 0 │ /** │ │ 0 │ * @notice Returns total number of signing keys of the staking provider #`_SP_id` │ │ 0 │ */ │ │ 0 │ function getTotalSigningKeyCount(uint256 _SP_id) external view SPExists(_SP_id) returns (uint256) { │ │ 0 │ return sps[_SP_id].totalSigningKeys; │ │ 0 │ } │ │ 0 │ │ │ 0 │ /** │ │ 0 │ * @notice Returns number of usable signing keys of the staking provider #`_SP_id` │ │ 0 │ */ │ │ 0 │ function getUnusedSigningKeyCount(uint256 _SP_id) external view SPExists(_SP_id) returns (uint256) { │ │ 0 │ return sps[_SP_id].totalSigningKeys.sub(sps[_SP_id].usedSigningKeys); │ │ 0 │ } │ │ 0 │ │ │ 0 │ /** │ │ 0 │ * @notice Returns n-th signing key of the staking provider #`_SP_id` │ │ 0 │ * @param _SP_id Staking provider id │ │ 0 │ * @param _index Index of the key, starting with 0 │ │ 0 │ * @return key Key │ │ 0 │ * @return depositSignature Signature needed for a validator_registration.deposit call │ │ 0 │ * @return used Flag indication if the key was used in the staking │ │ 0 │ */ │ │ 964 │ function getSigningKey(uint256 _SP_id, uint256 _index) external view │ │ 6 │ SPExists(_SP_id) │ │ 18 │ returns (bytes key, bytes depositSignature, bool used) │ │ 0 │ { │ │ 1824 │ require(_index < sps[_SP_id].totalSigningKeys, "KEY_NOT_FOUND"); │ │ 0 │ │ │ 66 │ (bytes memory key_, bytes memory signature) = _loadSigningKey(_SP_id, _index); │ │ 0 │ │ │ 1820 │ return (key_, signature, _index < sps[_SP_id].usedSigningKeys); │ │ 0 │ } │ │ 0 │ │ │ 0 │ function _isEmptySigningKey(bytes memory _key) internal pure returns (bool) { │ │ 0 │ assert(_key.length == PUBKEY_LENGTH); │ │ 0 │ // algorithm applicability constraint │ │ 0 │ assert(PUBKEY_LENGTH >= 32 && PUBKEY_LENGTH <= 64); │ │ 0 │ │ │ 0 │ uint256 k1; │ │ 0 │ uint256 k2; │ │ 0 │ assembly { │ │ 0 │ k1 := mload(add(_key, 0x20)) │ │ 0 │ k2 := mload(add(_key, 0x40)) │ │ 0 │ } │ │ 0 │ │ │ 0 │ return 0 == k1 && 0 == (k2 >> ((2 * 32 - PUBKEY_LENGTH) * 8)); │ │ 0 │ } │ │ 0 │ │ │ 0 │ function to64(uint256 v) internal pure returns (uint64) { │ │ 0 │ assert(v <= uint256(uint64(-1))); │ │ 0 │ return uint64(v); │ │ 0 │ } │ │ 0 │ │ │ 30 │ function _signingKeyOffset(uint256 _SP_id, uint256 _keyIndex) internal pure returns (uint256) { │ │ 444 │ return uint256(keccak256(abi.encodePacked(SIGNING_KEYS_MAPPING_NAME, _SP_id, _keyIndex))); │ │ 0 │ } │ │ 0 │ │ │ 0 │ function _storeSigningKey(uint256 _SP_id, uint256 _keyIndex, bytes memory _key, bytes memory _signature) internal { │ │ 0 │ assert(_key.length == PUBKEY_LENGTH); │ │ 0 │ assert(_signature.length == SIGNATURE_LENGTH); │ │ 0 │ // algorithm applicability constraints │ │ 0 │ assert(PUBKEY_LENGTH >= 32 && PUBKEY_LENGTH <= 64); │ │ 0 │ assert(0 == SIGNATURE_LENGTH % 32); │ │ 0 │ │ │ 0 │ // key │ │ 0 │ uint256 offset = _signingKeyOffset(_SP_id, _keyIndex); │ │ 0 │ uint256 keyExcessBits = (2 * 32 - PUBKEY_LENGTH) * 8; │ │ 0 │ assembly { │ │ 0 │ sstore(offset, mload(add(_key, 0x20))) │ │ 0 │ sstore(add(offset, 1), shl(keyExcessBits, shr(keyExcessBits, mload(add(_key, 0x40))))) │ │ 0 │ } │ │ 0 │ offset += 2; │ │ 0 │ │ │ 0 │ // signature │ │ 0 │ for (uint256 i = 0; i < SIGNATURE_LENGTH; i += 32) { │ │ 0 │ assembly { │ │ 0 │ sstore(offset, mload(add(_signature, add(0x20, i)))) │ │ 0 │ } │ │ 0 │ offset++; │ │ 0 │ } │ │ 0 │ } │ │ 0 │ │ │ 0 │ function _addSigningKeys(uint256 _SP_id, uint256 _quantity, bytes _pubkeys, bytes _signatures) internal │ │ 0 │ SPExists(_SP_id) │ │ 0 │ { │ │ 0 │ require(_quantity != 0, "NO_KEYS"); │ │ 0 │ require(_pubkeys.length == _quantity.mul(PUBKEY_LENGTH), "INVALID_LENGTH"); │ │ 0 │ require(_signatures.length == _quantity.mul(SIGNATURE_LENGTH), "INVALID_LENGTH"); │ │ 0 │ │ │ 0 │ for (uint256 i = 0; i < _quantity; ++i) { │ │ 0 │ bytes memory key = BytesLib.slice(_pubkeys, i * PUBKEY_LENGTH, PUBKEY_LENGTH); │ │ 0 │ require(!_isEmptySigningKey(key), "EMPTY_KEY"); │ │ 0 │ bytes memory sig = BytesLib.slice(_signatures, i * SIGNATURE_LENGTH, SIGNATURE_LENGTH); │ │ 0 │ │ │ 0 │ _storeSigningKey(_SP_id, sps[_SP_id].totalSigningKeys + i, key, sig); │ │ 0 │ emit SigningKeyAdded(_SP_id, key); │ │ 0 │ } │ │ 0 │ │ │ 0 │ sps[_SP_id].totalSigningKeys = sps[_SP_id].totalSigningKeys.add(to64(_quantity)); │ │ 0 │ } │ │ 0 │ │ │ 0 │ function _removeSigningKey(uint256 _SP_id, uint256 _index) internal │ │ 0 │ SPExists(_SP_id) │ │ 0 │ { │ │ 0 │ require(_index < sps[_SP_id].totalSigningKeys, "KEY_NOT_FOUND"); │ │ 0 │ require(_index >= sps[_SP_id].usedSigningKeys, "KEY_WAS_USED"); │ │ 0 │ │ │ 0 │ (bytes memory removedKey, ) = _loadSigningKey(_SP_id, _index); │ │ 0 │ │ │ 0 │ uint256 lastIndex = sps[_SP_id].totalSigningKeys.sub(1); │ │ 0 │ if (_index < lastIndex) { │ │ 0 │ (bytes memory key, bytes memory signature) = _loadSigningKey(_SP_id, lastIndex); │ │ 0 │ _storeSigningKey(_SP_id, _index, key, signature); │ │ 0 │ } │ │ 0 │ │ │ 0 │ _deleteSigningKey(_SP_id, lastIndex); │ │ 0 │ sps[_SP_id].totalSigningKeys = sps[_SP_id].totalSigningKeys.sub(1); │ │ 0 │ │ │ 0 │ emit SigningKeyRemoved(_SP_id, removedKey); │ │ 0 │ } │ │ 0 │ │ │ 0 │ function _deleteSigningKey(uint256 _SP_id, uint256 _keyIndex) internal { │ │ 0 │ uint256 offset = _signingKeyOffset(_SP_id, _keyIndex); │ │ 0 │ for (uint256 i = 0; i < (PUBKEY_LENGTH + SIGNATURE_LENGTH) / 32 + 1; ++i) { │ │ 0 │ assembly { │ │ 0 │ sstore(add(offset, i), 0) │ │ 0 │ } │ │ 0 │ } │ │ 0 │ } │ │ 0 │ │ │ 74 │ function _loadSigningKey(uint256 _SP_id, uint256 _keyIndex) internal view returns (bytes memory key, bytes memory signature) { │ │ 0 │ // algorithm applicability constraints │ │ 0 │ assert(PUBKEY_LENGTH >= 32 && PUBKEY_LENGTH <= 64); │ │ 0 │ assert(0 == SIGNATURE_LENGTH % 32); │ │ 0 │ │ │ 66 │ uint256 offset = _signingKeyOffset(_SP_id, _keyIndex); │ │ 0 │ │ │ 0 │ // key │ │ 102 │ bytes memory tmpKey = new bytes(64); │ │ 0 │ assembly { │ │ 1630 │ mstore(add(tmpKey, 0x20), sload(offset)) │ │ 1642 │ mstore(add(tmpKey, 0x40), sload(add(offset, 1))) │ │ 0 │ } │ │ 30 │ offset += 2; │ │ 42 │ key = BytesLib.slice(tmpKey, 0, PUBKEY_LENGTH); │ │ 0 │ │ │ 0 │ // signature │ │ 110 │ signature = new bytes(SIGNATURE_LENGTH); │ │ 268 │ for (uint256 i = 0; i < SIGNATURE_LENGTH; i += 32) { │ │ 0 │ assembly { │ │ 5016 │ mstore(add(signature, add(0x20, i)), sload(offset)) │ │ 0 │ } │ │ 90 │ offset++; │ │ 0 │ } │ │ 0 │ } │ │ 0 │ } │ │ 0 │ └───┴────────┘ File solidity-bytes-utils/contracts/BytesLib.sol ┌───┬────────┐ │ C │ GAS │ ├───┼────────┤ │ │ 0 │ /* │ │ 0 │ * @title Solidity Bytes Arrays Utils │ │ 0 │ * @author Gonçalo Sá │ │ 0 │ * │ │ 0 │ * @dev Bytes tightly packed arrays utility library for ethereum contracts written in Solidity. │ │ 0 │ * The library lets you concatenate, slice and type cast bytes arrays both in memory and storage. │ │ 0 │ */ │ │ 0 │ │ │ 0 │ pragma solidity ^0.4.19; │ │ 0 │ │ │ 0 │ │ │ 0 │ library BytesLib { │ │ 60 │ function concat(bytes memory _preBytes, bytes memory _postBytes) internal pure returns (bytes) { │ │ 12 │ bytes memory tempBytes; │ │ 0 │ │ │ 0 │ assembly { │ │ 0 │ // Get a location of some free memory and store it in tempBytes as │ │ 0 │ // Solidity does for memory variables. │ │ 44 │ tempBytes := mload(0x40) │ │ 0 │ │ │ 0 │ // Store the length of the first bytes array at the beginning of │ │ 0 │ // the memory for tempBytes. │ │ 24 │ let length := mload(_preBytes) │ │ 36 │ mstore(tempBytes, length) │ │ 0 │ │ │ 0 │ // Maintain a memory counter for the current write location in the │ │ 0 │ // temp bytes array by adding the 32 bytes for the array length to │ │ 0 │ // the starting location. │ │ 36 │ let mc := add(tempBytes, 0x20) │ │ 0 │ // Stop copying when the memory counter reaches the length of the │ │ 0 │ // first bytes array. │ │ 36 │ let end := add(mc, length) │ │ 0 │ │ │ 240 │ for { │ │ 0 │ // Initialize a copy counter to the start of the _preBytes data, │ │ 0 │ // 32 bytes into its memory. │ │ 36 │ let cc := add(_preBytes, 0x20) │ │ 90 │ } lt(mc, end) { │ │ 0 │ // Increase both counters by 32 bytes each iteration. │ │ 90 │ mc := add(mc, 0x20) │ │ 18 │ cc := add(cc, 0x20) │ │ 0 │ } { │ │ 0 │ // Write the _preBytes data into the tempBytes memory 32 bytes │ │ 0 │ // at a time. │ │ 72 │ mstore(mc, mload(cc)) │ │ 0 │ } │ │ 0 │ │ │ 0 │ // Add the length of _postBytes to the current length of tempBytes │ │ 0 │ // and store it as the new length in the first 32 bytes of the │ │ 0 │ // tempBytes memory. │ │ 36 │ length := mload(_postBytes) │ │ 72 │ mstore(tempBytes, add(length, mload(tempBytes))) │ │ 0 │ │ │ 0 │ // Move the memory counter back from a multiple of 0x20 to the │ │ 0 │ // actual end of the _preBytes data. │ │ 12 │ mc := end │ │ 0 │ // Stop copying when the memory counter reaches the new combined │ │ 0 │ // length of the arrays. │ │ 36 │ end := add(mc, length) │ │ 0 │ │ │ 184 │ for { │ │ 36 │ let cc := add(_postBytes, 0x20) │ │ 72 │ } lt(mc, end) { │ │ 60 │ mc := add(mc, 0x20) │ │ 12 │ cc := add(cc, 0x20) │ │ 0 │ } { │ │ 48 │ mstore(mc, mload(cc)) │ │ 0 │ } │ │ 0 │ │ │ 0 │ // Update the free-memory pointer by padding our last write location │ │ 0 │ // to 32 bytes: add 31 bytes to the end of tempBytes to move to the │ │ 0 │ // next 32 byte block, then round down to the nearest multiple of │ │ 0 │ // 32. If the sum of the length of the two arrays is zero then add │ │ 0 │ // one before rounding down to leave a blank 32 bytes (the length block with 0). │ │ 36 │ mstore(0x40, and( │ │ 144 │ add(add(end, iszero(add(length, mload(_preBytes)))), 31), │ │ 12 │ not(31) // Round down to the nearest 32 bytes. │ │ 0 │ )) │ │ 0 │ } │ │ 0 │ │ │ 12 │ return tempBytes; │ │ 0 │ } │ │ 0 │ │ │ 0 │ function concatStorage(bytes storage _preBytes, bytes memory _postBytes) internal { │ │ 0 │ assembly { │ │ 0 │ // Read the first 32 bytes of _preBytes storage, which is the length │ │ 0 │ // of the array. (We don't need to use the offset into the slot │ │ 0 │ // because arrays use the entire slot.) │ │ 0 │ let fslot := sload(_preBytes_slot) │ │ 0 │ // Arrays of 31 bytes or less have an even value in their slot, │ │ 0 │ // while longer arrays have an odd value. The actual length is │ │ 0 │ // the slot divided by two for odd values, and the lowest order │ │ 0 │ // byte divided by two for even values. │ │ 0 │ // If the slot is even, bitwise and the slot with 255 and divide by │ │ 0 │ // two to get the length. If the slot is odd, bitwise and the slot │ │ 0 │ // with -1 and divide by two. │ │ 0 │ let slength := div(and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 2) │ │ 0 │ let mlength := mload(_postBytes) │ │ 0 │ let newlength := add(slength, mlength) │ │ 0 │ // slength can contain both the length and contents of the array │ │ 0 │ // if length < 32 bytes so let's prepare for that │ │ 0 │ // v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage │ │ 0 │ switch add(lt(slength, 32), lt(newlength, 32)) │ │ 0 │ case 2 { │ │ 0 │ // Since the new array still fits in the slot, we just need to │ │ 0 │ // update the contents of the slot. │ │ 0 │ // uint256(bytes_storage) = uint256(bytes_storage) + uint256(bytes_memory) + new_length │ │ 0 │ sstore( │ │ 0 │ _preBytes_slot, │ │ 0 │ // all the modifications to the slot are inside this │ │ 0 │ // next block │ │ 0 │ add( │ │ 0 │ // we can just add to the slot contents because the │ │ 0 │ // bytes we want to change are the LSBs │ │ 0 │ fslot, │ │ 0 │ add( │ │ 0 │ mul( │ │ 0 │ div( │ │ 0 │ // load the bytes from memory │ │ 0 │ mload(add(_postBytes, 0x20)), │ │ 0 │ // zero all bytes to the right │ │ 0 │ exp(0x100, sub(32, mlength)) │ │ 0 │ ), │ │ 0 │ // and now shift left the number of bytes to │ │ 0 │ // leave space for the length in the slot │ │ 0 │ exp(0x100, sub(32, newlength)) │ │ 0 │ ), │ │ 0 │ // increase length by the double of the memory │ │ 0 │ // bytes length │ │ 0 │ mul(mlength, 2) │ │ 0 │ ) │ │ 0 │ ) │ │ 0 │ ) │ │ 0 │ } │ │ 0 │ case 1 { │ │ 0 │ // The stored value fits in the slot, but the combined value │ │ 0 │ // will exceed it. │ │ 0 │ // get the keccak hash to get the contents of the array │ │ 0 │ mstore(0x0, _preBytes_slot) │ │ 0 │ let sc := add(keccak256(0x0, 0x20), div(slength, 32)) │ │ 0 │ │ │ 0 │ // save new length │ │ 0 │ sstore(_preBytes_slot, add(mul(newlength, 2), 1)) │ │ 0 │ │ │ 0 │ // The contents of the _postBytes array start 32 bytes into │ │ 0 │ // the structure. Our first read should obtain the `submod` │ │ 0 │ // bytes that can fit into the unused space in the last word │ │ 0 │ // of the stored array. To get this, we read 32 bytes starting │ │ 0 │ // from `submod`, so the data we read overlaps with the array │ │ 0 │ // contents by `submod` bytes. Masking the lowest-order │ │ 0 │ // `submod` bytes allows us to add that value directly to the │ │ 0 │ // stored value. │ │ 0 │ │ │ 0 │ let submod := sub(32, slength) │ │ 0 │ let mc := add(_postBytes, submod) │ │ 0 │ let end := add(_postBytes, mlength) │ │ 0 │ let mask := sub(exp(0x100, submod), 1) │ │ 0 │ │ │ 0 │ sstore( │ │ 0 │ sc, │ │ 0 │ add( │ │ 0 │ and( │ │ 0 │ fslot, │ │ 0 │ 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00 │ │ 0 │ ), │ │ 0 │ and(mload(mc), mask) │ │ 0 │ ) │ │ 0 │ ) │ │ 0 │ │ │ 0 │ for { │ │ 0 │ mc := add(mc, 0x20) │ │ 0 │ sc := add(sc, 1) │ │ 0 │ } lt(mc, end) { │ │ 0 │ sc := add(sc, 1) │ │ 0 │ mc := add(mc, 0x20) │ │ 0 │ } { │ │ 0 │ sstore(sc, mload(mc)) │ │ 0 │ } │ │ 0 │ │ │ 0 │ mask := exp(0x100, sub(mc, end)) │ │ 0 │ │ │ 0 │ sstore(sc, mul(div(mload(mc), mask), mask)) │ │ 0 │ } │ │ 0 │ default { │ │ 0 │ // get the keccak hash to get the contents of the array │ │ 0 │ mstore(0x0, _preBytes_slot) │ │ 0 │ // Start copying to the last used word of the stored array. │ │ 0 │ let sc := add(keccak256(0x0, 0x20), div(slength, 32)) │ │ 0 │ │ │ 0 │ // save new length │ │ 0 │ sstore(_preBytes_slot, add(mul(newlength, 2), 1)) │ │ 0 │ │ │ 0 │ // Copy over the first `submod` bytes of the new data as in │ │ 0 │ // case 1 above. │ │ 0 │ let slengthmod := mod(slength, 32) │ │ 0 │ let mlengthmod := mod(mlength, 32) │ │ 0 │ let submod := sub(32, slengthmod) │ │ 0 │ let mc := add(_postBytes, submod) │ │ 0 │ let end := add(_postBytes, mlength) │ │ 0 │ let mask := sub(exp(0x100, submod), 1) │ │ 0 │ │ │ 0 │ sstore(sc, add(sload(sc), and(mload(mc), mask))) │ │ 0 │ │ │ 0 │ for { │ │ 0 │ sc := add(sc, 1) │ │ 0 │ mc := add(mc, 0x20) │ │ 0 │ } lt(mc, end) { │ │ 0 │ sc := add(sc, 1) │ │ 0 │ mc := add(mc, 0x20) │ │ 0 │ } { │ │ 0 │ sstore(sc, mload(mc)) │ │ 0 │ } │ │ 0 │ │ │ 0 │ mask := exp(0x100, sub(mc, end)) │ │ 0 │ │ │ 0 │ sstore(sc, mul(div(mload(mc), mask), mask)) │ │ 0 │ } │ │ 0 │ } │ │ 0 │ } │ │ 0 │ │ │ 120 │ function slice(bytes _bytes, uint _start, uint _length) internal pure returns (bytes) { │ │ 328 │ require(_bytes.length >= (_start + _length)); │ │ 0 │ │ │ 24 │ bytes memory tempBytes; │ │ 0 │ │ │ 0 │ assembly { │ │ 56 │ switch iszero(_length) │ │ 160 │ case 0 { │ │ 0 │ // Get a location of some free memory and store it in tempBytes as │ │ 0 │ // Solidity does for memory variables. │ │ 88 │ tempBytes := mload(0x40) │ │ 0 │ │ │ 0 │ // The first word of the slice result is potentially a partial │ │ 0 │ // word read from the original array. To read it, we calculate │ │ 0 │ // the length of that partial word and start copying that many │ │ 0 │ // bytes into the array. The first word we copy will start with │ │ 0 │ // data we don't care about, but the last `lengthmod` bytes will │ │ 0 │ // land at the beginning of the contents of the new array. When │ │ 0 │ // we're done copying, we overwrite the full first word with │ │ 0 │ // the actual length of the slice. │ │ 72 │ let lengthmod := and(_length, 31) │ │ 0 │ │ │ 0 │ // The multiplication in the next line is necessary │ │ 0 │ // because when slicing multiples of 32 bytes (lengthmod == 0) │ │ 0 │ // the following copy loop was copying the origin's length │ │ 0 │ // and then ending prematurely not copying everything it should. │ │ 208 │ let mc := add(add(tempBytes, lengthmod), mul(0x20, iszero(lengthmod))) │ │ 72 │ let end := add(mc, _length) │ │ 0 │ │ │ 480 │ for { │ │ 0 │ // The multiplication in the next line has the same exact purpose │ │ 0 │ // as the one above. │ │ 256 │ let cc := add(add(add(_bytes, lengthmod), mul(0x20, iszero(lengthmod))), _start) │ │ 180 │ } lt(mc, end) { │ │ 180 │ mc := add(mc, 0x20) │ │ 36 │ cc := add(cc, 0x20) │ │ 0 │ } { │ │ 144 │ mstore(mc, mload(cc)) │ │ 0 │ } │ │ 0 │ │ │ 72 │ mstore(tempBytes, _length) │ │ 0 │ │ │ 0 │ //update free-memory pointer │ │ 0 │ //allocating the array padded to 32 bytes like the compiler does now │ │ 120 │ mstore(0x40, and(add(mc, 31), not(31))) │ │ 0 │ } │ │ 0 │ //if we want a zero-length slice let's just return a zero-length array │ │ 0 │ default { │ │ 0 │ tempBytes := mload(0x40) │ │ 0 │ │ │ 0 │ mstore(0x40, add(tempBytes, 0x20)) │ │ 0 │ } │ │ 0 │ } │ │ 0 │ │ │ 24 │ return tempBytes; │ │ 0 │ } │ │ 0 │ │ │ 0 │ function toAddress(bytes _bytes, uint _start) internal pure returns (address) { │ │ 0 │ require(_bytes.length >= (_start + 20)); │ │ 0 │ address tempAddress; │ │ 0 │ │ │ 0 │ assembly { │ │ 0 │ tempAddress := div(mload(add(add(_bytes, 0x20), _start)), 0x1000000000000000000000000) │ │ 0 │ } │ │ 0 │ │ │ 0 │ return tempAddress; │ │ 0 │ } │ │ 0 │ │ │ 0 │ function toUint8(bytes _bytes, uint _start) internal pure returns (uint8) { │ │ 0 │ require(_bytes.length >= (_start + 1)); │ │ 0 │ uint8 tempUint; │ │ 0 │ │ │ 0 │ assembly { │ │ 0 │ tempUint := mload(add(add(_bytes, 0x1), _start)) │ │ 0 │ } │ │ 0 │ │ │ 0 │ return tempUint; │ │ 0 │ } │ │ 0 │ │ │ 0 │ function toUint16(bytes _bytes, uint _start) internal pure returns (uint16) { │ │ 0 │ require(_bytes.length >= (_start + 2)); │ │ 0 │ uint16 tempUint; │ │ 0 │ │ │ 0 │ assembly { │ │ 0 │ tempUint := mload(add(add(_bytes, 0x2), _start)) │ │ 0 │ } │ │ 0 │ │ │ 0 │ return tempUint; │ │ 0 │ } │ │ 0 │ │ │ 0 │ function toUint32(bytes _bytes, uint _start) internal pure returns (uint32) { │ │ 0 │ require(_bytes.length >= (_start + 4)); │ │ 0 │ uint32 tempUint; │ │ 0 │ │ │ 0 │ assembly { │ │ 0 │ tempUint := mload(add(add(_bytes, 0x4), _start)) │ │ 0 │ } │ │ 0 │ │ │ 0 │ return tempUint; │ │ 0 │ } │ │ 0 │ │ │ 0 │ function toUint(bytes _bytes, uint _start) internal pure returns (uint256) { │ │ 0 │ require(_bytes.length >= (_start + 32)); │ │ 0 │ uint256 tempUint; │ │ 0 │ │ │ 0 │ assembly { │ │ 0 │ tempUint := mload(add(add(_bytes, 0x20), _start)) │ │ 0 │ } │ │ 0 │ │ │ 0 │ return tempUint; │ │ 0 │ } │ │ 0 │ │ │ 0 │ function toBytes32(bytes _bytes, uint _start) internal pure returns (bytes32) { │ │ 0 │ require(_bytes.length >= (_start + 32)); │ │ 0 │ bytes32 tempBytes32; │ │ 0 │ │ │ 0 │ assembly { │ │ 0 │ tempBytes32 := mload(add(add(_bytes, 0x20), _start)) │ │ 0 │ } │ │ 0 │ │ │ 0 │ return tempBytes32; │ │ 0 │ } │ │ 0 │ │ │ 0 │ function equal(bytes memory _preBytes, bytes memory _postBytes) internal pure returns (bool) { │ │ 0 │ bool success = true; │ │ 0 │ │ │ 0 │ assembly { │ │ 0 │ let length := mload(_preBytes) │ │ 0 │ │ │ 0 │ // if lengths don't match the arrays are not equal │ │ 0 │ switch eq(length, mload(_postBytes)) │ │ 0 │ case 1 { │ │ 0 │ // cb is a circuit breaker in the for loop since there's │ │ 0 │ // no said feature for inline assembly loops │ │ 0 │ // cb = 1 - don't breaker │ │ 0 │ // cb = 0 - break │ │ 0 │ let cb := 1 │ │ 0 │ │ │ 0 │ let mc := add(_preBytes, 0x20) │ │ 0 │ let end := add(mc, length) │ │ 0 │ │ │ 0 │ for { │ │ 0 │ let cc := add(_postBytes, 0x20) │ │ 0 │ // the next line is the loop condition: │ │ 0 │ // while(uint(mc < end) + cb == 2) │ │ 0 │ } eq(add(lt(mc, end), cb), 2) { │ │ 0 │ mc := add(mc, 0x20) │ │ 0 │ cc := add(cc, 0x20) │ │ 0 │ } { │ │ 0 │ // if any of these checks fails then arrays are not equal │ │ 0 │ if iszero(eq(mload(mc), mload(cc))) { │ │ 0 │ // unsuccess: │ │ 0 │ success := 0 │ │ 0 │ cb := 0 │ │ 0 │ } │ │ 0 │ } │ │ 0 │ } │ │ 0 │ default { │ │ 0 │ // unsuccess: │ │ 0 │ success := 0 │ │ 0 │ } │ │ 0 │ } │ │ 0 │ │ │ 0 │ return success; │ │ 0 │ } │ │ 0 │ │ │ 0 │ function equalStorage(bytes storage _preBytes, bytes memory _postBytes) internal view returns (bool) { │ │ 0 │ bool success = true; │ │ 0 │ │ │ 0 │ assembly { │ │ 0 │ // we know _preBytes_offset is 0 │ │ 0 │ let fslot := sload(_preBytes_slot) │ │ 0 │ // Decode the length of the stored array like in concatStorage(). │ │ 0 │ let slength := div(and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 2) │ │ 0 │ let mlength := mload(_postBytes) │ │ 0 │ │ │ 0 │ // if lengths don't match the arrays are not equal │ │ 0 │ switch eq(slength, mlength) │ │ 0 │ case 1 { │ │ 0 │ // slength can contain both the length and contents of the array │ │ 0 │ // if length < 32 bytes so let's prepare for that │ │ 0 │ // v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage │ │ 0 │ if iszero(iszero(slength)) { │ │ 0 │ switch lt(slength, 32) │ │ 0 │ case 1 { │ │ 0 │ // blank the last byte which is the length │ │ 0 │ fslot := mul(div(fslot, 0x100), 0x100) │ │ 0 │ │ │ 0 │ if iszero(eq(fslot, mload(add(_postBytes, 0x20)))) { │ │ 0 │ // unsuccess: │ │ 0 │ success := 0 │ │ 0 │ } │ │ 0 │ } │ │ 0 │ default { │ │ 0 │ // cb is a circuit breaker in the for loop since there's │ │ 0 │ // no said feature for inline assembly loops │ │ 0 │ // cb = 1 - don't breaker │ │ 0 │ // cb = 0 - break │ │ 0 │ let cb := 1 │ │ 0 │ │ │ 0 │ // get the keccak hash to get the contents of the array │ │ 0 │ mstore(0x0, _preBytes_slot) │ │ 0 │ let sc := keccak256(0x0, 0x20) │ │ 0 │ │ │ 0 │ let mc := add(_postBytes, 0x20) │ │ 0 │ let end := add(mc, mlength) │ │ 0 │ │ │ 0 │ // the next line is the loop condition: │ │ 0 │ // while(uint(mc < end) + cb == 2) │ │ 0 │ for {} eq(add(lt(mc, end), cb), 2) { │ │ 0 │ sc := add(sc, 1) │ │ 0 │ mc := add(mc, 0x20) │ │ 0 │ } { │ │ 0 │ if iszero(eq(sload(sc), mload(mc))) { │ │ 0 │ // unsuccess: │ │ 0 │ success := 0 │ │ 0 │ cb := 0 │ │ 0 │ } │ │ 0 │ } │ │ 0 │ } │ │ 0 │ } │ │ 0 │ } │ │ 0 │ default { │ │ 0 │ // unsuccess: │ │ 0 │ success := 0 │ │ 0 │ } │ │ 0 │ } │ │ 0 │ │ │ 0 │ return success; │ │ 0 │ } │ │ 0 │ } │ │ 0 │ └───┴────────┘ File contracts/0.4.24/test_helpers/ValidatorRegistrationMock.sol ┌───┬────────┐ │ C │ GAS │ ├───┼────────┤ │ │ 0 │ pragma solidity 0.4.24; │ │ 0 │ │ │ 0 │ import "../interfaces/IValidatorRegistration.sol"; │ │ 0 │ │ │ 0 │ │ │ 0 │ /** │ │ 0 │ * @dev This is a mock. Don't use in production. │ │ 0 │ */ │ │ 367640 │ contract ValidatorRegistrationMock is IValidatorRegistration { │ │ 0 │ struct Call { │ │ 0 │ bytes pubkey; │ │ 0 │ bytes withdrawal_credentials; │ │ 0 │ bytes signature; │ │ 0 │ bytes32 deposit_data_root; │ │ 0 │ uint256 value; │ │ 0 │ } │ │ 0 │ │ │ 0 │ Call[] public calls; │ │ 0 │ │ │ 254 │ function deposit( │ │ 0 │ bytes /* 48 */ pubkey, │ │ 0 │ bytes /* 32 */ withdrawal_credentials, │ │ 0 │ bytes /* 96 */ signature, │ │ 0 │ bytes32 deposit_data_root │ │ 0 │ ) │ │ 0 │ external │ │ 0 │ payable │ │ 0 │ { │ │ 81688 │ calls.push(Call(pubkey, withdrawal_credentials, signature, deposit_data_root, msg.value)); │ │ 0 │ } │ │ 0 │ │ │ 72 │ function totalCalls() external view returns (uint256) { │ │ 0 │ return calls.length; │ │ 0 │ } │ │ 0 │ │ │ 0 │ function reset() external { │ │ 0 │ calls.length = 0; │ │ 0 │ } │ │ 0 │ } │ │ 0 │ └───┴────────┘ Lines marked with + contain calls to other contracts, and gas usage of such lines includes the gas spent by the called code.