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.

Do I need to migrate?

If you are currently using enterStaking and leaveStaking on the SMBSwap MasterChef (0x3d03d12F95Bdc4509804f9Bcee4139b7789DC516), you will need to migrate to the new contract.

No more compounding

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.

Fee on flexible staking rewards

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).

Withdrawal fee

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.

Staking Balance and Fees

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)) 
selfBalNew = SELF.balanceOf(address(this)) 
selfWithdrawn = selfBalNew - selfBalPrev

Or, calculate and subtract the withdraw fee when estimating the amount.

How to calculate the SELF per block distributed to the new SELF pool?

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 selfPool.allocPoint using MasterChef.poolInfo(0)

Mainnet Contract Address

Contract name: SelfPool Contract address: 0x54EFf9d9539206514fbc2E5b00fBE4168faD4Aa0

View the SMBSwapSwap: SELF Pool Contract on BscScan.

Last updated