PowerPassStaking
Inherits: Initializable, AccessControlUpgradeable, PausableUpgradeable, ReentrancyGuardUpgradeable, IERC721Receiver, IPowerPassStaking
Title: PowerPassStaking - NFT Staking with Claim Request/Approval Workflow
Author: ShareX Team
Upgradeable staking contract for PowerPass NFTs with admin-approved claim flow Key Features:
- Users stake NFTs to accumulate rewards from powerbank device orders
- Users request to claim rewards, which requires admin approval
- Minimal on-chain storage: only Pending/Approved requests stored
- Rejected/Claimed requests are deleted, tracked via events for off-chain indexing
State Variables
BPS_DENOMINATOR
uint256 public constant BPS_DENOMINATOR = 10_000
DEFAULT_REWARD_BPS
uint256 public constant DEFAULT_REWARD_BPS = 2500
OPERATOR_ROLE
bytes32 public constant OPERATOR_ROLE = keccak256("OPERATOR_ROLE")
_powerPassContract
IERC721 private _powerPassContract
_paymentToken
IERC20 private _paymentToken
_rewardBps
uint96 private _rewardBps
_totalStaked
uint256 private _totalStaked
_totalPendingRewards
uint256 private _totalPendingRewards
_stakingInfo
tokenId => StakingInfo
mapping(uint256 tokenId => StakingInfoInternal info) private _stakingInfo
_userStakedTokens
user => staked tokenIds
mapping(address user => uint256[] tokenIds) private _userStakedTokens
_tokenIndex
tokenId => index in user's array
mapping(uint256 tokenId => uint256 index) private _tokenIndex
_rewardBatchCounter
Counter for reward upload batches (used in events for off-chain indexing)
uint64 private _rewardBatchCounter
_requestCounter
Request counter (never resets, ensures unique requestId)
uint256 private _requestCounter
_claimRequests
requestId => ClaimRequest (only stores Pending/Approved)
mapping(uint256 requestId => ClaimRequestInternal request) private _claimRequests
_userActiveRequests
user => active requestIds (Pending + Approved)
mapping(address user => uint256[] requestIds) private _userActiveRequests
_requestIndexInUser
requestId => index in user's active requests array
mapping(uint256 requestId => uint256 index) private _requestIndexInUser
_userClaimInfo
user => UserClaimInfo
mapping(address user => UserClaimInfoInternal info) private _userClaimInfo
_pendingRequestIds
All pending request IDs (for admin query)
uint256[] private _pendingRequestIds
_pendingRequestIndex
requestId => index + 1 in pending array (0 means not in array)
mapping(uint256 requestId => uint256 indexPlusOne) private _pendingRequestIndex
__gap
uint256[30] private __gap
Functions
validAddress
modifier validAddress(address addr) ;
initialize
function initialize(address powerPass_, address paymentToken_, address admin_)
external
initializer
validAddress(powerPass_)
validAddress(paymentToken_)
validAddress(admin_);
stake
function stake(uint256 tokenId) external override nonReentrant whenNotPaused;
stakeBatch
function stakeBatch(uint256[] calldata tokenIds) external override nonReentrant whenNotPaused;
unstake
function unstake(uint256 tokenId) external override nonReentrant whenNotPaused;
unstakeBatch
function unstakeBatch(uint256[] calldata tokenIds)
external
override
nonReentrant
whenNotPaused;
requestClaim
function requestClaim(uint256 amount)
external
override
nonReentrant
whenNotPaused
returns (uint256 requestId);
claimRewards
function claimRewards(uint256 requestId) external override nonReentrant whenNotPaused;
claimAllApprovedRewards
function claimAllApprovedRewards() external override nonReentrant whenNotPaused;
approveClaim
function approveClaim(uint256 requestId)
external
override
onlyRole(DEFAULT_ADMIN_ROLE)
nonReentrant;
rejectClaim
function rejectClaim(uint256 requestId)
external
override
onlyRole(DEFAULT_ADMIN_ROLE)
nonReentrant;
batchApproveClaims
function batchApproveClaims(uint256[] calldata requestIds)
external
override
onlyRole(DEFAULT_ADMIN_ROLE)
nonReentrant;
batchRejectClaims
function batchRejectClaims(uint256[] calldata requestIds)
external
override
onlyRole(DEFAULT_ADMIN_ROLE)
nonReentrant;
directClaim
function directClaim(uint256 amount) external override nonReentrant whenNotPaused;
uploadOrders
function uploadOrders(uint256[] calldata tokenIds, OrderInfo[][] calldata orders)
external
override
onlyRole(OPERATOR_ROLE)
nonReentrant;
setRewardPercentage
function setRewardPercentage(uint256 rewardBps) external override onlyRole(DEFAULT_ADMIN_ROLE);
setPaymentToken
function setPaymentToken(address token)
external
override
onlyRole(DEFAULT_ADMIN_ROLE)
validAddress(token);
withdrawExcessRewards
function withdrawExcessRewards(uint256 amount)
external
override
onlyRole(DEFAULT_ADMIN_ROLE)
nonReentrant;
emergencyWithdrawNFT
function emergencyWithdrawNFT(uint256 tokenId, address to)
external
override
onlyRole(DEFAULT_ADMIN_ROLE)
validAddress(to)
nonReentrant;
pause
function pause() external onlyRole(DEFAULT_ADMIN_ROLE);
unpause
function unpause() external onlyRole(DEFAULT_ADMIN_ROLE);
getStakingInfo
function getStakingInfo(uint256 tokenId)
external
view
override
returns (StakingInfo memory info);
isStaked
function isStaked(uint256 tokenId) external view override returns (bool);
getStaker
function getStaker(uint256 tokenId) external view override returns (address staker);
getStakedTokenIds
function getStakedTokenIds(address user) external view override returns (uint256[] memory);
powerPassContract
function powerPassContract() external view override returns (address);
paymentToken
function paymentToken() external view override returns (address);
rewardPercentage
function rewardPercentage() external view override returns (uint256);
totalStaked
function totalStaked() external view override returns (uint256);
getUserTotalRewards
function getUserTotalRewards(address user) external view override returns (uint256);
getClaimableAmount
function getClaimableAmount(address user) external view override returns (uint256);
getUserRewardsSummary
function getUserRewardsSummary(address user)
external
view
override
returns (UserRewardsSummary memory summary);
getUserClaimInfo
function getUserClaimInfo(address user)
external
view
override
returns (UserClaimInfo memory info);
getUserActiveRequests
function getUserActiveRequests(address user)
external
view
override
returns (ClaimRequest[] memory requests);
getClaimRequest
function getClaimRequest(uint256 requestId)
external
view
override
returns (ClaimRequest memory request);
getPendingClaimRequests
function getPendingClaimRequests()
external
view
override
returns (ClaimRequest[] memory requests);
getPendingClaimRequestCount
function getPendingClaimRequestCount() external view override returns (uint256);
onERC721Received
function onERC721Received(address, address, uint256, bytes calldata)
external
pure
override
returns (bytes4);
_stake
function _stake(address user, uint256 tokenId) internal;
_unstake
function _unstake(address user, uint256 tokenId) internal;
_removeTokenFromUser
function _removeTokenFromUser(address user, uint256 tokenId) internal;
_approveClaim
function _approveClaim(uint256 requestId) internal;
_approveClaimInternal
function _approveClaimInternal(uint256 requestId) internal returns (uint128 amount);
_rejectClaim
function _rejectClaim(uint256 requestId) internal;
_rejectClaimInternal
function _rejectClaimInternal(uint256 requestId) internal returns (uint128 amount);
_removeRequest
function _removeRequest(uint256 requestId, address user) internal;
_removeFromPendingList
function _removeFromPendingList(uint256 requestId) internal;
_processOrdersBatch
function _processOrdersBatch(
uint256[] calldata tokenIds,
OrderInfo[][] calldata orders,
uint64 batchId
) internal returns (int256 netRewardChange, uint256 totalOrders);
_processTokenOrders
function _processTokenOrders(
uint256 tokenId,
OrderInfo[] calldata tokenOrders,
uint256 rewardBps,
uint64 batchId
) internal returns (int256 tokenRewardChange, uint256 orderCount);
_getUserTotalRewards
function _getUserTotalRewards(address user) internal view returns (uint256 totalRewards);
_getClaimableAmount
function _getClaimableAmount(address user) internal view returns (uint256);
Structs
StakingInfoInternal
Packed staking info (3 storage slots)
struct StakingInfoInternal {
address owner; // 20 bytes
uint64 stakedAt; // 8 bytes
bool isStaked; // 1 byte
// 3 bytes padding
uint128 totalRewards; // 16 bytes - slot 2 (cumulative, never resets)
uint128 rewardsAtStake; // 16 bytes - slot 3 (snapshot when user staked)
}
ClaimRequestInternal
Packed claim request (2 storage slots)
struct ClaimRequestInternal {
address user; // 20 bytes
uint64 requestTime; // 8 bytes
ClaimRequestStatus status; // 1 byte
uint128 amount; // 16 bytes - new slot
}
UserClaimInfoInternal
Packed user claim info (2 storage slots)
struct UserClaimInfoInternal {
uint128 totalEarned; // 16 bytes - total rewards earned (includes settled from unstaked
// NFTs)
uint128 claimed; // 16 bytes - total claimed amount
uint64 pendingApproval; // 8 bytes - amount waiting for approval
uint64 approved; // 8 bytes - amount approved but not yet claimed
}