Juicebox Buyback Delegate - pfapostol'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: 26/72

Findings: 1

Award: $27.98

Gas:
grade-b

🌟 Selected for report: 0

🚀 Solo Findings: 0

Findings Information

🌟 Selected for report: JCN

Also found by: 0x4non, Arz, Dimagu, K42, QiuhaoLi, Sathish9098, Tripathi, hunter_w3b, niser93, pfapostol

Labels

bug
G (Gas Optimization)
grade-b
G-05

Awards

27.9811 USDC - $27.98

External Links

Summary

Gas estimation is performed using existing tests (forge test --gas-report) and self-written program for snapshots calculation.

Gas Optimizations

IssueInstancesEstimated gas(deployments)Estimated gas(method call)
1Store external immutable variables in storage to reduce external calls120 507564
2Cast data directly to uint256160035
3Uncheck arithmetic that can't fail24 20074
4Reduce useless variables and operations13 00038
Overall526 314685

1. Store external immutable variables in storage to reduce external calls

NOTE:The directory is an immutable variable in the terminal contract, so we can store it in the constructor.

Deployment gas cost reduced by 20 507

Minimum functions call gas cost reduced by 544

Average functions call gas cost reduced by 564

Maximum functions call gas cost reduced by 610

diff --git a/juice-buyback/contracts/JBXBuybackDelegate.sol b/juice-buyback/contracts/JBXBuybackDelegate.sol
index 0ee751b..d25742b 100644
--- a/juice-buyback/contracts/JBXBuybackDelegate.sol
+++ b/juice-buyback/contracts/JBXBuybackDelegate.sol
@@ -89,6 +89,8 @@ contract JBXBuybackDelegate is IJBFundingCycleDataSource, IJBPayDelegate, IUnisw
      */
     IJBPayoutRedemptionPaymentTerminal3_1 public immutable jbxTerminal;

+    IJBDirectory public immutable directory;
+
     /**
      * @notice The WETH contract
      */
@@ -124,6 +126,7 @@ contract JBXBuybackDelegate is IJBFundingCycleDataSource, IJBPayDelegate, IUnisw
         projectToken = _projectToken;
         pool = _pool;
         jbxTerminal = _jbxTerminal;
+        directory = _jbxTerminal.directory();
         _projectTokenIsZero = address(_projectToken) < address(_weth);
         weth = _weth;
     }
