SELF Made Pool
Migrate to new SELF Syrup Pool
The new SelfPool is a new $SELF staking contract built based on the SelfVault (the current auto SELF pool) and designed to work with SMBSwap MasterChef v2 to provide "stake $SELF, earn $SELF" functionality while offering more features such as fixed-term staking. The current Manual SELF pool will be retired after the migration.
The new SelfPool will use a dummy token to harvest $SELF from MasterChef v2 and reward them to users who are staking $SELF. Users who lock their $SELF for longer will receive a more significant number of shares (boosted linearly based on duration), therefore, enjoy a higher yield.
If you are currently using
enterStaking
and leaveStaking
on the SMBSwap MasterChef (0x3d03d12F95Bdc4509804f9Bcee4139b7789DC516), you will need to migrate to the new contract.With the new SelfPool, rewards are distributed proportionally to all pool users based on shares. Similar to “interest-bearing tokens” or other share-based models, users’ staking balance will grow when more rewards are being put into the pool. Users don’t need to harvest and compound their rewards.
In the new SelfPool, all flexible staking users will be subjected to two sets of fees.
A 2% fee will apply to all the rewards generated by flexible staking. The amount of the fee will be calculated and realized upon the next deposit or withdrawal action, cut from users’ shares. To query the number of the unrealized performance fee, use
calculatePerformanceFee(address _user)
. A 0.1% withdrawal fee will apply to the unstaking amount if you withdraw within 72 hours of the last deposit action. The withdrawal fee is cut from the final withdrawal amount before the SELF transfer.
If you are currently using the
enterStaking(uint256 _amount)
on the current SMBSwap MasterChef, you need to migrate to deposit(uint256 _amount, uint256 _lockDuration)
. For flexible staking, simply use “0” as _lockDuration
.Global variables: SELFPoolContract // SELF pool contract
struct UserInfo {
uint256 shares; // number of shares for a user.
uint256 lastDepositedTime; // keep track of deposited time for potential penalty.
uint256 SELFAtLastUserAction; // keep track of SELF deposited at the last user action.
uint256 lastUserActionTime; // keep track of the last user action time.
uint256 lockStartTime; // lock start time.
uint256 lockEndTime; // lock end time.
uint256 userBoostedShare; // boost share, in order to give the user higher reward. The user only enjoys the reward, so the principal needs to be recorded as a debt.
bool locked; //lock status.
uint256 lockedAmount; // amount deposited during lock period.
}
const userInfo. = await SELFPoolContract.userInfo(address);
const PricePerFullShare = await SELFPoolContract.getPricePerFullShare();
const selfAmount = userInfo.shares * PricePerFullShare / 1e18 - userInfo.userBoostedShare ; // self amount (wei), in flexible staking, userInfo.userBoostedShare should be 0.
Performance Fee
Query from contract:
const performanceFeeAmount = await SELFPoolContract.calculatePerformanceFee(address);
Calculate it manually:
async function calculatePerformanceFeeAmount(_user:address){ const user = await SELFPoolContract.userInfo(address); const isFreeFee = await SELFPoolContract.freeFeeUsers(_user); //normal free fee users are some special contracts , so you can set default false if(user.shares > 0 && !user.locked && !isFreeFee){ const PricePerFullShare = await SELFPoolContract.getPricePerFullShare(); uint256 totalAmount = user.shares * PricePerFullShare / 1e18; uint256 earnAmount = totalAmount - user.SELFAtLastUserAction; uint256 performanceFee = await SELFPoolContract.performanceFee(); uint256 currentPerformanceFee = (earnAmount * performanceFee) / 10000; return currentPerformanceFee; } return 0; }
function calculateOverdueFee(address _user) public view returns (uint256) {
Overdue Fee: (only applies to locked staking)
Query from contract:
const overdueFeeAmount = await SELFPoolContract.calculateOverdueFee(address);
UserInfo storage user = userInfo[_user];
Calculate it manually:
async function calculateOverdueFee(_user:address){
const user = await SELFPoolContract.userInfo(address);
const isFreeFee = await SELFPoolContract.freeFeeUsers(_user); //normal free fee users are some special contracts , so you can set default false
const UNLOCK_FREE_DURATION = 1 week seconds (or you can get from smart contract, const UNLOCK_FREE_DURATION = await SELFPoolContract.UNLOCK_FREE_DURATION())
const DURATION_FACTOR_OVERDUE = 180 * 24 * 3600; // 180 days, in order to calculate overdue fee. you can get it from contract too.
if (
user.shares > 0 &&
user.locked &&
!isFreeFee &&
((user.lockEndTime + UNLOCK_FREE_DURATION) < block.timestamp)
) {
const PricePerFullShare = await SELFPoolContract.getPricePerFullShare();
uint256 currentAmount = user.shares * PricePerFullShare / 1e18 - user.userBoostedShare;
uint256 earnAmount = currentAmount - user.lockedAmount;
uint256 overdueDuration = block.timestamp - user.lockEndTime - UNLOCK_FREE_DURATION; // you can use UTC timestamp to replace current block.timestamp.
if (overdueDuration > DURATION_FACTOR_OVERDUE) {
overdueDuration = DURATION_FACTOR_OVERDUE;
}
// Rates are calculated based on the user's overdue duration.
uint256 overdueWeight = (overdueDuration * overdueFee) / DURATION_FACTOR_OVERDUE;
uint256 currentOverdueFee = (earnAmount * overdueWeight) / PRECISION_FACTOR;
return currentOverdueFee;
}
return 0;
}
Withdraw Fee
const user = await SELFPoolContract.userInfo(address); const withdrawFee = await SELFPoolContract.withdrawFee(); const isFreeFee = await SELFPoolContract.freeFeeUsers(_user); //normal free fee users are some special contracts , so you can set default false
let WithdrawFeeAmount = 0;
// you can use UTC timestamp to replace current block.timestamp.
// withdrawFeePeriod = 72 * 3600 (S)
// _amount : withdraw amount
if (!isFreeFee && (block.timestamp < user.lastDepositedTime + withdrawFeePeriod)) {
WithdrawFeeAmount = _amount * withdrawFee;
}
SELF staking amount (after subtracting all the fees)
const user = await SELFPoolContract.userInfo(address);
const selfAmountWithoutFee = selfAmount - (!user.locked ? performanceFeeAmount : overdueFeeAmount) - withdrawFeeAmount
Pending Rewards
Please note that the new pool does not require any compounding. Rewards are being put into your staking balance automatically.
However, you can query the number of SELF earned since the last action, using the difference between the current staking balance (mentioned above), and the number from
userInfo.SELFAtLastUserAction
.If you are using the
leaveStaking(uint256 _amount)
method on the current SMBSwap MasterChef. You need to migrate to withdraw(uint256 _shares)
.When doing flexible staking. Please note that upon withdrawing, the pending reward fees will be calculated and cut from the number of users’ shares, the actual number of shares being withdrawn will be re-calibrated, based on the percentage of the shares you are withdrawing against the total shares you have. See the example below:
// the number of SELF being withdrawn can be calculated by:
withdrawPercentage = _sharesToWithdraw / userInfo.shares
stakingBalance = userInfo.shares * PricePerFullShare / 1e18 - userInfo.userBoostedShare - !userInfo.locked ? calculatePerformanceFee(_userAddress) : calculateOverdueFee(_userAddress)
finalWithdrawAmount = withdrawPercentage * stakingBalance
Please note that the final receiving amount will be affected by withdraw fee. If your function relies crucially on the final number of SELF being withdrawn, we recommend calculating that using the difference in SELF balance before and after the withdrawal action:
selfBalPrev = SELF.balanceOf(address(this))
SELFPool.withdraw(_sharesToWithdraw)
selfBalNew = SELF.balanceOf(address(this))
selfWithdrawn = selfBalNew - selfBalPrev
Or, calculate and subtract the withdraw fee when estimating the amount.
Previously, the manual SELF pool had a fixed 10 SELF/ block emission. After migrating to MasterChef v2 and the new SELF pool, we can now adjust its emissions.
And here's how you can calculate the SELF per block distributed to the new SELF pool:
selfPerBlockToPool = MasterChef.selfPerBlock(false) * (selfPool.allocPoint / MasterChef.totalSpecialAllocPoint)
You can query the self
Pool.allocPoint
using MasterChef.poolInfo(0)
Contract name: SelfPool
Contract address: 0x54EFf9d9539206514fbc2E5b00fBE4168faD4Aa0
Last modified 10mo ago