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
Rank: 31/84
Findings: 5
Award: $372.19
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: 0xsomeone
Also found by: 0xhacksmithh, 8olidity, Critical, Ruhum, SamGMK, Secureverse, Tointer, __141345__, aviggiano, rotcivegaf
133.3608 USDC - $133.36
In deposit()
, the user can send a certain amount of token
and then get a certain amount of stable token
. The original design here should only support stable coins for token
. But there is no operation to judge the price of stablecoins here. Too much trust in the price of stablecoins. We should learn from the events of USTC
. Once the price of a certain stablecoin falls. The stablevault
contract is not aware of price changes. Then the attacker can use the low-priced token
to deposit()
, exchange for stable token
, and then redeem other stable coins. Profit from it.
function deposit(address _token, uint256 _amount) public {// @audit require(allowed[_token], "Token not listed"); IERC20(_token).transferFrom(_msgSender(), address(this), _amount); IERC20Mintable(stable).mintFor( _msgSender(), _amount*(10**(18-IERC20Mintable(_token).decimals())) // 不同的decimals代币,计算之后都是一样的值。 ); }
You can also refer to arbitrageurs in ankr, because there is no immediate price update in the Helio protocol. cause to be attacked https://twitter.com/PeckShieldAlert/status/1598527823224111104
vscode
A price oracle should be added to the deposit
#0 - c4-judge
2022-12-20T16:12:28Z
GalloDaSballo marked the issue as duplicate of #462
#1 - c4-judge
2023-01-22T17:36:49Z
GalloDaSballo marked the issue as satisfactory
60.3691 USDC - $60.37
https://github.com/code-423n4/2022-12-tigris/blob/main/contracts/Trading.sol#L807
When using the approval mechanism in USDT, the approval must be set to 0 before it is updated. https://etherscan.io/address/0xdac17f958d2ee523a2206206994597c13d831ec7#code
require(!((_value != 0) && (allowed[msg.sender][_spender] != 0)));
When _tigAsset
is USDT, _handleCloseFees()
should first approve(0) and then re-approve, in order to solve the problem that the user has operated before
IStable(_tigAsset).mintFor(address(this), _daoFeesPaid); IStable(_tigAsset).approve(address(gov), type(uint).max); gov.distribute(_tigAsset, _daoFeesPaid);
vscode
Set the allowance to 0 before setting it to the new value.
#0 - c4-judge
2022-12-20T17:42:41Z
GalloDaSballo marked the issue as duplicate of #198
#1 - c4-judge
2023-01-22T17:52:48Z
GalloDaSballo marked the issue as satisfactory
🌟 Selected for report: 0xA5DF
Also found by: 0x4non, 0xmuxyz, 8olidity, HollaDieWaldfee
165.6217 USDC - $165.62
https://github.com/code-423n4/2022-12-tigris/blob/588c84b7bb354d20cbca6034544c4faa46e6a80e/contracts/GovNFT.sol#L101 https://github.com/code-423n4/2022-12-tigris/blob/588c84b7bb354d20cbca6034544c4faa46e6a80e/contracts/BondNFT.sol#L336 https://github.com/code-423n4/2022-12-tigris/blob/588c84b7bb354d20cbca6034544c4faa46e6a80e/contracts/GovNFTBridged.sol#L79
The GovNFT/BondNFT/GovNFTBridged contract allows users to call safeTransferMany()
to send nft to the to address, and safeTransferMany()
bottom layer will call _transfer()
to send nft, but here if the to address does not support ERC721. Then the nft will be locked in the to address.
function _transfer( address from, address to, uint256 tokenId ) internal override { require(ownerOf(tokenId) == from, "!Owner"); for (uint i=0; i<assetsLength(); i++) { userDebt[from][assets[i]] += accRewardsPerNFT[assets[i]]; userDebt[from][assets[i]] -= userPaid[from][assets[i]]/balanceOf(from); userPaid[from][assets[i]] -= userPaid[from][assets[i]]/balanceOf(from); userPaid[to][assets[i]] += accRewardsPerNFT[assets[i]]; } super._transfer(from, to, tokenId);//@audit }
vscode
It is recommended to use safeTransferFrom() to send nft
#0 - c4-judge
2022-12-20T15:54:16Z
GalloDaSballo marked the issue as duplicate of #356
#1 - c4-judge
2023-01-22T17:46:42Z
GalloDaSballo marked the issue as satisfactory
🌟 Selected for report: 0xA5DF
Also found by: 0xA5DF, 0xNazgul, 0xSmartContract, 0xbepresent, 0xdeadbeef0x, 8olidity, Englave, Faith, HE1M, JohnnyTime, Madalad, Mukund, Ruhum, SmartSek, __141345__, aviggiano, carlitox477, cccz, chaduke, francoHacker, gz627, gzeon, hansfriese, hihen, imare, jadezti, kwhuo68, ladboy233, orion, peanuts, philogy, rbserver, wait, yjrwkk
1.1472 USDC - $1.15
Administrators can set feeMultiplier
to any value through updateAssetFeeMultiplier()
. If set too large. The user's funds will all pay the handling fee, for example, in _handleCloseFees()
, if the sum of handling fees is equal to _payout
. Then the final _payout
will be 0, and all funds are used to pay the handling fee
payout_ = _payout - _daoFeesPaid - _burnFeesPaid - _botFeesPaid; IStable(_tigAsset).mintFor(address(this), _daoFeesPaid); IStable(_tigAsset).approve(address(gov), type(uint).max); gov.distribute(_tigAsset, _daoFeesPaid); return payout_;
function updateAssetFeeMultiplier(uint256 _asset, uint256 _feeMultiplier) external onlyOwner { bytes memory _name = bytes(_idToAsset[_asset].name); require(_name.length > 0, "!Asset"); _idToAsset[_asset].feeMultiplier = _feeMultiplier; }
vscode
Set the maximum value of feeMultiplier
#0 - TriHaz
2023-01-03T12:46:36Z
As mentioned before, owner of all contracts will be a multi-sig to reduce the centralization, we are aware of the centralization risks.
In my opinion, this should be QA, updateAssetFeeMultiplier()
needs to check the inputs.
#1 - c4-sponsor
2023-01-03T12:46:41Z
TriHaz marked the issue as sponsor acknowledged
#2 - c4-sponsor
2023-01-03T12:46:46Z
TriHaz marked the issue as disagree with severity
#3 - c4-judge
2023-01-19T19:50:37Z
GalloDaSballo marked the issue as duplicate of #377
#4 - c4-judge
2023-01-22T17:34:53Z
GalloDaSballo marked the issue as satisfactory
🌟 Selected for report: rbserver
Also found by: 0x52, 0xDecorativePineapple, 0xdeadbeef0x, 8olidity, Jeiwan, Rolezn, __141345__, bin2chen, eierina, fs0c, gzeon, joestakey, koxuan, kwhuo68, ladboy233, rvierdiiev, yixxas
11.6941 USDC - $11.69
latestAnswer()
this method will return the last value, but you won't be able to check if the data is fresh.
On the other hand, calling the method latestRoundData allow you to run some extra validations
ref:https://github.com/code-423n4/2021-07-wildcredit-findings/issues/75
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();
vscode
Use V3 interface functions: https://docs.chain.link/docs/price-feeds-api-reference/
#0 - c4-judge
2022-12-20T16:34:55Z
GalloDaSballo marked the issue as duplicate of #655
#1 - c4-judge
2023-01-22T17:31:02Z
GalloDaSballo marked the issue as satisfactory