57884 sc low staking tier manipulation via erc4626 shares slong
Submitted on Oct 29th 2025 at 11:21:47 UTC by @jo13 for Audit Comp | Belong
Report ID: #57884
Report Type: Smart Contract
Report severity: Low
Target: https://github.com/immunefi-team/audit-comp-belong/blob/main/contracts/v2/platform/BelongCheckIn.sol
Impacts: Theft of unclaimed yield
Description
Brief / Intro
Tiers are determined using the staking vault’s ERC20 share balance (sLONG) via staking.balanceOf(addr), while tier thresholds are expressed in LONG assets (18 decimals). Because sLONG shares are freely transferable and their asset backing varies with the ERC4626 exchange rate, an attacker can temporarily inflate their share balance (for example, by borrowing or transferring in shares at call time) to qualify for a higher tier. This reduces platform fees or increases payouts, causing direct protocol revenue loss and unfair advantages in production.
Vulnerability Details
Tier lookups use share balance (not assets):
// contracts/v2/platform/BelongCheckIn.sol // Venue tier lookup VenueStakingRewardInfo memory stakingInfo = stakingRewards[ _storage.contracts.staking.balanceOf(venueInfo.venue).stakingTiers() ].venueStakingInfo; // Promoter tier lookup PromoterStakingRewardInfo memory stakingInfo = stakingRewards[ _storage.contracts.staking .balanceOf(promoterInfo.promoter) .stakingTiers() ].promoterStakingInfo;Tier thresholds expect LONG asset amounts (18 decimals):
// contracts/v2/utils/Helper.sol /// @notice Resolves the staking tier based on the staked amount of LONG (18 decimals). function stakingTiers(uint256 amountStaked) external pure returns (StakingTiers tier) { if (amountStaked < 50000e18) { return StakingTiers.NoStakes; } else if (amountStaked >= 50000e18 && amountStaked < 250000e18) { return StakingTiers.BronzeTier; } else if (amountStaked >= 250000e18 && amountStaked < 500000e18) { return StakingTiers.SilverTier; } else if (amountStaked >= 500000e18 && amountStaked < 1000000e18) { return StakingTiers.GoldTier; } return StakingTiers.PlatinumTier; }ERC4626 shares vs assets distinction (shares are transferable):
// contracts/v2/periphery/Staking.sol contract Staking is Initializable, ERC4626, Ownable { // Deposit mints shares; share-based bookkeeping function _deposit(address by, address to, uint256 assets, uint256 shares) internal override { super._deposit(by, to, assets, shares); stakes[to].push(Stake({shares: shares, timestamp: block.timestamp})); } // Clear separation of assets and shares function emergencyWithdraw(uint256 assets, address to, address _owner) external returns (uint256 shares) { if (assets > maxWithdraw(_owner)) revert WithdrawMoreThanMax(); shares = previewWithdraw(assets); // convert assets -> shares _emergencyWithdraw(msg.sender, to, _owner, assets, shares); } }balanceOf(address)returns the sLONG share balance (ERC20), not LONG assets.Shares are freely transferable and their asset backing changes with reward distributions (exchange-rate effect).
Where tiers impact economics:
Venue deposit fees:
// contracts/v2/platform/BelongCheckIn.sol uint256 platformFee = stakingInfo.depositFeePercentage.calculateRate(venueInfo.amount);Promoter settlement fees:
// contracts/v2/platform/BelongCheckIn.sol uint24 percentage = promoterInfo.paymentInUSDC ? stakingInfo.usdcPercentage : stakingInfo.longPercentage; uint256 platformFees = percentage.calculateRate(toPromoter);
Exploitable patterns (how manipulation works):
Share-transfer spoof: Move/borrow sLONG shares into the address just-in-time, call the fee-sensitive function (
venueDeposit/distributePromoterPayments), then move them back.Exchange-rate gaming: Deposit before a large
distributeRewards()to mint more shares per asset; since tiers read shares, user qualifies for higher tiers with fewer underlying LONG than intended.
Impact Details
Direct revenue loss
Venues can momentarily appear in higher tiers to reduce
depositFeePercentageon large deposits.Promoters can reduce
usdcPercentage/longPercentagefees taken from their settlements by inflating tier at call time.
Systemic unfairness
Tiers are meant to reflect sustained asset commitment; using transferable shares allows benefits without genuine or time-weighted stake.
Severity mapping (Immunefi scope)
High – Theft of unclaimed yield/royalties (platform fee leakage across deposits/settlements).
References
Proof of Concept
The following test demonstrates manipulating the venue deposit fee by temporarily transferring sLONG shares to the venue address prior to calling venueDeposit, then returning them after the call.
Add this test to /home/jo/audit-comp-belong/test/v2/platform/belong-check-in.test.ts and run:
LEDGER_ADDRESS=0x0000000000000000000000000000000000000001 PK=0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef npx hardhat test test/v2/platform/belong-check-in.test.ts --grep "Staking tier manipulation"
Recommended Fixes (not exhaustive)
Use asset-equivalent balances when evaluating tiers. For ERC4626 vaults this means converting shares to assets using the vault’s conversion function (e.g.,
convertToAssets(balanceOf(addr))orpreviewRedeem(balanceOf(addr))) before passing tostakingTiers(...).Alternatively, track time-weighted or non-transferable stake accounting (e.g., maintain internal staking records that reflect lockups or non-transferable positions) if tiers should represent committed stakes.
Was this helpful?