From d9d29bb529acbd399014de4e2bed7d3cffe65202 Mon Sep 17 00:00:00 2001 From: xBA5ED <83727748+xBA5ED@users.noreply.github.com> Date: Thu, 31 Aug 2023 01:55:50 +0200 Subject: [PATCH 1/7] feat: initial version of Permit2 support --- contracts/JBERC20PaymentTerminal3_1_3.sol | 216 ++++++++++++++++++ ...JBPayoutRedemptionPaymentTerminal3_1_2.sol | 107 +++++---- contracts/structs/JBSingleAllowanceData.sol | 15 ++ package.json | 5 +- yarn.lock | 4 + 5 files changed, 290 insertions(+), 57 deletions(-) create mode 100644 contracts/JBERC20PaymentTerminal3_1_3.sol create mode 100644 contracts/structs/JBSingleAllowanceData.sol diff --git a/contracts/JBERC20PaymentTerminal3_1_3.sol b/contracts/JBERC20PaymentTerminal3_1_3.sol new file mode 100644 index 000000000..5429d527c --- /dev/null +++ b/contracts/JBERC20PaymentTerminal3_1_3.sol @@ -0,0 +1,216 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.16; + +import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol'; +import {IERC20Metadata} from '@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol'; +import {SafeERC20} from '@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol'; +import {JBPayoutRedemptionPaymentTerminal3_1_2, JBTokens} from './abstract/JBPayoutRedemptionPaymentTerminal3_1_2.sol'; +import {IJBDirectory} from './interfaces/IJBDirectory.sol'; +import {IJBOperatorStore} from './interfaces/IJBOperatorStore.sol'; +import {IJBProjects} from './interfaces/IJBProjects.sol'; +import {IJBSplitsStore} from './interfaces/IJBSplitsStore.sol'; +import {IJBPrices} from './interfaces/IJBPrices.sol'; +import {JBSingleAllowanceData} from './structs/JBSingleAllowanceData.sol'; +import {IPermit2, IAllowanceTransfer} from 'permit2/src/interfaces/IPermit2.sol'; + +/// @notice Manages the inflows and outflows of an ERC-20 token. +contract JBERC20PaymentTerminal3_1_2 is JBPayoutRedemptionPaymentTerminal3_1_2 { + using SafeERC20 for IERC20; + + //*********************************************************************// + // --------------------------- custom errors ------------------------- // + //*********************************************************************// + error PERMIT_ALLOWANCE_NOT_ENOUGH(uint256 _transactionAmount, uint256 _permitAllowance); + + //*********************************************************************// + // ---------------- public immutable stored properties --------------- // + //*********************************************************************// + IPermit2 immutable PERMIT2; + + //*********************************************************************// + // -------------------------- internal views ------------------------- // + //*********************************************************************// + + /// @notice Checks the balance of tokens in this contract. + /// @return The contract's balance, as a fixed point number with the same amount of decimals as this terminal. + function _balance() internal view override returns (uint256) { + return IERC20(token).balanceOf(address(this)); + } + + //*********************************************************************// + // -------------------------- constructor ---------------------------- // + //*********************************************************************// + + /// @param _token The token that this terminal manages. + /// @param _currency The currency that this terminal's token adheres to for price feeds. + /// @param _baseWeightCurrency The currency to base token issuance on. + /// @param _payoutSplitsGroup The group that denotes payout splits from this terminal in the splits store. + /// @param _operatorStore A contract storing operator assignments. + /// @param _projects A contract which mints ERC-721's that represent project ownership and transfers. + /// @param _directory A contract storing directories of terminals and controllers for each project. + /// @param _splitsStore A contract that stores splits for each project. + /// @param _prices A contract that exposes price feeds. + /// @param _store A contract that stores the terminal's data. + /// @param _owner The address that will own this contract. + constructor( + IERC20Metadata _token, + uint256 _currency, + uint256 _baseWeightCurrency, + uint256 _payoutSplitsGroup, + IJBOperatorStore _operatorStore, + IJBProjects _projects, + IJBDirectory _directory, + IJBSplitsStore _splitsStore, + IJBPrices _prices, + address _store, + address _owner, + IPermit2 _permit2 + ) + JBPayoutRedemptionPaymentTerminal3_1_2( + address(_token), + _token.decimals(), + _currency, + _baseWeightCurrency, + _payoutSplitsGroup, + _operatorStore, + _projects, + _directory, + _splitsStore, + _prices, + _store, + _owner + ) + // solhint-disable-next-line no-empty-blocks + { + PERMIT2 = _permit2; + } + + //*********************************************************************// + // ----------------------- public transactions ----------------------- // + //*********************************************************************// + + /// @notice Contribute tokens to a project and sets an allowance for this terminal (using Permit2). + /// @param _projectId The ID of the project being paid. + /// @param _amount The amount of terminal tokens being received, as a fixed point number with the same amount of decimals as this terminal. If this terminal's token is ETH, this is ignored and msg.value is used in its place. + /// @param _token The token being paid. This terminal ignores this property since it only manages one token. + /// @param _beneficiary The address to mint tokens for and pass along to the funding cycle's data source and delegate. + /// @param _minReturnedTokens The minimum number of project tokens expected in return, as a fixed point number with the same amount of decimals as this terminal. + /// @param _preferClaimedTokens A flag indicating whether the request prefers to mint project tokens into the beneficiaries wallet rather than leaving them unclaimed. This is only possible if the project has an attached token contract. Leaving them unclaimed saves gas. + /// @param _memo A memo to pass along to the emitted event, and passed along the the funding cycle's data source and delegate. A data source can alter the memo before emitting in the event and forwarding to the delegate. + /// @param _metadata Bytes to send along to the data source, delegate, and emitted event, if provided. + /// @param _allowance The allowance to set for this terminal (using Permit2). + /// @return The number of tokens minted for the beneficiary, as a fixed point number with 18 decimals. + function payAndSetAllowance( + uint256 _projectId, + uint256 _amount, + address _token, + address _beneficiary, + uint256 _minReturnedTokens, + bool _preferClaimedTokens, + string calldata _memo, + bytes calldata _metadata, + JBSingleAllowanceData calldata _allowance + ) external virtual returns (uint256) { + // If the `_allowance.amount` is less than `_amount` then + // setting the permit will still not result in a succeful payment + if (_amount < _allowance.amount) revert PERMIT_ALLOWANCE_NOT_ENOUGH(_amount, _allowance.amount); + // Get allowance to `spend` tokens for the sender + _permitAllowance(_allowance); + // Continue with the regular pay flow + return + pay( + _projectId, + _amount, + _token, + _beneficiary, + _minReturnedTokens, + _preferClaimedTokens, + _memo, + _metadata + ); + } + + /// @notice Receives funds belonging to the specified project. + /// @param _projectId The ID of the project to which the funds received belong. + /// @param _amount The amount of tokens to add, as a fixed point number with the same number of decimals as this terminal. If this is an ETH terminal, this is ignored and msg.value is used instead. + /// @param _token The token being paid. This terminal ignores this property since it only manages one currency. + /// @param _shouldRefundHeldFees A flag indicating if held fees should be refunded based on the amount being added. + /// @param _memo A memo to pass along to the emitted event. + /// @param _metadata Extra data to pass along to the emitted event. + /// @param _allowance The allowance to set for this terminal (using Permit2). + function addToBalanceOfAndSetAllowance( + uint256 _projectId, + uint256 _amount, + address _token, + bool _shouldRefundHeldFees, + string calldata _memo, + bytes calldata _metadata, + JBSingleAllowanceData calldata _allowance + ) external virtual { + // If the `_allowance.amount` is less than `_amount` then + // setting the permit will still not result in a succeful payment + if (_amount < _allowance.amount) revert PERMIT_ALLOWANCE_NOT_ENOUGH(_amount, _allowance.amount); + // Get allowance to `spend` tokens for the user + _permitAllowance(_allowance); + // Continue with the regular addToBalanceOf flow + return addToBalanceOf(_projectId, _amount, _token, _shouldRefundHeldFees, _memo, _metadata); + } + + //*********************************************************************// + // ---------------------- internal transactions ---------------------- // + //*********************************************************************// + + /** + * @notice Gets allowance + * @param _allowance the allowance to get using permit2 + */ + function _permitAllowance(JBSingleAllowanceData calldata _allowance) internal { + // Use Permit2 to set the allowance + PERMIT2.permit( + msg.sender, + IAllowanceTransfer.PermitSingle({ + details: IAllowanceTransfer.PermitDetails({ + token: address(token), + amount: _allowance.amount, + expiration: _allowance.expiration, + nonce: _allowance.nonce + }), + spender: address(this), + sigDeadline: _allowance.sigDeadline + }), + _allowance.signature + ); + } + + /// @notice Transfers tokens. + /// @param _from The address from which the transfer should originate. + /// @param _to The address to which the transfer should go. + /// @param _amount The amount of the transfer, as a fixed point number with the same number of decimals as this terminal. + function _transferFrom(address _from, address payable _to, uint256 _amount) internal override { + // If this terminal is the sender we send the tokens directly + if (_from == address(this)) return IERC20(token).safeTransfer(_to, _amount); + + // Get the approval that the `_from` has given us + // If we have enough direct approval we use this method + uint256 _approvalAmount = IERC20(token).allowance(address(this), address(_from)); + if (_approvalAmount >= _amount) return IERC20(token).safeTransferFrom(_from, _to, _amount); + + // Otherwise we attempt to use the PERMIT2 method + // TODO: Should we add safeCast for casting 256 to 160? + PERMIT2.transferFrom(_from, _to, uint160(_amount), address(token)); + } + + /// @notice Logic to be triggered before transferring tokens from this terminal. + /// @param _to The address to which the transfer is going. + /// @param _amount The amount of the transfer, as a fixed point number with the same number of decimals as this terminal. + function _beforeTransferTo(address _to, uint256 _amount) internal override { + IERC20(token).safeIncreaseAllowance(_to, _amount); + } + + /// @notice Logic to be triggered if a transfer should be undone + /// @param _to The address to which the transfer went. + /// @param _amount The amount of the transfer, as a fixed point number with the same number of decimals as this terminal. + function _cancelTransferTo(address _to, uint256 _amount) internal override { + IERC20(token).safeDecreaseAllowance(_to, _amount); + } +} diff --git a/contracts/abstract/JBPayoutRedemptionPaymentTerminal3_1_2.sol b/contracts/abstract/JBPayoutRedemptionPaymentTerminal3_1_2.sol index 393822e05..b6378f626 100644 --- a/contracts/abstract/JBPayoutRedemptionPaymentTerminal3_1_2.sol +++ b/contracts/abstract/JBPayoutRedemptionPaymentTerminal3_1_2.sol @@ -244,57 +244,6 @@ abstract contract JBPayoutRedemptionPaymentTerminal3_1_2 is // ---------------------- external transactions ---------------------- // //*********************************************************************// - /// @notice Contribute tokens to a project. - /// @param _projectId The ID of the project being paid. - /// @param _amount The amount of terminal tokens being received, as a fixed point number with the same amount of decimals as this terminal. If this terminal's token is ETH, this is ignored and msg.value is used in its place. - /// @param _token The token being paid. This terminal ignores this property since it only manages one token. - /// @param _beneficiary The address to mint tokens for and pass along to the funding cycle's data source and delegate. - /// @param _minReturnedTokens The minimum number of project tokens expected in return, as a fixed point number with the same amount of decimals as this terminal. - /// @param _preferClaimedTokens A flag indicating whether the request prefers to mint project tokens into the beneficiaries wallet rather than leaving them unclaimed. This is only possible if the project has an attached token contract. Leaving them unclaimed saves gas. - /// @param _memo A memo to pass along to the emitted event, and passed along the the funding cycle's data source and delegate. A data source can alter the memo before emitting in the event and forwarding to the delegate. - /// @param _metadata Bytes to send along to the data source, delegate, and emitted event, if provided. - /// @return The number of tokens minted for the beneficiary, as a fixed point number with 18 decimals. - function pay( - uint256 _projectId, - uint256 _amount, - address _token, - address _beneficiary, - uint256 _minReturnedTokens, - bool _preferClaimedTokens, - string calldata _memo, - bytes calldata _metadata - ) external payable virtual override returns (uint256) { - _token; // Prevents unused var compiler and natspec complaints. - - // ETH shouldn't be sent if this terminal's token isn't ETH. - if (token != JBTokens.ETH) { - if (msg.value != 0) revert NO_MSG_VALUE_ALLOWED(); - - // Get a reference to the balance before receiving tokens. - uint256 _balanceBefore = _balance(); - - // Transfer tokens to this terminal from the msg sender. - _transferFrom(msg.sender, payable(address(this)), _amount); - - // The amount should reflect the change in balance. - _amount = _balance() - _balanceBefore; - } - // If this terminal's token is ETH, override _amount with msg.value. - else _amount = msg.value; - - return - _pay( - _amount, - msg.sender, - _projectId, - _beneficiary, - _minReturnedTokens, - _preferClaimedTokens, - _memo, - _metadata - ); - } - /// @notice Holders can redeem their tokens to claim the project's overflowed tokens, or to trigger rules determined by the project's current funding cycle's data source. /// @dev Only a token holder or a designated operator can redeem its tokens. /// @param _holder The account to redeem tokens for. @@ -541,6 +490,57 @@ abstract contract JBPayoutRedemptionPaymentTerminal3_1_2 is // ----------------------- public transactions ----------------------- // //*********************************************************************// + /// @notice Contribute tokens to a project. + /// @param _projectId The ID of the project being paid. + /// @param _amount The amount of terminal tokens being received, as a fixed point number with the same amount of decimals as this terminal. If this terminal's token is ETH, this is ignored and msg.value is used in its place. + /// @param _token The token being paid. This terminal ignores this property since it only manages one token. + /// @param _beneficiary The address to mint tokens for and pass along to the funding cycle's data source and delegate. + /// @param _minReturnedTokens The minimum number of project tokens expected in return, as a fixed point number with the same amount of decimals as this terminal. + /// @param _preferClaimedTokens A flag indicating whether the request prefers to mint project tokens into the beneficiaries wallet rather than leaving them unclaimed. This is only possible if the project has an attached token contract. Leaving them unclaimed saves gas. + /// @param _memo A memo to pass along to the emitted event, and passed along the the funding cycle's data source and delegate. A data source can alter the memo before emitting in the event and forwarding to the delegate. + /// @param _metadata Bytes to send along to the data source, delegate, and emitted event, if provided. + /// @return The number of tokens minted for the beneficiary, as a fixed point number with 18 decimals. + function pay( + uint256 _projectId, + uint256 _amount, + address _token, + address _beneficiary, + uint256 _minReturnedTokens, + bool _preferClaimedTokens, + string calldata _memo, + bytes calldata _metadata + ) public payable virtual override returns (uint256) { + _token; // Prevents unused var compiler and natspec complaints. + + // ETH shouldn't be sent if this terminal's token isn't ETH. + if (token != JBTokens.ETH) { + if (msg.value != 0) revert NO_MSG_VALUE_ALLOWED(); + + // Get a reference to the balance before receiving tokens. + uint256 _balanceBefore = _balance(); + + // Transfer tokens to this terminal from the msg sender. + _transferFrom(msg.sender, payable(address(this)), _amount); + + // The amount should reflect the change in balance. + _amount = _balance() - _balanceBefore; + } + // If this terminal's token is ETH, override _amount with msg.value. + else _amount = msg.value; + + return + _pay( + _amount, + msg.sender, + _projectId, + _beneficiary, + _minReturnedTokens, + _preferClaimedTokens, + _memo, + _metadata + ); + } + /// @notice Receives funds belonging to the specified project. /// @param _projectId The ID of the project to which the funds received belong. /// @param _amount The amount of tokens to add, as a fixed point number with the same number of decimals as this terminal. If this is an ETH terminal, this is ignored and msg.value is used instead. @@ -1504,10 +1504,8 @@ abstract contract JBPayoutRedemptionPaymentTerminal3_1_2 is // Process each fee. for (uint256 _i; _i < _heldFeesLength; ) { - - if (leftoverAmount == 0) { + if (leftoverAmount == 0) { _heldFeesOf[_projectId].push(_heldFees[_i]); - } else { // Notice here we take feeIn the stored .amount uint256 _feeAmount = ( @@ -1542,7 +1540,6 @@ abstract contract JBPayoutRedemptionPaymentTerminal3_1_2 is } leftoverAmount = 0; } - } unchecked { diff --git a/contracts/structs/JBSingleAllowanceData.sol b/contracts/structs/JBSingleAllowanceData.sol new file mode 100644 index 000000000..344795bc2 --- /dev/null +++ b/contracts/structs/JBSingleAllowanceData.sol @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +/// @custom:member sigDeadline deadline on the permit signature +/// @custom:member amount the maximum amount allowed to spend +/// @custom:member expiration timestamp at which a spender's token allowances become invalid +/// @custom:member nonce an incrementing value indexed per owner,token,and spender for each signature +/// @custom:member signature the signature over the permit data. Supports EOA signatures, compact signatures defined by EIP-2098, and contract signatures defined by EIP-1271 +struct JBSingleAllowanceData { + uint256 sigDeadline; + uint160 amount; + uint48 expiration; + uint48 nonce; + bytes signature; +} diff --git a/package.json b/package.json index d1ea23ad7..7fba1251e 100644 --- a/package.json +++ b/package.json @@ -11,8 +11,9 @@ "license": "MIT", "dependencies": { "@chainlink/contracts": "^0.1.6", + "@openzeppelin/contracts": "^4.5.0-rc.0", "@paulrberg/contracts": "^3.4.0", - "@openzeppelin/contracts": "^4.5.0-rc.0" + "permit2": "https://github.com/Uniswap/permit2" }, "devDependencies": { "@defi-wonderland/smock": "^2.2.0", @@ -44,4 +45,4 @@ "compile": "yarn clean && hardhat compile", "pretty": "prettier --write \"./**/*.{js,jsx,json,sol}\"" } -} \ No newline at end of file +} diff --git a/yarn.lock b/yarn.lock index c75e9bfdf..77100a064 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8310,6 +8310,10 @@ performance-now@^2.1.0: resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= +"permit2@https://github.com/Uniswap/permit2": + version "0.0.0" + resolved "https://github.com/Uniswap/permit2#576f549a7351814f112edcc42f3f8472d1712673" + picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.3: version "2.3.1" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" From 5eb3be90957de14074aed1b20363e02ef8b04404 Mon Sep 17 00:00:00 2001 From: mejango Date: Fri, 1 Sep 2023 10:03:16 -0400 Subject: [PATCH 2/7] merged into 3.2 --- contracts/JBERC20PaymentTerminal3_1_3.sol | 216 ---------------------- contracts/JBERC20PaymentTerminal3_2.sol | 105 +++++++++++ 2 files changed, 105 insertions(+), 216 deletions(-) delete mode 100644 contracts/JBERC20PaymentTerminal3_1_3.sol diff --git a/contracts/JBERC20PaymentTerminal3_1_3.sol b/contracts/JBERC20PaymentTerminal3_1_3.sol deleted file mode 100644 index 5429d527c..000000000 --- a/contracts/JBERC20PaymentTerminal3_1_3.sol +++ /dev/null @@ -1,216 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.16; - -import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol'; -import {IERC20Metadata} from '@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol'; -import {SafeERC20} from '@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol'; -import {JBPayoutRedemptionPaymentTerminal3_1_2, JBTokens} from './abstract/JBPayoutRedemptionPaymentTerminal3_1_2.sol'; -import {IJBDirectory} from './interfaces/IJBDirectory.sol'; -import {IJBOperatorStore} from './interfaces/IJBOperatorStore.sol'; -import {IJBProjects} from './interfaces/IJBProjects.sol'; -import {IJBSplitsStore} from './interfaces/IJBSplitsStore.sol'; -import {IJBPrices} from './interfaces/IJBPrices.sol'; -import {JBSingleAllowanceData} from './structs/JBSingleAllowanceData.sol'; -import {IPermit2, IAllowanceTransfer} from 'permit2/src/interfaces/IPermit2.sol'; - -/// @notice Manages the inflows and outflows of an ERC-20 token. -contract JBERC20PaymentTerminal3_1_2 is JBPayoutRedemptionPaymentTerminal3_1_2 { - using SafeERC20 for IERC20; - - //*********************************************************************// - // --------------------------- custom errors ------------------------- // - //*********************************************************************// - error PERMIT_ALLOWANCE_NOT_ENOUGH(uint256 _transactionAmount, uint256 _permitAllowance); - - //*********************************************************************// - // ---------------- public immutable stored properties --------------- // - //*********************************************************************// - IPermit2 immutable PERMIT2; - - //*********************************************************************// - // -------------------------- internal views ------------------------- // - //*********************************************************************// - - /// @notice Checks the balance of tokens in this contract. - /// @return The contract's balance, as a fixed point number with the same amount of decimals as this terminal. - function _balance() internal view override returns (uint256) { - return IERC20(token).balanceOf(address(this)); - } - - //*********************************************************************// - // -------------------------- constructor ---------------------------- // - //*********************************************************************// - - /// @param _token The token that this terminal manages. - /// @param _currency The currency that this terminal's token adheres to for price feeds. - /// @param _baseWeightCurrency The currency to base token issuance on. - /// @param _payoutSplitsGroup The group that denotes payout splits from this terminal in the splits store. - /// @param _operatorStore A contract storing operator assignments. - /// @param _projects A contract which mints ERC-721's that represent project ownership and transfers. - /// @param _directory A contract storing directories of terminals and controllers for each project. - /// @param _splitsStore A contract that stores splits for each project. - /// @param _prices A contract that exposes price feeds. - /// @param _store A contract that stores the terminal's data. - /// @param _owner The address that will own this contract. - constructor( - IERC20Metadata _token, - uint256 _currency, - uint256 _baseWeightCurrency, - uint256 _payoutSplitsGroup, - IJBOperatorStore _operatorStore, - IJBProjects _projects, - IJBDirectory _directory, - IJBSplitsStore _splitsStore, - IJBPrices _prices, - address _store, - address _owner, - IPermit2 _permit2 - ) - JBPayoutRedemptionPaymentTerminal3_1_2( - address(_token), - _token.decimals(), - _currency, - _baseWeightCurrency, - _payoutSplitsGroup, - _operatorStore, - _projects, - _directory, - _splitsStore, - _prices, - _store, - _owner - ) - // solhint-disable-next-line no-empty-blocks - { - PERMIT2 = _permit2; - } - - //*********************************************************************// - // ----------------------- public transactions ----------------------- // - //*********************************************************************// - - /// @notice Contribute tokens to a project and sets an allowance for this terminal (using Permit2). - /// @param _projectId The ID of the project being paid. - /// @param _amount The amount of terminal tokens being received, as a fixed point number with the same amount of decimals as this terminal. If this terminal's token is ETH, this is ignored and msg.value is used in its place. - /// @param _token The token being paid. This terminal ignores this property since it only manages one token. - /// @param _beneficiary The address to mint tokens for and pass along to the funding cycle's data source and delegate. - /// @param _minReturnedTokens The minimum number of project tokens expected in return, as a fixed point number with the same amount of decimals as this terminal. - /// @param _preferClaimedTokens A flag indicating whether the request prefers to mint project tokens into the beneficiaries wallet rather than leaving them unclaimed. This is only possible if the project has an attached token contract. Leaving them unclaimed saves gas. - /// @param _memo A memo to pass along to the emitted event, and passed along the the funding cycle's data source and delegate. A data source can alter the memo before emitting in the event and forwarding to the delegate. - /// @param _metadata Bytes to send along to the data source, delegate, and emitted event, if provided. - /// @param _allowance The allowance to set for this terminal (using Permit2). - /// @return The number of tokens minted for the beneficiary, as a fixed point number with 18 decimals. - function payAndSetAllowance( - uint256 _projectId, - uint256 _amount, - address _token, - address _beneficiary, - uint256 _minReturnedTokens, - bool _preferClaimedTokens, - string calldata _memo, - bytes calldata _metadata, - JBSingleAllowanceData calldata _allowance - ) external virtual returns (uint256) { - // If the `_allowance.amount` is less than `_amount` then - // setting the permit will still not result in a succeful payment - if (_amount < _allowance.amount) revert PERMIT_ALLOWANCE_NOT_ENOUGH(_amount, _allowance.amount); - // Get allowance to `spend` tokens for the sender - _permitAllowance(_allowance); - // Continue with the regular pay flow - return - pay( - _projectId, - _amount, - _token, - _beneficiary, - _minReturnedTokens, - _preferClaimedTokens, - _memo, - _metadata - ); - } - - /// @notice Receives funds belonging to the specified project. - /// @param _projectId The ID of the project to which the funds received belong. - /// @param _amount The amount of tokens to add, as a fixed point number with the same number of decimals as this terminal. If this is an ETH terminal, this is ignored and msg.value is used instead. - /// @param _token The token being paid. This terminal ignores this property since it only manages one currency. - /// @param _shouldRefundHeldFees A flag indicating if held fees should be refunded based on the amount being added. - /// @param _memo A memo to pass along to the emitted event. - /// @param _metadata Extra data to pass along to the emitted event. - /// @param _allowance The allowance to set for this terminal (using Permit2). - function addToBalanceOfAndSetAllowance( - uint256 _projectId, - uint256 _amount, - address _token, - bool _shouldRefundHeldFees, - string calldata _memo, - bytes calldata _metadata, - JBSingleAllowanceData calldata _allowance - ) external virtual { - // If the `_allowance.amount` is less than `_amount` then - // setting the permit will still not result in a succeful payment - if (_amount < _allowance.amount) revert PERMIT_ALLOWANCE_NOT_ENOUGH(_amount, _allowance.amount); - // Get allowance to `spend` tokens for the user - _permitAllowance(_allowance); - // Continue with the regular addToBalanceOf flow - return addToBalanceOf(_projectId, _amount, _token, _shouldRefundHeldFees, _memo, _metadata); - } - - //*********************************************************************// - // ---------------------- internal transactions ---------------------- // - //*********************************************************************// - - /** - * @notice Gets allowance - * @param _allowance the allowance to get using permit2 - */ - function _permitAllowance(JBSingleAllowanceData calldata _allowance) internal { - // Use Permit2 to set the allowance - PERMIT2.permit( - msg.sender, - IAllowanceTransfer.PermitSingle({ - details: IAllowanceTransfer.PermitDetails({ - token: address(token), - amount: _allowance.amount, - expiration: _allowance.expiration, - nonce: _allowance.nonce - }), - spender: address(this), - sigDeadline: _allowance.sigDeadline - }), - _allowance.signature - ); - } - - /// @notice Transfers tokens. - /// @param _from The address from which the transfer should originate. - /// @param _to The address to which the transfer should go. - /// @param _amount The amount of the transfer, as a fixed point number with the same number of decimals as this terminal. - function _transferFrom(address _from, address payable _to, uint256 _amount) internal override { - // If this terminal is the sender we send the tokens directly - if (_from == address(this)) return IERC20(token).safeTransfer(_to, _amount); - - // Get the approval that the `_from` has given us - // If we have enough direct approval we use this method - uint256 _approvalAmount = IERC20(token).allowance(address(this), address(_from)); - if (_approvalAmount >= _amount) return IERC20(token).safeTransferFrom(_from, _to, _amount); - - // Otherwise we attempt to use the PERMIT2 method - // TODO: Should we add safeCast for casting 256 to 160? - PERMIT2.transferFrom(_from, _to, uint160(_amount), address(token)); - } - - /// @notice Logic to be triggered before transferring tokens from this terminal. - /// @param _to The address to which the transfer is going. - /// @param _amount The amount of the transfer, as a fixed point number with the same number of decimals as this terminal. - function _beforeTransferTo(address _to, uint256 _amount) internal override { - IERC20(token).safeIncreaseAllowance(_to, _amount); - } - - /// @notice Logic to be triggered if a transfer should be undone - /// @param _to The address to which the transfer went. - /// @param _amount The amount of the transfer, as a fixed point number with the same number of decimals as this terminal. - function _cancelTransferTo(address _to, uint256 _amount) internal override { - IERC20(token).safeDecreaseAllowance(_to, _amount); - } -} diff --git a/contracts/JBERC20PaymentTerminal3_2.sol b/contracts/JBERC20PaymentTerminal3_2.sol index 384ef035e..cc687ca27 100644 --- a/contracts/JBERC20PaymentTerminal3_2.sol +++ b/contracts/JBERC20PaymentTerminal3_2.sol @@ -4,17 +4,31 @@ pragma solidity ^0.8.16; import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol'; import {IERC20Metadata} from '@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol'; import {SafeERC20} from '@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol'; +import {IPermit2, IAllowanceTransfer} from 'permit2/src/interfaces/IPermit2.sol'; import {JBPayoutRedemptionPaymentTerminal3_2} from './abstract/JBPayoutRedemptionPaymentTerminal3_2.sol'; import {IJBDirectory} from './interfaces/IJBDirectory.sol'; import {IJBOperatorStore} from './interfaces/IJBOperatorStore.sol'; import {IJBProjects} from './interfaces/IJBProjects.sol'; import {IJBSplitsStore} from './interfaces/IJBSplitsStore.sol'; import {IJBPrices} from './interfaces/IJBPrices.sol'; +import {JBSingleAllowanceData} from './structs/JBSingleAllowanceData.sol'; /// @notice Manages the inflows and outflows of an ERC-20 token. contract JBERC20PaymentTerminal3_1_2 is JBPayoutRedemptionPaymentTerminal3_2 { using SafeERC20 for IERC20; + //*********************************************************************// + // --------------------------- custom errors ------------------------- // + //*********************************************************************// + + error PERMIT_ALLOWANCE_NOT_ENOUGH(uint256 _transactionAmount, uint256 _permitAllowance); + + //*********************************************************************// + // ---------------- public immutable stored properties --------------- // + //*********************************************************************// + + IPermit2 immutable PERMIT2; + //*********************************************************************// // -------------------------- internal views ------------------------- // //*********************************************************************// @@ -67,10 +81,101 @@ contract JBERC20PaymentTerminal3_1_2 is JBPayoutRedemptionPaymentTerminal3_2 { } + //*********************************************************************// + // ----------------------- public transactions ----------------------- // + //*********************************************************************// + + /// @notice Contribute tokens to a project and sets an allowance for this terminal (using Permit2). + /// @param _projectId The ID of the project being paid. + /// @param _amount The amount of terminal tokens being received, as a fixed point number with the same amount of decimals as this terminal. If this terminal's token is ETH, this is ignored and msg.value is used in its place. + /// @param _token The token being paid. This terminal ignores this property since it only manages one token. + /// @param _beneficiary The address to mint tokens for and pass along to the funding cycle's data source and delegate. + /// @param _minReturnedTokens The minimum number of project tokens expected in return, as a fixed point number with the same amount of decimals as this terminal. + /// @param _preferClaimedTokens A flag indicating whether the request prefers to mint project tokens into the beneficiaries wallet rather than leaving them unclaimed. This is only possible if the project has an attached token contract. Leaving them unclaimed saves gas. + /// @param _memo A memo to pass along to the emitted event, and passed along the the funding cycle's data source and delegate. A data source can alter the memo before emitting in the event and forwarding to the delegate. + /// @param _metadata Bytes to send along to the data source, delegate, and emitted event, if provided. + /// @param _allowance The allowance to set for this terminal (using Permit2). + /// @return The number of tokens minted for the beneficiary, as a fixed point number with 18 decimals. + function payAndSetAllowance( + uint256 _projectId, + uint256 _amount, + address _token, + address _beneficiary, + uint256 _minReturnedTokens, + bool _preferClaimedTokens, + string calldata _memo, + bytes calldata _metadata, + JBSingleAllowanceData calldata _allowance + ) external virtual returns (uint256) { + // If the `_allowance.amount` is less than `_amount` then + // setting the permit will still not result in a succeful payment + if (_amount < _allowance.amount) revert PERMIT_ALLOWANCE_NOT_ENOUGH(_amount, _allowance.amount); + // Get allowance to `spend` tokens for the sender + _permitAllowance(_allowance); + // Continue with the regular pay flow + return + pay( + _projectId, + _amount, + _token, + _beneficiary, + _minReturnedTokens, + _preferClaimedTokens, + _memo, + _metadata + ); + } + + /// @notice Receives funds belonging to the specified project. + /// @param _projectId The ID of the project to which the funds received belong. + /// @param _amount The amount of tokens to add, as a fixed point number with the same number of decimals as this terminal. If this is an ETH terminal, this is ignored and msg.value is used instead. + /// @param _token The token being paid. This terminal ignores this property since it only manages one currency. + /// @param _shouldRefundHeldFees A flag indicating if held fees should be refunded based on the amount being added. + /// @param _memo A memo to pass along to the emitted event. + /// @param _metadata Extra data to pass along to the emitted event. + /// @param _allowance The allowance to set for this terminal (using Permit2). + function addToBalanceOfAndSetAllowance( + uint256 _projectId, + uint256 _amount, + address _token, + bool _shouldRefundHeldFees, + string calldata _memo, + bytes calldata _metadata, + JBSingleAllowanceData calldata _allowance + ) external virtual { + // If the `_allowance.amount` is less than `_amount` then + // setting the permit will still not result in a succeful payment + if (_amount < _allowance.amount) revert PERMIT_ALLOWANCE_NOT_ENOUGH(_amount, _allowance.amount); + // Get allowance to `spend` tokens for the user + _permitAllowance(_allowance); + // Continue with the regular addToBalanceOf flow + return addToBalanceOf(_projectId, _amount, _token, _shouldRefundHeldFees, _memo, _metadata); + } + //*********************************************************************// // ---------------------- internal transactions ---------------------- // //*********************************************************************// + /// @notice Gets allowance + /// @param _allowance the allowance to get using permit2 + function _permitAllowance(JBSingleAllowanceData calldata _allowance) internal { + // Use Permit2 to set the allowance + PERMIT2.permit( + msg.sender, + IAllowanceTransfer.PermitSingle({ + details: IAllowanceTransfer.PermitDetails({ + token: address(token), + amount: _allowance.amount, + expiration: _allowance.expiration, + nonce: _allowance.nonce + }), + spender: address(this), + sigDeadline: _allowance.sigDeadline + }), + _allowance.signature + ); + } + /// @notice Transfers tokens. /// @param _from The address from which the transfer should originate. /// @param _to The address to which the transfer should go. From fce2bdff152a6eb648521e8f066d395f670c30c1 Mon Sep 17 00:00:00 2001 From: mejango Date: Fri, 1 Sep 2023 10:05:13 -0400 Subject: [PATCH 3/7] change 3.2 --- ...JBPayoutRedemptionPaymentTerminal3_1_2.sol | 102 +++++++++--------- .../JBPayoutRedemptionPaymentTerminal3_2.sol | 102 +++++++++--------- 2 files changed, 102 insertions(+), 102 deletions(-) diff --git a/contracts/abstract/JBPayoutRedemptionPaymentTerminal3_1_2.sol b/contracts/abstract/JBPayoutRedemptionPaymentTerminal3_1_2.sol index b6378f626..926ee9504 100644 --- a/contracts/abstract/JBPayoutRedemptionPaymentTerminal3_1_2.sol +++ b/contracts/abstract/JBPayoutRedemptionPaymentTerminal3_1_2.sol @@ -244,6 +244,57 @@ abstract contract JBPayoutRedemptionPaymentTerminal3_1_2 is // ---------------------- external transactions ---------------------- // //*********************************************************************// + /// @notice Contribute tokens to a project. + /// @param _projectId The ID of the project being paid. + /// @param _amount The amount of terminal tokens being received, as a fixed point number with the same amount of decimals as this terminal. If this terminal's token is ETH, this is ignored and msg.value is used in its place. + /// @param _token The token being paid. This terminal ignores this property since it only manages one token. + /// @param _beneficiary The address to mint tokens for and pass along to the funding cycle's data source and delegate. + /// @param _minReturnedTokens The minimum number of project tokens expected in return, as a fixed point number with the same amount of decimals as this terminal. + /// @param _preferClaimedTokens A flag indicating whether the request prefers to mint project tokens into the beneficiaries wallet rather than leaving them unclaimed. This is only possible if the project has an attached token contract. Leaving them unclaimed saves gas. + /// @param _memo A memo to pass along to the emitted event, and passed along the the funding cycle's data source and delegate. A data source can alter the memo before emitting in the event and forwarding to the delegate. + /// @param _metadata Bytes to send along to the data source, delegate, and emitted event, if provided. + /// @return The number of tokens minted for the beneficiary, as a fixed point number with 18 decimals. + function pay( + uint256 _projectId, + uint256 _amount, + address _token, + address _beneficiary, + uint256 _minReturnedTokens, + bool _preferClaimedTokens, + string calldata _memo, + bytes calldata _metadata + ) public payable virtual override returns (uint256) { + _token; // Prevents unused var compiler and natspec complaints. + + // ETH shouldn't be sent if this terminal's token isn't ETH. + if (token != JBTokens.ETH) { + if (msg.value != 0) revert NO_MSG_VALUE_ALLOWED(); + + // Get a reference to the balance before receiving tokens. + uint256 _balanceBefore = _balance(); + + // Transfer tokens to this terminal from the msg sender. + _transferFrom(msg.sender, payable(address(this)), _amount); + + // The amount should reflect the change in balance. + _amount = _balance() - _balanceBefore; + } + // If this terminal's token is ETH, override _amount with msg.value. + else _amount = msg.value; + + return + _pay( + _amount, + msg.sender, + _projectId, + _beneficiary, + _minReturnedTokens, + _preferClaimedTokens, + _memo, + _metadata + ); + } + /// @notice Holders can redeem their tokens to claim the project's overflowed tokens, or to trigger rules determined by the project's current funding cycle's data source. /// @dev Only a token holder or a designated operator can redeem its tokens. /// @param _holder The account to redeem tokens for. @@ -490,57 +541,6 @@ abstract contract JBPayoutRedemptionPaymentTerminal3_1_2 is // ----------------------- public transactions ----------------------- // //*********************************************************************// - /// @notice Contribute tokens to a project. - /// @param _projectId The ID of the project being paid. - /// @param _amount The amount of terminal tokens being received, as a fixed point number with the same amount of decimals as this terminal. If this terminal's token is ETH, this is ignored and msg.value is used in its place. - /// @param _token The token being paid. This terminal ignores this property since it only manages one token. - /// @param _beneficiary The address to mint tokens for and pass along to the funding cycle's data source and delegate. - /// @param _minReturnedTokens The minimum number of project tokens expected in return, as a fixed point number with the same amount of decimals as this terminal. - /// @param _preferClaimedTokens A flag indicating whether the request prefers to mint project tokens into the beneficiaries wallet rather than leaving them unclaimed. This is only possible if the project has an attached token contract. Leaving them unclaimed saves gas. - /// @param _memo A memo to pass along to the emitted event, and passed along the the funding cycle's data source and delegate. A data source can alter the memo before emitting in the event and forwarding to the delegate. - /// @param _metadata Bytes to send along to the data source, delegate, and emitted event, if provided. - /// @return The number of tokens minted for the beneficiary, as a fixed point number with 18 decimals. - function pay( - uint256 _projectId, - uint256 _amount, - address _token, - address _beneficiary, - uint256 _minReturnedTokens, - bool _preferClaimedTokens, - string calldata _memo, - bytes calldata _metadata - ) public payable virtual override returns (uint256) { - _token; // Prevents unused var compiler and natspec complaints. - - // ETH shouldn't be sent if this terminal's token isn't ETH. - if (token != JBTokens.ETH) { - if (msg.value != 0) revert NO_MSG_VALUE_ALLOWED(); - - // Get a reference to the balance before receiving tokens. - uint256 _balanceBefore = _balance(); - - // Transfer tokens to this terminal from the msg sender. - _transferFrom(msg.sender, payable(address(this)), _amount); - - // The amount should reflect the change in balance. - _amount = _balance() - _balanceBefore; - } - // If this terminal's token is ETH, override _amount with msg.value. - else _amount = msg.value; - - return - _pay( - _amount, - msg.sender, - _projectId, - _beneficiary, - _minReturnedTokens, - _preferClaimedTokens, - _memo, - _metadata - ); - } - /// @notice Receives funds belonging to the specified project. /// @param _projectId The ID of the project to which the funds received belong. /// @param _amount The amount of tokens to add, as a fixed point number with the same number of decimals as this terminal. If this is an ETH terminal, this is ignored and msg.value is used instead. diff --git a/contracts/abstract/JBPayoutRedemptionPaymentTerminal3_2.sol b/contracts/abstract/JBPayoutRedemptionPaymentTerminal3_2.sol index 5d2fb671a..eae33372b 100644 --- a/contracts/abstract/JBPayoutRedemptionPaymentTerminal3_2.sol +++ b/contracts/abstract/JBPayoutRedemptionPaymentTerminal3_2.sol @@ -229,57 +229,6 @@ abstract contract JBPayoutRedemptionPaymentTerminal3_2 is // ---------------------- external transactions ---------------------- // //*********************************************************************// - /// @notice Contribute tokens to a project. - /// @param _projectId The ID of the project being paid. - /// @param _amount The amount of terminal tokens being received, as a fixed point number with the same amount of decimals as this terminal. If this terminal's token is ETH, this is ignored and msg.value is used in its place. - /// @param _token The token being paid. This terminal ignores this property since it only manages one token. - /// @param _beneficiary The address to mint tokens for and pass along to the funding cycle's data source and delegate. - /// @param _minReturnedTokens The minimum number of project tokens expected in return, as a fixed point number with the same amount of decimals as this terminal. - /// @param _preferClaimedTokens A flag indicating whether the request prefers to mint project tokens into the beneficiaries wallet rather than leaving them unclaimed. This is only possible if the project has an attached token contract. Leaving them unclaimed saves gas. - /// @param _memo A memo to pass along to the emitted event, and passed along the the funding cycle's data source and delegate. A data source can alter the memo before emitting in the event and forwarding to the delegate. - /// @param _metadata Bytes to send along to the data source, delegate, and emitted event, if provided. - /// @return The number of tokens minted for the beneficiary, as a fixed point number with 18 decimals. - function pay( - uint256 _projectId, - uint256 _amount, - address _token, - address _beneficiary, - uint256 _minReturnedTokens, - bool _preferClaimedTokens, - string calldata _memo, - bytes calldata _metadata - ) external payable virtual override returns (uint256) { - _token; // Prevents unused var compiler and natspec complaints. - - // ETH shouldn't be sent if this terminal's token isn't ETH. - if (token != JBTokens.ETH) { - if (msg.value != 0) revert NO_MSG_VALUE_ALLOWED(); - - // Get a reference to the balance before receiving tokens. - uint256 _balanceBefore = _balance(); - - // Transfer tokens to this terminal from the msg sender. - _transferFrom(msg.sender, payable(address(this)), _amount); - - // The amount should reflect the change in balance. - _amount = _balance() - _balanceBefore; - } - // If this terminal's token is ETH, override _amount with msg.value. - else _amount = msg.value; - - return - _pay( - _amount, - msg.sender, - _projectId, - _beneficiary, - _minReturnedTokens, - _preferClaimedTokens, - _memo, - _metadata - ); - } - /// @notice Holders can redeem their tokens to claim the project's overflowed tokens, or to trigger rules determined by the project's current funding cycle's data source. /// @dev Only a token holder or a designated operator can redeem its tokens. /// @param _holder The account to redeem tokens for. @@ -514,6 +463,57 @@ abstract contract JBPayoutRedemptionPaymentTerminal3_2 is // ----------------------- public transactions ----------------------- // //*********************************************************************// + /// @notice Contribute tokens to a project. + /// @param _projectId The ID of the project being paid. + /// @param _amount The amount of terminal tokens being received, as a fixed point number with the same amount of decimals as this terminal. If this terminal's token is ETH, this is ignored and msg.value is used in its place. + /// @param _token The token being paid. This terminal ignores this property since it only manages one token. + /// @param _beneficiary The address to mint tokens for and pass along to the funding cycle's data source and delegate. + /// @param _minReturnedTokens The minimum number of project tokens expected in return, as a fixed point number with the same amount of decimals as this terminal. + /// @param _preferClaimedTokens A flag indicating whether the request prefers to mint project tokens into the beneficiaries wallet rather than leaving them unclaimed. This is only possible if the project has an attached token contract. Leaving them unclaimed saves gas. + /// @param _memo A memo to pass along to the emitted event, and passed along the the funding cycle's data source and delegate. A data source can alter the memo before emitting in the event and forwarding to the delegate. + /// @param _metadata Bytes to send along to the data source, delegate, and emitted event, if provided. + /// @return The number of tokens minted for the beneficiary, as a fixed point number with 18 decimals. + function pay( + uint256 _projectId, + uint256 _amount, + address _token, + address _beneficiary, + uint256 _minReturnedTokens, + bool _preferClaimedTokens, + string calldata _memo, + bytes calldata _metadata + ) external payable virtual override returns (uint256) { + _token; // Prevents unused var compiler and natspec complaints. + + // ETH shouldn't be sent if this terminal's token isn't ETH. + if (token != JBTokens.ETH) { + if (msg.value != 0) revert NO_MSG_VALUE_ALLOWED(); + + // Get a reference to the balance before receiving tokens. + uint256 _balanceBefore = _balance(); + + // Transfer tokens to this terminal from the msg sender. + _transferFrom(msg.sender, payable(address(this)), _amount); + + // The amount should reflect the change in balance. + _amount = _balance() - _balanceBefore; + } + // If this terminal's token is ETH, override _amount with msg.value. + else _amount = msg.value; + + return + _pay( + _amount, + msg.sender, + _projectId, + _beneficiary, + _minReturnedTokens, + _preferClaimedTokens, + _memo, + _metadata + ); + } + /// @notice Receives funds belonging to the specified project. /// @param _projectId The ID of the project to which the funds received belong. /// @param _amount The amount of tokens to add, as a fixed point number with the same number of decimals as this terminal. If this is an ETH terminal, this is ignored and msg.value is used instead. From 6a0ff7d08225f7886988649844b5e00bba24ad38 Mon Sep 17 00:00:00 2001 From: mejango Date: Fri, 1 Sep 2023 10:06:27 -0400 Subject: [PATCH 4/7] - --- contracts/abstract/JBPayoutRedemptionPaymentTerminal3_1_2.sol | 2 +- contracts/abstract/JBPayoutRedemptionPaymentTerminal3_2.sol | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/contracts/abstract/JBPayoutRedemptionPaymentTerminal3_1_2.sol b/contracts/abstract/JBPayoutRedemptionPaymentTerminal3_1_2.sol index 926ee9504..ab02990b7 100644 --- a/contracts/abstract/JBPayoutRedemptionPaymentTerminal3_1_2.sol +++ b/contracts/abstract/JBPayoutRedemptionPaymentTerminal3_1_2.sol @@ -263,7 +263,7 @@ abstract contract JBPayoutRedemptionPaymentTerminal3_1_2 is bool _preferClaimedTokens, string calldata _memo, bytes calldata _metadata - ) public payable virtual override returns (uint256) { + ) external payable virtual override returns (uint256) { _token; // Prevents unused var compiler and natspec complaints. // ETH shouldn't be sent if this terminal's token isn't ETH. diff --git a/contracts/abstract/JBPayoutRedemptionPaymentTerminal3_2.sol b/contracts/abstract/JBPayoutRedemptionPaymentTerminal3_2.sol index eae33372b..02a05d499 100644 --- a/contracts/abstract/JBPayoutRedemptionPaymentTerminal3_2.sol +++ b/contracts/abstract/JBPayoutRedemptionPaymentTerminal3_2.sol @@ -482,7 +482,7 @@ abstract contract JBPayoutRedemptionPaymentTerminal3_2 is bool _preferClaimedTokens, string calldata _memo, bytes calldata _metadata - ) external payable virtual override returns (uint256) { + ) public payable virtual override returns (uint256) { _token; // Prevents unused var compiler and natspec complaints. // ETH shouldn't be sent if this terminal's token isn't ETH. From c1b0939344d4600e04b7f877e7dff9d4582a95f7 Mon Sep 17 00:00:00 2001 From: mejango Date: Fri, 1 Sep 2023 10:15:10 -0400 Subject: [PATCH 5/7] - --- contracts/abstract/JBPayoutRedemptionPaymentTerminal3_2.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contracts/abstract/JBPayoutRedemptionPaymentTerminal3_2.sol b/contracts/abstract/JBPayoutRedemptionPaymentTerminal3_2.sol index 02a05d499..53efdb26a 100644 --- a/contracts/abstract/JBPayoutRedemptionPaymentTerminal3_2.sol +++ b/contracts/abstract/JBPayoutRedemptionPaymentTerminal3_2.sol @@ -132,13 +132,13 @@ abstract contract JBPayoutRedemptionPaymentTerminal3_2 is ); // Adjust the decimals of the fixed point number if needed to have 18 decimals. - uint256 _adjustedOverflow = (decimals == 18) + uint256 _adjustedOverflow = decimals == 18 ? _overflow : JBFixedPointNumber.adjustDecimals(_overflow, decimals, 18); // Return the amount converted to ETH. return - (currency == JBCurrencies.ETH) + currency == JBCurrencies.ETH ? _adjustedOverflow : PRBMath.mulDiv( _adjustedOverflow, From d67386c86199461a9c37a980b07ab72ed7e8c238 Mon Sep 17 00:00:00 2001 From: xBA5ED <83727748+xBA5ED@users.noreply.github.com> Date: Sat, 2 Sep 2023 21:40:42 +0200 Subject: [PATCH 6/7] feat: Added ERC165 and fixed build errors --- contracts/JBERC20PaymentTerminal3_2.sol | 29 +++++++++++++++--- contracts/JBETHPaymentTerminal3_2.sol | 3 +- .../JBPayoutRedemptionPaymentTerminal3_2.sol | 21 ++++++------- .../IJBPayoutRedemptionPaymentTerminal3_2.sol | 6 ++-- .../interfaces/IJBPermit2PaymentTerminal.sol | 30 +++++++++++++++++++ 5 files changed, 68 insertions(+), 21 deletions(-) create mode 100644 contracts/interfaces/IJBPermit2PaymentTerminal.sol diff --git a/contracts/JBERC20PaymentTerminal3_2.sol b/contracts/JBERC20PaymentTerminal3_2.sol index cc687ca27..91ce27c04 100644 --- a/contracts/JBERC20PaymentTerminal3_2.sol +++ b/contracts/JBERC20PaymentTerminal3_2.sol @@ -5,16 +5,20 @@ import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol'; import {IERC20Metadata} from '@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol'; import {SafeERC20} from '@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol'; import {IPermit2, IAllowanceTransfer} from 'permit2/src/interfaces/IPermit2.sol'; -import {JBPayoutRedemptionPaymentTerminal3_2} from './abstract/JBPayoutRedemptionPaymentTerminal3_2.sol'; +import {JBPayoutRedemptionPaymentTerminal3_2, IERC165} from './abstract/JBPayoutRedemptionPaymentTerminal3_2.sol'; import {IJBDirectory} from './interfaces/IJBDirectory.sol'; import {IJBOperatorStore} from './interfaces/IJBOperatorStore.sol'; import {IJBProjects} from './interfaces/IJBProjects.sol'; import {IJBSplitsStore} from './interfaces/IJBSplitsStore.sol'; import {IJBPrices} from './interfaces/IJBPrices.sol'; +import {IJBPermit2PaymentTerminal} from './interfaces/IJBPermit2PaymentTerminal.sol'; import {JBSingleAllowanceData} from './structs/JBSingleAllowanceData.sol'; /// @notice Manages the inflows and outflows of an ERC-20 token. -contract JBERC20PaymentTerminal3_1_2 is JBPayoutRedemptionPaymentTerminal3_2 { +contract JBERC20PaymentTerminal3_1_2 is + JBPayoutRedemptionPaymentTerminal3_2, + IJBPermit2PaymentTerminal +{ using SafeERC20 for IERC20; //*********************************************************************// @@ -29,6 +33,22 @@ contract JBERC20PaymentTerminal3_1_2 is JBPayoutRedemptionPaymentTerminal3_2 { IPermit2 immutable PERMIT2; + //*********************************************************************// + // -------------------------- public views --------------------------- // + //*********************************************************************// + + /// @notice Indicates if this contract adheres to the specified interface. + /// @dev See {IERC165-supportsInterface}. + /// @param _interfaceId The ID of the interface to check for adherance to. + /// @return A flag indicating if the provided interface ID is supported. + function supportsInterface( + bytes4 _interfaceId + ) public view virtual override(JBPayoutRedemptionPaymentTerminal3_2, IERC165) returns (bool) { + return + _interfaceId == type(IJBPermit2PaymentTerminal).interfaceId || + super.supportsInterface(_interfaceId); + } + //*********************************************************************// // -------------------------- internal views ------------------------- // //*********************************************************************// @@ -61,7 +81,8 @@ contract JBERC20PaymentTerminal3_1_2 is JBPayoutRedemptionPaymentTerminal3_2 { IJBSplitsStore _splitsStore, IJBPrices _prices, address _store, - address _owner + address _owner, + IPermit2 _permit2 ) JBPayoutRedemptionPaymentTerminal3_2( address(_token), @@ -78,7 +99,7 @@ contract JBERC20PaymentTerminal3_1_2 is JBPayoutRedemptionPaymentTerminal3_2 { ) // solhint-disable-next-line no-empty-blocks { - + PERMIT2 = _permit2; } //*********************************************************************// diff --git a/contracts/JBETHPaymentTerminal3_2.sol b/contracts/JBETHPaymentTerminal3_2.sol index 35aef45f8..3c94c9f1e 100644 --- a/contracts/JBETHPaymentTerminal3_2.sol +++ b/contracts/JBETHPaymentTerminal3_2.sol @@ -46,11 +46,10 @@ contract JBETHPaymentTerminal3_1_2 is JBPayoutRedemptionPaymentTerminal3_2 { address _store, address _owner ) - JBPayoutRedemptionPaymentTerminal3_1_2( + JBPayoutRedemptionPaymentTerminal3_2( JBTokens.ETH, 18, // 18 decimals. JBCurrencies.ETH, - _baseWeightCurrency, JBSplitsGroups.ETH_PAYOUT, _operatorStore, _projects, diff --git a/contracts/abstract/JBPayoutRedemptionPaymentTerminal3_2.sol b/contracts/abstract/JBPayoutRedemptionPaymentTerminal3_2.sol index 53efdb26a..89b7a04be 100644 --- a/contracts/abstract/JBPayoutRedemptionPaymentTerminal3_2.sol +++ b/contracts/abstract/JBPayoutRedemptionPaymentTerminal3_2.sol @@ -17,7 +17,7 @@ import {IJBPayoutTerminal3_1} from './../interfaces/IJBPayoutTerminal3_1.sol'; import {IJBPrices} from './../interfaces/IJBPrices.sol'; import {IJBProjects} from './../interfaces/IJBProjects.sol'; import {IJBRedemptionTerminal} from './../interfaces/IJBRedemptionTerminal.sol'; -import {IJBSingleTokenPaymentTerminalStore3_1_1} from './../interfaces/IJBSingleTokenPaymentTerminalStore3_1_1.sol'; +import {IJBSingleTokenPaymentTerminalStore3_2} from './../interfaces/IJBSingleTokenPaymentTerminalStore3_2.sol'; import {IJBSplitAllocator} from './../interfaces/IJBSplitAllocator.sol'; import {JBConstants} from './../libraries/JBConstants.sol'; import {JBCurrencies} from './../libraries/JBCurrencies.sol'; @@ -126,7 +126,7 @@ abstract contract JBPayoutRedemptionPaymentTerminal3_2 is uint256 _projectId ) external view virtual override returns (uint256) { // Get this terminal's current overflow. - uint256 _overflow = IJBSingleTokenPaymentTerminalStore3_1_1(store).currentOverflowOf( + uint256 _overflow = IJBSingleTokenPaymentTerminalStore3_2(store).currentOverflowOf( this, _projectId ); @@ -355,7 +355,7 @@ abstract contract JBPayoutRedemptionPaymentTerminal3_2 is if (!_to.acceptsToken(token, _projectId)) revert TERMINAL_TOKENS_INCOMPATIBLE(); // Record the migration in the store. - balance = IJBSingleTokenPaymentTerminalStore3_1_1(store).recordMigration(_projectId); + balance = IJBSingleTokenPaymentTerminalStore3_2(store).recordMigration(_projectId); // Transfer the balance if needed. if (balance != 0) { @@ -626,7 +626,7 @@ abstract contract JBPayoutRedemptionPaymentTerminal3_2 is reclaimAmount, _delegateAllocations, _memo - ) = IJBSingleTokenPaymentTerminalStore3_1_1(store).recordRedemptionFor( + ) = IJBSingleTokenPaymentTerminalStore3_2(store).recordRedemptionFor( _holder, _projectId, _tokenCount, @@ -780,7 +780,7 @@ abstract contract JBPayoutRedemptionPaymentTerminal3_2 is ( JBFundingCycle memory _fundingCycle, uint256 _distributedAmount - ) = IJBSingleTokenPaymentTerminalStore3_1_1(store).recordDistributionFor( + ) = IJBSingleTokenPaymentTerminalStore3_2(store).recordDistributionFor( _projectId, _amount, _currency @@ -891,7 +891,7 @@ abstract contract JBPayoutRedemptionPaymentTerminal3_2 is ( JBFundingCycle memory _fundingCycle, uint256 _distributedAmount - ) = IJBSingleTokenPaymentTerminalStore3_1_1(store).recordUsedAllowanceOf( + ) = IJBSingleTokenPaymentTerminalStore3_2(store).recordUsedAllowanceOf( _projectId, _amount, _currency @@ -1259,10 +1259,7 @@ abstract contract JBPayoutRedemptionPaymentTerminal3_2 is if (_allowanceAmount != 0) _cancelTransferTo(_expectedDestination, _allowanceAmount); // Add undistributed amount back to project's balance. - IJBSingleTokenPaymentTerminalStore3_1_1(store).recordAddedBalanceFor( - _projectId, - _depositAmount - ); + IJBSingleTokenPaymentTerminalStore3_2(store).recordAddedBalanceFor(_projectId, _depositAmount); } /// @notice Contribute tokens to a project. @@ -1306,7 +1303,7 @@ abstract contract JBPayoutRedemptionPaymentTerminal3_2 is _tokenCount, _delegateAllocations, _memo - ) = IJBSingleTokenPaymentTerminalStore3_1_1(store).recordPaymentFrom( + ) = IJBSingleTokenPaymentTerminalStore3_2(store).recordPaymentFrom( _payer, _bundledAmount, _projectId, @@ -1414,7 +1411,7 @@ abstract contract JBPayoutRedemptionPaymentTerminal3_2 is uint256 _refundedFees = _shouldRefundHeldFees ? _refundHeldFees(_projectId, _amount) : 0; // Record the added funds with any refunded fees. - IJBSingleTokenPaymentTerminalStore3_1_1(store).recordAddedBalanceFor( + IJBSingleTokenPaymentTerminalStore3_2(store).recordAddedBalanceFor( _projectId, _amount + _refundedFees ); diff --git a/contracts/interfaces/IJBPayoutRedemptionPaymentTerminal3_2.sol b/contracts/interfaces/IJBPayoutRedemptionPaymentTerminal3_2.sol index 28db42023..4dac77e88 100644 --- a/contracts/interfaces/IJBPayoutRedemptionPaymentTerminal3_2.sol +++ b/contracts/interfaces/IJBPayoutRedemptionPaymentTerminal3_2.sol @@ -128,7 +128,7 @@ interface IJBPayoutRedemptionPaymentTerminal3_2 is event SetFee(uint256 fee, address caller); - event SetFeeGauge(address indexed feeGauge, address caller); + // event SetFeeGauge(address indexed feeGauge, address caller); event SetFeelessAddress(address indexed addrs, bool indexed flag, address caller); @@ -179,7 +179,7 @@ interface IJBPayoutRedemptionPaymentTerminal3_2 is function fee() external view returns (uint256); - function feeGauge() external view returns (address); + // function feeGauge() external view returns (address); function isFeelessAddress(address account) external view returns (bool); @@ -189,7 +189,7 @@ interface IJBPayoutRedemptionPaymentTerminal3_2 is function setFee(uint256 fee) external; - function setFeeGauge(address feeGauge) external; + // function setFeeGauge(address feeGauge) external; function setFeelessAddress(address account, bool flag) external; } diff --git a/contracts/interfaces/IJBPermit2PaymentTerminal.sol b/contracts/interfaces/IJBPermit2PaymentTerminal.sol new file mode 100644 index 000000000..7c24a0410 --- /dev/null +++ b/contracts/interfaces/IJBPermit2PaymentTerminal.sol @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {IERC165} from '@openzeppelin/contracts/utils/introspection/IERC165.sol'; +import {JBSingleAllowanceData} from '../structs/JBSingleAllowanceData.sol'; +import {IJBPaymentTerminal} from './IJBPaymentTerminal.sol'; + +interface IJBPermit2PaymentTerminal is IJBPaymentTerminal { + function payAndSetAllowance( + uint256 _projectId, + uint256 _amount, + address _token, + address _beneficiary, + uint256 _minReturnedTokens, + bool _preferClaimedTokens, + string calldata _memo, + bytes calldata _metadata, + JBSingleAllowanceData calldata _allowance + ) external returns (uint256 beneficiaryTokenCount); + + function addToBalanceOfAndSetAllowance( + uint256 _projectId, + uint256 _amount, + address _token, + bool _shouldRefundHeldFees, + string calldata _memo, + bytes calldata _metadata, + JBSingleAllowanceData calldata _allowance + ) external; +} From b40a271d43d384a271635cff34b67ecbf3fb5687 Mon Sep 17 00:00:00 2001 From: xBA5ED <83727748+xBA5ED@users.noreply.github.com> Date: Sat, 2 Sep 2023 21:48:20 +0200 Subject: [PATCH 7/7] feat: added permit immutable to the interface --- contracts/JBERC20PaymentTerminal3_2.sol | 2 +- contracts/interfaces/IJBPermit2PaymentTerminal.sol | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/contracts/JBERC20PaymentTerminal3_2.sol b/contracts/JBERC20PaymentTerminal3_2.sol index 91ce27c04..186f8bacb 100644 --- a/contracts/JBERC20PaymentTerminal3_2.sol +++ b/contracts/JBERC20PaymentTerminal3_2.sol @@ -31,7 +31,7 @@ contract JBERC20PaymentTerminal3_1_2 is // ---------------- public immutable stored properties --------------- // //*********************************************************************// - IPermit2 immutable PERMIT2; + IPermit2 public immutable PERMIT2; //*********************************************************************// // -------------------------- public views --------------------------- // diff --git a/contracts/interfaces/IJBPermit2PaymentTerminal.sol b/contracts/interfaces/IJBPermit2PaymentTerminal.sol index 7c24a0410..3e933d566 100644 --- a/contracts/interfaces/IJBPermit2PaymentTerminal.sol +++ b/contracts/interfaces/IJBPermit2PaymentTerminal.sol @@ -2,10 +2,13 @@ pragma solidity ^0.8.0; import {IERC165} from '@openzeppelin/contracts/utils/introspection/IERC165.sol'; +import {IPermit2} from 'permit2/src/interfaces/IPermit2.sol'; import {JBSingleAllowanceData} from '../structs/JBSingleAllowanceData.sol'; import {IJBPaymentTerminal} from './IJBPaymentTerminal.sol'; interface IJBPermit2PaymentTerminal is IJBPaymentTerminal { + function PERMIT2() external returns (IPermit2 _permit2); + function payAndSetAllowance( uint256 _projectId, uint256 _amount,