The `StakingMaster` contract is the central contract of the staking system. It handles the logic for users staking ETH, managing their stakes,unstake and interacting with the `CLETH` token and individual `StakeHolder` contracts.
Functions
stake():
Users can put their ETH into this system by using the stake() function. When they do this, the system keeps track of how much ETH they've put in and gives them special tokens called CLETH tokens as a reward. This function also creates a separate place for each user to keep their ETH safe within the system.
functionstake() publicpayable {require(msg.value >0,"Must send ETH to stake"); StakeHolder stakeHolder = stakeHolders[msg.sender];if (address(stakeHolder) ==address(0)) {// Create aaaaa new StakeHolder contract stakeHolder =new StakeHolder{value: msg.value}(msg.sender,address(this)); stakeHolders[msg.sender] = stakeHolder; } else {// Send the ETH to the existing StakeHolder contract (bool success, ) =address(stakeHolder).call{value: msg.value}("");require(success,"Failed to send ETH to StakeHolder"); } stakedBalance[msg.sender] += msg.value; lastStakeTime[msg.sender] = block.timestamp; totalPool += msg.value; userHistory[msg.sender].push(UserAction(ActionType.Staked, msg.value, block.timestamp)); clethToken.mint(msg.sender, msg.value);emitStaked(msg.sender, stakeHolder, msg.value); }
unstake(uint256 amount):
When users want to withdraw their ETH, they use the unstake() function. But they can only do this after a certain amount of time has passed. Once that time is up, they can take their ETH back, and the CLETH tokens they received earlier will be burned.
The addToWhitelist() function allows the owner to include a user in the whitelist, enabling them to participate in staking and unstaking activities within the system. It is restricted to the owner to maintain control over who can access these features.
With the addToBlacklist() function, the owner can designate a user to be part of the blacklist, preventing them from engaging in staking activities within the system. This authority is exclusive to the owner role to regulate user access.
removeFromWhitelist(address user) and removeFromBlacklist(address user):
Using removeFromWhitelist() and removeFromBlacklist() functions, the owner can remove a user from either the whitelist or the blacklist. This action reverts the user's status to 'Unknown', reinstating their ability to potentially participate based on the system's criteria.
The transferOwnership() function allows the current owner to transfer ownership of the contract to a new address. This action should be performed cautiously as it hands over control and management rights to the specified new owner.
functiontransferOwnership(address newOwner) publiconlyOwner {require(newOwner !=address(0),"New owner cannot be the zero address."); owner = newOwner; }
Utility Functions:
The 'Get Staked Balance' function reveals the amount of Ethereum an individual user has invested in the system, while the 'Get Last Stake Time' function discloses the precise time of the user's last investment.
On a broader scale, the 'Get Total Pool' function offers insight into the cumulative Ethereum amassed from all users within the contract.
Additionally, the 'Get Stake Holder Information' function furnishes comprehensive details about an individual user's stake, encompassing both the quantity of Ethereum invested and the timestamp of their most recent investment.
functiongetStakedBalance(address account) publicviewreturns (uint256) {return stakedBalance[account]; }functiongetLastStakeTime(address account) publicviewreturns (uint256) {return lastStakeTime[account]; }functiongetTotalPool() publicviewreturns (uint256) {return totalPool; }functiongetStakeHolderInfo(address user) publicviewreturns (address,uint256) { StakeHolder stakeHolder = stakeHolders[user];// Ensure the user has a StakeHolder contractrequire(address(stakeHolder) !=address(0),"User has no stake holder contract");// The address of the StakeHolder contractaddress stakeHolderAddress =address(stakeHolder);// The amount of ETH stored in the StakeHolder contractuint256 stakeHolderBalance =address(stakeHolder).balance;return (stakeHolderAddress, stakeHolderBalance); }
depositToNodeOperators
The "depositToNodeOperators" function serves the purpose of facilitating ETH deposits to Node Operators. This function operates within a smart contract and is designed with certain restrictions and functionalities:
Role and Access Control: The function is restricted to only be called by the owner of the contract, ensuring that only authorized personnel, usually the contract owner, can initiate deposits to the staking service.
Function Signature: The function is named "depositToNodeOperators" and is designed to be callable from outside the contract (external). Additionally, it has a custom modifier called "onlyOwner" to restrict usage to the contract's owner.
/functionsetNodeOperatorsDepositor(address_NodeOperatorsDepositor) externalonlyOwner { NodeOperatorsDepositor =INodeOperatorsEth2Depositor(_NodeOperatorsDepositor); }functiondepositToNodeOperators(address payable stakeHolderAddress,stringcalldata pubkey,stringcalldata withdrawal_credentials,stringcalldata signature,stringcalldata deposit_data_root ) externalonlyOwner { StakeHolder stakeHolder =StakeHolder(stakeHolderAddress);// Convert strings to bytesbytesmemory pubkeyBytes =hexStringToBytes(pubkey);bytesmemory withdrawalCredentialsBytes =hexStringToBytes(withdrawal_credentials);bytesmemory signatureBytes =hexStringToBytes(signature);bytes32 depositDataRootBytes32 =hexStringToBytes32(deposit_data_root);// Create single-element arraysbytes[] memory pubkeys =newbytes[](1); pubkeys[0] = pubkeyBytes;bytes[] memory withdrawalCredentials =newbytes[](1); withdrawalCredentials[0] = withdrawalCredentialsBytes;bytes[] memory signatures =newbytes[](1); signatures[0] = signatureBytes;bytes32[] memory depositDataRoots =newbytes32[](1); depositDataRoots[0] = depositDataRootBytes32;uint256 depositAmount =32ether; // Assuming 32 ETH per depositrequire(address(stakeHolder).balance >= depositAmount,"Insufficient funds in StakeHolder"); stakeHolder.transferETHToContract(depositAmount,payable(address(this)));require(address(this).balance >= depositAmount,"Insufficient ETH for deposit"); NodeOperatorsDepositor.deposit{value: depositAmount}( pubkeys, withdrawalCredentials, signatures, depositDataRoots ); }functionhexStringToBytes(stringmemory hexString) internalpurereturns (bytesmemory) {bytesmemory b =bytes(hexString);require(b.length % 2==0,"String must have an even number of characters");bytesmemory bstr =newbytes(b.length /2);for (uint i =0; i < b.length /2; i++) {uint8 hi =uint8(b[i *2]);uint8 lo =uint8(b[i *2+1]); bstr[i] =bytes1((hi <<4) | lo); }return bstr; }functionhexStringToBytes32(stringmemory source) internalpurereturns (bytes32 result) {bytesmemory tempEmptyStringTest =bytes(source);require(tempEmptyStringTest.length <=32,"String too long");assembly { result :=mload(add(source,32)) } }
Function Logic
The function executes the following steps:
Converts the hexadecimal string parameters (pubkey, withdrawal_credentials, signature, deposit_data_root) into bytes or bytes32 format using helper functions.
Stores the converted byte arrays in single-element arrays (pubkeys, withdrawalCredentials, signatures, depositDataRoots) as the NodeOperators staking function expects arrays of these parameters.
Calculates the deposit amount (32 ETH for each deposit, a standard in Ethereum 2.0 staking) and checks if the StakeHolder contract holds enough ETH.
Calls the stakeHolder.transferETHToContract function to transfer the required amount of ETH from the StakeHolder contract to the current contract for further processing or staking by the NodeOperators service.