Tigris Trade contest - hansfriese's results

A multi-chain decentralized leveraged exchange featuring instant settlement and guaranteed price execution on 30+ pairs.

General Information

Platform: Code4rena

Start Date: 09/12/2022

Pot Size: $90,500 USDC

Total HM: 35

Participants: 84

Period: 7 days

Judge: GalloDaSballo

Total Solo HM: 12

Id: 192

League: ETH

Tigris Trade

Findings Distribution

Researcher Performance

Rank: 4/84

Findings: 7

Award: $4,709.84

QA:
grade-a

🌟 Selected for report: 2

🚀 Solo Findings: 0

Findings Information

🌟 Selected for report: hihen

Also found by: HollaDieWaldfee, __141345__, hansfriese, rvierdiiev, unforgiven

Labels

bug
3 (High Risk)
satisfactory
duplicate-170

Awards

414.0541 USDC - $414.05

External Links

Lines of code

https://github.com/code-423n4/2022-12-tigris/blob/588c84b7bb354d20cbca6034544c4faa46e6a80e/contracts/BondNFT.sol#L178 https://github.com/code-423n4/2022-12-tigris/blob/588c84b7bb354d20cbca6034544c4faa46e6a80e/contracts/Lock.sol#L38

Vulnerability details

Impact

LP Rewards can be increased infinitely by a malicious liquidity provider

Proof of Concept

A Bond NFT holder can claim pending rewards from a bond using the function Lock.claim().

function claim(
    uint256 _id
) public returns (address) {
    claimGovFees();
    (uint _amount, address _tigAsset) = bondNFT.claim(_id, msg.sender);
    IERC20(_tigAsset).transfer(msg.sender, _amount);
    return _tigAsset;
}

This claim() function calls the BondNFT.claim() and if the bond is an expired one there is an additional process to reimburse the rewards for that expired bond.

So the protocol calculates how much rewards were allocated to that expired bond from the bond.expireEpoch to epoch[bond.asset] and reimburse that to the current accRewardsPerShare.

The problem is it calculates the _pendingDelta using the bond.expireEpoch and epoch[bond.asset] while this can be called multiple times.

    function claim(
        uint _id,
        address _claimer
    ) public onlyManager() returns(uint amount, address tigAsset) {
        Bond memory bond = idToBond(_id);
        require(_claimer == bond.owner, "!owner");
        amount = bond.pending;
        tigAsset = bond.asset;
        unchecked {
            if (bond.expired) {
                uint _pendingDelta = (bond.shares * accRewardsPerShare[bond.asset][epoch[bond.asset]] / 1e18 - bondPaid[_id][bond.asset]) - (bond.shares * accRewardsPerShare[bond.asset][bond.expireEpoch-1] / 1e18 - bondPaid[_id][bond.asset]);
                if (totalShares[bond.asset] > 0) {
                    accRewardsPerShare[bond.asset][epoch[bond.asset]] += _pendingDelta*1e18/totalShares[bond.asset];
                }
            }
            bondPaid[_id][bond.asset] += amount;
        }
        IERC20(tigAsset).transfer(manager, amount);
        emit ClaimFees(tigAsset, amount, _claimer, _id);
    }

As we can see, the claim() function does not do anything to release the expired bond or remember the time that this reimbursement happens. So an expired bond holder can trigger this function by calling Lock.claim() repeatedly to increase the accRewardsPerShare. This means the rewards for liquidity providers can be increased to infinite.

Tools Used

Manual Review

Release the bond if expired in the function BondNFT.claim() or remember the last time that the _pendingDelta was calculated to make sure the reimbursement does not happen more than once.

#0 - c4-judge

2022-12-20T16:21:32Z

GalloDaSballo marked the issue as duplicate of #68

#1 - c4-judge

2022-12-20T16:23:31Z

GalloDaSballo marked the issue as duplicate of #170

#2 - c4-judge

2023-01-22T17:39:23Z

GalloDaSballo marked the issue as satisfactory

Findings Information

