Smart Staking Liquidity Pool Model Solidity, Web3

👤 Sharing: AI
```solidity
pragma solidity ^0.8.0;

// SPDX-License-Identifier: MIT

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/math/SafeMath.sol";

/**
 * @title Smart Staking Liquidity Pool
 * @dev  This contract implements a basic staking pool with liquidity provision.
 *       Users can deposit LP tokens (assumed ERC20) into the pool to earn rewards.
 *       The rewards are distributed based on the amount of LP tokens staked and the time staked.
 *       The contract also includes basic governance features like setting rewards per block.
 *       This contract requires an ERC20 compatible LP token to be already deployed.
 */
contract SmartStakingLiquidityPool is Ownable {
    using SafeMath for uint256;

    // --- Structs ---

    /**
     * @dev Represents a user's staking information.
     */
    struct UserInfo {
        uint256 amount;    // Amount of LP tokens staked.
        uint256 rewardDebt; // Accumulated reward debt.
        uint256 stakedTime; // Timestamp when the user staked.
    }

    // --- State Variables ---

    IERC20 public lpToken;       // The LP token contract.
    IERC20 public rewardToken;   // The reward token contract.
    uint256 public rewardPerBlock; // Reward tokens distributed per block.
    uint256 public lastRewardBlock; // Last block when rewards were distributed.
    uint256 public accRewardPerShare; // Accumulated rewards per share of LP tokens.

    mapping(address => UserInfo) public userInfo; // Mapping from user address to user info.
    uint256 public totalStaked;        // Total LP tokens staked in the pool.

    event Deposit(address indexed user, uint256 amount);
    event Withdraw(address indexed user, uint256 amount);
    event RewardPaid(address indexed user, uint256 reward);
    event RewardPerBlockUpdated(uint256 newRewardPerBlock);

    // --- Constructor ---

    /**
     * @param _lpToken Address of the LP token contract.
     * @param _rewardToken Address of the reward token contract.
     * @param _rewardPerBlock Initial reward tokens distributed per block.
     */
    constructor(
        address _lpToken,
        address _rewardToken,
        uint256 _rewardPerBlock
    )  {
        lpToken = IERC20(_lpToken);
        rewardToken = IERC20(_rewardToken);
        rewardPerBlock = _rewardPerBlock;
        lastRewardBlock = block.number;
    }

    // --- Modifiers ---

    /**
     * @dev Modifier to ensure sufficient balance of reward tokens in the contract.
     */
    modifier hasSufficientReward() {
        require(rewardToken.balanceOf(address(this)) >= (block.number - lastRewardBlock) * rewardPerBlock, "Insufficient reward balance.");
        _;
    }

    // --- Core Functions ---

    /**
     * @dev Updates the accumulated rewards per share.  This is crucial for fair reward distribution.
     */
    function updatePool() internal {
        if (block.number <= lastRewardBlock) {
            return;
        }
        if (totalStaked == 0) {
            lastRewardBlock = block.number;
            return;
        }

        uint256 multiplier = block.number.sub(lastRewardBlock);
        uint256 reward = multiplier.mul(rewardPerBlock);
        accRewardPerShare = accRewardPerShare.add(reward.mul(1e12).div(totalStaked)); // use 1e12 for better precision
        lastRewardBlock = block.number;
    }

    /**
     * @dev Calculates the pending rewards for a user.
     * @param _user Address of the user.
     * @return The amount of reward tokens the user is entitled to.
     */
    function pendingReward(address _user) public view returns (uint256) {
        UserInfo storage user = userInfo[_user];
        uint256 accReward = accRewardPerShare;
        uint256 stakedAmount = user.amount;

        if (block.number > lastRewardBlock && totalStaked != 0) {
            uint256 multiplier = block.number.sub(lastRewardBlock);
            uint256 reward = multiplier.mul(rewardPerBlock);
            accReward = accRewardPerShare.add(reward.mul(1e12).div(totalStaked));
        }

        return stakedAmount.mul(accReward).div(1e12).sub(user.rewardDebt); // use 1e12 precision again
    }

    /**
     * @dev Deposits LP tokens into the pool.
     * @param _amount Amount of LP tokens to deposit.
     */
    function deposit(uint256 _amount) external hasSufficientReward {
        require(_amount > 0, "Cannot deposit zero amount.");

        updatePool();

        UserInfo storage user = userInfo[msg.sender];
        uint256 pending = pendingReward(msg.sender);

        if (pending > 0) {
            safeRewardTransfer(msg.sender, pending);
            emit RewardPaid(msg.sender, pending);
        }

        if (user.amount > 0) {
            user.rewardDebt = user.amount.mul(accRewardPerShare).div(1e12); // use 1e12 precision
        }

        lpToken.transferFrom(msg.sender, address(this), _amount);
        user.amount = user.amount.add(_amount);
        totalStaked = totalStaked.add(_amount);
        user.stakedTime = block.timestamp;
        user.rewardDebt = user.amount.mul(accRewardPerShare).div(1e12); // update reward debt for new amount

        emit Deposit(msg.sender, _amount);
    }

    /**
     * @dev Withdraws LP tokens from the pool.
     * @param _amount Amount of LP tokens to withdraw.
     */
    function withdraw(uint256 _amount) external hasSufficientReward {
        require(_amount > 0, "Cannot withdraw zero amount.");

        updatePool();

        UserInfo storage user = userInfo[msg.sender];
        require(user.amount >= _amount, "Insufficient staked balance.");

        uint256 pending = pendingReward(msg.sender);
        if (pending > 0) {
            safeRewardTransfer(msg.sender, pending);
            emit RewardPaid(msg.sender, pending);
        }


        user.rewardDebt = user.amount.mul(accRewardPerShare).div(1e12); // update reward debt before withdraw

        user.amount = user.amount.sub(_amount);
        totalStaked = totalStaked.sub(_amount);

        lpToken.transfer(msg.sender, _amount);
        user.rewardDebt = user.amount.mul(accRewardPerShare).div(1e12); // update again if some amount remains

        emit Withdraw(msg.sender, _amount);
    }

    /**
     * @dev Withdraws all LP tokens from the pool. Convenience function.
     */
    function withdrawAll() external {
        withdraw(userInfo[msg.sender].amount);
    }

    /**
     * @dev Emergency withdraw function. Withdraws LP tokens without rewarding. Useful in case of an exploit.
     */
    function emergencyWithdraw() external {
        UserInfo storage user = userInfo[msg.sender];
        uint256 amount = user.amount;
        user.amount = 0;
        user.rewardDebt = 0;
        totalStaked = totalStaked.sub(amount);

        lpToken.transfer(msg.sender, amount);
        emit Withdraw(msg.sender, amount);
    }

    /**
     * @dev Safe transfer of reward tokens to the user.
     * @param _to Address to transfer the tokens to.
     * @param _amount Amount of tokens to transfer.
     */
    function safeRewardTransfer(address _to, uint256 _amount) internal {
        uint256 rewardBalance = rewardToken.balanceOf(address(this));
        if (_amount > rewardBalance) {
            rewardToken.transfer(_to, rewardBalance);
        } else {
            rewardToken.transfer(_to, _amount);
        }
    }

    // --- Governance Functions ---

    /**
     * @dev Sets the reward tokens distributed per block.
     * @param _rewardPerBlock New reward tokens distributed per block.
     */
    function setRewardPerBlock(uint256 _rewardPerBlock) external onlyOwner {
        updatePool();
        rewardPerBlock = _rewardPerBlock;
        emit RewardPerBlockUpdated(_rewardPerBlock);
    }

    /**
     * @dev Adds reward tokens to the contract. Must be called by the owner.
     * @param _amount Amount of reward tokens to add.
     */
    function addReward(uint256 _amount) external onlyOwner {
        rewardToken.transferFrom(msg.sender, address(this), _amount);
    }

    /**
     * @dev Allows the owner to withdraw any remaining reward tokens from the contract.
     */
    function withdrawRemainingReward() external onlyOwner {
        uint256 balance = rewardToken.balanceOf(address(this));
        rewardToken.transfer(owner(), balance);
    }
}
```

