// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
pragma solidity ^0.8.20;
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/structs/EnumerableSet.sol)
// This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.
pragma solidity ^0.8.20;
/**
* @dev Library for managing
* https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
* types.
*
* Sets have the following properties:
*
* - Elements are added, removed, and checked for existence in constant time
* (O(1)).
* - Elements are enumerated in O(n). No guarantees are made on the ordering.
*
* ```solidity
* contract Example {
* // Add the library methods
* using EnumerableSet for EnumerableSet.AddressSet;
*
* // Declare a set state variable
* EnumerableSet.AddressSet private mySet;
* }
* ```
*
* As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
* and `uint256` (`UintSet`) are supported.
*
* [WARNING]
* ====
* Trying to delete such a structure from storage will likely result in data corruption, rendering the structure
* unusable.
* See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
*
* In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an
* array of EnumerableSet.
* ====
*/
library EnumerableSet {
// To implement this library for multiple types with as little code
// repetition as possible, we write it in terms of a generic Set type with
// bytes32 values.
// The Set implementation uses private functions, and user-facing
// implementations (such as AddressSet) are just wrappers around the
// underlying Set.
// This means that we can only create new EnumerableSets for types that fit
// in bytes32.
struct Set {
// Storage of set values
bytes32[] _values;
// Position is the index of the value in the `values` array plus 1.
// Position 0 is used to mean a value is not in the set.
mapping(bytes32 value => uint256) _positions;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function _add(Set storage set, bytes32 value) private returns (bool) {
if (!_contains(set, value)) {
set._values.push(value);
// The value is stored at length-1, but we add 1 to all indexes
// and use 0 as a sentinel value
set._positions[value] = set._values.length;
return true;
} else {
return false;
}
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function _remove(Set storage set, bytes32 value) private returns (bool) {
// We cache the value's position to prevent multiple reads from the same storage slot
uint256 position = set._positions[value];
if (position != 0) {
// Equivalent to contains(set, value)
// To delete an element from the _values array in O(1), we swap the element to delete with the last one in
// the array, and then remove the last element (sometimes called as 'swap and pop').
// This modifies the order of the array, as noted in {at}.
uint256 valueIndex = position - 1;
uint256 lastIndex = set._values.length - 1;
if (valueIndex != lastIndex) {
bytes32 lastValue = set._values[lastIndex];
// Move the lastValue to the index where the value to delete is
set._values[valueIndex] = lastValue;
// Update the tracked position of the lastValue (that was just moved)
set._positions[lastValue] = position;
}
// Delete the slot where the moved value was stored
set._values.pop();
// Delete the tracked position for the deleted slot
delete set._positions[value];
return true;
} else {
return false;
}
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function _contains(Set storage set, bytes32 value) private view returns (bool) {
return set._positions[value] != 0;
}
/**
* @dev Returns the number of values on the set. O(1).
*/
function _length(Set storage set) private view returns (uint256) {
return set._values.length;
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function _at(Set storage set, uint256 index) private view returns (bytes32) {
return set._values[index];
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function _values(Set storage set) private view returns (bytes32[] memory) {
return set._values;
}
// Bytes32Set
struct Bytes32Set {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _add(set._inner, value);
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _remove(set._inner, value);
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
return _contains(set._inner, value);
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(Bytes32Set storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
return _at(set._inner, index);
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
bytes32[] memory store = _values(set._inner);
bytes32[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
// AddressSet
struct AddressSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(AddressSet storage set, address value) internal returns (bool) {
return _add(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(AddressSet storage set, address value) internal returns (bool) {
return _remove(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(AddressSet storage set, address value) internal view returns (bool) {
return _contains(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(AddressSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(AddressSet storage set, uint256 index) internal view returns (address) {
return address(uint160(uint256(_at(set._inner, index))));
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(AddressSet storage set) internal view returns (address[] memory) {
bytes32[] memory store = _values(set._inner);
address[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
// UintSet
struct UintSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(UintSet storage set, uint256 value) internal returns (bool) {
return _add(set._inner, bytes32(value));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(UintSet storage set, uint256 value) internal returns (bool) {
return _remove(set._inner, bytes32(value));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(UintSet storage set, uint256 value) internal view returns (bool) {
return _contains(set._inner, bytes32(value));
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(UintSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(UintSet storage set, uint256 index) internal view returns (uint256) {
return uint256(_at(set._inner, index));
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(UintSet storage set) internal view returns (uint256[] memory) {
bytes32[] memory store = _values(set._inner);
uint256[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the value of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the value of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 value) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the
* allowance mechanism. `value` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 value) external returns (bool);
}
// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;
interface IRewardPool {
function sendRewards(uint256 amount, address to) external;
function fundPool(uint256 amount) external;
function withdrawRemainingTokens() external;
function getPooledLDAmount() external returns (uint256);
}
// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { IRewardPool } from "./Interfaces/IRewardPool.sol";
import { EnumerableSet } from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
contract LdStaking is Ownable {
error ZeroAddress(string);
error NotsufficientStake();
error StakeNotFound();
error InvalidAPR();
error StakingNotStarted();
error InsufficientRewardLiquidity();
error ClaimOrUnstakeWindowNotOpen();
using EnumerableSet for EnumerableSet.AddressSet;
struct Stake {
uint256 stakedLdAmount;
uint256 lastClaimed;
}
/**
* @param userAddress address of interacting user
* @param amountStaked amount staked by the user
*/
event Staked(address indexed userAddress, uint256 amountStaked);
/**
* @param userAddress address of interacting user
* @param amountUnstaked amount unstaked by the user
*/
event Unstake(address indexed userAddress, uint256 amountUnstaked);
/**
* @param userAddress address of interacting user
* @param rewardsClaimed amount of rewards claimed by the user
*/
event RewardClaimed(address indexed userAddress, uint256 rewardsClaimed);
/// LD token & pool interface instance
IERC20 public _ldToken;
IRewardPool public _rewardPool;
/// variable to toggle state of staking and claims
bool public lock;
/// APR rate[annual percentage rate of rewards for staking]
uint64 public _aprRate;
/// total staked LD tokens by all user
uint256 public _totalStakedLdAmount;
/// individual staker's stake record
mapping(address stakeHolder => Stake[] stakes) public _stakeByUser;
/**
* @param aprRate reward rate with extra 2 decimals eg 50% as 5000
* @param owner owner
* @param ldTokenAddress ld token address
*/
constructor(uint64 aprRate, address owner, address ldTokenAddress) Ownable(owner) {
if (ldTokenAddress == address(0)) revert ZeroAddress("LD token address can't be zero");
if (aprRate < 100) revert InvalidAPR();
_ldToken = IERC20(ldTokenAddress);
_aprRate = aprRate;
lock = true;
}
/**
* @notice This function opens staking for users
* @dev Can only be called by owner
*/
function enableStaking() external onlyOwner {
lock = false;
}
function disableStaking() external onlyOwner {
lock = true;
}
/**
* @notice This function sets the address for new pool contract
* @dev Can only be called by owner
* @param newPoolAddress Address of new pool contract
*/
function setPool(address newPoolAddress) external onlyOwner {
if (newPoolAddress == address(0)) revert ZeroAddress("Pool address can't be zero");
_rewardPool = IRewardPool(newPoolAddress);
}
/**
* @notice This function changes the APR rate
* @dev Can only be called by owner
* @param newApr New APR rate
*/
function changeApr(uint64 newApr) external onlyOwner {
if (newApr < 100) revert InvalidAPR();
_aprRate = newApr;
}
/**
* @notice This function returns amount of LD tokens staked by user
* @param _stakeHolder address of stake holder
*/
function getuserStake(address _stakeHolder, uint256 stakeIndex) external view returns (Stake memory) {
return _stakeByUser[_stakeHolder][stakeIndex];
}
/**
* @notice This function handles both cases of staking LD token first time and adding to exisiting stake
* @param amount Amount of LD tokens to be staked by user
*/
function stakeLd(uint256 amount) external {
if (lock) revert StakingNotStarted();
if (amount == 0) revert("Invalid amount");
Stake memory newStake = Stake({ stakedLdAmount: amount, lastClaimed: block.timestamp });
_totalStakedLdAmount += amount;
_stakeByUser[_msgSender()].push(newStake);
_ldToken.transferFrom(_msgSender(), address(this), amount);
emit Staked(_msgSender(), amount);
}
/**
* @notice This function can be used to fully or partially unstake LD tokens
* @param amount Amount of LD tokens to be unstaked
*/
function unstakeLD(uint256 amount, uint256 stakeIndex) external {
if (_stakeByUser[_msgSender()].length < stakeIndex + 1) revert StakeNotFound();
Stake memory userStake = _stakeByUser[_msgSender()][stakeIndex];
if (!canClaimOrUnstake(userStake.lastClaimed)) revert ClaimOrUnstakeWindowNotOpen();
if (userStake.stakedLdAmount < amount) revert NotsufficientStake();
userStake.stakedLdAmount -= amount;
_totalStakedLdAmount -= amount;
if (userStake.stakedLdAmount == 0) {
uint256 len = _stakeByUser[_msgSender()].length;
_stakeByUser[_msgSender()][stakeIndex] = _stakeByUser[_msgSender()][len - 1];
_stakeByUser[_msgSender()].pop();
} else {
_stakeByUser[_msgSender()][stakeIndex] = userStake;
}
_ldToken.transfer(_msgSender(), amount);
emit Unstake(_msgSender(), amount);
}
function reStake(uint256 stakeIndex) external {
if (_stakeByUser[_msgSender()].length < stakeIndex + 1) revert StakeNotFound();
Stake memory userStake = _stakeByUser[_msgSender()][stakeIndex];
if (!canClaimOrUnstake(userStake.lastClaimed)) revert ClaimOrUnstakeWindowNotOpen();
uint256 weeksStaked = (block.timestamp - userStake.lastClaimed) / 7 days;
uint256 rewardAmount = calculateRewards(weeksStaked, userStake.stakedLdAmount);
userStake.lastClaimed = block.timestamp;
userStake.stakedLdAmount += rewardAmount;
_stakeByUser[_msgSender()][stakeIndex] = userStake;
if (_rewardPool.getPooledLDAmount() < rewardAmount) revert InsufficientRewardLiquidity();
_rewardPool.sendRewards(rewardAmount, address(this));
emit RewardClaimed(_msgSender(), rewardAmount);
}
/**
* @notice Function for claiming weekly rewards
*/
function claimRewards(uint256 stakeIndex) external {
if (_stakeByUser[_msgSender()].length < stakeIndex + 1) revert StakeNotFound();
Stake memory userStake = _stakeByUser[_msgSender()][stakeIndex];
if (!canClaimOrUnstake(userStake.lastClaimed)) revert ClaimOrUnstakeWindowNotOpen();
uint256 weeksStaked = (block.timestamp - userStake.lastClaimed) / 7 days;
uint256 rewardAmount = calculateRewards(weeksStaked, userStake.stakedLdAmount);
userStake.lastClaimed = block.timestamp;
_stakeByUser[_msgSender()][stakeIndex] = userStake;
if (_rewardPool.getPooledLDAmount() < rewardAmount) revert InsufficientRewardLiquidity();
_rewardPool.sendRewards(rewardAmount, _msgSender());
emit RewardClaimed(_msgSender(), rewardAmount);
}
/// This function checks whether rewards are claimable or not
/// Rewards are only claimable after 7 days
/// Is user misses out the claiming he will need to claim at the end of next week
/// @param lastClaimedByUser timestamp of last reward claim by user
function canClaimOrUnstake(uint256 lastClaimedByUser) public view returns (bool) {
uint256 daysSinceLastClaim = (block.timestamp - lastClaimedByUser) / 1 days;
//on 8th day
if (daysSinceLastClaim == 7) {
return true;
} else if (daysSinceLastClaim > 7 && daysSinceLastClaim % 7 == 0) {
//on end of weeks
return true;
}
return false;
}
/// @param weeksstaked number of weeks
/// @param initPrinciple principle amount staked
/// this function follows a linear approach to calculate rewards
function calculateRewards(uint weeksstaked, uint initPrinciple) internal view returns (uint) {
uint principle = initPrinciple;
for (uint i = 0; i < weeksstaked; i++) {
// calculating reward as per APR for one week
uint cur = (principle * _aprRate) / 10000;
uint reward = cur / 52;
// adding the reward to principle amount so for next week it will be compounded
principle += reward;
}
// returning rewards by subtracting principle staked initially
return principle - initPrinciple;
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)
pragma solidity ^0.8.20;
import {Context} from "../utils/Context.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* The initial owner is set to the address provided by the deployer. This can
* later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
abstract contract Ownable is Context {
address private _owner;
/**
* @dev The caller account is not authorized to perform an operation.
*/
error OwnableUnauthorizedAccount(address account);
/**
* @dev The owner is not a valid owner account. (eg. `address(0)`)
*/
error OwnableInvalidOwner(address owner);
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the address provided by the deployer as the initial owner.
*/
constructor(address initialOwner) {
if (initialOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(initialOwner);
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
_checkOwner();
_;
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
return _owner;
}
/**
* @dev Throws if the sender is not the owner.
*/
function _checkOwner() internal view virtual {
if (owner() != _msgSender()) {
revert OwnableUnauthorizedAccount(_msgSender());
}
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby disabling any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
if (newOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
{
"compilationTarget": {
"contracts/LdStaking.sol": "LdStaking"
},
"evmVersion": "paris",
"libraries": {},
"metadata": {
"bytecodeHash": "none"
},
"optimizer": {
"enabled": true,
"runs": 800
},
"remappings": []
}
[{"inputs":[{"internalType":"uint64","name":"aprRate","type":"uint64"},{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"ldTokenAddress","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"ClaimOrUnstakeWindowNotOpen","type":"error"},{"inputs":[],"name":"InsufficientRewardLiquidity","type":"error"},{"inputs":[],"name":"InvalidAPR","type":"error"},{"inputs":[],"name":"NotsufficientStake","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"OwnableInvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"OwnableUnauthorizedAccount","type":"error"},{"inputs":[],"name":"StakeNotFound","type":"error"},{"inputs":[],"name":"StakingNotStarted","type":"error"},{"inputs":[{"internalType":"string","name":"","type":"string"}],"name":"ZeroAddress","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"userAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"rewardsClaimed","type":"uint256"}],"name":"RewardClaimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"userAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"amountStaked","type":"uint256"}],"name":"Staked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"userAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"amountUnstaked","type":"uint256"}],"name":"Unstake","type":"event"},{"inputs":[],"name":"_aprRate","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_ldToken","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_rewardPool","outputs":[{"internalType":"contract IRewardPool","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"stakeHolder","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"_stakeByUser","outputs":[{"internalType":"uint256","name":"stakedLdAmount","type":"uint256"},{"internalType":"uint256","name":"lastClaimed","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_totalStakedLdAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"lastClaimedByUser","type":"uint256"}],"name":"canClaimOrUnstake","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint64","name":"newApr","type":"uint64"}],"name":"changeApr","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"stakeIndex","type":"uint256"}],"name":"claimRewards","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"disableStaking","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"enableStaking","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_stakeHolder","type":"address"},{"internalType":"uint256","name":"stakeIndex","type":"uint256"}],"name":"getuserStake","outputs":[{"components":[{"internalType":"uint256","name":"stakedLdAmount","type":"uint256"},{"internalType":"uint256","name":"lastClaimed","type":"uint256"}],"internalType":"struct LdStaking.Stake","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lock","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"stakeIndex","type":"uint256"}],"name":"reStake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newPoolAddress","type":"address"}],"name":"setPool","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"stakeLd","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"stakeIndex","type":"uint256"}],"name":"unstakeLD","outputs":[],"stateMutability":"nonpayable","type":"function"}]