🌟 Selected for report: hansfriese

Also found by: 0x52, 0xA5DF, bin2chen

Labels

bug
3 (High Risk)
disagree with severity
judge review requested
primary issue
selected for report
sponsor confirmed
H-09

Awards

996.797 USDC - $996.80

External Links

Lines of code

https://github.com/code-423n4/2022-12-tigris/blob/b2ebb8ea1def4927a747e7a185174892506540ab/contracts/Trading.sol#L625-L627

Vulnerability details

Impact

Users can bypass the maxWinPercent limit using a partial closing.

As a result, users can receive more funds than their upper limit from the protocol.

Proof of Concept

As we can see from the documentation, there is limitation of a maximum PnL.

Maximum PnL is +500%. The trade won't be closed unless the user sets a Take Profit order or closes the position manually.

And this logic was implemented like below in _closePosition().

File: 2022-12-tigris\contracts\Trading.sol
624:                 _toMint = _handleCloseFees(_trade.asset, uint256(_payout)*_percent/DIVISION_CONSTANT, _trade.tigAsset, _positionSize*_percent/DIVISION_CONSTANT, _trade.trader, _isBot);
625:                 if (maxWinPercent > 0 && _toMint > _trade.margin*maxWinPercent/DIVISION_CONSTANT) { //@audit bypass limit
626:                     _toMint = _trade.margin*maxWinPercent/DIVISION_CONSTANT;
627:                 }

But it checks the maxWinPercent between the partial payout and full margin so the below scenario is possible.

  1. Alice opened an order of margin = 100 and PnL = 1000 after taking closing fees.
  2. If maxWinPercent = 500%, Alice should receive 500 at most.
  3. But Alice closed 50% of the position and she got 500 for a 50% margin because it checks maxWinPercent with _toMint = 500 and _trade.margin = 100
  4. After she closed 50% of the position, the remaining margin = 50 and PnL = 500 so she can continue step 3 again and again.
  5. As a result, she can withdraw almost 100% of the initial PnL(1000) even though she should receive at most 500.

Tools Used

Manual Review

We should check the maxWinPercent between the partial payout and partial margin like below.

    _toMint = _handleCloseFees(_trade.asset, uint256(_payout)*_percent/DIVISION_CONSTANT, _trade.tigAsset, _positionSize*_percent/DIVISION_CONSTANT, _trade.trader, _isBot);

    uint256 partialMarginToClose = _trade.margin * _percent / DIVISION_CONSTANT; //+++++++++++++++++++++++
    if (maxWinPercent > 0 && _toMint > partialMarginToClose*maxWinPercent/DIVISION_CONSTANT) { 
        _toMint = partialMarginToClose*maxWinPercent/DIVISION_CONSTANT;
    }

#0 - c4-judge

2022-12-22T02:07:48Z

GalloDaSballo marked the issue as duplicate of #111

#1 - TriHaz

2022-12-26T07:12:15Z

I don't think any of this, #339, #487 is a duplicate of #111 But I would label this as primary for better mitigation. Also I would label it as med risk as a +500% win is required so assets are not in a direct risk. @GalloDaSballo

#2 - c4-sponsor

2022-12-26T07:12:25Z

TriHaz marked the issue as sponsor confirmed

#3 - c4-sponsor

2022-12-26T07:12:30Z

TriHaz marked the issue as disagree with severity

#4 - c4-sponsor

2023-01-03T19:12:40Z

TriHaz requested judge review

#5 - GalloDaSballo

2023-01-12T11:11:29Z

Thank you for flagging @TriHaz , will re-dedoup

#6 - c4-judge

2023-01-22T10:04:12Z

GalloDaSballo marked the issue as not a duplicate

#7 - GalloDaSballo

2023-01-22T10:05:41Z

@TriHaz I see your point, and agree that the findings are different, thank you for the flag

#8 - c4-judge

2023-01-22T10:05:58Z

GalloDaSballo marked the issue as primary issue

#9 - GalloDaSballo