**Explanation:**

1.  **`pragma solidity ^0.8.0;`**: Specifies the Solidity compiler version.  `^0.8.0` means any compiler version from 0.8.0 up to (but not including) 0.9.0.

2.  **`import "@openzeppelin/contracts/token/ERC20/IERC20.sol";`**: Imports the `IERC20` interface from OpenZeppelin, which defines the standard functions for interacting with ERC20 tokens.

3.  **`import "@openzeppelin/contracts/access/Ownable.sol";`**: Imports the `Ownable` contract from OpenZeppelin, providing a basic access control mechanism where only the owner can call certain functions.

4.  **`import "@openzeppelin/contracts/utils/math/SafeMath.sol";`**: Imports the `SafeMath` library from OpenZeppelin for performing safe arithmetic operations, preventing integer overflows and underflows.  While Solidity 0.8.0 has built-in overflow protection, this is still included for clarity and potential compatibility with older compiler versions.

5.  **`contract SmartStakingLiquidityPool is Ownable { ... }`**: Defines the main contract, inheriting from `Ownable`.

6.  **`using SafeMath for uint256;`**:  Enables the `SafeMath` library's functions to be used directly on `uint256` variables.

7.  **`struct UserInfo { ... }`**: Defines a struct to store user-specific information:
    *   `amount`:  The amount of LP tokens the user has staked.
    *   `rewardDebt`:  A crucial variable for tracking the user's accumulated reward debt. This prevents rounding errors from affecting reward distribution. It represents the user's share of rewards *already* accounted for.
    *   `stakedTime`:  The timestamp when the user staked (or last deposited).

