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
Rank: 122/189
Findings: 1
Award: $24.83
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: degensec
Also found by: 0x3b, 0xnev, HChang26, KmanOfficial, QiuhaoLi, T1MOH, WoolCentaur, Yanchuan, ayden, bart1e, jasonxiale, kutugu, mert_eren, nirlin, peakbolt, peanuts, pep7siup, qpzm, tapir, ubermensch, wintermute
24.8267 USDC - $24.83
Leftover WETH is stuck in the contract after adding liquidity in the reLP Contract.
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.
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); }
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