Staking Protocolskeletons

StakingSkeleton

This facet is a core component (skeleton) of a staking platform, enabling users to participate in existing staking campaigns and manage their staked positions represented by NFTs.

_Each user position is represented by an ERC721 NFT staking position token, minted upon successfully staking. Each position is uniquely tied to a specific campaign and governed by its respective rules.

This contract allows users to:

  • Stake assets in specific campaigns, creating unique NFT positions.
  • Partially or fully unstake assets, respecting lock periods.
  • Claim accrued rewards without affecting their staked position.

Staking requires eligibility criteria to be met, and subsequent operations require ownership of the corresponding NFT as well._

CampaignDoesNotExist

error CampaignDoesNotExist(uint256 invalidCampaignId)

Thrown when a staking campaign with the given invalidCampaignId does not exist.

StakingPositionNotOwned

error StakingPositionNotOwned(uint256 nftId, address nonOwnerAccount)

Thrown when nonOwnerAccount does not own the staking position identified by nftId.

InvalidUnlockTimestampAtRestake

error InvalidUnlockTimestampAtRestake(uint256 nftId, uint256 currentUnlockTimestamp, uint256 requestedUnlockTimestamp)

Thrown at restake attemps with requested unlock timestamps that are before the existing one.

RewardsNotYetAvailableForPosition

error RewardsNotYetAvailableForPosition(uint256 nftId, uint256 provisionStartingTimestamp, uint256 currentTimestamp)

Thrown when rewards are not yet available for the specified staking position.

StakingPositionCreated

event StakingPositionCreated(uint256 campaignId, uint256 nftId, address owner, address operator, uint256 inputPacketsStaked, uint256 stakeStartActiveTimestamp, uint256 unlockTimestamp)

Emitted at stake() and stakeBeneficiary().

StakingPositionIncreased

event StakingPositionIncreased(uint256 campaignId, uint256 nftId, address owner, address operator, uint256 inputPacketsStaked, uint256 stakeStartActiveTimestamp, uint256 previousUnlockTimestamp, uint256 newUnlockTimestamp)

Emitted at restake() and restakeBeneficiary().

StakingPositionDecreased

event StakingPositionDecreased(uint256 campaignId, uint256 nftId, address owner, uint256 inputPacketsWithdrawn)

Emitted at partialUnstake().

StakingPositionClosed

event StakingPositionClosed(uint256 campaignId, uint256 nftId, address owner, uint256 inputPacketsWithdrawn)

Emitted at fullyUnstake().

RewardsReceived

event RewardsReceived(uint256 campaignId, uint256 nftId, address owner, uint256 rewardPackets)

Emitted on condition at restake(), restakeBeneficiary(), partialUnstake(), fullyUnstake() and getReward().

PositionBalanceUpdate

event PositionBalanceUpdate(uint256 campaignId, uint256 nftId, address owner, uint256 previousInputPacketBalance, uint256 previousInputPacketVirtualBalance, uint256 newInputPacketBalance, uint256 newInputPacketVirtualBalance)

Emitted at stake(), stakeBeneficiary(), restake(), restakeBeneficiary(), partialUnstake() & fullyUnstake().

stake

function stake(uint256 campaignId, uint256 amountOfPackets, uint256 timeLockPeriod) external

Allows users to create a position within a specified campaign by allocating or locking assets that are predefined by the campaign's input configuration. The position is represented and tracked by an ERC721 NFT token, which is minted to the user upon staking.

_Instant stake rewards may or may not be featured, depending on the campaign's rewards distribution configuration.

A packet encapsulates individual or groups of different tokens with distinct ratios among them. The input packet for a campaign is defined by the creator of the campaign. Some examples of input packets are:

  • packet = [2 Token A (ERC20)]
  • packet = [5 Token A (ERC20), 10 Token B (ERC20)]
  • packet = [3 Token A (ERC20), 2 Token C with ID == 9 (ERC1155)]

Users must meet eligibility criteria to create a position (see StakersEligibility and AccessControl facets).

Emits a {StakingPositionCreated} and {PositionBalanceUpdate} event._

Parameters

NameTypeDescription
campaignIduint256The unique identifier of the targeted staking campaign in which the user is participating.
amountOfPacketsuint256The number of input packets to stake.
timeLockPerioduint256The duration, in seconds, during which the position's staked assets will be locked.

stakeBeneficiary

function stakeBeneficiary(uint256 campaignId, uint256 amountOfPackets, uint256 timeLockPeriod, address beneficiary) external

Allows users to create a staking position within a specified campaign on behalf of a beneficiary, by locking assets that are predefined by the campaign's input configuration. The position is represented and tracked by an ERC721 NFT token, which is minted to the specified beneficiary upon staking.

_This function enables staking on behalf of another address (beneficiary). The staking input packets are transferred from the caller (msg.sender), but the NFT staking position and any instant stake rewards are allocated to the beneficiary. Instant stake rewards depend on the campaign's reward distribution configuration.

IMPORTANT: Only the beneficiary must meet the eligibility criteria required to create a position (see StakersEligibility and AccessControl facets).

Emits a {StakingPositionCreated} and {PositionBalanceUpdate} event.

For more information, see stake()._

Parameters

NameTypeDescription
campaignIduint256The unique identifier of the targeted staking campaign in which the user is participating.
amountOfPacketsuint256The number of input packets to stake.
timeLockPerioduint256The duration, in seconds, during which the position's staked assets will be locked.
beneficiaryaddressThe address that will receive the NFT staking position and any instant rewards.

_stakeBeneficiary

function _stakeBeneficiary(uint256 campaignId, uint256 amountOfPackets, uint256 timeLockPeriod, address beneficiary) internal