8.  **State Variables:**
    *   `IERC20 public lpToken;`: The address of the LP token (the token being staked).  It's declared as `IERC20` so the contract can interact with it using the ERC20 interface.
    *   `IERC20 public rewardToken;`:  The address of the reward token (the token users receive for staking).
    *   `uint256 public rewardPerBlock;`: The amount of reward tokens distributed per block.
    *   `uint256 public lastRewardBlock;`: The block number of the last reward distribution.
    *   `uint256 public accRewardPerShare;`: Accumulated reward per share of LP tokens staked.  This is the core mechanism for distributing rewards fairly. It represents the total reward distributed divided by the total LP tokens staked.
    *   `mapping(address => UserInfo) public userInfo;`:  A mapping that stores the `UserInfo` for each user.
    *   `uint256 public totalStaked;`:  The total amount of LP tokens staked in the pool.

9.  **Events:**  `Deposit`, `Withdraw`, `RewardPaid`, and `RewardPerBlockUpdated` events are emitted to log important actions.

10. **`constructor(address _lpToken, address _rewardToken, uint256 _rewardPerBlock) { ... }`**:  The constructor initializes the contract:
    *   Sets the `lpToken`, `rewardToken`, and `rewardPerBlock` variables.
    *   Sets the `lastRewardBlock` to the current block number.

11. **`modifier hasSufficientReward() { ... }`**: A modifier that checks if the contract has enough reward tokens to distribute based on the time elapsed since the last reward distribution and the `rewardPerBlock`.  Prevents reward distribution if the contract is out of reward tokens.

12. **`updatePool() internal { ... }`**: This is the heart of the reward distribution logic. It updates the `accRewardPerShare` variable, which is used to calculate individual user rewards.
    *   It checks if any rewards have been issued since the last update and adjusts accordingly.
    *   It's called at the beginning of `deposit()` and `withdraw()` to ensure that the reward pool is up-to-date before any user actions.
    * It uses `1e12` for increased precision when calculating the accumulated rewards per share. This helps to minimize rounding errors.

13. **`pendingReward(address _user) public view returns (uint256) { ... }`**: Calculates the pending rewards for a given user *without* actually distributing them. This is a `view` function, meaning it doesn't modify the contract's state.

14. **`deposit(uint256 _amount) external hasSufficientReward { ... }`**: Allows users to deposit LP tokens into the pool:
    *   Requires the deposit amount to be greater than 0.
    *   Calls `updatePool()` to update the reward state.
    *   Calculates and transfers any pending rewards to the user.
    *   Transfers the deposited LP tokens from the user to the contract.
    *   Updates the user's `amount`, `stakedTime`, and `rewardDebt`.
    *   Increments `totalStaked`.

15. **`withdraw(uint256 _amount) external hasSufficientReward { ... }`**: Allows users to withdraw LP tokens from the pool:
    *   Requires the withdraw amount to be greater than 0.
    *   Calls `updatePool()` to update the reward state.
    *   Calculates and transfers any pending rewards to the user.
    *   Transfers the withdrawn LP tokens from the contract to the user.
    *   Updates the user's `amount` and `rewardDebt`.
    *   Decrements `totalStaked`.

16. **`withdrawAll() external { ... }`**:  A convenience function to withdraw all of a user's staked tokens.

