xanoutas/solidity-security-findings huggingface dataset:
Precision Loss 
Audit: 2022-12-tigris-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2022-12-tigris/blob/main/contracts/BondNFT.sol#L65
https://github.com/code-423n4/2022-12-tigris/blob/main/contracts/BondNFT.sol#L113
https://github.com/code-423n4/2022-12-tigris/blob/main/contracts/BondNFT.sol#L122
https://github.com/code-423n4/2022-12-tigris/blob/main/contracts/BondNFT.sol#L180
https://github.com/code-423n4/2022-12-tigris/blob/main/contracts/BondNFT.sol#L225
https://github.com/code-423n4/2022-12-tigris/blob/main/contracts/BondNFT.sol#L241
https://github.com/code-423n4/2022-12-tigris/blob/main/contracts/GovNFT.sol#L175


# Vulnerability details

## Impact

As a result of operator precedence (division is done before multiplication, and multiplication before addition and subtraction), use of paranthesis is a must to avoid loss of precision. This is because if two values are divided first and are rounded down in case of incomplete division (solidity default behaviour), the final value (after multiplication and other operations) is lesser than what it would have been had all other requisite operations been done before division (as per the calculation requirements).

Following are explanations of the issues where there were found in the repository.

### BondNFT

1.  In `createLock`, the share calculation is done in the follwing manner:

`uint shares = _amount * _period / 365`

Since the division will be done first, and given that solidity always rounds down, there is loss of precision here. This leads to a lower share amount for a given lock amount.

It is recommended to change the share calculation so the multiplication happens first:

`uint shares = (_amount * _period) / 365`

2.  In `extendLock()`, the shares calculation `uint shares = (bond.amount + _amount) * (bond.period + _period) / 365;` loses precision by dividing the second parentheses first and then multipliying th result by the first parentheses. This causes the shares be a lower number.

Instead, it should changed so that all multiplication is done before the division:

`uint shares = ((bond.amount + _amount) * (bond.period + _period)) / 365;`

Similarly, the `bondPaid` calculation should be chenged likewise:

`bondPaid[_id][bond.asset] = (accRewardsPerShare[bond.asset][epoch[bond.asset]] * _bond.shares) / 1e18;`

3.  In `claim(_id, _claimer)`, the calculation for incrementing `accRewardsPerShare` loses precision by dividing 1e18 by totalShares\[bond.asset\] first. Instead, it should be changed to the following:

`accRewardsPerShare[bond.asset][epoch[bond.asset]] += (_pendingDelta*1e18)/totalShares[bond.asset];`

4.  In `distribute(_tigAsset, _amount)`, on this line:

`accRewardsPerShare[_tigAsset][aEpoch] += _amount * 1e18 / totalShares[_tigAsset];`

The accRewardsPerShare is incremented by a lower share amount due to loss of precision and rounding down. This should be changed to:

`accRewardsPerShare[_tigAsset][aEpoch] += (_amount * 1e18) / totalShares[_tigAsset];`

5.  In `idToBond(_id)`, the calculation:

`bond.pending =
[NAZ-M9] `isMinter` Can Be Granted By The Deployer Of `StableToken` And Mint/Burn Arbitrary Amount Of Tokens
Audit: 2022-12-tigris-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2022-12-tigris/blob/main/contracts/StableToken.sol#L9
https://github.com/code-423n4/2022-12-tigris/blob/main/contracts/StableToken.sol#L38


# Vulnerability details

## Impact
If the private key of the deployer or an address in the `isMinter` mapping is compromised, the attacker will be able to mint/burn an unlimited amount of tigUSD tokens.

## Tools Used
Manual Review

## Recommended Mitigation Steps
Consider removing the `isMinter` mapping making tigUSD only mintable by the owner, and make the `stableToken.sol` contract to be the owner and therefore the only minter.
in withdraw function in StableVault.sol Decimal overflow can happen.
Audit: 2022-12-tigris-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Impact
tokens with decimals higher than 18 will always revert.

## Proof of Concept
```
function withdraw(address _token, uint256 _amount) external returns (uint256 _output) {
        IERC20Mintable(stable).burnFrom(_msgSender(), _amount);
        _output = _amount/10**(18-IERC20Mintable(_token).decimals());
        IERC20(_token).transfer(
            _msgSender(),
            _output
        );
    }

