From f8994baf32e35faa5c292816066bf0268e012e38 Mon Sep 17 00:00:00 2001 From: Justin Pulley Date: Fri, 22 Sep 2023 02:20:07 -0500 Subject: [PATCH 1/9] fix: stack too deep, edge-case payment amount 0 case, fixing calcs & tests next --- contracts/JBBuybackDelegate.sol | 20 +++++++++++++------- contracts/test/JBBuybackDelegate_Fork.t.sol | 2 ++ contracts/test/JBBuybackDelegate_Unit.t.sol | 9 +++++++++ 3 files changed, 24 insertions(+), 7 deletions(-) diff --git a/contracts/JBBuybackDelegate.sol b/contracts/JBBuybackDelegate.sol index 818a68c..d78211d 100644 --- a/contracts/JBBuybackDelegate.sol +++ b/contracts/JBBuybackDelegate.sol @@ -142,6 +142,12 @@ contract JBBuybackDelegate is ERC165, JBOperatable, IJBBuybackDelegate { override returns (uint256 weight, string memory memo, JBPayDelegateAllocation3_1_1[] memory delegateAllocations) { + // Keep a reference to the payment total + uint256 _totalPaid = _data.amount.value; + + // Keep a reference to the weight + uint256 _weight = _data.weight; + // Keep a reference to the minimum number of tokens expected to be swapped for. uint256 _minimumSwapAmountOut; @@ -161,10 +167,10 @@ contract JBBuybackDelegate is ERC165, JBOperatable, IJBBuybackDelegate { } // If no amount was specified to swap with, default to the full amount of the payment. - if (_amountToSwapWith == 0) _amountToSwapWith = _data.amount.value; + if (_amountToSwapWith == 0) _amountToSwapWith = _totalPaid; // Find the default total number of tokens to mint as if no Buyback Delegate were installed, as a fixed point number with 18 decimals - uint256 _tokenCountWithoutDelegate = mulDiv(_amountToSwapWith, _data.weight, _data.amount.decimals); + uint256 _tokenCountWithoutDelegate = mulDiv(_amountToSwapWith, _weight, _data.amount.decimals); // Keep a reference to the project's token. address _projectToken = projectTokenOf[_data.projectId]; @@ -180,7 +186,7 @@ contract JBBuybackDelegate is ERC165, JBOperatable, IJBBuybackDelegate { // If the minimum amount received from swapping is greather than received when minting, use the swap path. if (_tokenCountWithoutDelegate < _minimumSwapAmountOut) { // Make sure the amount to swap with is at most the full amount being paid. - if (_amountToSwapWith > _data.amount.value) revert JuiceBuyback_InsufficientPayAmount(); + if (_amountToSwapWith > _totalPaid || _totalPaid == 0) revert JuiceBuyback_InsufficientPayAmount(); // Keep a reference to a flag indicating if the pool will reference the project token as the first in the pair. bool _projectTokenIs0 = address(_projectToken) < _terminalToken; @@ -190,7 +196,7 @@ contract JBBuybackDelegate is ERC165, JBOperatable, IJBBuybackDelegate { delegateAllocations[0] = JBPayDelegateAllocation3_1_1({ delegate: IJBPayDelegate3_1_1(this), amount: _amountToSwapWith, - metadata: abi.encode(_quoteExists, _projectTokenIs0, _minimumSwapAmountOut, _data.amount.value - _amountToSwapWith, _data.weight) + metadata: abi.encode(_quoteExists, _projectTokenIs0, _minimumSwapAmountOut, _totalPaid == _amountToSwapWith ? 0 : _totalPaid - _amountToSwapWith, _weight) }); // All the mint will be done in didPay, return 0 as weight to avoid minting via the terminal @@ -263,7 +269,7 @@ contract JBBuybackDelegate is ERC165, JBOperatable, IJBBuybackDelegate { uint256 _minimumSwapAmountOut, uint256 _amountToMintWith, uint256 _weight - ) = abi.decode(_data.dataSourceMetadata, (bool, bool, uint256, uint256)); + ) = abi.decode(_data.dataSourceMetadata, (bool, bool, uint256, uint256, uint256)); // Get a reference to the amount of tokens that was swapped for. uint256 _exactSwapAmountOut = _swap(_data, _projectTokenIs0); @@ -296,12 +302,12 @@ contract JBBuybackDelegate is ERC165, JBOperatable, IJBBuybackDelegate { } // Get a reference to any amount of token paid which are in the terminal's balance (ie extra-funds used to mint) - uint256 _tokensToMintFromTerminal = mulDiv(_amountToMintWith, _weight, _data.amount.decimals); + _partialMintTokenCount += mulDiv(_amountToMintWith, _weight, _data.amount.decimals); // Mint the whole amount of tokens again together with the (optional partial mint), such that the correct portion of reserved tokens get taken into account. CONTROLLER.mintTokensOf({ projectId: _data.projectId, - tokenCount: _exactSwapAmountOut + _partialMintTokenCount + _tokensToMintFromTerminal, + tokenCount: _exactSwapAmountOut + _partialMintTokenCount, beneficiary: address(_data.beneficiary), memo: _data.memo, preferClaimedTokens: _data.preferClaimedTokens, diff --git a/contracts/test/JBBuybackDelegate_Fork.t.sol b/contracts/test/JBBuybackDelegate_Fork.t.sol index 6c1a50d..97b39cd 100644 --- a/contracts/test/JBBuybackDelegate_Fork.t.sol +++ b/contracts/test/JBBuybackDelegate_Fork.t.sol @@ -16,6 +16,8 @@ import "@exhausted-pigeon/uniswap-v3-forge-quoter/src/UniswapV3ForgeQuoter.sol"; import "../JBBuybackDelegate.sol"; +import {mulDiv18} from "@prb/math/src/Common.sol"; + /** * @notice Buyback fork integration tests, using $jbx v3 */ diff --git a/contracts/test/JBBuybackDelegate_Unit.t.sol b/contracts/test/JBBuybackDelegate_Unit.t.sol index e5f051e..3e6e30d 100644 --- a/contracts/test/JBBuybackDelegate_Unit.t.sol +++ b/contracts/test/JBBuybackDelegate_Unit.t.sol @@ -20,6 +20,8 @@ import "./helpers/PoolAddress.sol"; import "../JBBuybackDelegate.sol"; import "../libraries/JBBuybackDelegateOperations.sol"; +import {mulDiv18} from "@prb/math/src/Common.sol"; + /** * @notice Unit tests for the JBBuybackDelegate contract. * @@ -136,6 +138,13 @@ contract TestJBBuybackDelegate_Units is Test { ); } + function test_tokenAmounts() public { + uint256 _tokenCountWithoutDelegate = mulDiv(1000000, 1 ether, 18); + emit log_uint(_tokenCountWithoutDelegate); + + assertEq(_tokenCountWithoutDelegate, 1); + } + /** * @notice Test payParams when a quote is provided as metadata * From 658c77b1152ccd820a830c8c861ae77761bb5ea5 Mon Sep 17 00:00:00 2001 From: Justin Pulley Date: Fri, 22 Sep 2023 03:47:48 -0500 Subject: [PATCH 2/9] fix: token decimal calculations --- contracts/JBBuybackDelegate.sol | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/contracts/JBBuybackDelegate.sol b/contracts/JBBuybackDelegate.sol index d78211d..9ebe5d3 100644 --- a/contracts/JBBuybackDelegate.sol +++ b/contracts/JBBuybackDelegate.sol @@ -170,7 +170,8 @@ contract JBBuybackDelegate is ERC165, JBOperatable, IJBBuybackDelegate { if (_amountToSwapWith == 0) _amountToSwapWith = _totalPaid; // Find the default total number of tokens to mint as if no Buyback Delegate were installed, as a fixed point number with 18 decimals - uint256 _tokenCountWithoutDelegate = mulDiv(_amountToSwapWith, _weight, _data.amount.decimals); + + uint256 _tokenCountWithoutDelegate = mulDiv(_amountToSwapWith, _weight, 10 ** _data.amount.decimals); // Keep a reference to the project's token. address _projectToken = projectTokenOf[_data.projectId]; @@ -286,7 +287,7 @@ contract JBBuybackDelegate is ERC165, JBOperatable, IJBBuybackDelegate { // Keep a reference to the number of tokens being minted. uint256 _partialMintTokenCount; if (_terminalTokenInThisContract != 0) { - _partialMintTokenCount = mulDiv(_terminalTokenInThisContract, _weight, _data.amount.decimals); + _partialMintTokenCount = mulDiv(_terminalTokenInThisContract, _weight, 10 ** _data.amount.decimals); // If the token paid in wasn't ETH, give the terminal permission to pull them back into its balance. if (_data.forwardedAmount.token != JBTokens.ETH) { @@ -301,8 +302,8 @@ contract JBBuybackDelegate is ERC165, JBOperatable, IJBBuybackDelegate { emit BuybackDelegate_Mint(_data.projectId, _terminalTokenInThisContract, _partialMintTokenCount, msg.sender); } - // Get a reference to any amount of token paid which are in the terminal's balance (ie extra-funds used to mint) - _partialMintTokenCount += mulDiv(_amountToMintWith, _weight, _data.amount.decimals); + // Add amount to mint to leftover mint amount (avoiding stack too deep here) + _partialMintTokenCount += mulDiv(_amountToMintWith, _weight, 10 ** _data.amount.decimals); // Mint the whole amount of tokens again together with the (optional partial mint), such that the correct portion of reserved tokens get taken into account. CONTROLLER.mintTokensOf({ From a0d8554de43f44ce80a5266d6b5fc02c1f29ab58 Mon Sep 17 00:00:00 2001 From: Justin Pulley Date: Fri, 22 Sep 2023 03:49:21 -0500 Subject: [PATCH 3/9] fix: updated metadata in unit tests --- contracts/test/JBBuybackDelegate_Unit.t.sol | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/contracts/test/JBBuybackDelegate_Unit.t.sol b/contracts/test/JBBuybackDelegate_Unit.t.sol index 3e6e30d..61d6cb6 100644 --- a/contracts/test/JBBuybackDelegate_Unit.t.sol +++ b/contracts/test/JBBuybackDelegate_Unit.t.sol @@ -138,13 +138,6 @@ contract TestJBBuybackDelegate_Units is Test { ); } - function test_tokenAmounts() public { - uint256 _tokenCountWithoutDelegate = mulDiv(1000000, 1 ether, 18); - emit log_uint(_tokenCountWithoutDelegate); - - assertEq(_tokenCountWithoutDelegate, 1); - } - /** * @notice Test payParams when a quote is provided as metadata * @@ -198,7 +191,7 @@ contract TestJBBuybackDelegate_Units is Test { assertEq(_allocationsReturned[0].amount, _amountIn, "worng amount in returned"); assertEq( _allocationsReturned[0].metadata, - abi.encode(true, address(projectToken) < address(weth), _swapOutCount, payParams.weight), + abi.encode(true, address(projectToken) < address(weth), _swapOutCount, payParams.amount.value - _amountIn, payParams.weight), "wrong metadata" ); @@ -276,7 +269,7 @@ contract TestJBBuybackDelegate_Units is Test { assertEq( _allocationsReturned[0].metadata, abi.encode( - false, address(projectToken) < address(weth), _twapAmountOut, payParams.weight + false, address(projectToken) < address(weth), _twapAmountOut, 0, payParams.weight ), "wrong metadata" ); @@ -375,6 +368,7 @@ contract TestJBBuybackDelegate_Units is Test { true, // use quote address(projectToken) < address(weth), _tokenCount, + 0, _twapQuote ); @@ -472,6 +466,7 @@ contract TestJBBuybackDelegate_Units is Test { true, // use quote address(projectToken) < address(weth), _tokenCount, + 0, _twapQuote ); @@ -572,6 +567,7 @@ contract TestJBBuybackDelegate_Units is Test { true, // use quote address(projectToken) < address(weth), _tokenCount, + 0, 1 ether // weight - unused ); @@ -635,6 +631,7 @@ contract TestJBBuybackDelegate_Units is Test { false, // use quote address(otherRandomProjectToken) < address(randomTerminalToken), _tokenCount, + 0, _weight ); @@ -749,6 +746,7 @@ contract TestJBBuybackDelegate_Units is Test { false, // use quote address(projectToken) < address(weth), _tokenCount, + 0, _weight ); From dccbaae269fbca1afff6dff10c1cdbe78b5e9711 Mon Sep 17 00:00:00 2001 From: drgorillamd Date: Tue, 26 Sep 2023 22:11:10 +0200 Subject: [PATCH 4/9] fix: remove revert on 0 amount --- contracts/JBBuybackDelegate.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/JBBuybackDelegate.sol b/contracts/JBBuybackDelegate.sol index 9ebe5d3..cbea20b 100644 --- a/contracts/JBBuybackDelegate.sol +++ b/contracts/JBBuybackDelegate.sol @@ -187,7 +187,7 @@ contract JBBuybackDelegate is ERC165, JBOperatable, IJBBuybackDelegate { // If the minimum amount received from swapping is greather than received when minting, use the swap path. if (_tokenCountWithoutDelegate < _minimumSwapAmountOut) { // Make sure the amount to swap with is at most the full amount being paid. - if (_amountToSwapWith > _totalPaid || _totalPaid == 0) revert JuiceBuyback_InsufficientPayAmount(); + if (_amountToSwapWith > _totalPaid) revert JuiceBuyback_InsufficientPayAmount(); // Keep a reference to a flag indicating if the pool will reference the project token as the first in the pair. bool _projectTokenIs0 = address(_projectToken) < _terminalToken; From bbe0bf97420a406995baea41e2ef402d186e0a00 Mon Sep 17 00:00:00 2001 From: drgorillamd Date: Wed, 27 Sep 2023 00:36:04 +0200 Subject: [PATCH 5/9] chore: wip --- contracts/test/JBBuybackDelegate_Unit.t.sol | 28 +++++++++++++-------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/contracts/test/JBBuybackDelegate_Unit.t.sol b/contracts/test/JBBuybackDelegate_Unit.t.sol index 61d6cb6..5badc7a 100644 --- a/contracts/test/JBBuybackDelegate_Unit.t.sol +++ b/contracts/test/JBBuybackDelegate_Unit.t.sol @@ -450,15 +450,17 @@ contract TestJBBuybackDelegate_Units is Test { /** * @notice Test didPay with token received from swapping */ - function test_didPay_swap_ERC20(uint256 _tokenCount, uint256 _twapQuote ) public { + function test_didPay_swap_ERC20(uint256 _tokenCount, uint256 _twapQuote, uint256 _decimals ) public { // Bound to avoid overflow and insure swap quote > mint quote _tokenCount = bound(_tokenCount, 2, type(uint256).max - 1); _twapQuote = bound(_twapQuote, _tokenCount + 1, type(uint256).max); + _decimals = bound(_decimals, 1, 18); + didPayData.amount = - JBTokenAmount({token: address(randomTerminalToken), value: 1 ether, decimals: 18, currency: 1}); + JBTokenAmount({token: address(randomTerminalToken), value: 1 ether, decimals: _decimals, currency: 1}); didPayData.forwardedAmount = - JBTokenAmount({token: address(randomTerminalToken), value: 1 ether, decimals: 18, currency: 1}); + JBTokenAmount({token: address(randomTerminalToken), value: 1 ether, decimals: _decimals, currency: 1}); didPayData.projectId = randomId; // The metadata coming from payParams(..) @@ -607,17 +609,19 @@ contract TestJBBuybackDelegate_Units is Test { /** * @notice Test didPay with swap reverting while using the twap, should then mint with the delegate balance, random erc20 is terminal token */ - function test_didPay_swapRevertWithoutQuote_ERC20(uint256 _tokenCount, uint256 _weight) public { + function test_didPay_swapRevertWithoutQuote_ERC20(uint256 _tokenCount, uint256 _weight, uint256 _decimals) public { // The current weight _weight = bound(_weight, 1, 1 ether); // The amount of termminal token in this delegate (avoid overflowing when mul by weight) _tokenCount = bound(_tokenCount, 2, type(uint128).max); + _decimals = bound(_decimals, 1, 18); + didPayData.amount = - JBTokenAmount({token: address(randomTerminalToken), value: _tokenCount, decimals: 18, currency: 1}); + JBTokenAmount({token: address(randomTerminalToken), value: _tokenCount, decimals: _decimals, currency: 1}); didPayData.forwardedAmount = - JBTokenAmount({token: address(randomTerminalToken), value: _tokenCount, decimals: 18, currency: 1}); + JBTokenAmount({token: address(randomTerminalToken), value: _tokenCount, decimals: _decimals, currency: 1}); didPayData.projectId = randomId; vm.mockCall( @@ -678,7 +682,7 @@ contract TestJBBuybackDelegate_Units is Test { address(controller), abi.encodeCall( controller.mintTokensOf, - (didPayData.projectId, mulDiv18(_tokenCount, _weight), didPayData.beneficiary, didPayData.memo, didPayData.preferClaimedTokens, true) + (didPayData.projectId, mulDiv(_tokenCount, _weight, _decimals), didPayData.beneficiary, didPayData.memo, didPayData.preferClaimedTokens, true) ), abi.encode(true) ); @@ -686,7 +690,7 @@ contract TestJBBuybackDelegate_Units is Test { address(controller), abi.encodeCall( controller.mintTokensOf, - (didPayData.projectId, _tokenCount * _weight / 1e18, didPayData.beneficiary, didPayData.memo, didPayData.preferClaimedTokens, true) + (didPayData.projectId, _tokenCount * _weight / _decimals, didPayData.beneficiary, didPayData.memo, didPayData.preferClaimedTokens, true) ) ); @@ -728,18 +732,20 @@ contract TestJBBuybackDelegate_Units is Test { /** * @notice Test didPay with swap reverting while using the twap, should then mint with the delegate balance, random erc20 is terminal token */ - function test_didPay_swapRevertWithoutQuote_ETH(uint256 _tokenCount, uint256 _weight) public { + function test_didPay_swapRevertWithoutQuote_ETH(uint256 _tokenCount, uint256 _weight, uint256 _decimals) public { // The current weight _weight = bound(_weight, 1, 1 ether); // The amount of termminal token in this delegate (avoid overflowing when mul by weight) _tokenCount = bound(_tokenCount, 2, type(uint128).max); + _decimals = bound(_decimals, 1, 18); + didPayData.amount = - JBTokenAmount({token: JBTokens.ETH, value: _tokenCount, decimals: 18, currency: 1}); + JBTokenAmount({token: JBTokens.ETH, value: _tokenCount, decimals: _decimals, currency: 1}); didPayData.forwardedAmount = - JBTokenAmount({token: JBTokens.ETH, value: _tokenCount, decimals: 18, currency: 1}); + JBTokenAmount({token: JBTokens.ETH, value: _tokenCount, decimals: _decimals, currency: 1}); // The metadata coming from payParams(..) didPayData.dataSourceMetadata = abi.encode( From 91adb2131c45bd1fa51ac8465534e343a5a181b1 Mon Sep 17 00:00:00 2001 From: drgorillamd Date: Wed, 27 Sep 2023 17:25:42 +0200 Subject: [PATCH 6/9] feat: erc20 unit test fuzzed dec and extra mints --- contracts/test/JBBuybackDelegate_Unit.t.sol | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/contracts/test/JBBuybackDelegate_Unit.t.sol b/contracts/test/JBBuybackDelegate_Unit.t.sol index 5badc7a..e42dea7 100644 --- a/contracts/test/JBBuybackDelegate_Unit.t.sol +++ b/contracts/test/JBBuybackDelegate_Unit.t.sol @@ -609,13 +609,17 @@ contract TestJBBuybackDelegate_Units is Test { /** * @notice Test didPay with swap reverting while using the twap, should then mint with the delegate balance, random erc20 is terminal token */ - function test_didPay_swapRevertWithoutQuote_ERC20(uint256 _tokenCount, uint256 _weight, uint256 _decimals) public { + function test_didPay_swapRevertWithoutQuote_ERC20(uint256 _tokenCount, uint256 _weight, uint256 _decimals, uint256 _extraMint) public { // The current weight _weight = bound(_weight, 1, 1 ether); // The amount of termminal token in this delegate (avoid overflowing when mul by weight) _tokenCount = bound(_tokenCount, 2, type(uint128).max); + // An extra amount of token to mint, based on fund which stayed in the terminal + _extraMint = bound(_extraMint, 2, type(uint128).max); + + // The terminal token decimal _decimals = bound(_decimals, 1, 18); didPayData.amount = @@ -635,7 +639,7 @@ contract TestJBBuybackDelegate_Units is Test { false, // use quote address(otherRandomProjectToken) < address(randomTerminalToken), _tokenCount, - 0, + _extraMint, // extra amount to mint with _weight ); @@ -682,7 +686,7 @@ contract TestJBBuybackDelegate_Units is Test { address(controller), abi.encodeCall( controller.mintTokensOf, - (didPayData.projectId, mulDiv(_tokenCount, _weight, _decimals), didPayData.beneficiary, didPayData.memo, didPayData.preferClaimedTokens, true) + (didPayData.projectId, mulDiv(_tokenCount, _weight, 10**_decimals) + mulDiv(_extraMint, _weight, 10**_decimals), didPayData.beneficiary, didPayData.memo, didPayData.preferClaimedTokens, true) ), abi.encode(true) ); @@ -690,7 +694,7 @@ contract TestJBBuybackDelegate_Units is Test { address(controller), abi.encodeCall( controller.mintTokensOf, - (didPayData.projectId, _tokenCount * _weight / _decimals, didPayData.beneficiary, didPayData.memo, didPayData.preferClaimedTokens, true) + (didPayData.projectId, mulDiv(_tokenCount, _weight, 10**_decimals) + mulDiv(_extraMint, _weight, 10**_decimals), didPayData.beneficiary, didPayData.memo, didPayData.preferClaimedTokens, true) ) ); @@ -721,9 +725,9 @@ contract TestJBBuybackDelegate_Units is Test { ) ); - // expect event + // expect event - only for the non-extra mint vm.expectEmit(true, true, true, true); - emit BuybackDelegate_Mint(didPayData.projectId, _tokenCount, _tokenCount * _weight / 10**18, address(jbxTerminal)); + emit BuybackDelegate_Mint(didPayData.projectId, _tokenCount, mulDiv(_tokenCount, _weight, 10**_decimals), address(jbxTerminal)); vm.prank(address(jbxTerminal)); delegate.didPay(didPayData); From 384fb4d388ad344f919d8fa9412279949f673dfa Mon Sep 17 00:00:00 2001 From: drgorillamd Date: Wed, 27 Sep 2023 17:28:30 +0200 Subject: [PATCH 7/9] feat: fuzz unit tests dec+extramint for eth term --- contracts/test/JBBuybackDelegate_Unit.t.sol | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/contracts/test/JBBuybackDelegate_Unit.t.sol b/contracts/test/JBBuybackDelegate_Unit.t.sol index e42dea7..2fc5301 100644 --- a/contracts/test/JBBuybackDelegate_Unit.t.sol +++ b/contracts/test/JBBuybackDelegate_Unit.t.sol @@ -736,13 +736,17 @@ contract TestJBBuybackDelegate_Units is Test { /** * @notice Test didPay with swap reverting while using the twap, should then mint with the delegate balance, random erc20 is terminal token */ - function test_didPay_swapRevertWithoutQuote_ETH(uint256 _tokenCount, uint256 _weight, uint256 _decimals) public { + function test_didPay_swapRevertWithoutQuote_ETH(uint256 _tokenCount, uint256 _weight, uint256 _decimals, uint256 _extraMint) public { // The current weight _weight = bound(_weight, 1, 1 ether); // The amount of termminal token in this delegate (avoid overflowing when mul by weight) _tokenCount = bound(_tokenCount, 2, type(uint128).max); + // An extra amount of token to mint, based on fund which stayed in the terminal + _extraMint = bound(_extraMint, 2, type(uint128).max); + + // The terminal token decimal _decimals = bound(_decimals, 1, 18); didPayData.amount = @@ -756,7 +760,7 @@ contract TestJBBuybackDelegate_Units is Test { false, // use quote address(projectToken) < address(weth), _tokenCount, - 0, + _extraMint, _weight ); @@ -797,7 +801,7 @@ contract TestJBBuybackDelegate_Units is Test { address(controller), abi.encodeCall( controller.mintTokensOf, - (didPayData.projectId, mulDiv18(_tokenCount, _weight), didPayData.beneficiary, didPayData.memo, didPayData.preferClaimedTokens, true) + (didPayData.projectId, mulDiv(_tokenCount, _weight, 10**_decimals) + mulDiv(_extraMint, _weight, 10**_decimals), didPayData.beneficiary, didPayData.memo, didPayData.preferClaimedTokens, true) ), abi.encode(true) ); @@ -805,7 +809,7 @@ contract TestJBBuybackDelegate_Units is Test { address(controller), abi.encodeCall( controller.mintTokensOf, - (didPayData.projectId, _tokenCount * _weight / 1e18, didPayData.beneficiary, didPayData.memo, didPayData.preferClaimedTokens, true) + (didPayData.projectId, mulDiv(_tokenCount, _weight, 10**_decimals) + mulDiv(_extraMint, _weight, 10**_decimals), didPayData.beneficiary, didPayData.memo, didPayData.preferClaimedTokens, true) ) ); @@ -830,7 +834,7 @@ contract TestJBBuybackDelegate_Units is Test { // expect event vm.expectEmit(true, true, true, true); - emit BuybackDelegate_Mint(didPayData.projectId, _tokenCount, _tokenCount * _weight / 10**18, address(jbxTerminal)); + emit BuybackDelegate_Mint(didPayData.projectId, _tokenCount, mulDiv(_tokenCount, _weight, 10**_decimals), address(jbxTerminal)); vm.prank(address(jbxTerminal)); delegate.didPay(didPayData); From 0312756b362cc0941ed61e458334a14279b1b3b1 Mon Sep 17 00:00:00 2001 From: drgorillamd Date: Wed, 27 Sep 2023 17:34:30 +0200 Subject: [PATCH 8/9] feat: unit test payParams fuzzed dec --- contracts/test/JBBuybackDelegate_Unit.t.sol | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/contracts/test/JBBuybackDelegate_Unit.t.sol b/contracts/test/JBBuybackDelegate_Unit.t.sol index 2fc5301..69858ab 100644 --- a/contracts/test/JBBuybackDelegate_Unit.t.sol +++ b/contracts/test/JBBuybackDelegate_Unit.t.sol @@ -143,14 +143,20 @@ contract TestJBBuybackDelegate_Units is Test { * * @dev _tokenCount == weight, as we use a value of 1. */ - function test_payParams_callWithQuote(uint256 _weight, uint256 _swapOutCount, uint256 _amountIn) public { + function test_payParams_callWithQuote(uint256 _weight, uint256 _swapOutCount, uint256 _amountIn, uint256 _decimals) public { + // Avoid accidentally using the twap (triggered if out == 0) + _swapOutCount = bound(_swapOutCount, 1, type(uint256).max); + + // Avoid mulDiv overflow + _weight = bound(_weight, 1, 1 ether); + // Use between 1 wei and the whole amount from pay(..) _amountIn = bound(_amountIn, 1, payParams.amount.value); - // Avoid accidentally using the twap (triggered if out == 0) - _swapOutCount = bound(_swapOutCount, 1, type(uint256).max); + // The terminal token decimals + _decimals = bound(_decimals, 1, 18); - uint256 _tokenCount = mulDiv18(_amountIn, _weight); + uint256 _tokenCount = mulDiv(_amountIn, _weight, 10**_decimals); // Pass the quote as metadata bytes[] memory _data = new bytes[](1); @@ -166,6 +172,7 @@ contract TestJBBuybackDelegate_Units is Test { // Set the relevant payParams data payParams.weight = _weight; payParams.metadata = _metadata; + payParams.amount = JBTokenAmount({token: address(weth), value: 1 ether, decimals: _decimals, currency: 1}); // Returned values to catch: JBPayDelegateAllocation3_1_1[] memory _allocationsReturned; From 1431a5d12e228477394fd4b2180c5a74a87c2c5a Mon Sep 17 00:00:00 2001 From: drgorillamd Date: Wed, 27 Sep 2023 17:36:04 +0200 Subject: [PATCH 9/9] fix: rm last mulDiv18 from unit tests --- contracts/test/JBBuybackDelegate_Unit.t.sol | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/contracts/test/JBBuybackDelegate_Unit.t.sol b/contracts/test/JBBuybackDelegate_Unit.t.sol index 69858ab..18ca00a 100644 --- a/contracts/test/JBBuybackDelegate_Unit.t.sol +++ b/contracts/test/JBBuybackDelegate_Unit.t.sol @@ -20,8 +20,6 @@ import "./helpers/PoolAddress.sol"; import "../JBBuybackDelegate.sol"; import "../libraries/JBBuybackDelegateOperations.sol"; -import {mulDiv18} from "@prb/math/src/Common.sol"; - /** * @notice Unit tests for the JBBuybackDelegate contract. * @@ -330,7 +328,7 @@ contract TestJBBuybackDelegate_Units is Test { uint256 _weight = 1 ether; - uint256 _tokenCount = mulDiv18(_amountIn, _weight); + uint256 _tokenCount = mulDiv(_amountIn, _weight, 10**18); // Avoid accidentally using the twap (triggered if out == 0) _swapOutCount = bound(_swapOutCount, _tokenCount + 1, type(uint256).max);