17. **`emergencyWithdraw() external { ... }`**:  A function designed to allow users to withdraw their LP tokens quickly in case of an emergency or exploit. It does *not* distribute any rewards.

18. **`safeRewardTransfer(address _to, uint256 _amount) internal { ... }`**: A helper function to safely transfer reward tokens to a user. It checks if the contract has enough reward tokens and only transfers what's available.  Important to prevent the contract from failing if it doesn't have enough rewards.

19. **`setRewardPerBlock(uint256 _rewardPerBlock) external onlyOwner { ... }`**: Allows the owner to set the `rewardPerBlock`.

20. **`addReward(uint256 _amount) external onlyOwner { ... }`**: Allows the owner to add more reward tokens to the contract.

21. **`withdrawRemainingReward() external onlyOwner { ... }`**: Allows the owner to withdraw any remaining reward tokens from the contract.

**Key Concepts:**

*   **Staking Pool:** A smart contract that allows users to lock up their tokens (LP tokens in this case) in exchange for rewards.
*   **Liquidity Pool (Implied):**  This contract is designed to work *with* a liquidity pool. The `lpToken` represents tokens received for providing liquidity to another contract (e.g., a decentralized exchange like Uniswap or PancakeSwap).  The staking contract rewards users for providing liquidity elsewhere.
*   **Reward Distribution:** The core logic of this contract is the fair distribution of reward tokens.  The `accRewardPerShare` and `rewardDebt` variables are crucial for accurately tracking and distributing rewards.
*   **Governance:**  The `Ownable` contract provides basic governance, allowing only the owner to modify certain parameters like the `rewardPerBlock`.
*   **Security:** The use of `SafeMath` (or Solidity 0.8+ overflow protection) and the `safeRewardTransfer` function are important for security.  The `emergencyWithdraw` function provides a way for users to withdraw their funds quickly in case of an issue.
*   **Precision:**  The use of `1e12` (or any large number) for precision in the `accRewardPerShare` calculation is a common technique to reduce rounding errors in Solidity.  This is especially important when dealing with small amounts of tokens.

**How to Use (Conceptual):**

1.  **Deploy LP Token:**  Deploy an ERC20 compatible LP token contract.  This token represents a user's share in a liquidity pool (e.g., Uniswap v2 or PancakeSwap).
2.  **Deploy Reward Token:** Deploy another ERC20 token contract. This is the token that will be given as a reward for staking LP tokens.
3.  **Deploy Staking Contract:** Deploy the `SmartStakingLiquidityPool` contract, providing the addresses of the LP token and reward token contracts, and the initial reward per block.
4.  **Add Reward Tokens:**  The owner needs to transfer reward tokens to the `SmartStakingLiquidityPool` contract using the `addReward` function.
5.  **Users Deposit:** Users deposit their LP tokens into the staking contract using the `deposit` function. They must first approve the staking contract to spend their LP tokens using the `lpToken.approve(address(stakingContract), amount)` function.
6.  **Rewards Accumulate:** Rewards accumulate based on the `rewardPerBlock` and the amount of LP tokens staked.
7.  **Users Withdraw:** Users can withdraw their LP tokens and claim their accumulated rewards using the `withdraw` function.
8.  **Owner Updates Rewards:** The owner can update the `rewardPerBlock` as needed.

**Important Considerations:**

*   **Auditing:**  This contract should be thoroughly audited before being deployed to a production environment.
*   **Front-Running:**  Be aware of potential front-running attacks, especially when setting the `rewardPerBlock`.  Consider using a more complex governance mechanism to mitigate this risk.
*   **Reentrancy:** While the OpenZeppelin contracts used are generally reentrancy safe, always consider the possibility of reentrancy vulnerabilities when interacting with external contracts.
*   **Gas Costs:**  Reward distribution can be gas-intensive. Consider optimizing the contract for gas efficiency. The use of `1e12` helps but other optimizations may be needed.  Potentially batch operations.
*   **Immutability:** Once deployed, the contract's core logic is immutable.  Carefully consider the design and ensure it meets your requirements before deployment.

This example provides a solid foundation for a smart staking liquidity pool. You can extend it with more features, such as:

*   Tiered rewards based on staking duration.
*   Lockup periods for staked tokens.
*   Governance features like voting on reward parameters.
*   Fee structures for withdrawals.
*   Integration with oracles for dynamically adjusting rewards.
👁️ Viewed: 9

Comments