# Overview

The liquidity mining staker design is comprised of one canonical position staking contract, Staker. The technical reference for this contract is [here](/dragonswap/resources/developer-resources/smart-contracts/dragonswapv2/staker/dragonswapv2staker.md) and the source code is [here](https://github.com/dragonswap-app/v2-staker).

### Data Structures[​](https://docs.uniswap.org/contracts/v3/reference/periphery/staker/Design#data-structures) <a href="#data-structures" id="data-structures"></a>

```solidity
struct Incentive {
  uint128 totalRewardUnclaimed;
  uint128 numberOfStakes;
  uint160 totalSecondsClaimedX128;
}

struct Deposit {
  address owner;
  uint96 numberOfStakes;
}

struct Stake {
  uint160 secondsPerLiquidityInsideInitialX128;
  uint128 liquidity;
}

struct IncentiveKey {
        IERC20Minimal rewardToken;
        IDragonswapV2Pool pool;
        uint256 startTime;
        uint256 endTime;
        address refundee;
}
```

State:

```solidity
IDragonswapV2Factory public immutable factory;
INonfungiblePositionManager public immutable nonfungiblePositionManager;

/// @dev bytes32 refers to the return value of IncentiveId.compute
mapping(bytes32 => Incentive) public incentives;

/// @dev deposits[tokenId] => Deposit
mapping(uint256 => Deposit) public deposits;

/// @dev stakes[tokenId][incentiveHash] => Stake
mapping(uint256 => mapping(bytes32 => Stake)) public stakes;

/// @dev rewards[rewardToken][msg.sender] => uint256
mapping(address => mapping(address => uint256)) public rewards;
```

Params:

```solidity
struct CreateIncentiveParams {
  address rewardToken;
  address pool;
  uint256 startTime;
  uint256 endTime;
  uint128 totalReward;
}

struct EndIncentiveParams {
  address creator;
  address rewardToken;
  address pool;
  uint256 startTime;
  uint256 endTime;
}

```

### Incentives[​](https://docs.uniswap.org/contracts/v3/reference/periphery/staker/Design#incentives) <a href="#incentives" id="incentives"></a>

#### `createIncentive(CreateIncentiveParams memory params)`[​](https://docs.uniswap.org/contracts/v3/reference/periphery/staker/Design#createincentivecreateincentiveparams-memory-params) <a href="#createincentivecreateincentiveparams-memory-params" id="createincentivecreateincentiveparams-memory-params"></a>

`createIncentive` creates a liquidity mining incentive program. The key used to look up an Incentive is the hash of its immutable properties.

**Check:**

* Incentive with these params does not already exist
* Timestamps: `params.endTime >= params.startTime`, `params.startTime >= block.timestamp`
* Incentive with this ID does not already exist.

**Effects:**

* Sets `incentives[key] = Incentive(totalRewardUnclaimed=totalReward, totalSecondsClaimedX128=0, rewardToken=rewardToken)`

**Interaction:**

* Transfers `params.totalReward` from `msg.sender` to self.
  * Make sure there is a check here and it fails if the transfer fails
* Emits `IncentiveCreated`

#### `endIncentive(EndIncentiveParams memory params)`[​](https://docs.uniswap.org/contracts/v3/reference/periphery/staker/Design#endincentiveendincentiveparams-memory-params) <a href="#endincentiveendincentiveparams-memory-params" id="endincentiveendincentiveparams-memory-params"></a>

`endIncentive` can be called by anyone to end an Incentive after the `endTime` has passed, transferring `totalRewardUnclaimed` of `rewardToken` back to `refundee`.

**Check:**

* `block.timestamp > params.endTime`
* Incentive exists (`incentive.totalRewardUnclaimed != 0`)

**Effects:**

* deletes `incentives[key]` (This is a new change)

**Interactions:**

* safeTransfers `totalRewardUnclaimed` of `rewardToken` to the incentive creator `msg.sender`
* emits `IncentiveEnded`

### Deposit/Withdraw Token[​](https://docs.uniswap.org/contracts/v3/reference/periphery/staker/Design#depositwithdraw-token) <a href="#depositwithdraw-token" id="depositwithdraw-token"></a>

**Interactions**

* `nonfungiblePositionManager.safeTransferFrom(sender, this, tokenId)`
  * This transfer triggers the onERC721Received hook

#### `onERC721Received(address, address from, uint256 tokenId, bytes calldata data)`[​](https://docs.uniswap.org/contracts/v3/reference/periphery/staker/Design#onerc721receivedaddress-address-from-uint256-tokenid-bytes-calldata-data) <a href="#onerc721receivedaddress-address-from-uint256-tokenid-bytes-calldata-data" id="onerc721receivedaddress-address-from-uint256-tokenid-bytes-calldata-data"></a>

**Check:**

* Make sure sender is dragonswapV2 nft

**Effects:**

* Creates a deposit for the token setting deposit `owner` to `from`.
  * Setting `owner` to `from` ensures that the owner of the token also owns the deposit. Approved addresses and operators may first transfer the token to themselves before depositing for deposit ownership.
* If `data.length>0`, stakes the token in one or more incentives

#### `withdrawToken(uint256 tokenId, address to, bytes memory data)`[​](https://docs.uniswap.org/contracts/v3/reference/periphery/staker/Design#withdrawtokenuint256-tokenid-address-to-bytes-memory-data) <a href="#withdrawtokenuint256-tokenid-address-to-bytes-memory-data" id="withdrawtokenuint256-tokenid-address-to-bytes-memory-data"></a>

**Checks**

* Check that a Deposit exists for the token and that `msg.sender` is the `owner` on that Deposit.
* Check that `numberOfStakes` on that Deposit is 0.

**Effects**

* Delete the Deposit `delete deposits[tokenId]`.

**Interactions**

* `safeTransferFrom` the token to `to` with `data`.
* emit `DepositTransferred(token, deposit.owner, address(0))`

### Stake/Unstake/Rewards[​](https://docs.uniswap.org/contracts/v3/reference/periphery/staker/Design#stakeunstakerewards) <a href="#stakeunstakerewards" id="stakeunstakerewards"></a>

#### `stakeToken`[​](https://docs.uniswap.org/contracts/v3/reference/periphery/staker/Design#staketoken) <a href="#staketoken" id="staketoken"></a>

**Check:**

* `deposits[params.tokenId].owner == msg.sender`
* Make sure incentive actually exists and has reward that could be claimed (incentive.rewardToken != address(0))
  * Check if this check can check totalRewardUnclaimed instead
* Make sure token not already staked

#### `claimReward`[​](https://docs.uniswap.org/contracts/v3/reference/periphery/staker/Design#claimreward) <a href="#claimreward" id="claimreward"></a>

**Interactions**

* `msg.sender` to withdraw all of their reward balance in a specific token to a specified `to` address.
* emit RewardClaimed(to, reward)

#### `unstakeToken`[​](https://docs.uniswap.org/contracts/v3/reference/periphery/staker/Design#unstaketoken) <a href="#unstaketoken" id="unstaketoken"></a>

To unstake an NFT, you call `unstakeToken`, which takes all the same arguments as `stake`.

**Checks**

* It checks that you are the owner of the Deposit
* It checks that there exists a `Stake` for the provided key (with exists=true).

**Effects**

* Deletes the Stake.
* Decrements `numberOfStakes` for the Deposit by 1.
* `totalRewardsUnclaimed` is decremented by `reward`.
* `totalSecondsClaimed` is incremented by `seconds`.
* Increments `rewards[rewardToken][msg.sender]` by the amount given by `getRewardInfo`.

#### `getRewardInfo`[​](https://docs.uniswap.org/contracts/v3/reference/periphery/staker/Design#getrewardinfo) <a href="#getrewardinfo" id="getrewardinfo"></a>

* It computes `secondsInsideX128` (the total liquidity seconds for which rewards are owed) for a given Stake, by:
  * using`snapshotCumulativesInside` from the DragonswapV2 core contract to get `secondsPerLiquidityInRangeX128`, and subtracting `secondsPerLiquidityInRangeInitialX128`.
  * Multiplying that by `stake.liquidity` to get the total seconds accrued by the liquidity in that period
* Note that X128 means it's a `UQ32X128`.
* It computes `totalSecondsUnclaimed` by taking `max(endTime, block.timestamp) - startTime`, casting it as a Q128, and subtracting `totalSecondsClaimedX128`.
* It computes `rewardRate` for the Incentive casting `incentive.totalRewardUnclaimed` as a Q128, then dividing it by `totalSecondsUnclaimedX128`.
* `reward` is then calculated as `secondsInsideX128` times the `rewardRate`, scaled down to a regular uint128.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.dragonswap.app/dragonswap/resources/developer-resources/smart-contracts/dragonswapv2/staker/overview.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
