Kingdomswap Rug Pull

Reported loss Not disclosed
BNB Chain
Rugpull

What happened

Kingdomswap's MasterKingdom contract (0xFDF9cAE8…2747bd on BNB Smart Chain) is a MasterChef-style farm that pays CROWN rewards for staked LP tokens and has an owner-only deposit-fee setter. set() writes depositFeeBP with no upper bound, and deposit() sends that share of every deposit to feeAddress. At 10000 basis points the entire deposit goes to feeAddress and the depositor is credited zero.

The owner also controls a launch-date gate that blocks deposit, withdraw, enterStaking and leaveStaking for everyone else while the date is in the future. De.Fi's REKT database lists Kingdomswap as an exit scam/rugpull dated 2021-08-25 with no loss figure or description; the source code alone does not show which of these powers, if any, was used.

How it happened

These steps describe what the code allows, not a recorded sequence of transactions.

  1. A user stakes an LP token with deposit(_pid, _amount), which pulls the tokens in with pool.lpToken.safeTransferFrom(address(msg.sender), address(this), _amount);.
  2. The owner can call set(_pid, _allocPoint, _depositFeeBP, _withUpdate), which does poolInfo[_pid].depositFeeBP = _depositFeeBP;. Unlike add(), which requires _depositFeeBP <= 10000, set() has no check at all.
  3. With a fee of 10000, deposit() computes uint256 depositFee = _amount.mul(pool.depositFeeBP).div(10000);, sends it with pool.lpToken.safeTransfer(feeAddress, depositFee); and credits user.amount = user.amount.add(_amount).sub(depositFee);, which is zero. The whole deposit goes to feeAddress and the depositor has nothing to withdraw. Values above 10000 make the subtraction underflow and the deposit revert.
  4. setLaunchDate() is onlyOwner and only requires launchDate < now. Every user-facing function carries onlyOwnerOrLaunched, which requires owner() == _msgSender() || (now > launchDate && launchDate > 0). Once a launch date has passed, the owner can set a new future date and non-owner calls to deposit, withdraw, enterStaking and leaveStaking revert until it arrives.
  5. emergencyWithdraw(uint256 _pid) has no onlyOwnerOrLaunched modifier and returns user.amount of staked LP tokens, so principal that was credited stays reachable through it even while the launch gate blocks the other functions. It forfeits pending rewards.
  6. Reward emission is also owner-tunable: updateMultiplierAtDate(uint256 multiplierNumber, uint256 date) accepts any multiplier, and updatePool() mints crown.mint(devaddr, crownReward.div(10)); plus crown.mint(address(serf), crownReward); scaled by it.

Protocol details

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

Build your security review skills

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