Juicebox Buyback Delegate - 0xStalin's results

Thousands of projects use Juicebox to fund, operate, and scale their ideas & communities transparently on Ethereum.

General Information

Platform: Code4rena

Start Date: 18/05/2023

Pot Size: $24,500 USDC

Total HM: 3

Participants: 72

Period: 4 days

Judge: LSDan

Id: 237

League: ETH

Juicebox

Findings Distribution

Researcher Performance

Rank: 17/72

Findings: 1

Award: $321.72

🌟 Selected for report: 0

🚀 Solo Findings: 0

Findings Information

Labels

bug
2 (Med Risk)
downgraded by judge
satisfactory
edited-by-warden
duplicate-162

Awards

321.7244 USDC - $321.72

External Links

Lines of code

https://github.com/code-423n4/2023-05-juicebox/blob/main/juice-buyback/contracts/JBXBuybackDelegate.sol#L258-L323

Vulnerability details

Impact

All the excess ETH send to make swaps on the JBXBuybackDelegate contract will be stuck forever inside the contract.

Proof of Concept

  • The issue is caused because when doing a swap(), there is no a track between how much ETH was really paid and how much ETH was sent.
    • The logic assumes that the _amountToSend calculated by the Uniswap pool will take a 100% of the sent ETH, which might not be always the case.

Coded a PoC to demonstrate this issue

  • For the purpose of this PoC I'll need your help to add this view function on the JBXBuybackDelegate contract, just to facilitate the monitoring of the ETH balance that the contract is holding, both, before and after doing a swap.
  1. Add the below view function to the JBXBuybackDelegate contract
function buyBackDelegateBalance() external view returns (uint256) {
    return address(this).balance;
}
  1. Now is time to make a slightly update to the testDatasourceDelegateSwapIfPreferenceIsToClaimTokens() in the DelegateUnit.t.sol test file, basically, we need to call the buyBackDelegateBalance() function at the beggining and at the end of the test.
function testDatasourceDelegateSwapIfPreferenceIsToClaimTokens() public {

+   // Check the balanceOf the JBXBuybackDelegate.sol contract before the calls!
+   uint256 buyBackDelegateBalanceBefore = _delegate.buyBackDelegateBalance();

    ...
    ...
    ...

+   // Check the balanceOf the JBXBuybackDelegate.sol contract after the calls!
+   uint256 buyBackDelegateBalanceAfter = _delegate.buyBackDelegateBalance();
}
  • The output will be something similar to the below logs, which as we can see, all the unspend ETH is left on the JBXBuybackDelegate contract.
  • NOTE: It is important to mention that on this PoC all the ETH that was sent to JBETHPaymentTerminal::pay() is left on the JBXBuybackDelegate contract, the reason is because in this PoC there is no an actual payment of WETH tokens to the uni pool, but nevertheless, when the real swaps are executed, the ETH that will be left on the contract will be the difference between the msg.value sent to JBETHPaymentTerminal::pay() and the _amountToSend calculated by Uniswap.
Running 1 test for contracts/test/DelegateUnit.t.sol:TestUnitJBXBuybackDelegate [PASS] testDatasourceDelegateSwapIfPreferenceIsToClaimTokens() (gas: 267256) Traces: [267256] TestUnitJBXBuybackDelegate::testDatasourceDelegateSwapIfPreferenceIsToClaimTokens() ├─ [290] JBXBuybackDelegate::buyBackDelegateBalance() [staticcall] │ └─ ← 0 ├─ [0] VM::prank(projectOwner: [0x000000000000000000000000000000000000007B]) ... ... ... ├─ [290] JBXBuybackDelegate::buyBackDelegateBalance() [staticcall] │ └─ ← 1000000000000000000 └─ ← ()

Take as reference the logic on the _mint() function that succesfully returns back the received ETH back to the PaymentTerminal contract

Tools Used

Manual Audit

  • Calculate the difference between how much ETH was really paid for the swap (_amountToSend), and deduct it from the total msg.value that was send to jbETHPaymentTerminal().pay()
  • Make sure to return back the difference to either the user or the PaymentTerminal, if any ETH is left on the JBXBuybackDelegate contract it will be stuck forever since there is no a function to withdraw it to another address.

Assessed type

Other

#0 - c4-pre-sort

2023-05-24T16:17:47Z

dmvt marked the issue as duplicate of #42

#1 - c4-judge

2023-06-02T14:26:03Z

dmvt changed the severity to 2 (Med Risk)

#2 - c4-judge

2023-06-02T14:45:30Z

dmvt 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