```
## Tools Used
manually

## Recommended Mitigation Steps
Consider modifying how withdraw works so it could handle tokens with higher than 18 decimals.
[NAZ-M7] Value Range Validity for Fee Setter
Audit: 2022-12-tigris-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2022-12-tigris/blob/main/contracts/PairsContract.sol#L104


# Vulnerability details

## Impact
This function is used to update the fee multiplier per asset and doesn't have any checks to ensure that the variables being set is within some kind of value range.

## Proof of Concept
If set poorly on accidentally or by a malicious owner this could be set to a high value and steal funds from users.

## Tools Used
Manual Review

## Recommended Mitigation Steps
Each variable input parameter updated should have it's own value range checks to ensure their validity otherwise it could cause increased issues for users.
[NAZ-M6] Usage of `send()` Can Result In Revert
Audit: 2022-12-tigris-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2022-12-tigris/blob/main/contracts/GovNFT.sol#L157


# Vulnerability details

## Impact
The function `crossChain()` is used to transfer ETH/WETH to the layerZero endpoint. `send()` uses a fixed amount of gas, which was used to prevent Reentrancy. However this limit your protocol to interact with others contracts that need more than that to process the transaction.

## Proof of Concept
These will inevitably fail when: 
1. The withdrawer smart contract does not implement a payable fallback function. 
2. The withdrawer smart contract implements a payable fallback function which uses more than `2_300` gas units. 
3. The withdrawer smart contract implements a payable fallback function which needs less than `2_300` gas units but is called through a proxy that raises the call’s gas usage above `2_300`.

`send()` uses a fixed amount of gas, which can result in revert. https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/

## Tools Used
Manual Review

## Recommended Mitigation Steps
Use call instead of `send()`. Example: 
`(bool succeeded, ) = _to.call{value: _amount}(""); require(succeeded, "Transfer failed.");`
[NAZ-M4] `claimGovFees()` Will Eventually Will Revert Because Of Block Gas Limit
Audit: 2022-12-tigris-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2022-12-tigris/blob/main/contracts/Lock.sol#L113


# Vulnerability details

## Impact
`claimGovFees()` function is looping through all `bondNFT` assets and does at least `5 * assets.length` amount of external calls. This all consumes a lot of gas so, the function will revert.

## Proof of Concept
This function will always revert depending on the amount of assets the `bondNFT` has and can cause additional issues within the protocol.

## Tools Used
Manual Review

## Recommended Mitigation Steps
Consider limiting the amount of assets the loop can run through or break up the external calls into separate loops and functions.
ERC20 `approve` can fail for some tokens 
Audit: 2022-12-tigris-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2022-12-tigris/blob/0cb05a462e78c4470662e9d9a4f9ab587f266bb5/contracts/Trading.sol#L652
https://github.com/code-423n4/2022-12-tigris/blob/0cb05a462e78c4470662e9d9a4f9ab587f266bb5/contracts/Lock.sol#L117


# Vulnerability details


## ERC20 `approve` can fail for some tokens 
### Impact
Some tokens (like `USDT`) do not work when changing the allowance from an
existing non-zero allowance value. They must first be approved by zero and then the actual allowance must be approved.

Also `approve()` will fail for certain token implementations that do not return a boolean value. Hence it is recommend to use `safeIncreaseAllowance()` and `safeDecreaseAllowance`

### Proof of Concept
- ERC20 token that can be for example `USDT`
- `approve()` return value ignored

https://github.com/code-423n4/2022-12-tigris/blob/0cb05a462e78c4470662e9d9a4f9ab587f266bb5/contracts/Trading.sol#L652
            IERC20(_marginAsset).approve(_stableVault, type(uint).max);

https://github.com/code-423n4/2022-12-tigris/blob/0cb05a462e78c4470662e9d9a4f9ab587f266bb5/contracts/Lock.sol#L117
            IERC20(assets[i]).approve(address(bondNFT), type(uint256).max);

### Mitigation
- Add 0 `approve` before `approve`
- Consider using `safeIncreaseAllowance`  and `safeDecreaseAllowance` instead of `approve`
- Check return value values and `revert`/`emit` events if needed
Margin can't be totally removed in `removeMargin`
Audit: 2022-12-tigris-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2022-12-tigris/blob/0cb05a462e78c4470662e9d9a4f9ab587f266bb5/contracts/Trading.sol#L404-L441


# Vulnerability details


## Margin can't be totally removed in `removeMargin`
### Impact
Margin can't be removed totally because of revert

### Proof of Concept
As we can see in the added tag `@audit line 1`:
`uint256 _newMargin = _trade.margin - _removeMargin; //@audit line 1`
What means that
`_newMargin` = to actual `trade.margin` - `_removeMargin`, that is the amount of margin to remove. This shouldn't be an issue, but the problem is that divisions by 0 revert, if we try to removed all the margin:
`uint256 _newLeverage = _trade.margin * _trade.leverage / _newMargin;//@audit line 2 `
We can see that it will divide by `_newMargin` that is 0, and therefore, removing all the margin is not possible.

