Tigris Trade contest - Critical'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: 38/84

Findings: 3

Award: $271.34

🌟 Selected for report: 0

🚀 Solo Findings: 0

Findings Information

Labels

bug
3 (High Risk)
satisfactory
duplicate-462

Awards

133.3608 USDC - $133.36

External Links

Lines of code

https://github.com/code-423n4/2022-12-tigris/blob/588c84b7bb354d20cbca6034544c4faa46e6a80e/contracts/StableVault.sol#L44-L51 https://github.com/code-423n4/2022-12-tigris/blob/588c84b7bb354d20cbca6034544c4faa46e6a80e/contracts/StableVault.sol#L65-L72

Vulnerability details

Impact

When any of the listed stablecoins depeg, the bad coins will drive out the good coins, leaving only bad coins in StableVault, resulting in the collapse of StableVault.

Proof of Concept

Given:

StableVault listed three stablecoins: USDD, USDC, and USDT.

  1. Alice deposited 1,000 USDC and got 1,000 tigUSD (stable) back.
  2. USDD depegged to 0.8 USD.
  3. Bob bought 1000 USDD with 800 USDC, then deposited to StableVault, getting 1,000 tigUSD.
  4. Bob called a withdrawal burn of 1,000 tigUSD and got 1,000 USDC, earning 200 USDC.

Alice could not get any USDC back; she could only choose to withdraw USDD and bear the loss.

Each StableVault should only be allowed to wrap one token to its corresponding tigAsset.

#0 - GalloDaSballo

2022-12-18T16:50:20Z

External risk (Med), but no liquidity premium in code leads me to think this is valid

#1 - c4-judge

2022-12-22T14:48:19Z

GalloDaSballo marked the issue as duplicate of #462

#2 - GalloDaSballo

2022-12-22T14:48:21Z

Marking as dup for No Liquidity Premium

#3 - c4-judge

2023-01-22T17:36:59Z

GalloDaSballo marked the issue as satisfactory

Findings Information

🌟 Selected for report: KingNFT

Also found by: 0x52, Critical, chaduke, noot, orion

Labels

bug
2 (Med Risk)
downgraded by judge
satisfactory
duplicate-108

Awards

124.2162 USDC - $124.22

External Links

Lines of code

https://github.com/code-423n4/2022-12-tigris/blob/588c84b7bb354d20cbca6034544c4faa46e6a80e/contracts/utils/TradingLibrary.sol#L91-L122

Vulnerability details

Impact

initiateMarketOrder() can be called with a signed off-chain price data from the past _validSignatureTimer and that price will be taken as the open price of the position.

Because the difference of the current price to the price from _validSignatureTimer ago is known to the caller, it will be possible for the user to create a long position at a price 2% lower than the current market price, or a short position at a price 2% higher than the current market price.

If the _validSignatureTimer is long enough, combining with leverage, the attacker will be able to disrupt the market by draining all the profits from the other regular users.

https://github.com/code-423n4/2022-12-tigris/blob/588c84b7bb354d20cbca6034544c4faa46e6a80e/contracts/utils/TradingLibrary.sol#L91-L122

function verifyPrice(
    uint256 _validSignatureTimer,
    uint256 _asset,
    bool _chainlinkEnabled,
    address _chainlinkFeed,
    PriceData calldata _priceData,
    bytes calldata _signature,
    mapping(address => bool) storage _isNode
)
    external view
{
    address _provider = (
        keccak256(abi.encode(_priceData))
    ).toEthSignedMessageHash().recover(_signature);
    require(_provider == _priceData.provider, "BadSig");
    require(_isNode[_provider], "!Node");
    require(_asset == _priceData.asset, "!Asset");
    require(!_priceData.isClosed, "Closed");
    require(block.timestamp >= _priceData.timestamp, "FutSig");
    require(block.timestamp <= _priceData.timestamp + _validSignatureTimer, "ExpSig");
    require(_priceData.price > 0, "NoPrice");
    if (_chainlinkEnabled && _chainlinkFeed != address(0)) {
        int256 assetChainlinkPriceInt = IPrice(_chainlinkFeed).latestAnswer();
        if (assetChainlinkPriceInt != 0) {
            uint256 assetChainlinkPrice = uint256(assetChainlinkPriceInt) * 10**(18 - IPrice(_chainlinkFeed).decimals());
            require(
                _priceData.price < assetChainlinkPrice+assetChainlinkPrice*2/100 &&
                _priceData.price > assetChainlinkPrice-assetChainlinkPrice*2/100, "!chainlinkPrice"
            );
        }
    }
}

Signed _priceData should only be allowed for SL/TP and match limit orders. For those orders, only a priceData with a timestamp after the created timestamp of the order can be used.

#0 - c4-judge

2022-12-22T14:52:01Z

GalloDaSballo marked the issue as duplicate of #108

#1 - c4-judge

2023-01-16T09:45:04Z

GalloDaSballo changed the severity to 2 (Med Risk)

#2 - c4-judge

2023-01-22T17:49:33Z

GalloDaSballo marked the issue as satisfactory

Awards

13.7578 USDC - $13.76

Labels

bug
2 (Med Risk)
satisfactory
duplicate-533

External Links

Lines of code

https://github.com/code-423n4/2022-12-tigris/blob/588c84b7bb354d20cbca6034544c4faa46e6a80e/contracts/StableVault.sol#L44-L51

Vulnerability details

Impact

function deposit(address _token, uint256 _amount) public {
        require(allowed[_token], "Token not listed");
        IERC20(_token).transferFrom(_msgSender(), address(this), _amount);
        IERC20Mintable(stable).mintFor(
            _msgSender(),
            _amount*(10**(18-IERC20Mintable(_token).decimals()))
        );
    }

deposit will revert at L49 for tokens with decimals > 18.

Consider checking if the token's decimals < 18 in listToken().

#0 - c4-judge

2022-12-20T15:43:24Z

GalloDaSballo marked the issue as duplicate of #533

#1 - c4-judge

2023-01-22T17:45:08Z

GalloDaSballo marked the issue as satisfactory

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