2023-01-22T10:07:38Z

The Warden has shown how, by partially closing an order, it is possible to bypass the maxWinPercent cap.

Per similar discussion to #111 the fact that not every trade can be above 500% in payout is not a guarantee that some trade will be, and those that will, will cause the invariant to be broken and LPs to be deeper in the red than they should.

Because this causes an immediate gain to the attacker, at a loss for LPs, I agree with High Severity.

#10 - c4-judge

2023-01-22T17:39:03Z

GalloDaSballo marked the issue as selected for report

Findings Information

🌟 Selected for report: 0x52

Also found by: hansfriese, noot

Labels

bug
3 (High Risk)
satisfactory
sponsor disputed
upgraded by judge
duplicate-622

Awards

1135.951 USDC - $1,135.95

External Links

Lines of code

https://github.com/code-423n4/2022-12-tigris/blob/b2ebb8ea1def4927a747e7a185174892506540ab/contracts/TradingExtension.sol#L118

Vulnerability details

Impact

TradingExtension._limitClose() returns a wrong stoploss which is favorable for users and it would be a significant loss for the protocol.

Proof of Concept

TradingExtension._limitClose() is used to set takeprofit/stoploss prices for the pending order and execute them.

File: 2022-12-tigris\contracts\TradingExtension.sol
088:     function _limitClose(
089:         uint _id,
090:         bool _tp,
091:         PriceData calldata _priceData,
092:         bytes calldata _signature
093:     ) external view returns(uint _limitPrice, address _tigAsset) {
    ...
111:         } else {
112:             if (_trade.slPrice == 0) revert("7"); //LimitNotSet
113:             if (_trade.direction) {
114:                 if (_trade.slPrice < _price) revert("6"); //LimitNotMet
115:             } else {
116:                 if (_trade.slPrice > _price) revert("6"); //LimitNotMet
117:             }
118:             _limitPrice = _trade.slPrice; //@audit should be _price
119:         }
120:     }

It always returns a preset takeprofit or stoploss after checking prices and stoploss price shouldn't be set like that.

For example, a user opens a long position at 100 and sets stoploss = 90 and if the price drops to 80 rapidly, the position will be closed automatically at 80(not 90 the stoploss price).

I've been a trader for several years and it's normal for many trading platforms.

But in the documentation, it says Stop order prices are not guaranteed. which is absolutely correct but no mention about stoploss which isn't guaranteed as well.

Totally, the current implementation of stoploss is profitable for users as they can close orders at predetermined stoploss without any slippage.

It means the protocol would suffer some loss because of slippage instead of traders.

Tools Used

Manual Review

Recommend using a not guaranteed stoploss like normal trading platforms.

