Blockchain / Global / Address
Contract
0x3B7f587E4e18fEFced4C15e89B1B57C4530fEA84
- Balance
- 1.2 SOMI
- Transactions sent
- 1
- Type
- Contract · 10,676 bytes of code
IntraverseStakeProtocol Verified
- Compiler
- solc 0.8.24+commit.e11b9ed9
- Language
- Solidity
- Optimization
- Yes, 1000 runs
- EVM version
- paris
- License
- —
- Verified
- 2026-10-12
contracts/IntraverseStakeProtocol.sol
and 8 more files
Solidity
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.24;
import "@openzeppelin/contracts/token/ERC1155/IERC1155.sol";
import "@openzeppelin/contracts/token/ERC1155/utils/ERC1155Holder.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
contract IntraverseStakeProtocol is ERC1155Holder, Ownable, ReentrancyGuard {
// Errors
error InvalidAddress();
error RewardPerDayTooLow();
error ContractNotWhitelisted();
error NoStakedTokens();
error NoRewardsToClaim();
error TokenNotStaked();
error TokenAlreadyStaked();
error InvalidAmount();
error LockPeriodNotOver();
error InvalidLockPeriod();
error InsufficientBalance();
error WithdrawalFailed();
// Events
event Staked(address indexed user, address indexed contractAddress, uint256 tokenId, uint256 amount, uint256 timestamp);
event Unstaked(address indexed user, address indexed contractAddress, uint256 tokenId, uint256 amount, uint256 timestamp);
event UnstakedAll(address indexed user, uint256 timestamp);
event RewardClaimed(address indexed user, uint256 amount, uint256 timestamp);
event RewardPerDayUpdated(uint256 oldValue, uint256 newValue, uint256 timestamp);
event ContractAddressAdded(address indexed contractAddress, uint256 timestamp);
event ContractAddressRemoved(address indexed contractAddress, uint256 timestamp);
event RewardedTokenIdUpdated(uint256 oldTokenId, uint256 newTokenId, uint256 timestamp);
event DailyTokenCountUpdated(uint256 day, uint256 count);
event LockPeriodUpdated(uint256 oldValue, uint256 newValue, uint256 timestamp);
// Structs
struct StakeInfo {
address contractAddress;
uint256 tokenId;
uint256 amount;
uint256 lastClaimTimestamp;
}
// Config settings
uint256 public rewardPerDay;
uint256 public deploymentTimestamp;
uint256 public DAY_IN_SECONDS; // configurable for testing
uint256 public constant MAX_DAYS_TO_PROCESS = 181; // Maximum days to process in one transaction
mapping(address => bool) public whitelistedContracts;
// The specific token ID that will receive rewards
uint256 public rewardedTokenId;
// Daily counts of rewardedTokenId NFTs staked
mapping(uint256 => uint256) public dailyRewardedTokenCount;
// Track the latest day for which token count was updated
uint256 public lastTokenCountUpdateDay;
// User data
mapping(address => StakeInfo[]) public userStakes;
mapping(address => mapping(address => mapping(uint256 => uint256))) public stakedTokenIndex; // user -> contract -> tokenId -> index+1 (0 means not staked)
mapping(address => uint256) public totalUserReward;
// Track all active stakers for reward updates
address[] public allStakers;
mapping(address => uint256) public stakerIndex; // Map staker to index in allStakers array (index+1, 0 means not in array)
// Lock period settings
uint256 public lockPeriodDays;
mapping(address => uint256) public userLockTimestamp;
constructor(uint256 _rewardPerDay, uint256 _rewardedTokenId, uint256 _dayInSeconds, uint256 _lockPeriodDays) Ownable(msg.sender) {
if (_lockPeriodDays == 0) revert InvalidLockPeriod();
rewardPerDay = _rewardPerDay;
rewardedTokenId = _rewardedTokenId;
deploymentTimestamp = block.timestamp;
DAY_IN_SECONDS = _dayInSeconds > 0 ? _dayInSeconds : 86400; // Use custom value or default to 24 hours
lockPeriodDays = _lockPeriodDays;
}
/**
* @notice Get the current day count since contract deployment
* @return The current day number (starting from 0)
*/
function getCurrentDay() public view returns (uint256) {
return (block.timestamp - deploymentTimestamp) / DAY_IN_SECONDS;
}
/**
* @notice Get the day number for a specific timestamp
* @param timestamp The timestamp to convert to a day number
* @return The day number since contract deployment
*/
function getDayForTimestamp(uint256 timestamp) public view returns (uint256) {
return (timestamp - deploymentTimestamp) / DAY_IN_SECONDS;
}
/**
* @notice Check if user's lock period is over
* @param user The address of the user to check
* @return Whether the lock period is over
*/
function isLockPeriodOver(address user) public view returns (bool) {
if (userLockTimestamp[user] == 0) return true;
return block.timestamp >= userLockTimestamp[user] + (lockPeriodDays * DAY_IN_SECONDS);
}
/**
* @notice Stake ERC1155 tokens in the contract
* @param contractAddress The address of the ERC1155 contract
* @param tokenId The ID of the token to stake
* @param amount The amount of tokens to stake
*/
function stake(address contractAddress, uint256 tokenId, uint256 amount) external nonReentrant returns (bool done) {
// Validate parameters
if (contractAddress == address(0)) revert InvalidAddress();
if (!whitelistedContracts[contractAddress]) revert ContractNotWhitelisted();
if (amount == 0) revert InvalidAmount();
address sender = msg.sender;
// Check if the user has approved this contract to transfer their tokens
require(IERC1155(contractAddress).isApprovedForAll(sender, address(this)),
"ERC1155: caller is not owner nor approved");
// Add user to stakers list if not already there
if (stakerIndex[sender] == 0) {
allStakers.push(sender);
stakerIndex[sender] = allStakers.length;
}
// Check if user already has this token staked
uint256 existingIndex = stakedTokenIndex[sender][contractAddress][tokenId];
if (existingIndex > 0) {
// Update existing stake
uint256 index = existingIndex - 1;
StakeInfo storage stakeInfo = userStakes[sender][index];
// Update rewards before changing stake amount
bool rewardsProcessed;
(rewardsProcessed, ) = _processRewardsWithDailyLimit(sender, index, MAX_DAYS_TO_PROCESS);
// Make sure all rewards are processed
if (!rewardsProcessed) {
// in this case, the user will have to call syncUserRewards()
// to process the rewards or call the function again until done is true
// note that in this case, nothing has changed in the contract except the lastClaimTimestamp
// of user's stakes, which should have been updated anyway at some point
// allStakers and stakerIndex are not affected (cause user is already in the array)
return false;
}
// Update stake amount
unchecked {
stakeInfo.amount += amount;
}
} else {
// Create new stake
StakeInfo memory newStake = StakeInfo({
contractAddress: contractAddress,
tokenId: tokenId,
amount: amount,
lastClaimTimestamp: block.timestamp
});
userStakes[sender].push(newStake);
stakedTokenIndex[sender][contractAddress][tokenId] = userStakes[sender].length;
}
// Transfer tokens to this contract
// Need to first get approval from msg.sender
IERC1155(contractAddress).safeTransferFrom(
sender,
address(this),
tokenId,
amount,
""
);
// Set or update lock period timestamp
userLockTimestamp[sender] = block.timestamp;
// Update daily token count if this is a rewarded token
if (tokenId == rewardedTokenId) {
uint256 nextDay;
unchecked {
nextDay = getCurrentDay() + 1;
}
_updateDailyRewardedTokenCount(nextDay, amount, true);
emit DailyTokenCountUpdated(nextDay, dailyRewardedTokenCount[nextDay]);
}
emit Staked(sender, contractAddress, tokenId, amount, block.timestamp);
return true;
}
/**
* @notice Unstake specific ERC1155 tokens
* @param contractAddress The address of the ERC1155 contract
* @param tokenId The ID of the token to unstake
* @param amount The amount of tokens to unstake
*/
function unstake(address contractAddress, uint256 tokenId, uint256 amount) external nonReentrant returns (bool done) {
// Validate parameters
if (contractAddress == address(0)) revert InvalidAddress();
if (amount == 0) revert InvalidAmount();
if (!isLockPeriodOver(msg.sender)) revert LockPeriodNotOver();
// Check if user has this token staked
uint256 existingIndex = stakedTokenIndex[msg.sender][contractAddress][tokenId];
if (existingIndex == 0) revert TokenNotStaked();
uint256 index = existingIndex - 1;
StakeInfo storage stakeInfo = userStakes[msg.sender][index];
// Ensure user has enough tokens staked
if (amount > stakeInfo.amount) revert InvalidAmount();
// Update rewards before changing stake amount
bool rewardsProcessed;
(rewardsProcessed, ) = _processRewardsWithDailyLimit(msg.sender, index, MAX_DAYS_TO_PROCESS);
if (!rewardsProcessed) {
// in this case, the user will have to call syncUserRewards()
// to process the rewards or call the function again until done is true
// note that in this case, nothing has changed in the contract except the lastClaimTimestamp
// of user's stakes, which should have been updated anyway at some point
return false;
}
// Update daily token count if this is a rewarded token
if (tokenId == rewardedTokenId) {
uint256 nextDay = getCurrentDay() + 1;
_updateDailyRewardedTokenCount(nextDay, amount, false);
emit DailyTokenCountUpdated(nextDay, dailyRewardedTokenCount[nextDay]);
}
// Update stake amount or remove if all tokens unstaked
if (amount == stakeInfo.amount) {
// Remove stake by swapping with the last one and popping
uint256 lastIndex = userStakes[msg.sender].length - 1;
if (index != lastIndex) {
// Swap with the last item
StakeInfo memory lastStake = userStakes[msg.sender][lastIndex];
userStakes[msg.sender][index] = lastStake;
stakedTokenIndex[msg.sender][lastStake.contractAddress][lastStake.tokenId] = index + 1;
}
// Remove the last item
userStakes[msg.sender].pop();
stakedTokenIndex[msg.sender][contractAddress][tokenId] = 0;
// Check if user has no more stakes
if (userStakes[msg.sender].length == 0) {
_removeStaker(msg.sender);
// Clear lock timestamp when all stakes are removed
userLockTimestamp[msg.sender] = 0;
}
} else {
// Update stake amount
stakeInfo.amount -= amount;
}
// Transfer tokens back to user
IERC1155(contractAddress).safeTransferFrom(
address(this),
msg.sender,
tokenId,
amount,
bytes("")
);
emit Unstaked(msg.sender, contractAddress, tokenId, amount, block.timestamp);
return true;
}
/**
* @notice Unstake all ERC1155 tokens for a user
*/
function unstakeAll() external nonReentrant returns (bool done) {
if (!isLockPeriodOver(msg.sender)) revert LockPeriodNotOver();
StakeInfo[] storage stakes = userStakes[msg.sender];
if (stakes.length == 0) revert NoStakedTokens();
uint256 daysQuota = MAX_DAYS_TO_PROCESS;
bool rewardsProcessed;
// Update rewards for all stakes before unstaking
for (uint256 i = 0; i < stakes.length; i++) {
(rewardsProcessed, daysQuota) = _processRewardsWithDailyLimit(msg.sender, i, daysQuota);
if (!rewardsProcessed) {
// in this case, the user will have to call syncUserRewards()
// to process the rewards or call the function again until done is true
// note that in this case, nothing has changed in the contract except the lastClaimTimestamp
// of user's stakes, which should have been updated anyway at some point
return false;
}
}
// Transfer all tokens back to user
for (uint256 i = 0; i < stakes.length; i++) {
StakeInfo memory stakeInfo = stakes[i];
// Update daily token count if this is a rewarded token
if (stakeInfo.tokenId == rewardedTokenId) {
uint256 nextDay = getCurrentDay() + 1;
_updateDailyRewardedTokenCount(nextDay, stakeInfo.amount, false);
emit DailyTokenCountUpdated(nextDay, dailyRewardedTokenCount[nextDay]);
}
IERC1155(stakeInfo.contractAddress).safeTransferFrom(
address(this),
msg.sender,
stakeInfo.tokenId,
stakeInfo.amount,
bytes("")
);
// Clear staking index mapping
stakedTokenIndex[msg.sender][stakeInfo.contractAddress][stakeInfo.tokenId] = 0;
}
// Clear all stakes
delete userStakes[msg.sender];
// Remove from active stakers list
_removeStaker(msg.sender);
// Clear lock timestamp
userLockTimestamp[msg.sender] = 0;
emit UnstakedAll(msg.sender, block.timestamp);
return true;
}
/**
* @notice Helper function to update daily rewarded token count
* @param day The day to update
* @param amount The amount to add or subtract
* @param isAddition Whether to add (true) or subtract (false) the amount
*/
function _updateDailyRewardedTokenCount(uint256 day, uint256 amount, bool isAddition) internal {
// Ensure token counts are current up to the requested day
_syncTokenCountsUpToDay(day);
// Update the token count for the specified day
if (isAddition) {
dailyRewardedTokenCount[day] += amount;
} else {
if (amount <= dailyRewardedTokenCount[day]) {
dailyRewardedTokenCount[day] -= amount;
} else {
dailyRewardedTokenCount[day] = 0;
}
}
}
/**
* @notice Synchronize token counts up to a specified day
* @param targetDay The day to sync up to
*/
function _syncTokenCountsUpToDay(uint256 targetDay) internal {
// If we already have data for this day or later, no need to sync
if (targetDay <= lastTokenCountUpdateDay) {
return;
}
// If this is the first update ever, just set the last updated day
// because there is no data to propagate
// (this is the first time the contract is being used for staking)
if (lastTokenCountUpdateDay == 0) {
lastTokenCountUpdateDay = targetDay;
return;
}
// Start from the day after the last update
uint256 startDay = lastTokenCountUpdateDay + 1;
// Get the token count from the last updated day
uint256 lastKnownCount = dailyRewardedTokenCount[lastTokenCountUpdateDay];
// Propagate the last known count forward to all days up to the target day
for (uint256 day = startDay; day <= targetDay; day++) {
dailyRewardedTokenCount[day] = lastKnownCount;
}
// Update the last updated day
lastTokenCountUpdateDay = targetDay;
}
/**
* @notice Get the adjusted token count for a day, handling days with missing data
* @param day The day to get the token count for
* @return The token count for the day, using the previous day's count if missing
*/
function _getAdjustedTokenCountForDay(uint256 day) internal view returns (uint256) {
// If the requested day is after the last updated day, use the last known count
if (day > lastTokenCountUpdateDay && lastTokenCountUpdateDay > 0) {
return dailyRewardedTokenCount[lastTokenCountUpdateDay];
}
// Return the token count for the requested day (will be 0 if not set)
return dailyRewardedTokenCount[day];
}
/**
* @notice Claim rewards for all staked tokens
*/
function claimRewards() external nonReentrant returns (bool done) {
uint256 daysQuota = MAX_DAYS_TO_PROCESS;
bool rewardsProcessed;
StakeInfo[] storage stakes = userStakes[msg.sender];
// Process rewards for any active stakes
for (uint256 i = 0; i < stakes.length; i++) {
(rewardsProcessed, daysQuota) = _processRewardsWithDailyLimit(msg.sender, i, daysQuota);
if (!rewardsProcessed) {
// in this case, the user will have to call syncUserRewards()
// to process the rewards or call the function again until done is true
// note that in this case, nothing has changed in the contract except the lastClaimTimestamp
// of user's stakes, which should have been updated anyway at some point
return false;
}
}
uint256 rewardAmount = totalUserReward[msg.sender];
// If no rewards to claim, just return success
if (rewardAmount == 0) return true;
totalUserReward[msg.sender] = 0;
_transferReward(msg.sender, rewardAmount);
emit RewardClaimed(msg.sender, rewardAmount, block.timestamp);
return true;
}
/**
* @notice Calculate the number of transactions required to fully sync a user's rewards
* @param user The address of the user
* @return The number of transactions needed to process all pending rewards
*/
function requiredSyncTrxs(address user) external view returns (uint256) {
StakeInfo[] storage stakes = userStakes[user];
uint256 totalDays = 0;
for (uint256 i = 0; i < stakes.length; i++) {
StakeInfo storage stakeInfo = stakes[i];
if (stakeInfo.tokenId != rewardedTokenId) {
continue;
}
uint256 lastClaimDay = getDayForTimestamp(stakeInfo.lastClaimTimestamp);
uint256 currentDay = getCurrentDay();
if (currentDay - lastClaimDay > totalDays) {
totalDays += currentDay - lastClaimDay;
}
}
return (totalDays + MAX_DAYS_TO_PROCESS - 1) / MAX_DAYS_TO_PROCESS;
}
/**
* @notice Process rewards for a user with a daily processing limit
* @param user The address of the user
* @return Whether the sync was fully completed
*/
function syncUserRewards(address user) external returns (bool) {
StakeInfo[] storage stakes = userStakes[user];
uint256 daysQuota = MAX_DAYS_TO_PROCESS;
bool rewardsProcessed;
for (uint256 i = 0; i < stakes.length; i++) {
(rewardsProcessed, daysQuota) = _processRewardsWithDailyLimit(user, i, daysQuota);
if (!rewardsProcessed) {
return false;
}
}
return true;
}
/**
* @notice Process rewards for a specific stake with a limit on days to process
* @param user The address of the user
* @param stakeIndex The index of the stake in the user's stakes array
* @param daysQuota The maximum number of days to process in this transaction
* @return done Whether all days were processed
* @return remainingQuota The remaining days quota that can be used for other stakes
*/
function _processRewardsWithDailyLimit(address user, uint256 stakeIndex, uint256 daysQuota) internal returns (bool, uint256) {
StakeInfo storage stakeInfo = userStakes[user][stakeIndex];
if (stakeInfo.tokenId != rewardedTokenId) {
return (true, daysQuota);
}
if (block.timestamp <= stakeInfo.lastClaimTimestamp) {
return (true, daysQuota);
}
uint256 lastClaimDay = getDayForTimestamp(stakeInfo.lastClaimTimestamp);
uint256 currentDay = getCurrentDay();
// Only rewards from the next day after last claim
if (currentDay <= lastClaimDay) {
return (true, daysQuota);
}
// Calculate reward for the unclaimed day
uint256 reward = 0;
bool done = false;
if (currentDay - lastClaimDay <= daysQuota) {
done = true;
}
// For each day, calculate the proportional share of the daily reward
for (uint256 day = lastClaimDay + 1; day <= currentDay && daysQuota > 0; day++) {
uint256 totalTokensStakedForDay = _getAdjustedTokenCountForDay(day);
daysQuota --;
// Update last claim timestamp
stakeInfo.lastClaimTimestamp += DAY_IN_SECONDS;
// Skip day with no staked tokens to avoid division by zero
if (totalTokensStakedForDay == 0) {
continue;
}
// Calculate the proportional share of the daily reward pool
uint256 dailyReward = (rewardPerDay * stakeInfo.amount) / totalTokensStakedForDay;
reward += dailyReward;
}
// Add to user's total reward
totalUserReward[user] += reward;
return (done, daysQuota);
}
/**
* @notice Transfer reward to a user
* @dev This is a placeholder for the actual reward transfer mechanism
* @param user The address of the user to reward
* @param amount The amount of reward to transfer
*/
function _transferReward(address user, uint256 amount) internal {
// Implement the reward transfer mechanism
// This could be a token transfer, ETH transfer, or other mechanism
// For now, we'll use a simple ETH transfer
(bool success, ) = payable(user).call{value: amount}("");
require(success, "Reward transfer failed");
}
/**
* @notice Get all stakes for a user
* @param user The address of the user
* @return An array of StakeInfo structs
*/
function getUserStakes(address user) external view returns (StakeInfo[] memory) {
return userStakes[user];
}
/**
* @notice Get the pending rewards for a user without claiming them
* @param user The address of the user
* @return The amount of pending rewards
*/
function getPendingRewards(address user) external view returns (uint256) {
StakeInfo[] storage stakes = userStakes[user];
uint256 pendingReward = totalUserReward[user];
uint256 currentDay = getCurrentDay();
// Calculate additional pending rewards for each stake
for (uint256 i = 0; i < stakes.length; i++) {
StakeInfo storage stakeInfo = stakes[i];
// Only calculate rewards for the specific token ID that's eligible
if (stakeInfo.tokenId != rewardedTokenId) {
continue;
}
uint256 lastClaimDay = getDayForTimestamp(stakeInfo.lastClaimTimestamp);
// Only calculate rewards from the next day after last claim
if (currentDay <= lastClaimDay) {
continue;
}
// For each day, calculate the proportional share of the daily reward
for (uint256 day = lastClaimDay + 1; day <= currentDay; day++) {
uint256 totalTokensStakedForDay = _getAdjustedTokenCountForDay(day);
// Skip day with no staked tokens to avoid division by zero
if (totalTokensStakedForDay == 0) {
continue;
}
// Calculate the proportional share of the daily reward pool
uint256 dailyReward = (rewardPerDay * stakeInfo.amount) / totalTokensStakedForDay;
pendingReward += dailyReward;
}
}
return pendingReward;
}
/**
* @notice Update the reward per day
* @param _rewardPerDay The new reward per day value
*/
function setRewardPerDay(uint256 _rewardPerDay, uint256 _quota) external onlyOwner returns (bool done) {
// Update all user stakes with the old reward rate until now
bool updateCompleted = _updateAllStakesForAllUsers(_quota);
if (!updateCompleted) {
return false;
}
uint256 oldRewardPerDay = rewardPerDay;
rewardPerDay = _rewardPerDay;
emit RewardPerDayUpdated(oldRewardPerDay, _rewardPerDay, block.timestamp);
return true;
}
/**
* @notice Add a contract address to the whitelist
* @param contractAddress The address to add
*/
function addContractAddress(address contractAddress) external onlyOwner {
if (contractAddress == address(0)) revert InvalidAddress();
whitelistedContracts[contractAddress] = true;
emit ContractAddressAdded(contractAddress, block.timestamp);
}
/**
* @notice Remove a contract address from the whitelist
* @param contractAddress The address to remove
*/
function removeContractAddress(address contractAddress) external onlyOwner {
if (contractAddress == address(0)) revert InvalidAddress();
whitelistedContracts[contractAddress] = false;
emit ContractAddressRemoved(contractAddress, block.timestamp);
}
/**
* @notice Helper function to remove a staker from the allStakers array
* @param staker The address of the staker to remove
*/
function _removeStaker(address staker) internal {
uint256 index = stakerIndex[staker];
if (index == 0) return; // Not in the array
unchecked {
index = index - 1;
}
// If this is not the last element, swap with the last one
uint256 lastIndex = allStakers.length - 1;
if (index != lastIndex) {
address lastStaker = allStakers[lastIndex];
allStakers[index] = lastStaker;
unchecked {
stakerIndex[lastStaker] = index + 1;
}
}
// Remove the last element
allStakers.pop();
stakerIndex[staker] = 0;
}
/**
* @notice Update rewards for all stakes of all users
* @dev This is called when reward rate changes to ensure fairness
*/
function _updateAllStakesForAllUsers(uint256 _quota) internal returns (bool) {
bool rewardsProcessed;
// Iterate through all active stakers and update their rewards
for (uint256 i = 0; i < allStakers.length; i++) {
address staker = allStakers[i];
// Update all stakes for this staker
StakeInfo[] storage stakes = userStakes[staker];
for (uint256 j = 0; j < stakes.length; j++) {
(rewardsProcessed, _quota) = _processRewardsWithDailyLimit(staker, j, _quota);
if (!rewardsProcessed) {
return false;
}
}
}
return true;
}
/**
* @dev Function to receive ETH when msg.data is empty
*/
receive() external payable {}
/**
* @dev Fallback function
*/
fallback() external payable {}
/**
* @notice Withdraw ETH from the contract (owner only)
* @param amount The amount to withdraw
*/
function withdraw(uint256 amount) external onlyOwner {
if (amount > address(this).balance) revert InsufficientBalance();
(bool success, ) = payable(owner()).call{value: amount}("");
if (!success) revert WithdrawalFailed();
}
/**
* @notice Get the total amount of rewarded token staked for a specific day
* @param day The day number to query
* @return The total amount of rewarded tokens staked on that day
*/
function getDailyRewardedTokenCount(uint256 day) external view returns (uint256) {
return _getAdjustedTokenCountForDay(day);
}
/**
* @notice Update the lock period days
* @param _lockPeriodDays The new lock period in days
*/
function setLockPeriodDays(uint256 _lockPeriodDays) external onlyOwner {
if (_lockPeriodDays == 0) revert InvalidLockPeriod();
uint256 oldLockPeriodDays = lockPeriodDays;
lockPeriodDays = _lockPeriodDays;
emit LockPeriodUpdated(oldLockPeriodDays, _lockPeriodDays, block.timestamp);
}
/**
* @notice Get the remaining lock time for a user in seconds
* @param user The address of the user to check
* @return Remaining lock time in seconds (0 if lock period is over)
*/
function getRemainingLockTime(address user) external view returns (uint256) {
if (userLockTimestamp[user] == 0) return 0;
uint256 unlockTime = userLockTimestamp[user] + (lockPeriodDays * DAY_IN_SECONDS);
if (block.timestamp >= unlockTime) return 0;
return unlockTime - block.timestamp;
}
}
Other files
@openzeppelin/contracts/access/Ownable.sol
// 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); } }@openzeppelin/contracts/token/ERC1155/IERC1155.sol
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.1.0) (token/ERC1155/IERC1155.sol) pragma solidity ^0.8.20; import {IERC165} from "../../utils/introspection/IERC165.sol"; /** * @dev Required interface of an ERC-1155 compliant contract, as defined in the * https://eips.ethereum.org/EIPS/eip-1155[ERC]. */ interface IERC1155 is IERC165 { /** * @dev Emitted when `value` amount of tokens of type `id` are transferred from `from` to `to` by `operator`. */ event TransferSingle(address indexed operator, address indexed from, address indexed to, uint256 id, uint256 value); /** * @dev Equivalent to multiple {TransferSingle} events, where `operator`, `from` and `to` are the same for all * transfers. */ event TransferBatch( address indexed operator, address indexed from, address indexed to, uint256[] ids, uint256[] values ); /** * @dev Emitted when `account` grants or revokes permission to `operator` to transfer their tokens, according to * `approved`. */ event ApprovalForAll(address indexed account, address indexed operator, bool approved); /** * @dev Emitted when the URI for token type `id` changes to `value`, if it is a non-programmatic URI. * * If an {URI} event was emitted for `id`, the standard * https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions[guarantees] that `value` will equal the value * returned by {IERC1155MetadataURI-uri}. */ event URI(string value, uint256 indexed id); /** * @dev Returns the value of tokens of token type `id` owned by `account`. */ function balanceOf(address account, uint256 id) external view returns (uint256); /** * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {balanceOf}. * * Requirements: * * - `accounts` and `ids` must have the same length. */ function balanceOfBatch( address[] calldata accounts, uint256[] calldata ids ) external view returns (uint256[] memory); /** * @dev Grants or revokes permission to `operator` to transfer the caller's tokens, according to `approved`, * * Emits an {ApprovalForAll} event. * * Requirements: * * - `operator` cannot be the zero address. */ function setApprovalForAll(address operator, bool approved) external; /** * @dev Returns true if `operator` is approved to transfer ``account``'s tokens. * * See {setApprovalForAll}. */ function isApprovedForAll(address account, address operator) external view returns (bool); /** * @dev Transfers a `value` amount of tokens of type `id` from `from` to `to`. * * WARNING: This function can potentially allow a reentrancy attack when transferring tokens * to an untrusted contract, when invoking {onERC1155Received} on the receiver. * Ensure to follow the checks-effects-interactions pattern and consider employing * reentrancy guards when interacting with untrusted contracts. * * Emits a {TransferSingle} event. * * Requirements: * * - `to` cannot be the zero address. * - If the caller is not `from`, it must have been approved to spend ``from``'s tokens via {setApprovalForAll}. * - `from` must have a balance of tokens of type `id` of at least `value` amount. * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the * acceptance magic value. */ function safeTransferFrom(address from, address to, uint256 id, uint256 value, bytes calldata data) external; /** * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {safeTransferFrom}. * * WARNING: This function can potentially allow a reentrancy attack when transferring tokens * to an untrusted contract, when invoking {onERC1155BatchReceived} on the receiver. * Ensure to follow the checks-effects-interactions pattern and consider employing * reentrancy guards when interacting with untrusted contracts. * * Emits either a {TransferSingle} or a {TransferBatch} event, depending on the length of the array arguments. * * Requirements: * * - `ids` and `values` must have the same length. * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the * acceptance magic value. */ function safeBatchTransferFrom( address from, address to, uint256[] calldata ids, uint256[] calldata values, bytes calldata data ) external; }@openzeppelin/contracts/token/ERC1155/IERC1155Receiver.sol
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.1.0) (token/ERC1155/IERC1155Receiver.sol) pragma solidity ^0.8.20; import {IERC165} from "../../utils/introspection/IERC165.sol"; /** * @dev Interface that must be implemented by smart contracts in order to receive * ERC-1155 token transfers. */ interface IERC1155Receiver is IERC165 { /** * @dev Handles the receipt of a single ERC-1155 token type. This function is * called at the end of a `safeTransferFrom` after the balance has been updated. * * NOTE: To accept the transfer, this must return * `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` * (i.e. 0xf23a6e61, or its own function selector). * * @param operator The address which initiated the transfer (i.e. msg.sender) * @param from The address which previously owned the token * @param id The ID of the token being transferred * @param value The amount of tokens being transferred * @param data Additional data with no specified format * @return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` if transfer is allowed */ function onERC1155Received( address operator, address from, uint256 id, uint256 value, bytes calldata data ) external returns (bytes4); /** * @dev Handles the receipt of a multiple ERC-1155 token types. This function * is called at the end of a `safeBatchTransferFrom` after the balances have * been updated. * * NOTE: To accept the transfer(s), this must return * `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` * (i.e. 0xbc197c81, or its own function selector). * * @param operator The address which initiated the batch transfer (i.e. msg.sender) * @param from The address which previously owned the token * @param ids An array containing ids of each token being transferred (order and length must match values array) * @param values An array containing amounts of each token being transferred (order and length must match ids array) * @param data Additional data with no specified format * @return `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` if transfer is allowed */ function onERC1155BatchReceived( address operator, address from, uint256[] calldata ids, uint256[] calldata values, bytes calldata data ) external returns (bytes4); }@openzeppelin/contracts/token/ERC1155/utils/ERC1155Holder.sol
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.1.0) (token/ERC1155/utils/ERC1155Holder.sol) pragma solidity ^0.8.20; import {IERC165, ERC165} from "../../../utils/introspection/ERC165.sol"; import {IERC1155Receiver} from "../IERC1155Receiver.sol"; /** * @dev Simple implementation of `IERC1155Receiver` that will allow a contract to hold ERC-1155 tokens. * * IMPORTANT: When inheriting this contract, you must include a way to use the received tokens, otherwise they will be * stuck. */ abstract contract ERC1155Holder is ERC165, IERC1155Receiver { /** * @dev See {IERC165-supportsInterface}. */ function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) { return interfaceId == type(IERC1155Receiver).interfaceId || super.supportsInterface(interfaceId); } function onERC1155Received( address, address, uint256, uint256, bytes memory ) public virtual override returns (bytes4) { return this.onERC1155Received.selector; } function onERC1155BatchReceived( address, address, uint256[] memory, uint256[] memory, bytes memory ) public virtual override returns (bytes4) { return this.onERC1155BatchReceived.selector; } }@openzeppelin/contracts/utils/Context.sol
// 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; } }@openzeppelin/contracts/utils/ReentrancyGuard.sol
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.1.0) (utils/ReentrancyGuard.sol) pragma solidity ^0.8.20; /** * @dev Contract module that helps prevent reentrant calls to a function. * * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier * available, which can be applied to functions to make sure there are no nested * (reentrant) calls to them. * * Note that because there is a single `nonReentrant` guard, functions marked as * `nonReentrant` may not call one another. This can be worked around by making * those functions `private`, and then adding `external` `nonReentrant` entry * points to them. * * TIP: If EIP-1153 (transient storage) is available on the chain you're deploying at, * consider using {ReentrancyGuardTransient} instead. * * TIP: If you would like to learn more about reentrancy and alternative ways * to protect against it, check out our blog post * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul]. */ abstract contract ReentrancyGuard { // Booleans are more expensive than uint256 or any type that takes up a full // word because each write operation emits an extra SLOAD to first read the // slot's contents, replace the bits taken up by the boolean, and then write // back. This is the compiler's defense against contract upgrades and // pointer aliasing, and it cannot be disabled. // The values being non-zero value makes deployment a bit more expensive, // but in exchange the refund on every call to nonReentrant will be lower in // amount. Since refunds are capped to a percentage of the total // transaction's gas, it is best to keep them low in cases like this one, to // increase the likelihood of the full refund coming into effect. uint256 private constant NOT_ENTERED = 1; uint256 private constant ENTERED = 2; uint256 private _status; /** * @dev Unauthorized reentrant call. */ error ReentrancyGuardReentrantCall(); constructor() { _status = NOT_ENTERED; } /** * @dev Prevents a contract from calling itself, directly or indirectly. * Calling a `nonReentrant` function from another `nonReentrant` * function is not supported. It is possible to prevent this from happening * by making the `nonReentrant` function external, and making it call a * `private` function that does the actual work. */ modifier nonReentrant() { _nonReentrantBefore(); _; _nonReentrantAfter(); } function _nonReentrantBefore() private { // On the first call to nonReentrant, _status will be NOT_ENTERED if (_status == ENTERED) { revert ReentrancyGuardReentrantCall(); } // Any calls to nonReentrant after this point will fail _status = ENTERED; } function _nonReentrantAfter() private { // By storing the original value once again, a refund is triggered (see // https://eips.ethereum.org/EIPS/eip-2200) _status = NOT_ENTERED; } /** * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a * `nonReentrant` function in the call stack. */ function _reentrancyGuardEntered() internal view returns (bool) { return _status == ENTERED; } }@openzeppelin/contracts/utils/introspection/ERC165.sol
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.1.0) (utils/introspection/ERC165.sol) pragma solidity ^0.8.20; import {IERC165} from "./IERC165.sol"; /** * @dev Implementation of the {IERC165} interface. * * Contracts that want to implement ERC-165 should inherit from this contract and override {supportsInterface} to check * for the additional interface id that will be supported. For example: * * ```solidity * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { * return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId); * } * ``` */ abstract contract ERC165 is IERC165 { /** * @dev See {IERC165-supportsInterface}. */ function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) { return interfaceId == type(IERC165).interfaceId; } }@openzeppelin/contracts/utils/introspection/IERC165.sol
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.1.0) (utils/introspection/IERC165.sol) pragma solidity ^0.8.20; /** * @dev Interface of the ERC-165 standard, as defined in the * https://eips.ethereum.org/EIPS/eip-165[ERC]. * * Implementers can declare support of contract interfaces, which can then be * queried by others ({ERC165Checker}). * * For an implementation, see {ERC165}. */ interface IERC165 { /** * @dev Returns true if this contract implements the interface defined by * `interfaceId`. See the corresponding * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[ERC section] * to learn more about how these ids are created. * * This function call must use less than 30 000 gas. */ function supportsInterface(bytes4 interfaceId) external view returns (bool); }
ABI · 68 entries
json
[
{
"type": "constructor",
"inputs": [
{
"name": "_rewardPerDay",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "_rewardedTokenId",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "_dayInSeconds",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "_lockPeriodDays",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "nonpayable"
},
{
"name": "ContractNotWhitelisted",
"type": "error",
"inputs": []
},
{
"name": "InsufficientBalance",
"type": "error",
"inputs": []
},
{
"name": "InvalidAddress",
"type": "error",
"inputs": []
},
{
"name": "InvalidAmount",
"type": "error",
"inputs": []
},
{
"name": "InvalidLockPeriod",
"type": "error",
"inputs": []
},
{
"name": "LockPeriodNotOver",
"type": "error",
"inputs": []
},
{
"name": "NoRewardsToClaim",
"type": "error",
"inputs": []
},
{
"name": "NoStakedTokens",
"type": "error",
"inputs": []
},
{
"name": "OwnableInvalidOwner",
"type": "error",
"inputs": [
{
"name": "owner",
"type": "address",
"internalType": "address"
}
]
},
{
"name": "OwnableUnauthorizedAccount",
"type": "error",
"inputs": [
{
"name": "account",
"type": "address",
"internalType": "address"
}
]
},
{
"name": "ReentrancyGuardReentrantCall",
"type": "error",
"inputs": []
},
{
"name": "RewardPerDayTooLow",
"type": "error",
"inputs": []
},
{
"name": "TokenAlreadyStaked",
"type": "error",
"inputs": []
},
{
"name": "TokenNotStaked",
"type": "error",
"inputs": []
},
{
"name": "WithdrawalFailed",
"type": "error",
"inputs": []
},
{
"name": "ContractAddressAdded",
"type": "event",
"inputs": [
{
"name": "contractAddress",
"type": "address",
"indexed": true,
"internalType": "address"
},
{
"name": "timestamp",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
}
],
"anonymous": false
},
{
"name": "ContractAddressRemoved",
"type": "event",
"inputs": [
{
"name": "contractAddress",
"type": "address",
"indexed": true,
"internalType": "address"
},
{
"name": "timestamp",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
}
],
"anonymous": false
},
{
"name": "DailyTokenCountUpdated",
"type": "event",
"inputs": [
{
"name": "day",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
},
{
"name": "count",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
}
],
"anonymous": false
},
{
"name": "LockPeriodUpdated",
"type": "event",
"inputs": [
{
"name": "oldValue",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
},
{
"name": "newValue",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
},
{
"name": "timestamp",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
}
],
"anonymous": false
},
{
"name": "OwnershipTransferred",
"type": "event",
"inputs": [
{
"name": "previousOwner",
"type": "address",
"indexed": true,
"internalType": "address"
},
{
"name": "newOwner",
"type": "address",
"indexed": true,
"internalType": "address"
}
],
"anonymous": false
},
{
"name": "RewardClaimed",
"type": "event",
"inputs": [
{
"name": "user",
"type": "address",
"indexed": true,
"internalType": "address"
},
{
"name": "amount",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
},
{
"name": "timestamp",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
}
],
"anonymous": false
},
{
"name": "RewardPerDayUpdated",
"type": "event",
"inputs": [
{
"name": "oldValue",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
},
{
"name": "newValue",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
},
{
"name": "timestamp",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
}
],
"anonymous": false
},
{
"name": "RewardedTokenIdUpdated",
"type": "event",
"inputs": [
{
"name": "oldTokenId",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
},
{
"name": "newTokenId",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
},
{
"name": "timestamp",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
}
],
"anonymous": false
},
{
"name": "Staked",
"type": "event",
"inputs": [
{
"name": "user",
"type": "address",
"indexed": true,
"internalType": "address"
},
{
"name": "contractAddress",
"type": "address",
"indexed": true,
"internalType": "address"
},
{
"name": "tokenId",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
},
{
"name": "amount",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
},
{
"name": "timestamp",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
}
],
"anonymous": false
},
{
"name": "Unstaked",
"type": "event",
"inputs": [
{
"name": "user",
"type": "address",
"indexed": true,
"internalType": "address"
},
{
"name": "contractAddress",
"type": "address",
"indexed": true,
"internalType": "address"
},
{
"name": "tokenId",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
},
{
"name": "amount",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
},
{
"name": "timestamp",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
}
],
"anonymous": false
},
{
"name": "UnstakedAll",
"type": "event",
"inputs": [
{
"name": "user",
"type": "address",
"indexed": true,
"internalType": "address"
},
{
"name": "timestamp",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
}
],
"anonymous": false
},
{
"type": "fallback",
"stateMutability": "payable"
},
{
"name": "DAY_IN_SECONDS",
"type": "function",
"inputs": [],
"outputs": [
{
"name": "",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "view"
},
{
"name": "MAX_DAYS_TO_PROCESS",
"type": "function",
"inputs": [],
"outputs": [
{
"name": "",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "view"
},
{
"name": "addContractAddress",
"type": "function",
"inputs": [
{
"name": "contractAddress",
"type": "address",
"internalType": "address"
}
],
"outputs": [],
"stateMutability": "nonpayable"
},
{
"name": "allStakers",
"type": "function",
"inputs": [
{
"name": "",
"type": "uint256",
"internalType": "uint256"
}
],
"outputs": [
{
"name": "",
"type": "address",
"internalType": "address"
}
],
"stateMutability": "view"
},
{
"name": "claimRewards",
"type": "function",
"inputs": [],
"outputs": [
{
"name": "done",
"type": "bool",
"internalType": "bool"
}
],
"stateMutability": "nonpayable"
},
{
"name": "dailyRewardedTokenCount",
"type": "function",
"inputs": [
{
"name": "",
"type": "uint256",
"internalType": "uint256"
}
],
"outputs": [
{
"name": "",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "view"
},
{
"name": "deploymentTimestamp",
"type": "function",
"inputs": [],
"outputs": [
{
"name": "",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "view"
},
{
"name": "getCurrentDay",
"type": "function",
"inputs": [],
"outputs": [
{
"name": "",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "view"
},
{
"name": "getDailyRewardedTokenCount",
"type": "function",
"inputs": [
{
"name": "day",
"type": "uint256",
"internalType": "uint256"
}
],
"outputs": [
{
"name": "",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "view"
},
{
"name": "getDayForTimestamp",
"type": "function",
"inputs": [
{
"name": "timestamp",
"type": "uint256",
"internalType": "uint256"
}
],
"outputs": [
{
"name": "",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "view"
},
{
"name": "getPendingRewards",
"type": "function",
"inputs": [
{
"name": "user",
"type": "address",
"internalType": "address"
}
],
"outputs": [
{
"name": "",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "view"
},
{
"name": "getRemainingLockTime",
"type": "function",
"inputs": [
{
"name": "user",
"type": "address",
"internalType": "address"
}
],
"outputs": [
{
"name": "",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "view"
},
{
"name": "getUserStakes",
"type": "function",
"inputs": [
{
"name": "user",
"type": "address",
"internalType": "address"
}
],
"outputs": [
{
"name": "",
"type": "tuple[]",
"components": [
{
"name": "contractAddress",
"type": "address",
"internalType": "address"
},
{
"name": "tokenId",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "amount",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "lastClaimTimestamp",
"type": "uint256",
"internalType": "uint256"
}
],
"internalType": "struct IntraverseStakeProtocol.StakeInfo[]"
}
],
"stateMutability": "view"
},
{
"name": "isLockPeriodOver",
"type": "function",
"inputs": [
{
"name": "user",
"type": "address",
"internalType": "address"
}
],
"outputs": [
{
"name": "",
"type": "bool",
"internalType": "bool"
}
],
"stateMutability": "view"
},
{
"name": "lastTokenCountUpdateDay",
"type": "function",
"inputs": [],
"outputs": [
{
"name": "",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "view"
},
{
"name": "lockPeriodDays",
"type": "function",
"inputs": [],
"outputs": [
{
"name": "",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "view"
},
{
"name": "onERC1155BatchReceived",
"type": "function",
"inputs": [
{
"name": "",
"type": "address",
"internalType": "address"
},
{
"name": "",
"type": "address",
"internalType": "address"
},
{
"name": "",
"type": "uint256[]",
"internalType": "uint256[]"
},
{
"name": "",
"type": "uint256[]",
"internalType": "uint256[]"
},
{
"name": "",
"type": "bytes",
"internalType": "bytes"
}
],
"outputs": [
{
"name": "",
"type": "bytes4",
"internalType": "bytes4"
}
],
"stateMutability": "nonpayable"
},
{
"name": "onERC1155Received",
"type": "function",
"inputs": [
{
"name": "",
"type": "address",
"internalType": "address"
},
{
"name": "",
"type": "address",
"internalType": "address"
},
{
"name": "",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "",
"type": "bytes",
"internalType": "bytes"
}
],
"outputs": [
{
"name": "",
"type": "bytes4",
"internalType": "bytes4"
}
],
"stateMutability": "nonpayable"
},
{
"name": "owner",
"type": "function",
"inputs": [],
"outputs": [
{
"name": "",
"type": "address",
"internalType": "address"
}
],
"stateMutability": "view"
},
{
"name": "removeContractAddress",
"type": "function",
"inputs": [
{
"name": "contractAddress",
"type": "address",
"internalType": "address"
}
],
"outputs": [],
"stateMutability": "nonpayable"
},
{
"name": "renounceOwnership",
"type": "function",
"inputs": [],
"outputs": [],
"stateMutability": "nonpayable"
},
{
"name": "requiredSyncTrxs",
"type": "function",
"inputs": [
{
"name": "user",
"type": "address",
"internalType": "address"
}
],
"outputs": [
{
"name": "",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "view"
},
{
"name": "rewardPerDay",
"type": "function",
"inputs": [],
"outputs": [
{
"name": "",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "view"
},
{
"name": "rewardedTokenId",
"type": "function",
"inputs": [],
"outputs": [
{
"name": "",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "view"
},
{
"name": "setLockPeriodDays",
"type": "function",
"inputs": [
{
"name": "_lockPeriodDays",
"type": "uint256",
"internalType": "uint256"
}
],
"outputs": [],
"stateMutability": "nonpayable"
},
{
"name": "setRewardPerDay",
"type": "function",
"inputs": [
{
"name": "_rewardPerDay",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "_quota",
"type": "uint256",
"internalType": "uint256"
}
],
"outputs": [
{
"name": "done",
"type": "bool",
"internalType": "bool"
}
],
"stateMutability": "nonpayable"
},
{
"name": "stake",
"type": "function",
"inputs": [
{
"name": "contractAddress",
"type": "address",
"internalType": "address"
},
{
"name": "tokenId",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "amount",
"type": "uint256",
"internalType": "uint256"
}
],
"outputs": [
{
"name": "done",
"type": "bool",
"internalType": "bool"
}
],
"stateMutability": "nonpayable"
},
{
"name": "stakedTokenIndex",
"type": "function",
"inputs": [
{
"name": "",
"type": "address",
"internalType": "address"
},
{
"name": "",
"type": "address",
"internalType": "address"
},
{
"name": "",
"type": "uint256",
"internalType": "uint256"
}
],
"outputs": [
{
"name": "",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "view"
},
{
"name": "stakerIndex",
"type": "function",
"inputs": [
{
"name": "",
"type": "address",
"internalType": "address"
}
],
"outputs": [
{
"name": "",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "view"
},
{
"name": "supportsInterface",
"type": "function",
"inputs": [
{
"name": "interfaceId",
"type": "bytes4",
"internalType": "bytes4"
}
],
"outputs": [
{
"name": "",
"type": "bool",
"internalType": "bool"
}
],
"stateMutability": "view"
},
{
"name": "syncUserRewards",
"type": "function",
"inputs": [
{
"name": "user",
"type": "address",
"internalType": "address"
}
],
"outputs": [
{
"name": "",
"type": "bool",
"internalType": "bool"
}
],
"stateMutability": "nonpayable"
},
{
"name": "totalUserReward",
"type": "function",
"inputs": [
{
"name": "",
"type": "address",
"internalType": "address"
}
],
"outputs": [
{
"name": "",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "view"
},
{
"name": "transferOwnership",
"type": "function",
"inputs": [
{
"name": "newOwner",
"type": "address",
"internalType": "address"
}
],
"outputs": [],
"stateMutability": "nonpayable"
},
{
"name": "unstake",
"type": "function",
"inputs": [
{
"name": "contractAddress",
"type": "address",
"internalType": "address"
},
{
"name": "tokenId",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "amount",
"type": "uint256",
"internalType": "uint256"
}
],
"outputs": [
{
"name": "done",
"type": "bool",
"internalType": "bool"
}
],
"stateMutability": "nonpayable"
},
{
"name": "unstakeAll",
"type": "function",
"inputs": [],
"outputs": [
{
"name": "done",
"type": "bool",
"internalType": "bool"
}
],
"stateMutability": "nonpayable"
},
{
"name": "userLockTimestamp",
"type": "function",
"inputs": [
{
"name": "",
"type": "address",
"internalType": "address"
}
],
"outputs": [
{
"name": "",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "view"
},
{
"name": "userStakes",
"type": "function",
"inputs": [
{
"name": "",
"type": "address",
"internalType": "address"
},
{
"name": "",
"type": "uint256",
"internalType": "uint256"
}
],
"outputs": [
{
"name": "contractAddress",
"type": "address",
"internalType": "address"
},
{
"name": "tokenId",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "amount",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "lastClaimTimestamp",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "view"
},
{
"name": "whitelistedContracts",
"type": "function",
"inputs": [
{
"name": "",
"type": "address",
"internalType": "address"
}
],
"outputs": [
{
"name": "",
"type": "bool",
"internalType": "bool"
}
],
"stateMutability": "view"
},
{
"name": "withdraw",
"type": "function",
"inputs": [
{
"name": "amount",
"type": "uint256",
"internalType": "uint256"
}
],
"outputs": [],
"stateMutability": "nonpayable"
},
{
"type": "receive",
"stateMutability": "payable"
}
]