Internal version of stakeBeneficiary(), handling staking logic for a specified beneficiary.

For more information, see stakeBeneficiary().

restake

function restake(uint256 nftId, uint256 amountOfPackets, uint256 timeLockPeriod) external

Allows users to increase their position specified by nftId by allocating or locking additional amounts of staked assets defined in the associated campaign's input packet.

_Restake is not supported when instant staking rewards are featured. Each position is associated with a specific campaign at the time it is created (see stake()).

The recalculated unlockTimestamp (block.timestamp + timeLockPeriod) must be greater than or equal to the current position's unlockTimestamp.

Users must own the NFT representing the position they wish to increase and must meet eligibility criteria (see StakersEligibility and AccessControl facets).

Emits a {StakingPositionIncreased} and {PositionBalanceUpdate} event. Also emits a {RewardsReceived} event on condition._

Parameters

NameTypeDescription
nftIduint256The unique identifier of the NFT associated with the position.
amountOfPacketsuint256The number of input packets to allocate on the existing position, thereby increasing it.
timeLockPerioduint256The duration, in seconds, during which the position's staked assets will be locked.

restakeBeneficiary

function restakeBeneficiary(uint256 nftId, uint256 amountOfPackets, uint256 timeLockPeriod, address beneficiary) external

Allows users to increase a beneficiary's position specified by nftId within a campaign by allocating additional staked assets defined in the campaign's input packet.

_This function enables increasing an existing position on behalf of a beneficiary. The staking input packets are transferred from the caller (msg.sender), but any rewards are allocated to the beneficiary. The beneficiary must own the NFT representing the position.

Restake is not supported when instant staking rewards are featured. The recalculated unlockTimestamp (block.timestamp + timeLockPeriod) must be greater than or equal to the current position's unlockTimestamp.

IMPORTANT: Only the beneficiary must meet the eligibility criteria required to increase a position (see StakersEligibility and AccessControl facets).

Emits a {StakingPositionIncreased} and {PositionBalanceUpdate} event. Also emits a {RewardsReceived} event on condition._

Parameters

NameTypeDescription
nftIduint256The unique identifier of the NFT associated with the position.
amountOfPacketsuint256The number of input packets to allocate to the existing position, increasing it.
timeLockPerioduint256The duration, in seconds, for which the position's staked assets will be locked.
beneficiaryaddressThe address that will receive any instant rewards and must own the NFT position.

_restakeBeneficiary

function _restakeBeneficiary(uint256 nftId, uint256 amountOfPackets, uint256 timeLockPeriod, address beneficiary) internal

Internal version of restakeBeneficiary(), handling restaking logic for a specified beneficiary.

For more information, see restakeBeneficiary().

_applyRestakeAndTransferRewards

function _applyRestakeAndTransferRewards(uint256 campaignId, uint256 nftId, uint256 getRestakeRewards, address rewardAssetHandler, address beneficiary) internal

Called by _restakeBeneficiary().

Applies restake logic (specific to the employed RewardDistribution facet) and transfers the calculated rewards (if any) to beneficiary. Emits a {RewardsReceived} event on condition.

Parameters

NameTypeDescription
campaignIduint256The unique identifier of the targeted staking campaign.
nftIduint256The unique identifier of the NFT associated with the position.
getRestakeRewardsuint256The reward packets from IRewardsDistributionFacet.getRestakeReward().
rewardAssetHandleraddressThe address of the campaign's rewards handler (see CampaignAssetManager.sol)
beneficiaryaddressThe address to receive the rewards

partialUnstake

function partialUnstake(uint256 nftId, uint256 amountOfPacketsToUnstake) external

Allows users to decrease their position specified by nftId by withdrawing amounts of staked assets defined in the associated campaign's input packet.

_Partial unstaking is only supported when the position's lock period has expired. Each position is associated with a specific campaign at the time it is created (see stake()).

Claimable accrued rewards (if any) associated with the position are calculated and transferred to the owner of the position upon partial unstaking.

The amountOfPacketsToUnstake must be less than the total staked input packets of the position.

Users must own the NFT representing the position they wish to decrease and must meet eligibility criteria (see StakersEligibility and AccessControl facets).

Emits a {StakingPositionDecreased} and {PositionBalanceUpdate} event. Also emits a {RewardsReceived} event on condition._

Parameters

NameTypeDescription
nftIduint256The unique identifier of the NFT associated with the position.
amountOfPacketsToUnstakeuint256The number of input packets to withdraw from the position, thereby decreasing it.

fullyUnstake

function fullyUnstake(uint256 nftId) external

Allows users to close their position specified by nftId by withdrawing all the staked assets currently allocated to the position.

_Fully unstaking is only supported when the position's lock period has expired.

Users must own the NFT representing the position they wish to fully unstake and must meet eligibility criteria (see StakersEligibility and AccessControl facets).

Claimable accrued rewards (if any) associated with the position are calculated and transferred to the owner of the position upon fully unstaking.

The NFT token representing the position is burned when the fully unstaking process is successful.

Emits a {StakingPositionClosed} and {PositionBalanceUpdate} event. Also emits a {RewardsReceived} event on condition._

Parameters

NameTypeDescription
nftIduint256The unique identifier of the NFT associated with the position.

getReward

function getReward(uint256 nftId) external

Allows users to receive any accrued rewards associated with their position specified by nftId. This does not affect the total staked assets of the position.

_Users must own the NFT representing the position they wish to claim rewards from and must meet eligibility criteria (see StakersEligibility and AccessControl facets).

Emits a {RewardsReceived} event on condition._

Parameters

NameTypeDescription
nftIduint256The unique identifier of the NFT associated with the position.