@@ -287,7 +290,7 @@ contract JBXBuybackDelegate is IJBFundingCycleDataSource, IJBPayDelegate, IUnisw

         // If there are reserved token, add them to the reserve
         if (_reservedToken != 0) {
-            IJBController controller = IJBController(jbxTerminal.directory().controllerOf(_data.projectId));
+            IJBController controller = IJBController(directory.controllerOf(_data.projectId));

             // 1) Burn all the reserved token, which are in this address -> result: 0 here, 0 in reserve
             controller.burnTokensOf({
@@ -332,7 +335,7 @@ contract JBXBuybackDelegate is IJBFundingCycleDataSource, IJBPayDelegate, IUnisw
      * @param  _amount the amount of token out to mint
      */
     function _mint(JBDidPayData calldata _data, uint256 _amount) internal {
-        IJBController controller = IJBController(jbxTerminal.directory().controllerOf(_data.projectId));
+        IJBController controller = IJBController(directory.controllerOf(_data.projectId));

         // Mint to the beneficiary with the fc reserve rate
         controller.mintTokensOf({

2. Cast data directly to uint256

NOTE:date contains only one variable (there will be no revert if input is incorrect)

Deployment gas cost reduced by 600

Minimum functions call gas cost reduced by 23

Average functions call gas cost reduced by 35

Maximum functions call gas cost reduced by 40

diff --git a/juice-buyback/contracts/JBXBuybackDelegate.sol b/juice-buyback/contracts/JBXBuybackDelegate.sol
index 0ee751b..ef9876d 100644
--- a/juice-buyback/contracts/JBXBuybackDelegate.sol
+++ b/juice-buyback/contracts/JBXBuybackDelegate.sol
@@ -217,15 +217,12 @@ contract JBXBuybackDelegate is IJBFundingCycleDataSource, IJBPayDelegate, IUnisw
         // Check if this is really a callback
         if (msg.sender != address(pool)) revert JuiceBuyback_Unauthorized();

-        // Unpack the data
-        (uint256 _minimumAmountReceived) = abi.decode(data, (uint256));
-
         // Assign 0 and 1 accordingly
         uint256 _amountReceived = uint256(-(_projectTokenIsZero ? amount0Delta : amount1Delta));
         uint256 _amountToSend = uint256(_projectTokenIsZero ? amount1Delta : amount0Delta);

         // Revert if slippage is too high
-        if (_amountReceived < _minimumAmountReceived) revert JuiceBuyback_MaximumSlippage();
+        if (_amountReceived < uint256(bytes32(data))) revert JuiceBuyback_MaximumSlippage();

         // Wrap and transfer the weth to the pool
         weth.deposit{value: _amountToSend}();

3. Uncheck arithmetic that can't fail

NOTE: Underflow is not possible, since the right side is a percentage of the left.

Deployment gas cost reduced by 4 200

Minimum functions call gas cost reduced by 116

Average functions call gas cost reduced by 74

Maximum functions call gas cost reduced by 116

diff --git a/juice-buyback/contracts/JBXBuybackDelegate.sol b/juice-buyback/contracts/JBXBuybackDelegate.sol
index 0ee751b..e563203 100644
--- a/juice-buyback/contracts/JBXBuybackDelegate.sol
+++ b/juice-buyback/contracts/JBXBuybackDelegate.sol
@@ -280,7 +280,10 @@ contract JBXBuybackDelegate is IJBFundingCycleDataSource, IJBPayDelegate, IUnisw
         );

         // The amount to add to the reserved token
-        uint256 _reservedToken = _amountReceived - _nonReservedToken;
+        uint256 _reservedToken;
+        unchecked {
+            _reservedToken= _amountReceived - _nonReservedToken;
+        }

         // Send the non-reserved token to the beneficiary (if any / reserved rate is not max)
         if (_nonReservedToken != 0) projectToken.transfer(_data.beneficiary, _nonReservedToken);
@@ -309,7 +312,10 @@ contract JBXBuybackDelegate is IJBFundingCycleDataSource, IJBPayDelegate, IUnisw
             });

             // 3) Burn the non-reserve token which are now left in this address (can be 0) -> result: 0 here, reservedToken in reserve
-            uint256 _nonReservedTokenInContract = _amountReceived - _reservedToken;
+            uint256 _nonReservedTokenInContract;
+            unchecked {
+                _nonReservedTokenInContract= _amountReceived - _reservedToken;
+            }

             if (_nonReservedTokenInContract != 0) {
                 controller.burnTokensOf({

4. Reduce useless variables and operations

NOTE: _nonReservedToken is unchanged during the lifetime of the function

Deployment gas cost reduced by 3 000

Minimum functions call gas cost reduced by 68

Average functions call gas cost reduced by 38

Maximum functions call gas cost reduced by 68

diff --git a/juice-buyback/contracts/JBXBuybackDelegate.sol b/juice-buyback/contracts/JBXBuybackDelegate.sol
index 0ee751b..6795b3b 100644
--- a/juice-buyback/contracts/JBXBuybackDelegate.sol
+++ b/juice-buyback/contracts/JBXBuybackDelegate.sol
@@ -309,13 +309,11 @@ contract JBXBuybackDelegate is IJBFundingCycleDataSource, IJBPayDelegate, IUnisw
             });

             // 3) Burn the non-reserve token which are now left in this address (can be 0) -> result: 0 here, reservedToken in reserve
-            uint256 _nonReservedTokenInContract = _amountReceived - _reservedToken;
-
-            if (_nonReservedTokenInContract != 0) {
+            if (_nonReservedToken != 0) {
                 controller.burnTokensOf({
                     _holder: address(this),
                     _projectId: _data.projectId,
-                    _tokenCount: _nonReservedTokenInContract,
+                    _tokenCount: _nonReservedToken,
                     _memo: "",
                     _preferClaimedTokens: false
                 });

00. Overall gas effect

Deployment gas cost reduced by 26 314

Minimum functions call gas cost reduced by 694

Average functions call gas cost increased by 685

Maximum functions call gas cost reduced by 777

diff --git "a/gas-report\\orig.md" "b/gas-report\\fixed.md"
index cb57c9c..2f27a83 100644
--- "a/gas-report\\orig.md"
+++ "b/gas-report\\fixed.md"
@@ -1,23 +1,23 @@
 Running 4 tests for contracts/test/DelegateUnit.t.sol:TestUnitJBXBuybackDelegate
-[PASS] testDatasourceDelegateMintIfPreferenceIsNotToClaimTokens() (gas: 189895)
-[PASS] testDatasourceDelegateSwapIfPreferenceIsToClaimTokens() (gas: 265670)
-[PASS] testDatasourceDelegateWhenQuoteIsLowerThanTokenCount(uint256) (runs: 256, μ: 190807, ~: 190552)
-[PASS] testRevertIfSlippageIsTooMuchWhenSwapping() (gas: 12291)
-Test result: ok. 4 passed; 0 failed; finished in 171.36ms
+[PASS] testDatasourceDelegateMintIfPreferenceIsNotToClaimTokens() (gas: 189940)
+[PASS] testDatasourceDelegateSwapIfPreferenceIsToClaimTokens() (gas: 264848)
+[PASS] testDatasourceDelegateWhenQuoteIsLowerThanTokenCount(uint256) (runs: 256, μ: 190869, ~: 190597)
+[PASS] testRevertIfSlippageIsTooMuchWhenSwapping() (gas: 12246)
+Test result: ok. 4 passed; 0 failed; finished in 167.43ms
 
 Running 6 tests for contracts/test/DelegateE2E.t.sol:TestIntegrationJBXBuybackDelegate
-[PASS] test_mintIfPreferClaimedIsFalse() (gas: 995415)
-[PASS] test_mintIfSlippageTooHigh() (gas: 1082925)
-[PASS] test_mintIfWeightGreatherThanPrice(uint256) (runs: 256, μ: 958009, ~: 958102)
-[PASS] test_swapIfQuoteBetter(uint256) (runs: 256, μ: 1113600, ~: 1114547)
-[PASS] test_swapMultiple() (gas: 1361806)
-[PASS] test_swapRandomAmountIn(uint256) (runs: 256, μ: 1088084, ~: 1084099)
-Test result: ok. 6 passed; 0 failed; finished in 2.88s
+[PASS] test_mintIfPreferClaimedIsFalse() (gas: 994686)
+[PASS] test_mintIfSlippageTooHigh() (gas: 1082151)
+[PASS] test_mintIfWeightGreatherThanPrice(uint256) (runs: 256, μ: 958072, ~: 958147)
+[PASS] test_swapIfQuoteBetter(uint256) (runs: 256, μ: 1112903, ~: 1113612)
+[PASS] test_swapMultiple() (gas: 1359936)
+[PASS] test_swapRandomAmountIn(uint256) (runs: 256, μ: 1088465, ~: 1084001)
+Test result: ok. 6 passed; 0 failed; finished in 3.00s
 | contracts/JBXBuybackDelegate.sol:JBXBuybackDelegate contract |                       |       |        |        |         |
 | ------------------------------------------------------------ | --------------------- | ----- | ------ | ------ | ------- |
 | Deployment Cost                                              | Deployment Size       |       |        |        |         |
-|                                                              | 1260474               | 6573  |        |        |         |    |
+|                                                              | 1234160               | 6595  |        |        |         |    |
 | Function Name                                                | min                   | avg   | median | max    | # calls |
-|                                                              | didPay                | 51675 | 161926 | 148266 | 242106  | 7  |
-|                                                              | payParams             | 2075  | 8271   | 9981   | 12476   | 10 |
-|                                                              | uniswapV3SwapCallback | 867   | 20125  | 28794  | 30714   | 6  |
+| didPay                                                       | 50981           | 160447 | 147482 | 241322 | 7       |
+| payParams                                                    | 2120            | 8313   | 10017  | 12521  | 10      |
+| uniswapV3SwapCallback                                        | 822             | 20084  | 28756  | 30676  | 6       |

#0 - c4-judge

2023-06-02T10:53:06Z

dmvt marked the issue as grade-b

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