Dopex - peanuts's results

A rebate system for option writers in the Dopex Protocol.

General Information

Platform: Code4rena

Start Date: 21/08/2023

Pot Size: $125,000 USDC

Total HM: 26

Participants: 189

Period: 16 days

Judge: GalloDaSballo

Total Solo HM: 3

Id: 278

League: ETH

Dopex

Findings Distribution

Researcher Performance

Rank: 122/189

Findings: 1

Award: $24.83

🌟 Selected for report: 0

🚀 Solo Findings: 0

Awards

24.8267 USDC - $24.83

Labels

bug
2 (Med Risk)
satisfactory
sufficient quality report
duplicate-153

External Links

Lines of code

https://github.com/code-423n4/2023-08-dopex/blob/eb4d4a201b3a75dd4bddc74a34e9c42c71d0d12f/contracts/reLP/ReLPContract.sol#L257-L265

Vulnerability details

Impact

Leftover WETH is stuck in the contract after adding liquidity in the reLP Contract.

Proof of Concept

When adding liquidity using UniswapV2, a user first gets an equivalent amount of token A and token B. Then, he calls addLiquidity and adds in the corresponding amount of token A and token B. When adding liquidity, it is normal to have some leftover tokens because x number of token A may not equal to y number of token B.

For example, 1 token A = 5 token B. I want to add 10 token A and 50 token B into the pool. At the end, I only deposited 10 token A and 49 token B, and there is 1 token B leftover.

When calling addLiquidity using a wallet, it is fine because the leftover token will be returned to the wallet. However, if a contract calls addLiquidity, the leftover token will be left in the contract, and there must be a function in the contract to retrieve the token, otherwise the token will be stuck in the contract

In ReLPContract, after removing and adding WETH and rDPX, there is some WETH left in the contract which cannot be retrieved.

Attach this appended test in the Periphery.t.sol file, and run the test with

forge test --mt testReLpContract -vv

function testReLpContract() public { testV2Amo(); // set address in reLP contract and grant role reLpContract.setAddresses( address(rdpx), address(weth), address(pair), address(rdpxV2Core), address(rdpxReserveContract), address(uniV2LiquidityAMO), address(rdpxPriceOracle), address(factory), address(router) ); reLpContract.grantRole(reLpContract.RDPXV2CORE_ROLE(), address(rdpxV2Core)); reLpContract.setreLpFactor(9e4); // add liquidity uniV2LiquidityAMO.addLiquidity(5e18, 1e18, 0, 0); uniV2LiquidityAMO.approveContractToSpend( address(pair), address(reLpContract), type(uint256).max ); rdpxV2Core.setIsreLP(true); console.log("reLP contract rdpx balance:",rdpx.balanceOf(address(reLpContract))); console.log("reLP contract weth balance :",weth.balanceOf(address(reLpContract))); console.log("reLP contract LP balance :",pair.balanceOf(address(reLpContract))); rdpxV2Core.bond(1 * 1e18, 0, address(this)); uint256 lpBalance2 = pair.balanceOf(address(uniV2LiquidityAMO)); uint256 rdpxBalance2 = rdpx.balanceOf(address(rdpxV2Core)); uint256 reLpRdpxBalance = rdpx.balanceOf(address(reLpContract)); uint256 reLpLpBalance = pair.balanceOf(address(reLpContract)); uint256 reLpWETHBalance = weth.balanceOf(address(reLpContract)); console.log("reLP contract rdpx balance after:",rdpx.balanceOf(address(reLpContract))); console.log("reLP contract weth balance after:",weth.balanceOf(address(reLpContract))); console.log("reLP contract LP balance after:",pair.balanceOf(address(reLpContract))); assertEq(lpBalance2, 1422170988183415261); assertEq(rdpxBalance2, 53886041379169834724); assertEq(reLpRdpxBalance, 0); assertEq(reLpLpBalance, 0); }

The test should pass with the following logs:

Running 1 test for tests/rdpxV2-core/Periphery.t.sol:Periphery [PASS] testReLpContract() (gas: 4062193) Logs: reLP contract rdpx balance: 0 reLP contract weth balance : 0 reLP contract LP balance : 0 reLP contract rdpx balance after: 0 reLP contract weth balance after: 1022407518327480 reLP contract LP balance after: 0

This means that at the start of everything, there is no rdpx,weth and lp in the reLP contract. At the end, 1022407518327480 or 0.00102240751 weth is stuck in the contract.

Tools Used

Foundry

Have an emergencyWithdraw() function in the ReLP contract, similar to UniV2LiquidityAmo.sol to withdraw the dust WETH.

function emergencyWithdraw( address[] calldata tokens ) external onlyRole(DEFAULT_ADMIN_ROLE) { IERC20WithBurn token; for (uint256 i = 0; i < tokens.length; i++) { token = IERC20WithBurn(tokens[i]); token.safeTransfer(msg.sender, token.balanceOf(address(this))); } emit LogEmergencyWithdraw(msg.sender, tokens); }

Assessed type

Context

#0 - bytes032

2023-09-07T13:02:05Z

The same issue as #1286, but on a different instance.

#1 - c4-pre-sort

2023-09-07T13:02:05Z

bytes032 marked the issue as duplicate of #1286

#2 - c4-pre-sort

2023-09-11T15:38:17Z

bytes032 marked the issue as sufficient quality report

#3 - c4-judge

2023-10-18T12:13:29Z

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