Platform: Code4rena
Start Date: 14/06/2022
Pot Size: $100,000 USDC
Total HM: 26
Participants: 59
Period: 7 days
Judge: GalloDaSballo
Total Solo HM: 9
Id: 133
League: ETH
Rank: 2/59
Findings: 17
Award: $11,649.33
🌟 Selected for report: 2
🚀 Solo Findings: 0
194.9666 USDC - $194.97
1207.2233 CANTO - $194.97
The approve function of the WETH contract allows the user to spend anyone else's weth
function approve(address owner, address spender) external returns(bool) { _approve(owner, spender, _balanceOf[owner]); return true; } function _approve( address owner, address spender, uint256 amount ) internal { require(owner != address(0), "ERC20: approve from the zero address"); require(spender != address(0), "ERC20: approve to the zero address"); _allowance[owner][spender] = amount; emit Approval(owner, spender, amount); }
None
Modify the approve function as follows
function approve(address spender) external returns(bool) { _approve(msg.sender, spender, _balanceOf[msg.sender]); return true; }
#0 - tkkwon1998
2022-06-21T03:28:20Z
duplicate of issue #19
1467.456 USDC - $1,467.46
9086.4148 CANTO - $1,467.46
When the user borrows note tokens, the AccountantDelegate contract provides note tokens and gets cnote tokens. Later, when the user repays the note tokens, the cnote tokens are destroyed and the note tokens are transferred to the AccountantDelegate contract. However, in the sweepInterest function of the AccountantDelegate contract, all cnote tokens in the contract will be transferred to address 0. This will prevent the user from repaying the note tokens, and the sweepInterest function will not calculate the interest correctly later.
https://github.com/Plex-Engineer/lending-market/blob/ab31a612be354e252d72faead63d86b844172761/contracts/Accountant/AccountantDelegate.sol#L74-L92 https://github.com/Plex-Engineer/lending-market/blob/ab31a612be354e252d72faead63d86b844172761/contracts/CToken.sol#L533
None
function sweepInterest() external override returns(uint) { uint noteBalance = note.balanceOf(address(this)); uint CNoteBalance = cnote.balanceOf(address(this)); Exp memory expRate = Exp({mantissa: cnote.exchangeRateStored()}); // obtain exchange Rate from cNote Lending Market as a mantissa (scaled by 1e18) uint cNoteConverted = mul_ScalarTruncate(expRate, CNoteBalance); //calculate truncate(cNoteBalance* mantissa{expRate}) uint noteDifferential = sub_(note.totalSupply(), noteBalance); //cannot underflow, subtraction first to prevent against overflow, subtraction as integers require(cNoteConverted >= noteDifferential, "Note Loaned to LendingMarket must increase in value"); uint amtToSweep = sub_(cNoteConverted, noteDifferential); note.transfer(treasury, amtToSweep); - cnote.transfer(address(0), CNoteBalance); return 0; }
#0 - GalloDaSballo
2022-08-04T21:39:39Z
The warden has shown how, due to a programmer mistake, interest bearing Note will be burned
It is unclear why this decision was made, and I believe the sponsor should look into redeem
ing the cNote
over destroying it
The sponsor confirmed, and because this finding shows unconditional loss of assets, I agree with High Severity
9086.4148 CANTO - $1,467.46
1467.456 USDC - $1,467.46
In the initialize function of the AccountantDelegate contract, note._mint_to_Accountant is called. _mint_to_Accountant will mint note tokens to the AccountantDelegate contract instead of msg.sender. if note._initialSupply() ! = 0, note.balanceOf(msg.sender) == note._initialSupply() is false, the initialize function fails.
https://github.com/Plex-Engineer/lending-market/blob/ab31a612be354e252d72faead63d86b844172761/contracts/Accountant/AccountantDelegate.sol#L28-L29 https://github.com/Plex-Engineer/lending-market/blob/ab31a612be354e252d72faead63d86b844172761/contracts/Note.sol#L13-L19 https://github.com/Plex-Engineer/lending-market/blob/ab31a612be354e252d72faead63d86b844172761/contracts/ERC20.sol#L32 https://github.com/Plex-Engineer/lending-market/blob/ab31a612be354e252d72faead63d86b844172761/contracts/ERC20.sol#L242
None
Make sure _initialSupply == 0
#0 - nivasan1
2022-06-23T04:06:32Z
duplicate of #53
#1 - GalloDaSballo
2022-08-07T23:00:39Z
Dup of #53
🌟 Selected for report: Soosh
Also found by: 0x1f8b, Ruhum, TerrierLover, WatchPug, cccz, csanuragjain, hake, p4st13r4, zzzitron
126.3383 USDC - $126.34
782.2807 CANTO - $126.34
In the ProposalStore contract, AddProposal can be called by anyone, so that anyone can add a new proposal or overwrite an old proposal.
function AddProposal(uint propId, string memory title, string memory desc, address[] memory targets, uint[] memory values, string[] memory signatures, bytes[] memory calldatas) public { Proposal memory newProp = Proposal(propId, title, desc, targets, values, signatures, calldatas); proposals[propId] = newProp; }
None
Add the OnlyUniGov modifier to the AddProposal function
#0 - tkkwon1998
2022-06-22T17:41:31Z
Duplicate of issue #26
1987.1989 CANTO - $320.93
320.9326 USDC - $320.93
The Comptroller contract is forked from the Compound protocol and the reward token is modified, but the getWETHAddress function still uses the COMP token address
function getWETHAddress() virtual public view returns (address) { return 0xc00e94Cb662C3520282E6f5717214004A7f26888; }
None
Modify the return value of getWETHAddress to the weth token address
#0 - tkkwon1998
2022-06-21T04:08:57Z
duplicate of issue #46
1467.456 USDC - $1,467.46
9086.4148 CANTO - $1,467.46
In the ERC20 standard, _totalSupply indicates the number of tokens minted, but the tokens in the _initialSupply parts of the Note contract have not been minted, which causes _totalSupply to be incorrect
https://github.com/Plex-Engineer/lending-market/blob/ab31a612be354e252d72faead63d86b844172761/contracts/ERC20.sol#L29-L34 https://github.com/Plex-Engineer/lending-market/blob/ab31a612be354e252d72faead63d86b844172761/contracts/ERC20.sol#L237-L247
None
Consider making _initialSupply == 0
#0 - nivasan1
2022-06-23T04:09:10Z
duplicate of #107
#1 - Picodes
2022-06-29T05:58:53Z
This issue is a duplicate of https://github.com/code-423n4/2022-06-newblockchain-findings/issues/108 and not https://github.com/code-423n4/2022-06-newblockchain-findings/issues/107
#2 - GalloDaSballo
2022-08-12T00:39:59Z
Dup of #108
247.5766 USDC - $247.58
1532.982 CANTO - $247.58
In the queue function of the GovernorBravoDelegate contract, newProposal.executed = true will be set, which will make state(proposalId) != ProposalState.Queued, so the proposal cannot be executed.
function execute(uint proposalId) external payable { require(state(proposalId) == ProposalState.Queued, "GovernorBravo::execute: proposal can only be executed if it is queued");
None
Modify the following statements in the queue function
- newProposal.executed = true; + newProposal.executed = false;
#0 - tkkwon1998
2022-06-22T17:47:19Z
Duplicate of issue #39
440.2368 USDC - $440.24
2725.9244 CANTO - $440.24
In Note contract, if _initialSupply ! = 0, _totalSupply will overflow when the _mint_to_Accountant function executes _mint(msg.sender, type(uint).max)
constructor(string memory name_, string memory symbol_, uint256 totalSupply_) public { _name = name_; _symbol = symbol_; _initialSupply = totalSupply_; _totalSupply = totalSupply_; } ... function _mint(address account, uint256 amount) internal { require(account != address(0), "ERC20: mint to the zero address"); _beforeTokenTransfer(address(0), account, amount); _totalSupply += amount; _balances[account] += amount; emit Transfer(address(0), account, amount); _afterTokenTransfer(address(0), account, amount); }
https://github.com/Plex-Engineer/lending-market/blob/ab31a612be354e252d72faead63d86b844172761/contracts/Note.sol#L13-L19 https://github.com/Plex-Engineer/lending-market/blob/ab31a612be354e252d72faead63d86b844172761/contracts/ERC20.sol#L29-L34 https://github.com/Plex-Engineer/lending-market/blob/ab31a612be354e252d72faead63d86b844172761/contracts/ERC20.sol#L237-L247
None
ERC20.sol
constructor(string memory name_, string memory symbol_) public { _name = name_; _symbol = symbol_; }
note.sol
constructor() ERC20("Note", "NOTE") { admin = msg.sender; }
#0 - nivasan1
2022-06-23T04:07:08Z
duplicate of #53
#1 - GalloDaSballo
2022-08-07T23:03:30Z
In contrast to #53 -> Revert of initialize
This finding shows, how, based on constructor
arguments, the function _mint_to_accountant
can fail.
Am not convinced on the impact as I believe in the worst case the Sponsor would just be forced to re-deploy
#2 - GalloDaSballo
2022-08-12T00:38:46Z
The warden has shown how, because the function _mint_to_accountant
mints the maximum value representable, deploying Note with a non-zero initialSupply
will cause a revert.
Because this is contingent on a misconfiguration, I agree with Med Severity
#3 - liveactionllama
2022-10-18T00:20:35Z
Per discussion with @abhipingle - labeling this as sponsor confirmed
.
🌟 Selected for report: joestakey
Also found by: 0x1f8b, 0x29A, 0x52, 0xDjango, 0xNazgul, 0xf15ers, 0xmint, Bronicle, Dravee, Funen, JMukesh, Limbooo, MadWookie, Picodes, Ruhum, TerrierLover, TomJ, Tutturu, WatchPug, Waze, _Adam, asutorufos, c3phas, catchup, cccz, codexploder, cryptphi, csanuragjain, defsec, fatherOfBlocks, gzeon, hake, hansfriese, hyh, ignacio, k, nxrblsrpr, oyc_109, robee, sach1r0, saian, simon135, technicallyty, zzzitron
73.3392 USDC - $73.34
687.9945 CANTO - $111.11
In AccountantDelegator and TreasuryDelegator contracts, when using abi.decode(data, (uint)) to convert data to uint type, the length of data is not checked, when the returned data is of bytes type, the abi.decode will return 0x20.
https://github.com/Plex-Engineer/lending-market/blob/755424c1f9ab3f9f0408443e6606f94e4f08a990/contracts/Treasury/TreasuryDelegator.sol#L44-L56 https://github.com/Plex-Engineer/lending-market/blob/755424c1f9ab3f9f0408443e6606f94e4f08a990/contracts/Accountant/AccountantDelegator.sol#L54-L74 This contract can test that when the function returns bytes data, abi.encode will decode the return value as 0x20.
pragma solidity 0.8.10; contract A{ uint public destination; uint256 public number; function convertA() external{ (bool su,bytes memory ret )= address(this).call(abi.encodeWithSelector(this.ret.selector)); number = ret.length; destination = abi.decode(ret, (uint)); } function ret() public returns(bytes memory){ return "1234"; } }
None
Requires data.length == 32
#0 - GalloDaSballo
2022-08-07T23:09:22Z
I don't get why the return data would be of type bytes
, all signatures are specifying uint
.
Skipping the check may be a gas optimization.
Downgrading to QA