function _limitClose( uint _id, bool _tp, PriceData calldata _priceData, bytes calldata _signature ) external view returns(uint _limitPrice, address _tigAsset) { ... } else { if (_trade.slPrice == 0) revert("7"); //LimitNotSet if (_trade.direction) { if (_trade.slPrice < _price) revert("6"); //LimitNotMet } else { if (_trade.slPrice > _price) revert("6"); //LimitNotMet } _limitPrice = _price; //++++++++++++++++++++++++++++ } }

#0 - GalloDaSballo

2022-12-19T20:51:27Z

Stop loss being triggered with set price vs oracle price, definitely worth flagging

#1 - c4-judge

2022-12-22T15:00:30Z

GalloDaSballo marked the issue as primary issue

#2 - GalloDaSballo

2022-12-22T15:00:37Z

Making primary for marginally more interesting description

#3 - TriHaz

2023-01-06T22:08:08Z

I don't see what's wrong, we offer guaranteed stoploss.

For example, a user opens a long position at 100 and sets stoploss = 90 and if the price drops to 80 rapidly, the position will be closed automatically at 80(not 90 the stoploss price).

it will be closed whenever the price passes the target price but with the preset sl, so if dropped to 80 rapidly, position will be closed with price 90.

#4 - c4-sponsor

2023-01-06T22:08:22Z

TriHaz marked the issue as sponsor disputed

#5 - GalloDaSballo

2023-01-16T12:58:50Z

Will need to check further, but rationally speaking, if the SL offers a better price than what the oracle is offering, then the system is ripe for exploiting as it's offering an execution price that is favourable (+EV) vs the rest of the market, meaning this results in a risk free arbitrage for the trader at the detriment of the LPs

#6 - GalloDaSballo

2023-01-16T13:00:16Z

E.g.

-> Open at 100, go short on this or another platform to hedge -> SL at 90 -> Price goes to 80

-> My SL at 90 means I got the profit from the short + the reduced loss from the long (which should balance itself out, but instead the loss is capped as the execution price is higher than what should be offered)

#7 - c4-judge

2023-01-17T10:06:34Z

GalloDaSballo marked the issue as duplicate of #622

#8 - c4-judge

2023-01-17T10:06:56Z

GalloDaSballo changed the severity to 3 (High Risk)

#9 - c4-judge

2023-01-22T17:42:42Z

GalloDaSballo marked the issue as satisfactory

Findings Information

🌟 Selected for report: minhtrng

Also found by: 0Kage, Aymen0909, HollaDieWaldfee, Jeiwan, KingNFT, bin2chen, hansfriese, rvierdiiev

Labels

bug
3 (High Risk)
satisfactory
duplicate-659

Awards

201.2303 USDC - $201.23

External Links

Lines of code

https://github.com/code-423n4/2022-12-tigris/blob/b2ebb8ea1def4927a747e7a185174892506540ab/contracts/Trading.sol#L278

Vulnerability details

Impact

Users should deposit a full amount of _addMargin but it deducts _fee now.

As a result, users will pay less funds than they should.

Proof of Concept

In addToPosition(), users deposits _addMargin - _fee after the fee calculation.

File: 2022-12-tigris\contracts\Trading.sol
274:         uint _fee = _handleOpenFees(_trade.asset, _addMargin*_trade.leverage/1e18, _trader, _trade.tigAsset, false);
275:         _handleDeposit(
276:             _trade.tigAsset,
277:             _marginAsset,
278:             _addMargin - _fee, //@audit -fee?
279:             _stableVault,
280:             _permitData,
281:             _trader
282:         );

Actually, users should deposit _addMargin because several kinds of fees are paid in _handleOpenFees() already.

Other deposit logics like initiateMarketOrder() were implemented correctly but it's wrong only in addToPosition().

Tools Used

Manual Review

_handleDeposit() should use _addMargin instead of _addMargin - _fee.

#0 - c4-judge

2022-12-20T16:16:24Z

GalloDaSballo marked the issue as duplicate of #659

#1 - c4-judge

2023-01-22T17:43:16Z

GalloDaSballo marked the issue as satisfactory

Awards

1.1472 USDC - $1.15

Labels

bug
2 (Med Risk)
satisfactory
sponsor acknowledged
duplicate-377

External Links

Lines of code

https://github.com/code-423n4/2022-12-tigris/blob/b2ebb8ea1def4927a747e7a185174892506540ab/contracts/Trading.sol#L952 https://github.com/code-423n4/2022-12-tigris/blob/b2ebb8ea1def4927a747e7a185174892506540ab/contracts/Trading.sol#L493

Vulnerability details

Impact

New fee settings shouldn't be applied to already existing orders.

Otherwise, users might pay more fees than they've expected with the old fee settings.

Proof of Concept

Both OpenFees and CloseFees can be changed anytime by admin using setFees().

And when it calculates fees using _handleOpenFees() and _handleCloseFees(), it uses current fee settings and the below scenario would be possible.

  1. At the first time, the open fee was 0.05% and a user opened a limit order using initiateLimitOrder() as the fee is low.
  2. After that, the open fee was changed to 0.1% which is not good for a user.
  3. When the pending order is executed using executeLimitOrder(), new fee(0.1%) will be used and it's not fair for the user.

Similar cases would happen with close fees as well.

Tools Used

Manual Review

Recommend storing original fee settings when the order is opened and using those settings for the order.

#0 - TriHaz

2022-12-23T02:12:06Z

Valid but we are not going to change it as fees will only be changed by DAO voting, so traders will have enough time to decide if they want to close with current fees.

#1 - c4-sponsor

2022-12-23T02:12:12Z

TriHaz marked the issue as sponsor acknowledged

#2 - c4-judge

2022-12-23T17:56:16Z

GalloDaSballo marked the issue as primary issue

#3 - c4-judge

2023-01-22T13:48:26Z

GalloDaSballo marked the issue as duplicate of #377

#4 - c4-judge

2023-01-22T17:35:09Z

GalloDaSballo marked the issue as satisfactory

Findings Information

🌟 Selected for report: hansfriese

Also found by: bin2chen

Labels

bug
2 (Med Risk)
primary issue
selected for report
sponsor confirmed
M-18

Awards

738.3681 USDC - $738.37

External Links

Lines of code

https://github.com/code-423n4/2022-12-tigris/blob/b2ebb8ea1def4927a747e7a185174892506540ab/contracts/Trading.sol#L506

Vulnerability details

Impact

The open price of a stop order might be changed during execution but it doesn't validate StopLoss/TakeProfit for the changed price.

As a result, the executed market order might be closed immediately and there would be an unexpected loss for users.

Proof of Concept

As we can see from executeLimitOrder(), the open price might be changed to the current price for the stop order.

File: 2022-12-tigris\contracts\Trading.sol
480:     function executeLimitOrder(
481:         uint _id, 
482:         PriceData calldata _priceData,
483:         bytes calldata _signature
484:     ) 
485:         external
486:     {
487:         unchecked {
488:             _checkDelay(_id, true);
489:             tradingExtension._checkGas();
490:             if (tradingExtension.paused()) revert TradingPaused();
491:             require(block.timestamp >= limitDelay[_id]);
492:             IPosition.Trade memory trade = position.trades(_id);
493:             uint _fee = _handleOpenFees(trade.asset, trade.margin*trade.leverage/1e18, trade.trader, trade.tigAsset, true);
494:             (uint256 _price, uint256 _spread) = tradingExtension.getVerifiedPrice(trade.asset, _priceData, _signature, 0);
495:             if (trade.orderType == 0) revert("5");
496:             if (_price > trade.price+trade.price*limitOrderPriceRange/DIVISION_CONSTANT || _price < trade.price-trade.price*limitOrderPriceRange/DIVISION_CONSTANT) revert("6"); //LimitNotMet
497:             if (trade.direction && trade.orderType == 1) {
498:                 if (trade.price < _price) revert("6"); //LimitNotMet
499:             } else if (!trade.direction && trade.orderType == 1) {
500:                 if (trade.price > _price) revert("6"); //LimitNotMet
501:             } else if (!trade.direction && trade.orderType == 2) {
502:                 if (trade.price < _price) revert("6"); //LimitNotMet
503:                 trade.price = _price;
504:             } else {
505:                 if (trade.price > _price) revert("6"); //LimitNotMet
506:                 trade.price = _price; //@audit check sl/tp
507:             } 
508:             if(trade.direction) {
509:                 trade.price += trade.price * _spread / DIVISION_CONSTANT;
510:             } else {
511:                 trade.price -= trade.price * _spread / DIVISION_CONSTANT;
512:             }

But it doesn't validate sl/tp again for the new price so the order might have an invalid sl/tp.

The new price wouldn't satisfy the sl/tp requirements when the price was changed much from the original price due to the high slippage and the order might be closed immediately by sl or tp in this case.

Originally, the protocol validates stoploss only but I say to validate both of stoploss and takeprofit. (I submitted it as another issue to validate tp as well as sl).

Tools Used

Manual Review

Recommend validating sl/tp for the new trade.price in Trading.executeLimitOrder().

#0 - c4-judge

2022-12-22T00:55:54Z

GalloDaSballo marked the issue as primary issue

#1 - TriHaz

2023-01-10T15:07:05Z

The open price of a stop order might be changed during execution

Limit orders open price is guaranteed, so it will not be changed, so validating sl/tp again is not needed.

#2 - c4-sponsor

2023-01-10T15:07:12Z

TriHaz marked the issue as sponsor disputed

#3 - GalloDaSballo

2023-01-15T16:19:42Z

@TriHaz can you please check the following line

504:             } else {
505:                 if (trade.price > _price) revert("6"); //LimitNotMet
506:                 trade.price = _price; //@audit check sl/tp
507:             } 

and re-affirm your dispute?

Ultimately it looks like trade.price is changed to the new price from the feed, which is a "correct" price, but may not be a price the caller was originally willing to act on (not in range with SL / TP)

#4 - TriHaz

2023-01-16T22:19:56Z

Yes my review was not correct, the price for the stop orders are not guaranteed which makes this issue valid

#5 - c4-sponsor

2023-01-16T22:20:01Z

TriHaz marked the issue as sponsor confirmed

#6 - GalloDaSballo

2023-01-17T15:04:57Z

The warden has shown how, due to a lack of check, limit orders that pass the logic check may be executed even though the validation for their Stop Loss / Take Profit may not be hit

Given the level of detail I believe the finding to be of Medium Severity

#7 - c4-judge

2023-01-22T17:53:43Z

GalloDaSballo marked the issue as selected for report

#8 - GainsGoblin

2023-01-30T01:21:40Z

Mitigation: https://github.com/code-423n4/2022-12-tigris/pull/2#issuecomment-1419177423 Since this issue only affects TP and not SL, I only added a check for that.

Findings Information

Labels

bug
grade-a
QA (Quality Assurance)
sponsor confirmed
Q-06

Awards

1222.2894 USDC - $1,222.29

External Links

[L-01] GovNFT.setAllowedAsset() should check if the _asset is in assets array
    function setAllowedAsset(address _asset, bool _bool) external onlyOwner { 
        _allowedAsset[_asset] = _bool;
    }
    function distribute(address _tigAsset, uint _amount) external {
        if (assets.length == 0 || assets[assetsIndex[_tigAsset]] == address(0) || totalSupply() == 0 || !_allowedAsset[_tigAsset]) return;
        try IERC20(_tigAsset).transferFrom(_msgSender(), address(this), _amount) {
            accRewardsPerNFT[_tigAsset] += _amount/totalSupply();
        } catch {
            return;
        }
    }

When it checks a valid asset in distribute(), it checks if _tigAsset is in the assets array first.

So even if _allowedAsset[_asset] is true, the _asset will be considered as invalid if it's not in assets array.

Also, it's meaningless to set false to the _asset that is not in the array.

We can modify setAllowedAsset() like below.

    function setAllowedAsset(address _asset, bool _bool) external onlyOwner {
        require(assets[assetsIndex[_asset]] == _asset, "Should be added first"); //++++++++++++++++++
        _allowedAsset[_asset] = _bool;
    }
[L-02] There is no option to remove elements from assets array

There should be a deletion logic for assets even though an asset can be disabled using _allowedAsset.

[L-03] Needless logic that might bring an uint underflow
File: 2022-12-tigris\contracts\PairsContract.sol
161:             if (_idToOi[_asset][_tigAsset].longOi < 1e9) {
162:                 _idToOi[_asset][_tigAsset].longOi = 0;
163:             }

It resets longOi/shortOi for dust amounts and it might bring an uint underflow later because the longOi/shortOi are smaller than real values.

I think it would be almost impossible to happen as we check a minPositionSize but such unnecessary logic should be removed.

[L-04] Possible rounding issue
File: 2022-12-tigris\contracts\Trading.sol
295:             uint _newPrice = _trade.price*_trade.margin/_newMargin + _price*_addMargin/_newMargin;

There might be a rounding issue as it divides seperately and we can modify like below.

uint _newPrice = (_trade.price * _trade.margin + _price * _addMargin) / _newMargin;
[L-05] There should an lower limit for maxWinPercent

Currently, there is no lower limit of maxWinPercent and users will receive only 1/1e10 portion of margin when the admin sets maxWinPercent = 1 maliciously.

[L-06] Too strict requirements for _margin
File: 2022-12-tigris\contracts\Trading.sol
651:             IERC20(_marginAsset).transferFrom(_trader, address(this), _margin/_marginDecMultiplier);
652:             IERC20(_marginAsset).approve(_stableVault, type(uint).max);
653:             IStableVault(_stableVault).deposit(_marginAsset, _margin/_marginDecMultiplier);
654:             if (tigAsset.balanceOf(address(this)) != _balBefore + _margin) revert BadDeposit(); //@audit bad accuracy
655:             tigAsset.burnFrom(address(this), tigAsset.balanceOf(address(this)));

It reverts if the increased amount is the same as _margin and users might get unnecessary reverts.

For example, if _marginAsset is an USDC of 6 decimals, _margin should be divided by 1e12 and it will revert otherwise.

It would be confusing for normal users as they can input any _margin amount for the _tigAsset.

We can modify like below to work for any cases.

    IERC20(_marginAsset).transferFrom(_trader, address(this), _margin/_marginDecMultiplier);
    IERC20(_marginAsset).approve(_stableVault, type(uint).max);
    IStableVault(_stableVault).deposit(_marginAsset, (_margin + _marginDecMultiplier - 1)/_marginDecMultiplier); //++++++++++
    if (tigAsset.balanceOf(address(this)) < _balBefore + _margin) revert BadDeposit(); //++++++++++++
[L-07] Wrong comment
* @param _tp true if long, else short

It should be changed to @param _tp true if takeprofit, else stoploss

[L-08] Possible underflow

These parts are inside the unchecked block and the final amount after extracting fee would be huge for uint underflow.

This case would be happen when the fee is greater than margin which means feePercent * leverage > 1.

Currently it's not possible with the default fee/leverage settings but it might happen for some weird settings.

We should confirm the fee is less than margin for any cases.

[L-09] Not implemented like a documentation
    uint _newMargin = _trade.margin + _addMargin;
    uint _newPrice = _trade.price*_trade.margin/_newMargin + _price*_addMargin/_newMargin;
If you're adding to a long trade, your opening price will be higher and your liquidation price will be lower. If you're adding to a short trade, your opening price will be lower and your liquidation price will be higher.

But during the _newPrice calculation, the new price can be higher or lower than the original price according to current open price.

I am wondering if there should be another requirement to add positions in order to work as the document.

[N-01] Typo

Change interesr to interest

#0 - GalloDaSballo

2022-12-27T22:01:50Z

[L-01] GovNFT.setAllowedAsset() should check if the _asset is in assets array

L

[L-02] There is no option to remove elements from assets array

R

[L-03] Needless logic that might bring an uint underflow

L

[L-04] Possible rounding issue

L

[L-05] There should an lower limit for maxWinPercent

L

[L-06] Too strict requirements for _margin

L

[L-07] Wrong comment

NC

[L-08] Possible underflow

L

[L-09] Not implemented like a documentation

L

Another warden showed issues with the formula, but this one is too generic, I cannot find a logical connection between this comment and the report

[N-01] Typo

NC

#1 - c4-sponsor

2023-01-05T20:22:07Z

GainsGoblin marked the issue as sponsor confirmed

#2 - GalloDaSballo

2023-01-22T19:52:05Z

7L 1R 2NC

#3 - GalloDaSballo

2023-01-22T20:04:48Z

3L from Dups

10L 1R 2NC

#4 - c4-judge

2023-01-23T08:47:14Z

GalloDaSballo marked the issue as grade-a

AuditHub

A portfolio for auditors, a security profile for protocols, a hub for web3 security.

Built bymalatrax © 2024

Auditors

Browse

Contests

Browse

Get in touch

ContactTwitter