Pool
0x7B6D1201A9e98B16EFef58CC42afFeb8D805d120
The Pool contract handles the transfer of user funds into the LEEQUID protocol. Afterwards, it expects a transaction submitted by the Oracles, which are responsible for choosing the node operator and composing all the necessary data to register a new validator. This set of data is called the deposit data and it will be used to register the validator in the Proof of Stake protocol. The deposit data structure must contain:
Key features
Stake Management
The Pool contract provides functionality for staking. Users can send LYX to the contract using various methods (stake
, stakeOnBehalf
, stakeWithPartner
, stakeWithPartnerOnBehalf
, stakeWithReferrer
, stakeWithReferrerOnBehalf
). All these methods are non-reentrant, which means they prevent reentrancy attacks - a common smart contract vulnerability.
Validator Registration
The contract facilitates registering validators with the LUKSO Deposit Contract. It keeps track of the total number of activated validators and exited validators. It also manages the scheduling of activation and deactivation of validators.
Deposit Tracking
The Pool contract keeps track of the total pending validators and the minimum amount of LYX deposit that is considered for the activation period. It also maintains a limit on the pending validators percentage, a variable called pendingValidatorsLimit. If such limit is not exceeded, tokens can be minted immediately. This promotes efficient use of the staking pool and rewards early stakers.
Upgradeability and access control
The contract uses OwnablePausableUpgradeable
to provide the ability to pause functions during contract upgrades or in case of any suspicious activity. This improves the security of the contract and provides a way to prevent malicious activities.
Secure execution
The contract employs OpenZeppelin's ReentrancyGuardUpgradeable
contract to prevent re-entrancy attacks. This ensures that all internal calls are completed before the control flow is returned to the external caller.
The flow of registering validators
Once the oracles have noticed more than 32 LYX standing in the Pool contract, they will generate the data for a new validator, before passing it to the Pool contract in the deposit transaction. The Pool contract will register the validator on the LUKSO deposit contract, along with the LYX deposit.
Notice that the variable validatorRegistration, used in the code block above, is defined in the beginning of the Pool contract as:
This variable represents the LUKSO deposit contract, and that's why the Pool contract calls the deposit function on it, passing as a value VALIDATOR_TOTAL_DEPOSIT, which is, as expected, defined as 32 LYX:
Notice that because LUKSO is a fork of Ethereum, it inherits the solidity keywords, which include those for units such as ether and wei.
The LYX pathway inside the Pool contract
LYX enters the Pool contract via the payable stake function:
The transaction that the LEEQUID web app creates when you push the stake button is addressed to the Pool contract and to this specific part of the contract, triggering a cascade of conditions which determine different outcomes for the function call:
The minActivatingDeposit and the pendingValidators variables control the mechanism that queues incoming deposits if there’s too much unactivated stake (LYX waiting in line to become part of an active validator). This formula in particular:
...prevents what is called "socialization of rewards", or in other words, reward dilution due to big amounts of LYX flowing into the protocol. You can get an overview of this issue in the Staking section of this documentation.
Understanding multipliers as a workaround to floating point arithmetic
In the above formula, pendingValidatorsLimit acts as a multiplier, just as 1e4 does. This is due to solidity not handling floating point calculations (operations with numbers containing a decimal part). Thus, this math is necessary in order to obtain a fractional value of the total number of validators. In other words, we cannot multiply a number by 10% (0.1) in solidity. In order to do that, we must multiply all numbers in a formula by a base factor. The one you see in the code is 1e4, which corresponds to the number 10000 in decimal notation and allows us a precision of 2 decimals (0,01%). When we want to take a fraction of a number, instead of multiplying it by the base factor 10000, we multiply it, for example, by a number which is 10 times less, 10% of the base factor.
The variable pendingValidatorsLimit is set to 1000, 10% of 1e4. When applied as a multiplier to effectiveValidators, we get 10% of the effectiveValidators, i.e, 10% of the validators currently active and validating on-chain.
Last updated