DeFiFoundry
60,000 USDC
View results
Submission Details
Severity: high
Invalid

User Can Transfer Position Ownership to Lock Liquidity Forever

Summary

The notifyAccountTransfer function does not check for address(0), which allows any user to transfer ownership to address(0). This is especially damaging because the liquidateAccounts function calls loadExisting, which will revert when the owner is address(0). This leads to a position that cannot be liquidated, effectively locking that amount of liquidity away from the liquidity providers forever.

Vulnerability Details

The notifyAccountTransfer function has no check for what the to address will be.

function notifyAccountTransfer(address to, uint128 tradingAccountId) external {
_onlyTradingAccountToken();
TradingAccount.Data storage tradingAccount = TradingAccount.loadExisting(tradingAccountId);
tradingAccount.owner = to; //@audit can be 0, lock all LP funds
}

This allows any user to set address(0) as the owner.

Whenever the loadExisting function is called on a tradingAccountId, a check is performed to ensure the owner actually exists. If the owner does not exist, the function and the transaction itself will revert.

function loadExisting(uint128 tradingAccountId) internal view returns (Data storage tradingAccount) {
tradingAccount = load(tradingAccountId);
if (tradingAccount.owner == address(0)) { //@audit revert here
revert Errors.AccountNotFound(tradingAccountId, msg.sender);
}
}

An attacker can leverage this vulnerability to make their position impossible to liquidate, which means that the amount of size that position is taking up will persist forever. This will permanently raise the open interest, limiting how many users can interact with the protocol. Most importantly, this will prevent liquidity providers from withdrawing liquidity as the attacker's position cannot be removed, and that position is entitled to its leveraged liquidity as long as it is open.

This vulnerability would present itself in two ways:

  1. An attacker wants to lock all liquidity and is willing to do so at a small loss.

    • Alice (attacker) calculates how much liquidity is available for traders and deposits ~1/100th of that amount.

    • Alice opens up a 100x position consuming all available liquidity.

    • Alice transfers ownership to address(0).

    • The position is liquidatable but any attempt fails.

    • LP's liquidity is locked forever as it will always be associated with Alice's (address(0)) account.

    • Example:

      • Alice deposits $10,000 USDC.

      • Alice opens a position with a size of $1,000,000.

      • Alice calls notifyAccountTransfer.

      • All liquidation attempts fail.

      • Liquidity is locked.

  2. A user is about to be liquidated and wants to bring the ship down with them.

    • Alice has a long position that is about to be liquidated.

    • Alice knows that position is a lost cause and transfers ownership to address(0).

    • All liquidation attempts fail.

    • Liquidity is locked.

With a high likelihood and high impact, this is a high-severity issue.

  • The high likelihood comes from this being easy to pull off with no preconditions.

  • The impact is high as well since the position can never be liquidated, limiting both how much liquidity traders can use and how much liquidity LPs can withdraw.

It is important to note that this is different from a typical zero address check finding, and this instance is not a known issue as it was not referenced in the automated analysis report.

Impact

  • Impossible to liquidate.

  • Diminished liquidity for traders.

  • Altogether making the protocol unusable.

Tools Used

  • Manual analysis

Recommendations

Modify the notifyAccountTransfer function to:

function notifyAccountTransfer(address to, uint128 tradingAccountId) external {
_onlyTradingAccountToken();
if (to == address(0)) {
revert Errors.ZeroAddress();
}
TradingAccount.Data storage tradingAccount = TradingAccount.loadExisting(tradingAccountId);
tradingAccount.owner = to;
}
Updates

Lead Judging Commences

inallhonesty Lead Judge
about 1 year ago
inallhonesty Lead Judge about 1 year ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.