This project is a Non-Fungible Token (NFT) staking system. It allows users to stake their NFTs and earn rewards based on the staking score of their NFTs. The system supports multiple staking pools, each with its own staking module type and reward distribution.
-
Staking: Users can stake their NFTs in the staking pool. The staking score is determined by the staking module type of the pool. The system supports staking of multiple NFTs at once.
-
Reward Distribution: The system distributes rewards to the stakers based on their staking score. The reward distribution can be done for all staking pools or for a specific staking pool.
-
Claiming Rewards: Users can claim their rewards from the system. The rewards are distributed based on the staking score of the user's staked NFTs.
Here are the steps to setup and configure a staking pool in the NFT staking system:
Each token identifier has a 1:1 mapping with a staking pool type. One can register a new mapping using the following endpoint:
#[only_owner]
#[endpoint(createPool)]
fn register_new_staking_pool(
&self,
collection_token_identifier: TokenIdentifier,
staking_module_type: StakingModuleType,
)
Each NFT/SFT score can be granularly set using the score system:
base_asset_score
: a default score used for each nonce of the collectionnonce_asset_score
: a nonce specific score used to override thebase_asset_score
The collection score can be configured using the following endpoints:
#[only_owner]
#[endpoint(setBaseAssetScore)]
fn set_base_asset_score(
&self,
collection_token_identifier: &TokenIdentifier,
staking_module: &StakingModuleType,
score: usize,
)
#[only_owner]
#[endpoint(setNonceAssetScore)]
fn set_nonce_asset_score(
&self,
collection_token_identifier: &TokenIdentifier,
staking_module: &StakingModuleType,
score: usize,
nonces: MultiValueEncoded<u64>,
)
Each of the above endpoints expects both the collection token identifier and the staking module, although the staking module type was already configured during the prior step.
This is happening because an NFT/SFT collection can have one or more different scores based on the distributed staking reward.
Important notice: in order for a collection to benefit from the primary reward (distributed to the whole staking system) it must have a scored defined for the StakingModuleType::All
staking module.
The secondary level rewards are all rewards that will be distributed to a specific staking pool.
In order to support this feature, each reward token must be configured in the staking system and mapped to the corresponding StakingModuleType
s. The mapping relationship is 1 to many (1..*), which means one can distribute the same reward token to multiple pools as well as multiple tokens to the same pool.
Registering a token can be done using the following endpoint:
#[only_owner]
#[endpoint(registerRewardToken)]
fn register_reward_token(
&self,
reward_token_identifier: TokenIdentifier,
staking_module_type: StakingModuleType,
)
Users can stake their NFTs using the stake function. The function expects NFT/SFT transfers.
Limitations: The user can only send one NFT collection per transfer. This means the user can stake as many NFTs as needed but the items of each NFT collection must be sent in a new transaction. The user can, however, stake multiple NFTs/SFTs in the same transaction as long as they all have the same token_identifier
.
#[payable("*")]
#[endpoint(stake)]
fn stake(&self)
Each staked NFT or SFT will trigger a pending reward commit (to not affect the previously earned rewards with the stake change) and a score recalculation.
Each NFT/SFT collection must have their score values configured in order to be eligible for staking and earning rewards.
For supporting the currently staked SFTs, new stake endpoints will be added thus migrating the existing mechanisms to use this NFT staking system.
The system distributes rewards using the distribute_reward function. This function expects a single ESDT token payment transfer.
The reward_rate
each staked point will receive is computed as follows: transferred amount / aggregated score
for StakingModuleType::All
.
#[payable("*")]
#[endpoint(distributeGeneralReward)]
fn distribute_reward(&self)
For distributing rewards for a specific staking pool, the distribute_secondary_reward function is used. The function takes the reward token ID, staking pool token ID, and total reward amount as input.
#[payable("*")]
#[endpoint(distributeSecondaryReward)]
fn distribute_secondary_reward(&self, target: TokenIdentifier)
(Work in progress) This section is dedicated to breaking down the configurations DemiourgosHoldings will support within the NFT Staking System.
This collection will only yield rewards in the secondary reward distribution mechanism and 0 rewards from the primary reward distribution mechanism.
- module type:
StakingModuleType::SnakesSfts
base_asset_score(StakingModuleType::All)
set to 0base_asset_score(StakingModuleType::SnakesSfts)
set to 10000
This collection will yield primary rewards and, with the configuration below, will receive no secondary rewards. Each full set (of 10 pieces) will yield an extra 25 points for the staker.
- module type:
StakingModuleType::CodingDivisionSfts
base_asset_score(StakingModuleType::All)
set to 5full_set_bonus_score(StakingModuleType::All)
set to 25
NOT CONFIGURED YET - awaiting confirmation
This collection will yield both primary and secondary rewards.
- module type:
StakingModuleType::Bloodshed
base_asset_score(StakingModuleType::All)
set to 1nonce_asset_score(StakingModuleType::All)
set as follows:- Rare Bloodshed NFT: 3 Points
- Epic Bloodshed NFT: 4 Points
- Legendary Bloodshed NFT: 11 Points
base_asset_score(StakingModuleType::Bloodshed)
set to TBDnonce_asset_score(StakingModuleType::Bloodshed)
set to TBD
This collection will yield both primary and secondary rewards.
- module type:
StakingModuleType::XBunnies
base_asset_score(StakingModuleType::All)
set to 2nonce_asset_score(StakingModuleType::All)
set to 160 for legendary NFTs- Staking module specific scores to be set according to the holy GitBook.
Similar configuration to XBunnies and Bloodshed NFTs, details in the GitBook.
NOT CONFIGURED YET - awaiting confirmation
The whole logic can be used however and can support multiple scenarios that have not been defined here.
The logic breakdown is that one can configure:
- what algorithm should be used for computing the scores
- what reward is a staked NFT/SFT collection eligible for
- what share of the distributed reward each collection is eligible for
- different reward tokens for secondary reward distribution
- same/multiple token(s) for each/some/all staking pools
- and more..
- Open a terminal and go to the scripts folder
- Run
npm i
- Create the
.env
file (you can copy paste the.env.default
file, rename it and fill it with correct info) and update it accordingly
Running node index.js
should present a set of options, such as:
Please choose an option:
1. Deploy
2. Upgrade
3. Add Pool
Each of these options are being automatically configured based on the .env
file.
Each deployment will override the config.json
entry of the target .env ENVIRONMENT
variable.
Each upgrade will look for the pre-existing address in config.json
based on the target ENVIRONMENT
variable.
Deploys a new instance of the contract using the PEM file and ENVIRONMENT
settings provided in .env
. Will store the new address in config.json
upon successful execution.
Upgrades an existing instance of the contract using the same procedure explained above.
Creating or updating a pool happens using a .CSV configuration file. All the data that needs to be set for a pool must be formatted accordingly and saved in a .CSV file. Once prompted, specify the full path of the file and let the script do it's magic.
The script doesn't check if the CSV data is already committed to the blockchain. Every line of the CSV will be executed as a transaction - do not include data that is already been set unless you wish to spend more gas.
Possible columns:
- token identifier
- nonce filters:
- Nonces: semi-column separated - 11;23;409;1223;etc
- Nonce range: start and end nonces, inclusive values, one per column
- score
- full set bonus score
.CSV header
This is the parsing order and must be respected in order for the script to work properly. In case a nonce column does not apply, leave it empty.
Note
You can both include/exclude the headers, the script will ask you for confirmation before executing.
Token Identifier;Nonces;Nonce Range Start;Nonce Range End;Score;Full Set Score
You can check the scripts folder for a .CSV example used when initializing the subsidiaries.