[Code](https://github.com/code-423n4/2022-12-tigris/blob/0cb05a462e78c4470662e9d9a4f9ab587f266bb5/contracts/Trading.sol#L404-L441):
```
 function removeMargin(
        uint256 _id,
        address _stableVault,
        address _outputToken,
        uint256 _removeMargin,
        PriceData calldata _priceData,
        bytes calldata _signature,
        address _trader
    )
        external
    {
        _validateProxy(_trader);
        _checkOwner(_id, _trader);
        _checkVault(_stableVault, _outputToken); 
        IPosition.Trade memory _trade = position.trades(_id); 
        if (_trade.orderType != 0) revert(); //IsLimit 
        IPairsContract.Asset memory asset = pairsContract.idToAsset(_trade.asset); 

        uint256 _newMargin = _trade.margin - _removeMargin; //@audit line 1
        uint256 _newLeverage = _trade.margin * _trade.leverage / _newMargin;//@audit line 2 

        if (_newLeverage > asset.maxLeverage) revert("!lev"); 
        (uint _assetPrice,) = tradingExtension.getVerifiedPrice(_trade.asset, _priceData, _signature, 0);
        (,int256 _payout) = TradingLibrary.pnl(_trade.direction, _assetPrice, _trade.price, _newMargin, _newLeverage, _trade.accInterest);
        unchecked {
            if (_payout <= int256(_newMargin*(DIVISION_CONSTANT-liqPercent)/DIVISION_CONSTANT)) revert LiqThreshold();
        }
        position.modifyMargin(_trade.id, _newMargin, _newLeverage); 
        _handleWithdraw(_trade, _stableVault, _outputToken, _removeMargin);
        emit MarginModified(_trade.id, _newMargin, _newLeverage, false, _trader);
    }
```
### Recommended mitigation steps
Add an if path to handle case where margin is removed to 0 by calling `removeMargin`
[NAZ-M3] `claimGovFees()` Must Approve 0 First
Audit: 2022-12-tigris-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2022-12-tigris/blob/main/contracts/Lock.sol#L113


# Vulnerability details

## Impact
Some tokens (like USDT) do not work when changing the allowance from an existing non-zero allowance value. They must first be approved by zero and then the actual allowance must be approved.

## Proof of Concept
Tokens like USDT would cause reverts and possibly other issues within the protocol.

## Tools Used
Manual Review

## Recommended Mitigation Steps
Consider not using tokens like USDT or approve to 0 before changing or setting allowances.
ERC20 Tokens with more than 18 `decimals` are not compatible
Audit: 2022-12-tigris-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2022-12-tigris/blob/0cb05a462e78c4470662e9d9a4f9ab587f266bb5/contracts/utils/TradingLibrary.sol#L115
https://github.com/code-423n4/2022-12-tigris/blob/0cb05a462e78c4470662e9d9a4f9ab587f266bb5/contracts/Trading.sol#L675
https://github.com/code-423n4/2022-12-tigris/blob/0cb05a462e78c4470662e9d9a4f9ab587f266bb5/contracts/Trading.sol#L643-L659
https://github.com/code-423n4/2022-12-tigris/blob/0cb05a462e78c4470662e9d9a4f9ab587f266bb5/contracts/StableVault.sol#L65-L72
https://github.com/code-423n4/2022-12-tigris/blob/0cb05a462e78c4470662e9d9a4f9ab587f266bb5/contracts/StableVault.sol#L44-L51


# Vulnerability details

## ERC20 Tokens with more than 18 `decimals` are not compatible
### Summary
Some token won't be compatible as used a hardcoded value of `18-decimals`, if decimals is bigger than 18, it will revert due to an underflow, and therefore, not be compatible.
### Impact
Some erc20 tokens not compatible as `StableVault.sol` functions `withdraw`, `deposit` will revert when used
Same happens on:
    - `Trading.sol` functions `_handleDeposit` and `_handleWithdraw`
    - `TradingLibrary.sol#verifyPrice()`

### References
https://ethereum.stackexchange.com/questions/118896/can-an-erc-20-have-more-than-18-decimals

### Proof of Concept
https://github.com/code-423n4/2022-12-tigris/blob/0cb05a462e78c4470662e9d9a4f9ab587f266bb5/contracts/StableVault.sol#L44-L51
```
    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()))
        );
    }
```

https://github.com/code-423n4/2022-12-tigris/blob/0cb05a462e78c4470662e9d9a4f9ab587f266bb5/contracts/StableVault.sol#L65-L72
```
    function withdraw(address _token, uint256 _amount) external returns (uint256 _output) {
        IERC20Mintable(stable).burnFrom(_msgSender(), _amount);
        _output = _amount/10**(18-IERC20Mintable(_token).decimals());
        IERC20(_token).transfer(
            _msgSender(),
            _output
        );
    }
```
`_handleDeposit`
https://github.com/code-423n4/2022-12-tigris/blob/0cb05a462e78c4470662e9d9a4f9ab587f266bb5/contracts/Trading.sol#L643-L659
```
 uint _marginDecMultiplier = 10**(18-ExtendedIERC20(_marginAsset).decimals());
```

`_handleWithdraw`
https://github.com/code-423n4/2022-12-tigris/blob/0cb05a462e78c4470662e9d9a4f9ab587f266bb5/contracts/Trading.sol#L675
```
 if (IERC20(_outputToken).balanceOf(address(this)) != _balBefore + _toMint/(10**(18-ExtendedIERC20(_outputToken).decimals()))) revert BadWithdraw();
```

And in `TradingLibrary.sol#verifyPrice()`
https://github.com/code-423n4/2022-12-tigris/blob/0cb05a462e78c4470662e9d9a4f9ab587f266bb5/contracts/utils/TradingLibrary.sol#L115
```
                uint256 assetChainlinkPrice = uint256(assetChainlinkPriceInt)
[NAZ-M2] Gov NFTs Can Be Stolen By Compromised Owner
Audit: 2022-12-tigris-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2022-12-tigris/blob/main/contracts/Lock.sol#L138


# Vulnerability details

## Impact
The `sendNFTs()` function is meant to be called by the owner to retrieve Gov NFTs. However, a compromised owner can use this function to then steal Gov NFTs.

## Proof of Concept
Mallory compromised an owner and uses `sendNFTs()` to steal all Gov NFTs.

## Tools Used
Manual Review

## Recommended Mitigation Steps
Consider removing `sendNFTs()` or documenting the possibility of it being used in a malicious way.
[NAZ-M1] Missing Checks That Could Lead To Issues
Audit: 2022-12-tigris-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2022-12-tigris/blob/main/contracts/BondNFT.sol#L57


# Vulnerability details

## Impact
The `createLock()` function is used by a manager contract to create a bond. It has a counter part function called `extendLock()` which is used to extend the lock period and/or amount of a bond. `extendLock()` has a check `require(bond.period+_period <= 365, "MAX PERIOD");` which makes sure the bond doesn't go over a year. However, `createLock()` is missing this check and a bond could be created with a greater than a year period.

## Tools Used
Manual Review

## Recommended Mitigation Steps
Consider adding `require(bond.period+_period <= 365, "MAX PERIOD");` to `createLock()` to prevent this.
`executeLimitOrder()` modifies open-interest with a wrong position value
Audit: 2022-12-tigris-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2022-12-tigris/blob/496e1974ee3838be8759e7b4096dbee1b8795593/contracts/Trading.sol#L513-L517


# Vulnerability details


The `PairsContract` registeres the total long/short position that's open for a pair of assets, whenever a new position is created the total grows accordingly.
However at `executeLimitOrder()` the position size that's added is wrongly calculated - it uses margin before fees, while the actual position is created after subtracting fees.

## Impact
The OpenInterest would register wrong values (11% diff in the case of PoC), which will distort the balance between long and short positions (the whole point of the OpenInterest is to balance them to be about equal).


## Proof of Concept
In the following test, an order is created with a x100 leverage, and the position size registered for OI is 11% greater than the actual position created.


```diff
diff --git a/test/07.Trading.js b/test/07.Trading.js
index ebe9948..dfb7f98 100644
--- a/test/07.Trading.js
+++ b/test/07.Trading.js
@@ -778,7 +778,7 @@ describe("Trading", function () {
      */
     it("Creating and executing limit buy order, should have correct price and bot fees", async function () {
       // Create limit order
-      let TradeInfo = [parseEther("1000"), MockDAI.address, StableVault.address, parseEther("10"), 0, true, parseEther("0"), parseEther("0"), ethers.constants.HashZero];
+      let TradeInfo = [parseEther("1000"), MockDAI.address, StableVault.address, parseEther("100"), 0, true, parseEther("0"), parseEther("0"), ethers.constants.HashZero];
       let PermitData = [permitSig.deadline, ethers.constants.MaxUint256, permitSig.v, permitSig.r, permitSig.s, true];
       await trading.connect(owner).initiateLimitOrder(TradeInfo, 1, parseEther("20000"), PermitData, owner.address);
       expect(await position.limitOrdersLength(0)).to.equal(1); // Limit order opened
@@ -787,6 +787,9 @@ describe("Trading", function () {
       await network.provider.send("evm_increaseTime", [10]);
       await network.provider.send("evm_mine");
 
+      let count = await position.getCount();
+      let id = count.toNumber() - 1;
+
       // Execute limit order
       let PriceData = [node.address, 0, parseEther("10000"), 10000000, 2000000000, false]; // 0.1% spread
       let message = ethers.utils.keccak256(
@@ -798,8 +801,22 @@ describe("Trading", function () {
       let sig = await node.signMessage(
         Buffer.from(message.substring(2), 'hex')
       );
+      // trading.connect(owner).setFees(true,3e8,1e8,1e8,1e8,1e8);
       
-      await trading.connect(user).executeLimitOrder(1, PriceData, sig);
+
+      let oi = await pairscontract.idToOi(0, stabletoken.address);
+      expect(oi.longOi.toNumber()).to.equal(0);
+      console.log({oi, stable:stabletoken.address});
+
+      await trading.connect(user).executeLimitOrder(id, PriceData, sig);
+      let trade = await position.trades(id);
+      console.log(trade);
+      oi = await pa
CONTRACT BALANCE OF TIGASSET IS WIPED OUT WHEN CALLING _HANDLEDEPOSIT() 
Audit: 2022-12-tigris-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2022-12-tigris/blob/main/contracts/Trading.sol#L643-L659


# Vulnerability details

## Impact
`_handleDeposit()` is internally invoked when calling `initiateMarketOrder()`, `addToPosition()`, `initiateLimitOrder()` or `addMargin()`. But if you were to  look  into the function body of `_handleDeposit()`, you would notice the tigAsset balance of Trading.sol getting entirely burned whenever the if conditions equal true. As a result, all dependencies and protocol logic relating to this flaw are going to be messed up, upsetting the entire project mechanism.      

## Proof of Concept
[Trading.sol#L643-L659](https://github.com/code-423n4/2022-12-tigris/blob/main/contracts/Trading.sol#L643-L659)

```
655:            tigAsset.burnFrom(address(this), tigAsset.balanceOf(address(this)));

657:            tigAsset.burnFrom(_trader, _margin);
```
The amount supposedly burned is `_margin` on line 657 if the first if statement of `_handleDeposit()` is skipped. However, `tigAsset.balanceOf(address(this))` will be burned if the function execution gets to line 655. 

## Recommended Mitigation Steps
It is recommended that amount to be burned on line 655 be replaced with `_margin` to fix the error.
Incorrect Handling of ERC20 Token Decimals
Audit: 2022-12-tigris-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Impact
It has been identified that the Tigris project incorrectly handles the decimals of ERC20 tokens. Specifically:

1. In the `StableVault.sol` contract, the calculation that scales the value of the deposited amount assumes that the `_token` supplied by the user has less than 18 decimals: `_amount*(10**(18-IERC20Mintable(_token).decimals()))`. This is not always true, as ERC20 tokens may have more than 18 decimals, resulting in a contract call that will revert.

2. There are some places in the codebase that ERC20 tokens are assumed to have 18 decimals. For example, in the `TradingLibrary.sol:verifyPrice()` function (https://github.com/code-423n4/2022-12-tigris/blob/588c84b7bb354d20cbca6034544c4faa46e6a80e/contracts/utils/TradingLibrary.sol#L115), the price provided in the `PriceData` struct is assumed to have 18 decimals. The contract then scales the price retrieved from a Chainlink feed to 18 decimals, assuming that the provided price(in the  `PriceData` struct ) has 18 decimals as well. This `verifyPrice` function is used to verify that the price provided in the `PriceData` struct is close to the one fetched from Chainlink. However, if the decimals of the ERC20 token are different than 18, this could result in incorrect calculations and even prevent trades from being initiated/closed or margin from being removed.

## Tools Used

- Manual code review

## Recommended Mitigation Steps

To handle the fact that ERC20 token decimals may be different than 18, a solution can be implemented to scale the token decimals between different ERC20 tokens. An example of this can be found in Chainlink's `scalePrice` function (https://docs.chain.link/docs/get-the-latest-price/#getting-a-different-price-denomination).