SeaShell.Financ Rug Pull

Reported loss Not disclosed
BNB Chain
Rugpull

What happened

SeaShell.Finance was a yield farm on BNB Smart Chain: a MasterChef contract that paid out SHELL tokens for staked LP tokens. De.Fi's REKT database lists SHELL as an exit scam / rug pull dated 2021-08-26 and gives no loss figure. The verified farm code contains three controls that work against stakers.

updatePool contains a line that, when called by feeAddress, transfers the pool's entire LP token balance to feeAddress. withdraw and emergencyWithdraw debit the user's full recorded stake but pay back only one hundred-thousandth of it. withdraw also pays only half of the pending SHELL reward.

How it happened

  1. Stakers deposit LP tokens through deposit. The contract takes the full amount (pool.lpToken.safeTransferFrom(address(msg.sender), address(this), _amount);) and credits user.amount, minus any depositFeeBP fee, which the owner can set up to 10000 (100%) through add/set (require(_depositFeeBP <= 10000, "set: invalid deposit fee basis points");). feeAddress is set to the deployer in the constructor.
  2. updatePool is public and runs inside deposit and withdraw. Before minting rewards it contains if(address(msg.sender) == feeAddress){pool.lpToken.transfer(feeAddress,lpSupply);}, where lpSupply = pool.lpToken.balanceOf(address(this)). When feeAddress itself calls updatePool (directly, or through a function that calls it) on a pool with stake and a non-zero allocPoint, the full LP balance of that pool moves to feeAddress. user.amount is not reduced, so the records still show the stake after the tokens are gone.
  3. A regular staker's exit is also reduced by the code. withdraw runs user.amount = user.amount.sub(_amount); for the full amount and then pool.lpToken.safeTransfer(address(msg.sender), _amount.div(1e5));, returning 0.001% of the amount withdrawn. For amounts below 100000 base units the integer division returns zero.
  4. emergencyWithdraw has the same shape: user.amount = 0; followed by pool.lpToken.safeTransfer(address(msg.sender), amount.div(1e5));.
  5. Rewards are cut as well: withdraw sends safeShellTransfer(msg.sender, pending.div(2));, half of the pending amount (after any early-harvest penalty), and then resets rewardDebt, so the other half is not paid to the user. updatePool also mints a further 1% of each reward to devAddress (shell.mint(devAddress, shellReward.div(100));); ShellToken.mint is onlyOwner, so that works only while the farm owns the token, which the supplied source does not show.
  6. What happened: De.Fi's REKT database lists SHELL as an exit scam / rug pull dated 2021-08-26 with no funds-lost value. No transaction was opened showing the updatePool line being triggered by feeAddress or any user's reduced withdrawal, so the steps above describe what the code allowed, not a confirmed sequence.

Protocol details

Classification Token
Protocol Type Exit Scam/Rugpull
Affected asset / contract SHELL
Protocol links Website

Build your security review skills

Work through hands-on labs covering real exploit mechanics, review techniques, and defensive patterns.