sherlock-audit / 2023-12-dodo-gsp-judging

6 stars 5 forks source link

nuthan2x - Pool balancing swappers aren't attracted due to unupdated target state after sync action #138

Closed sherlock-admin closed 6 months ago

sherlock-admin commented 6 months ago

nuthan2x

medium

Pool balancing swappers aren't attracted due to unupdated target state after sync action

Summary

More pool balancers could have been attracted whenever there's a reserve change. But DODO v3 GSP doesn't update on certain actions.

Vulnerability Detail

Impact

Missing out the involvement of pool balancers due to unUpdated target states.

Code Snippet

    function _sync() internal {
        uint256 baseBalance = _BASE_TOKEN_.balanceOf(address(this)) - uint256(_MT_FEE_BASE_);
        uint256 quoteBalance = _QUOTE_TOKEN_.balanceOf(address(this)) - uint256(_MT_FEE_QUOTE_);
        require(baseBalance <= type(uint112).max && quoteBalance <= type(uint112).max, "OVERFLOW");
        if (baseBalance != _BASE_RESERVE_) {
            _BASE_RESERVE_ = uint112(baseBalance);
        }
        if (quoteBalance != _QUOTE_RESERVE_) {
            _QUOTE_RESERVE_ = uint112(quoteBalance);
        }
        if (_IS_OPEN_TWAP_) _twapUpdate();
    }

    function sync() external nonReentrant {
        _sync();
    }

Tool used

Manual Review

Recommendation

Skyewwww commented 6 months ago

Target is updated with the current R state before each swap action.

nevillehuang commented 6 months ago

Hi @Skyewwww could you point me to the logic where the swap action invokes update to the target based on current R state?

Skyewwww commented 6 months ago

Hi @Skyewwww could you point me to the logic where the swap action invokes update to the target based on current R state?

Hi, please refer to the querySellQuote/querySellBase. The target is updated using getPMMState().

    function getPMMState() public view returns (PMMPricing.PMMState memory state) {
        state.i = _I_;
        state.K = _K_;
        state.B = _BASE_RESERVE_;
        state.Q = _QUOTE_RESERVE_;
        state.B0 = _BASE_TARGET_; // will be calculated in adjustedTarget
        state.Q0 = _QUOTE_TARGET_;
        state.R = PMMPricing.RState(_RState_);
        PMMPricing.adjustedTarget(state);
    }

    function adjustedTarget(PMMState memory state) internal pure {
        if (state.R == RState.BELOW_ONE) {
            state.Q0 = DODOMath._SolveQuadraticFunctionForTarget(
                state.Q,
                state.B - state.B0,
                state.i,
                state.K
            );
        } else if (state.R == RState.ABOVE_ONE) {
            state.B0 = DODOMath._SolveQuadraticFunctionForTarget(
                state.B,
                state.Q - state.Q0,
                DecimalMath.reciprocalFloor(state.i),
                state.K
            );
        }
    }