Closed c4-bot-4 closed 10 months ago
https://github.com/code-423n4/2023-12-revolutionprotocol/blob/d42cc62b873a1b2b44f57310f9d4bbfdd875e8d6/packages/revolution/src/ERC20TokenEmitter.sol#L152-L230
ERC20TokenEmitter.sol:buyToken distributes the funds sent to purchase the tokens in the following way:
ERC20TokenEmitter.sol:buyToken
RevolutionProtocolRewards
creatorRateBps
treasury
entropyRateBps
creatorsAddress
This leaves in the contract the remainder of funds, which is equal to:
((msg.value * 0.975) * creatorRateBps / 10_000) * (10_000 - entropyRateBps) / 10_000;
Given that there is no withdrawal mechanism to get this remainder out of the contract, the funds are stuck in the contract.
Ether will get stuck in the contract.
function testAuditBuyToken() public { uint256 creatorRate = 8_000; // 80% uint256 entropyRate = 4_000; // 40% // Setup vm.startPrank(address(dao)); erc20TokenEmitter.setCreatorRateBps(creatorRate); erc20TokenEmitter.setEntropyRateBps(entropyRate); erc20TokenEmitter.setCreatorsAddress(address(80)); vm.stopPrank(); // Purchase data address[] memory recipients = new address[](1); recipients[0] = address(1); uint256[] memory bps = new uint256[](1); bps[0] = 10_000; // 100% uint256 valueSent = 1 ether; // Perform token purchase uint256 erc20TokenEmitterInitialBalance = address(erc20TokenEmitter).balance; vm.startPrank(address(this)); erc20TokenEmitter.buyToken{ value: valueSent }( recipients, bps, IERC20TokenEmitter.ProtocolRewardAddresses({ builder: address(0), purchaseReferral: address(0), deployer: address(0) }) ); // Distribution calculations uint256 rewardAmount = erc20TokenEmitter.computeTotalReward(valueSent); uint256 toPayTreasury = ((valueSent - rewardAmount) * (10_000 - creatorRate)) / 10_000; uint256 creatorDirectPayment = ((valueSent - rewardAmount - toPayTreasury) * entropyRate) / 10_000; uint256 valueLocked = valueSent - rewardAmount - toPayTreasury - creatorDirectPayment; assertEq(rewardAmount, 0.025 ether); assertEq(toPayTreasury, 0.195 ether); assertEq(toPayTreasury, erc20TokenEmitter.treasury().balance); assertEq(creatorDirectPayment, 0.312 ether); assertEq(creatorDirectPayment, erc20TokenEmitter.creatorsAddress().balance); assertEq(valueLocked, 0.468 ether); assertEq(valueLocked, address(erc20TokenEmitter).balance - erc20TokenEmitterInitialBalance); }
Manual inspection.
It is not clear who should be the recipient of the remainder of the funds. In any case, the solution would be to add the amount not distributed to the amounts sent to one of the existing recipients or send it to a new recipient.
ETH-Transfer
raymondfam marked the issue as sufficient quality report
raymondfam marked the issue as duplicate of #13
raymondfam marked the issue as duplicate of #406
MarioPoneder marked the issue as satisfactory
Lines of code
https://github.com/code-423n4/2023-12-revolutionprotocol/blob/d42cc62b873a1b2b44f57310f9d4bbfdd875e8d6/packages/revolution/src/ERC20TokenEmitter.sol#L152-L230
Vulnerability details
ERC20TokenEmitter.sol:buyToken
distributes the funds sent to purchase the tokens in the following way:RevolutionProtocolRewards
contract.creatorRateBps
variable is sent to thetreasury
.entropyRateBps
variable. One part is sent to thecreatorsAddress
.This leaves in the contract the remainder of funds, which is equal to:
Given that there is no withdrawal mechanism to get this remainder out of the contract, the funds are stuck in the contract.
Impact
Ether will get stuck in the contract.
Proof of Concept
Tools Used
Manual inspection.
Recommended Mitigation Steps
It is not clear who should be the recipient of the remainder of the funds. In any case, the solution would be to add the amount not distributed to the amounts sent to one of the existing recipients or send it to a new recipient.
Assessed type
ETH-Transfer