From d4ab7578337329679d80a9b173110d57fd475a54 Mon Sep 17 00:00:00 2001 From: ilpepepig <167773062+ilpepepig@users.noreply.github.com> Date: Tue, 27 Aug 2024 08:52:18 -0300 Subject: [PATCH] feat: make Transfer.sol a library and improve usage (#649) * feat: make Transfer.sol a library and remove functions * test: fix tests * refactor: remove virtual from library function * fix: custom transfer function * feat: add back transferAmountFrom and remove SafeTransferLib * refactor: fix library usage * fix: library usage and balance check --- contracts/core/Allo.sol | 66 +++++--------- contracts/core/Registry.sol | 11 +-- contracts/core/libraries/Transfer.sol | 88 ++++++------------- contracts/strategies/BaseStrategy.sol | 7 +- contracts/strategies/CoreBaseStrategy.sol | 16 ++-- contracts/strategies/DirectAllocation.sol | 20 +++-- .../strategies/DonationVotingOffchain.sol | 16 ++-- .../strategies/DonationVotingOnchain.sol | 14 +-- contracts/strategies/EasyRPGF.sol | 6 +- contracts/strategies/QVImpactStream.sol | 7 +- contracts/strategies/QVSimple.sol | 5 +- contracts/strategies/RFPSimple.sol | 15 ++-- .../DirectGrantsSimpleStrategy.sol | 7 +- .../DonationVotingStrategy.sol | 25 +++--- .../hedgey/HedgeyRFPCommitteeStrategy.sol | 48 +++++++++- .../micro-grants/MicroGrantsBaseStrategy.sol | 9 +- .../ProportionalPayoutStrategy.sol | 5 +- .../QVImpactStreamStrategy.sol | 10 ++- .../_poc/sablier-v2/LockupDynamicStrategy.sol | 4 +- .../_poc/sablier-v2/LockupLinearStrategy.sol | 4 +- .../sqf-superfluid/SQFSuperFluidStrategy.sol | 6 +- .../WrappedVotingNftMintStrategy.sol | 5 +- .../direct-grants-lite/DirectGrantsLite.sol | 15 ++-- ...onVotingMerkleDistributionBaseStrategy.sol | 9 +- ...rkleDistributionDirectTransferStrategy.sol | 7 +- ...nVotingMerkleDistributionVaultStrategy.sol | 14 +-- .../strategies/easy-rpgf/EasyRPGFStrategy.sol | 7 +- .../strategies/qv-base/QVBaseStrategy.sol | 9 +- .../rfp-simple/RFPSimpleStrategy.sol | 9 +- test/foundry/core/Registry.t.sol | 8 -- .../integration/DirectAllocation.t.sol | 5 +- .../foundry/strategies/DirectAllocation.t.sol | 7 +- .../strategies/DonationVotingStrategy.t.sol | 6 +- test/foundry/strategies/QVImpactStream.t.sol | 6 +- 34 files changed, 272 insertions(+), 224 deletions(-) diff --git a/contracts/core/Allo.sol b/contracts/core/Allo.sol index 6e029df96..2e7fb5923 100644 --- a/contracts/core/Allo.sol +++ b/contracts/core/Allo.sol @@ -9,11 +9,10 @@ import "openzeppelin-contracts-upgradeable/contracts/access/AccessControlUpgrade import "openzeppelin-contracts-upgradeable/contracts/security/ReentrancyGuardUpgradeable.sol"; // Interfaces import "./interfaces/IAllo.sol"; - // Internal Libraries import {Clone} from "./libraries/Clone.sol"; import {Errors} from "./libraries/Errors.sol"; -import "./libraries/Native.sol"; +import {Native} from "./libraries/Native.sol"; import {Transfer} from "./libraries/Transfer.sol"; // ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣾⣿⣷⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣼⣿⣿⣷⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ @@ -35,16 +34,9 @@ import {Transfer} from "./libraries/Transfer.sol"; /// @author @thelostone-mc , @0xKurt , @codenamejason , @0xZakk , @nfrgosselin /// @notice This contract is used to create & manage pools as well as manage the protocol. /// @dev The contract must be initialized with the 'initialize()' function. -contract Allo is - IAllo, - Native, - Transfer, - Initializable, - Ownable, - AccessControlUpgradeable, - ReentrancyGuardUpgradeable, - Errors -{ +contract Allo is IAllo, Native, Initializable, Ownable, AccessControlUpgradeable, ReentrancyGuardUpgradeable, Errors { + using Transfer for address; + // ========================== // === Storage Variables ==== // ========================== @@ -333,7 +325,7 @@ contract Allo is uint256 amount = _token == NATIVE ? address(this).balance : IERC20Upgradeable(_token).balanceOf(address(this)); // Transfer the amount to the recipient (pool owner) - _transferAmount(_token, _recipient, amount); + _token.transferAmount(_recipient, amount); } // ==================================== @@ -569,10 +561,11 @@ contract Allo is // To prevent paying the baseFee from the Allo contract's balance // If _token is NATIVE, then baseFee + _amount should be equal to _msgValue. // If _token is not NATIVE, then baseFee should be equal to _msgValue. - if ((_token == NATIVE && (baseFee + _amount != _msgValue)) || (_token != NATIVE && baseFee != _msgValue)) { - revert NOT_ENOUGH_FUNDS(); - } - _transferAmount(NATIVE, treasury, baseFee); + if (_token == NATIVE && (baseFee + _amount != _msgValue)) revert NOT_ENOUGH_FUNDS(); + if (_token != NATIVE && baseFee != _msgValue) revert NOT_ENOUGH_FUNDS(); + + address(treasury).transferAmountNative(baseFee); + emit BaseFeePaid(poolId, baseFee); } @@ -611,39 +604,28 @@ contract Allo is /// @param _poolId The 'poolId' for the pool you are funding /// @param _strategy The address of the strategy function _fundPool(uint256 _amount, address _funder, uint256 _poolId, IBaseStrategy _strategy) internal virtual { - uint256 feeAmount; - uint256 amountAfterFee = _amount; + uint256 feeAmount = (_amount * percentFee) / getFeeDenominator(); // Can be zero if percentFee is zero + uint256 amountAfterFee = _amount - feeAmount; Pool storage pool = pools[_poolId]; address _token = pool.token; - if (percentFee > 0) { - feeAmount = (_amount * percentFee) / getFeeDenominator(); - amountAfterFee -= feeAmount; - - if (feeAmount + amountAfterFee != _amount) revert INVALID(); + if (_token == NATIVE && msg.value < _amount) revert ETH_MISMATCH(); - if (_token == NATIVE) { - _transferAmountFrom(_token, TransferData({from: _funder, to: treasury, amount: feeAmount})); - } else { - uint256 balanceBeforeFee = _getBalance(_token, treasury); - _transferAmountFrom(_token, TransferData({from: _funder, to: treasury, amount: feeAmount})); - uint256 balanceAfterFee = _getBalance(_token, treasury); - // Track actual fee paid to account for fee on ERC20 token transfers - feeAmount = balanceAfterFee - balanceBeforeFee; - } - } - - if (_token == NATIVE) { - _transferAmountFrom(_token, TransferData({from: _funder, to: address(_strategy), amount: amountAfterFee})); - } else { - uint256 balanceBeforeFundingPool = _getBalance(_token, address(_strategy)); - _transferAmountFrom(_token, TransferData({from: _funder, to: address(_strategy), amount: amountAfterFee})); - uint256 balanceAfterFundingPool = _getBalance(_token, address(_strategy)); + if (feeAmount > 0) { + uint256 balanceBeforeFee = _token.getBalance(treasury); + _token.transferAmountFrom(_funder, treasury, feeAmount); + uint256 balanceAfterFee = _token.getBalance(treasury); // Track actual fee paid to account for fee on ERC20 token transfers - amountAfterFee = balanceAfterFundingPool - balanceBeforeFundingPool; + feeAmount = balanceAfterFee - balanceBeforeFee; } + uint256 balanceBeforeFundingPool = _token.getBalance(address(_strategy)); + _token.transferAmountFrom(_funder, address(_strategy), amountAfterFee); + uint256 balanceAfterFundingPool = _token.getBalance(address(_strategy)); + // Track actual fee paid to account for fee on ERC20 token transfers + amountAfterFee = balanceAfterFundingPool - balanceBeforeFundingPool; + _strategy.increasePoolAmount(amountAfterFee); emit PoolFunded(_poolId, amountAfterFee, feeAmount); diff --git a/contracts/core/Registry.sol b/contracts/core/Registry.sol index 405196bb1..5b6847708 100644 --- a/contracts/core/Registry.sol +++ b/contracts/core/Registry.sol @@ -11,8 +11,7 @@ import "./interfaces/IRegistry.sol"; import {Anchor} from "./Anchor.sol"; import {Errors} from "./libraries/Errors.sol"; import {Metadata} from "./libraries/Metadata.sol"; -import "./libraries/Native.sol"; -import "./libraries/Transfer.sol"; +import {Transfer} from "./libraries/Transfer.sol"; // ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣾⣿⣷⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣼⣿⣿⣷⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ // ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣿⣿⣿⣿⣷⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣼⣿⣿⣿⣿⣿⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ @@ -36,7 +35,9 @@ import "./libraries/Transfer.sol"; /// It is also used to deploy the anchor contract for each profile which acts as a proxy /// for the profile and is used to receive funds and execute transactions on behalf of the profile /// The Registry is also used to add and remove members from a profile and update the profile 'Metadata' -contract Registry is IRegistry, Initializable, Native, AccessControlUpgradeable, Transfer, Errors { +contract Registry is IRegistry, Initializable, AccessControlUpgradeable, Errors { + using Transfer for address; + /// ========================== /// === Storage Variables ==== /// ========================== @@ -392,7 +393,7 @@ contract Registry is IRegistry, Initializable, Native, AccessControlUpgradeable, function recoverFunds(address _token, address _recipient) external onlyRole(ALLO_OWNER) { if (_recipient == address(0)) revert ZERO_ADDRESS(); - uint256 amount = _token == NATIVE ? address(this).balance : ERC20(_token).balanceOf(address(this)); - _transferAmount(_token, _recipient, amount); + uint256 amount = _token.getBalance(address(this)); + _token.transferAmount(_recipient, amount); } } diff --git a/contracts/core/libraries/Transfer.sol b/contracts/core/libraries/Transfer.sol index 0f50f6b25..c04f9b47b 100644 --- a/contracts/core/libraries/Transfer.sol +++ b/contracts/core/libraries/Transfer.sol @@ -3,8 +3,6 @@ pragma solidity 0.8.19; // External Libraries import {SafeTransferLib} from "solady/utils/SafeTransferLib.sol"; -// Internal Libraries -import "./Native.sol"; // ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣾⣿⣷⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣼⣿⣿⣷⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ // ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣿⣿⣿⣿⣷⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣼⣿⣿⣿⣿⣿⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ @@ -21,90 +19,58 @@ import "./Native.sol"; // ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠙⠙⠋⠛⠙⠋⠛⠙⠋⠛⠙⠋⠃⠀⠀⠀⠀⠀⠀⠀⠀⠠⠿⠻⠟⠿⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠸⠟⠿⠟⠿⠆⠀⠸⠿⠿⠟⠯⠀⠀⠀⠸⠿⠿⠿⠏⠀⠀⠀⠀⠀⠈⠉⠻⠻⡿⣿⢿⡿⡿⠿⠛⠁⠀⠀⠀⠀⠀⠀ // allo.gitcoin.co -/// @title Transfer contract -/// @author @thelostone-mc , @0xKurt , @codenamejason , @0xZakk , @nfrgosselin -/// @notice A helper contract to transfer tokens within Allo protocol +/// @title Transfer library +/// @notice A helper library to transfer tokens within Allo protocol /// @dev Handles the transfer of tokens to an address -contract Transfer is Native { - /// @notice Thrown when the amount of tokens sent does not match the amount of tokens expected - error AMOUNT_MISMATCH(); +library Transfer { + using SafeTransferLib for address; - /// @notice This holds the details for a transfer - struct TransferData { - address from; - address to; - uint256 amount; - } - - /// @notice Transfer an amount of a token to an array of addresses - /// @param _token The address of the token - /// @param _transferData TransferData[] - /// @return Whether the transfer was successful or not - function _transferAmountsFrom(address _token, TransferData[] memory _transferData) - internal - virtual - returns (bool) - { - uint256 msgValue = msg.value; - - for (uint256 i; i < _transferData.length;) { - TransferData memory transferData = _transferData[i]; - - if (_token == NATIVE) { - msgValue -= transferData.amount; - SafeTransferLib.safeTransferETH(transferData.to, transferData.amount); - } else { - SafeTransferLib.safeTransferFrom(_token, transferData.from, transferData.to, transferData.amount); - } - - unchecked { - i++; - } - } - - if (msgValue != 0) revert AMOUNT_MISMATCH(); - - return true; - } + /// @notice Address of the native token + address public constant NATIVE = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; /// @notice Transfer an amount of a token to an address - /// @param _token The address of the token - /// @param _transferData Individual TransferData - /// @return Whether the transfer was successful or not - function _transferAmountFrom(address _token, TransferData memory _transferData) internal virtual returns (bool) { - uint256 amount = _transferData.amount; + /// @dev When this function is used, it must be checked that balances or msg.value is correct for native tokens + /// @param _token The token to transfer + /// @param _from The address to transfer to + /// @param _to The address to transfer to + /// @param _amount The amount to transfer + function transferAmountFrom(address _token, address _from, address _to, uint256 _amount) internal { if (_token == NATIVE) { - // Native Token - if (msg.value < amount) revert AMOUNT_MISMATCH(); - - SafeTransferLib.safeTransferETH(_transferData.to, amount); + // '_from' is ignored. The contract's balance is used. + if (_to != address(this)) _to.safeTransferETH(_amount); } else { - SafeTransferLib.safeTransferFrom(_token, _transferData.from, _transferData.to, amount); + _token.safeTransferFrom(_from, _to, _amount); } - return true; } /// @notice Transfer an amount of a token to an address /// @param _token The token to transfer /// @param _to The address to transfer to /// @param _amount The amount to transfer - function _transferAmount(address _token, address _to, uint256 _amount) internal virtual { + function transferAmount(address _token, address _to, uint256 _amount) internal { if (_token == NATIVE) { - SafeTransferLib.safeTransferETH(_to, _amount); + _to.safeTransferETH(_amount); } else { - SafeTransferLib.safeTransfer(_token, _to, _amount); + _token.safeTransfer(_to, _amount); } } + /// @notice Transfer an amount of native token to an address + /// @param _to The address to transfer to + /// @param _amount The amount to transfer + function transferAmountNative(address _to, uint256 _amount) internal { + _to.safeTransferETH(_amount); + } + /// @notice Get the balance of a token for an account /// @param _token The token to get the balance of /// @param _account The account to get the balance for /// @return The balance of the token for the account - function _getBalance(address _token, address _account) internal view returns (uint256) { + function getBalance(address _token, address _account) internal view returns (uint256) { if (_token == NATIVE) { return payable(_account).balance; } else { - return SafeTransferLib.balanceOf(_token, _account); + return _token.balanceOf(_account); } } } diff --git a/contracts/strategies/BaseStrategy.sol b/contracts/strategies/BaseStrategy.sol index 6d6ced165..c43fad456 100644 --- a/contracts/strategies/BaseStrategy.sol +++ b/contracts/strategies/BaseStrategy.sol @@ -2,11 +2,10 @@ pragma solidity ^0.8.19; // Interfaces -import "../core/interfaces/IStrategy.sol"; +import "contracts/core/interfaces/IStrategy.sol"; // Libraries -import {Transfer} from "../core/libraries/Transfer.sol"; -import {Errors} from "../core/libraries/Errors.sol"; +import {Errors} from "contracts/core/libraries/Errors.sol"; // ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣾⣿⣷⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣼⣿⣿⣷⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ // ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣿⣿⣿⣿⣷⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣼⣿⣿⣿⣿⣿⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ @@ -27,7 +26,7 @@ import {Errors} from "../core/libraries/Errors.sol"; /// @author @thelostone-mc , @0xKurt , @codenamejason , @0xZakk , @nfrgosselin /// @notice This contract is the base contract for all strategies /// @dev This contract is implemented by all strategies. -abstract contract BaseStrategy is IStrategy, Transfer, Errors { +abstract contract BaseStrategy is IStrategy, Errors { /// ========================== /// === Storage Variables ==== /// ========================== diff --git a/contracts/strategies/CoreBaseStrategy.sol b/contracts/strategies/CoreBaseStrategy.sol index 089e0c70b..7c7dbdbbc 100644 --- a/contracts/strategies/CoreBaseStrategy.sol +++ b/contracts/strategies/CoreBaseStrategy.sol @@ -2,16 +2,16 @@ pragma solidity 0.8.19; /// Interfaces -import "../core/interfaces/IBaseStrategy.sol"; - -/// Libraries -import {Transfer} from "./../core/libraries/Transfer.sol"; -import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import "contracts/core/interfaces/IBaseStrategy.sol"; +// Internal Libraries +import {Transfer} from "contracts/core/libraries/Transfer.sol"; /// @title BaseStrategy Contract /// @notice This contract is the base contract for all strategies /// @dev This contract is implemented by all strategies. -abstract contract CoreBaseStrategy is IBaseStrategy, Transfer { +abstract contract CoreBaseStrategy is IBaseStrategy { + using Transfer for address; + /// ========================== /// === Storage Variables ==== /// ========================== @@ -109,10 +109,10 @@ abstract contract CoreBaseStrategy is IBaseStrategy, Transfer { { _beforeWithdraw(_token, _amount, _recipient); // If the token is the pool token, revert if the amount is greater than the pool amount - if (_getBalance(_token, address(this)) - _amount < poolAmount) { + if (_token.getBalance(address(this)) - _amount < poolAmount) { revert BaseStrategy_WITHDRAW_MORE_THAN_POOL_AMOUNT(); } - _transferAmount(_token, _recipient, _amount); + _token.transferAmount(_recipient, _amount); _afterWithdraw(_token, _amount, _recipient); emit Withdrew(_token, _amount, _recipient); diff --git a/contracts/strategies/DirectAllocation.sol b/contracts/strategies/DirectAllocation.sol index d757140a0..5139f3e10 100644 --- a/contracts/strategies/DirectAllocation.sol +++ b/contracts/strategies/DirectAllocation.sol @@ -1,19 +1,23 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.19; -import {CoreBaseStrategy} from "./CoreBaseStrategy.sol"; +// Core Contracts +import {CoreBaseStrategy} from "contracts/strategies/CoreBaseStrategy.sol"; +// Internal Libraries +import {Native} from "contracts/core/libraries/Native.sol"; +import {Errors} from "contracts/core/libraries/Errors.sol"; +import {Transfer} from "contracts/core/libraries/Transfer.sol"; /// @title DirectAllocationStrategy /// @dev The strategy only implements the allocate logic /// @notice A strategy that directly allocates funds to a recipient -contract DirectAllocationStrategy is CoreBaseStrategy { +contract DirectAllocationStrategy is CoreBaseStrategy, Native, Errors { + using Transfer for address; + /// =============================== /// ============ Errors =========== /// =============================== - /// @notice Error when the function is not implemented - error NOT_IMPLEMENTED(); - /// @notice Error when the input is invalid error INVALID_INPUT(); @@ -69,15 +73,19 @@ contract DirectAllocationStrategy is CoreBaseStrategy { revert INVALID_INPUT(); } + uint256 _totalNativeAmount; for (uint256 _i = 0; _i < _recipientsLength;) { /// Direct allocate the funds - _transferAmountFrom(_tokens[_i], TransferData({from: _sender, to: _recipients[_i], amount: _amounts[_i]})); + if (_tokens[_i] == NATIVE) _totalNativeAmount += _amounts[_i]; + _tokens[_i].transferAmountFrom(_sender, _recipients[_i], _amounts[_i]); emit DirectAllocated(_recipients[_i], _amounts[_i], _tokens[_i], _sender); unchecked { ++_i; } } + + if (msg.value < _totalNativeAmount) revert ETH_MISMATCH(); } /// @notice Distribute funds to recipients diff --git a/contracts/strategies/DonationVotingOffchain.sol b/contracts/strategies/DonationVotingOffchain.sol index 2ec309402..021fb39e9 100644 --- a/contracts/strategies/DonationVotingOffchain.sol +++ b/contracts/strategies/DonationVotingOffchain.sol @@ -1,12 +1,14 @@ // SPDX-License-Identifier: AGPL-3.0-only pragma solidity 0.8.19; -import {SafeTransferLib} from "solady/utils/SafeTransferLib.sol"; // Interfaces import {IAllo} from "../core/interfaces/IAllo.sol"; // Core Contracts import {CoreBaseStrategy} from "./CoreBaseStrategy.sol"; import {RecipientsExtension} from "../extensions/contracts/RecipientsExtension.sol"; +// Internal Libraries +import {Transfer} from "contracts/core/libraries/Transfer.sol"; +import {Native} from "contracts/core/libraries/Native.sol"; // ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣾⣿⣷⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣼⣿⣿⣷⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ // ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣿⣿⣿⣿⣷⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣼⣿⣿⣿⣿⣿⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ @@ -26,7 +28,9 @@ import {RecipientsExtension} from "../extensions/contracts/RecipientsExtension.s /// @title Donation Voting Strategy with off-chain setup /// @notice Strategy that allows allocations in multiple tokens to accepted recipient. The actual payouts are set /// by the pool manager. -contract DonationVotingOffchain is CoreBaseStrategy, RecipientsExtension { +contract DonationVotingOffchain is CoreBaseStrategy, RecipientsExtension, Native { + using Transfer for address; + /// =============================== /// ========== Events ============= /// =============================== @@ -208,7 +212,7 @@ contract DonationVotingOffchain is CoreBaseStrategy, RecipientsExtension { amountAllocated[claim.recipientId][claim.token] = 0; - _transferAmount(claim.token, recipientAddress, amount); + claim.token.transferAmount(recipientAddress, amount); emit Claimed(claim.recipientId, amount, claim.token); } @@ -275,13 +279,13 @@ contract DonationVotingOffchain is CoreBaseStrategy, RecipientsExtension { if (tokens[i] == NATIVE) { totalNativeAmount += _amounts[i]; } else { - SafeTransferLib.safeTransferFrom(tokens[i], _sender, address(this), _amounts[i]); + tokens[i].transferAmountFrom(_sender, address(this), _amounts[i]); } emit Allocated(_recipients[i], _sender, _amounts[i], abi.encode(tokens[i])); } - if (msg.value != totalNativeAmount) revert AMOUNT_MISMATCH(); + if (msg.value != totalNativeAmount) revert ETH_MISMATCH(); } /// @notice Distributes funds (tokens) to recipients. @@ -304,7 +308,7 @@ contract DonationVotingOffchain is CoreBaseStrategy, RecipientsExtension { address recipientAddress = _recipients[recipientId].recipientAddress; IAllo.Pool memory pool = allo.getPool(poolId); - _transferAmount(pool.token, recipientAddress, amount); + pool.token.transferAmount(recipientAddress, amount); emit Distributed(recipientId, abi.encode(recipientAddress, amount, _sender)); } diff --git a/contracts/strategies/DonationVotingOnchain.sol b/contracts/strategies/DonationVotingOnchain.sol index 5a33f77bc..fb41dc2aa 100644 --- a/contracts/strategies/DonationVotingOnchain.sol +++ b/contracts/strategies/DonationVotingOnchain.sol @@ -1,7 +1,6 @@ // SPDX-License-Identifier: AGPL-3.0-only pragma solidity 0.8.19; -import {SafeTransferLib} from "solady/utils/SafeTransferLib.sol"; // Interfaces import {IAllo} from "../core/interfaces/IAllo.sol"; // Core Contracts @@ -9,6 +8,8 @@ import {CoreBaseStrategy} from "./CoreBaseStrategy.sol"; import {RecipientsExtension} from "../extensions/contracts/RecipientsExtension.sol"; // Internal Libraries import {QFHelper} from "../core/libraries/QFHelper.sol"; +import {Native} from "contracts/core/libraries/Native.sol"; +import {Transfer} from "contracts/core/libraries/Transfer.sol"; // ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣾⣿⣷⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣼⣿⣿⣷⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ // ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣿⣿⣿⣿⣷⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣼⣿⣿⣿⣿⣿⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ @@ -28,8 +29,9 @@ import {QFHelper} from "../core/libraries/QFHelper.sol"; /// @title Donation Voting Strategy with qudratic funding tracked on-chain /// @notice Strategy that allows allocations in a specified token to accepted recipient. Payouts are calculated from /// allocations based on the quadratic funding formula. -contract DonationVotingOnchain is CoreBaseStrategy, RecipientsExtension { +contract DonationVotingOnchain is CoreBaseStrategy, RecipientsExtension, Native { using QFHelper for QFHelper.State; + using Transfer for address; /// =============================== /// ========== Events ============= @@ -184,9 +186,9 @@ contract DonationVotingOnchain is CoreBaseStrategy, RecipientsExtension { } if (allocationToken == NATIVE) { - if (msg.value != totalAmount) revert AMOUNT_MISMATCH(); + if (msg.value != totalAmount) revert ETH_MISMATCH(); } else { - SafeTransferLib.safeTransferFrom(allocationToken, _sender, address(this), totalAmount); + allocationToken.transferAmountFrom(_sender, address(this), totalAmount); } QFState.fund(_recipients, _amounts); @@ -212,7 +214,7 @@ contract DonationVotingOnchain is CoreBaseStrategy, RecipientsExtension { // Transfer allocation uint256 allocationAmount = amountAllocated[recipientId]; amountAllocated[recipientId] = 0; - _transferAmount(allocationToken, recipientAddress, allocationAmount); + allocationToken.transferAmount(recipientAddress, allocationAmount); emit Distributed(recipientId, abi.encode(recipientAddress, allocationToken, allocationAmount, _sender)); @@ -220,7 +222,7 @@ contract DonationVotingOnchain is CoreBaseStrategy, RecipientsExtension { uint256 matchingAmount = QFState.calculateMatching(totalPayoutAmount, recipientId); poolAmount -= matchingAmount; IAllo.Pool memory pool = allo.getPool(poolId); - _transferAmount(pool.token, recipientAddress, matchingAmount); + pool.token.transferAmount(recipientAddress, matchingAmount); emit Distributed(recipientId, abi.encode(recipientAddress, pool.token, matchingAmount, _sender)); } diff --git a/contracts/strategies/EasyRPGF.sol b/contracts/strategies/EasyRPGF.sol index c39dabcf8..95e3eda89 100644 --- a/contracts/strategies/EasyRPGF.sol +++ b/contracts/strategies/EasyRPGF.sol @@ -5,6 +5,8 @@ pragma solidity 0.8.19; import {IAllo} from "contracts/core/interfaces/IAllo.sol"; // Contracts import {CoreBaseStrategy} from "contracts/strategies/CoreBaseStrategy.sol"; +// Internal Libraries +import {Transfer} from "contracts/core/libraries/Transfer.sol"; // ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣾⣿⣷⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣼⣿⣿⣷⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ // ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣿⣿⣿⣿⣷⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣼⣿⣿⣿⣿⣿⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ @@ -21,6 +23,8 @@ import {CoreBaseStrategy} from "contracts/strategies/CoreBaseStrategy.sol"; // ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠙⠙⠋⠛⠙⠋⠛⠙⠋⠛⠙⠋⠃⠀⠀⠀⠀⠀⠀⠀⠀⠠⠿⠻⠟⠿⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠸⠟⠿⠟⠿⠆⠀⠸⠿⠿⠟⠯⠀⠀⠀⠸⠿⠿⠿⠏⠀⠀⠀⠀⠀⠈⠉⠻⠻⡿⣿⢿⡿⡿⠿⠛⠁⠀⠀⠀⠀⠀⠀ // allo.gitcoin.co contract EasyRPGF is CoreBaseStrategy { + using Transfer for address; + /// =============================== /// ============ Errors =========== /// =============================== @@ -76,7 +80,7 @@ contract EasyRPGF is CoreBaseStrategy { address recipientAddress = _recipientIds[i]; poolAmount -= amount; - _transferAmount(pool.token, recipientAddress, amount); + pool.token.transferAmount(recipientAddress, amount); emit Distributed(recipientAddress, abi.encode(amount, _sender)); diff --git a/contracts/strategies/QVImpactStream.sol b/contracts/strategies/QVImpactStream.sol index c070a80e5..458a3d009 100644 --- a/contracts/strategies/QVImpactStream.sol +++ b/contracts/strategies/QVImpactStream.sol @@ -8,6 +8,8 @@ import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {IAllo} from "../core/interfaces/IAllo.sol"; // Core Contracts import {QVSimple} from "./QVSimple.sol"; +// Internal Libraries +import {Transfer} from "contracts/core/libraries/Transfer.sol"; // ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣾⣿⣷⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣼⣿⣿⣷⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ // ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣿⣿⣿⣿⣷⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣼⣿⣿⣿⣿⣿⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ @@ -23,7 +25,10 @@ import {QVSimple} from "./QVSimple.sol"; // ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠛⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⢰⣿⣿⣿⣿⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⣿⣿⣿⣿⣧⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⠀⠹⢿⣿⣿⣿⣿⣾⣾⣷⣿⣿⣿⣿⡿⠋⠀⠀⠀⠀ // ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠙⠙⠋⠛⠙⠋⠛⠙⠋⠛⠙⠋⠃⠀⠀⠀⠀⠀⠀⠀⠀⠠⠿⠻⠟⠿⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠸⠟⠿⠟⠿⠆⠀⠸⠿⠿⠟⠯⠀⠀⠀⠸⠿⠿⠿⠏⠀⠀⠀⠀⠀⠈⠉⠻⠻⡿⣿⢿⡿⡿⠿⠛⠁⠀⠀⠀⠀⠀⠀ // allo.gitcoin.co + contract QVImpactStream is QVSimple { + using Transfer for address; + /// ====================== /// ======= Events ======= /// ====================== @@ -160,7 +165,7 @@ contract QVImpactStream is QVSimple { delete payouts[recipientId]; - _transferAmount(poolToken, recipientAddress, amount); + poolToken.transferAmount(recipientAddress, amount); bytes memory data = abi.encode(recipientAddress, amount, _sender); diff --git a/contracts/strategies/QVSimple.sol b/contracts/strategies/QVSimple.sol index bd7f666e8..46150ecfd 100644 --- a/contracts/strategies/QVSimple.sol +++ b/contracts/strategies/QVSimple.sol @@ -7,6 +7,8 @@ import {IRecipientsExtension} from "contracts/extensions/interfaces/IRecipientsE // Contracts import {CoreBaseStrategy} from "contracts/strategies/CoreBaseStrategy.sol"; import {RecipientsExtension} from "contracts/extensions/contracts/RecipientsExtension.sol"; +// Internal Libraries +import {Transfer} from "contracts/core/libraries/Transfer.sol"; import {QVHelper} from "contracts/core/libraries/QVHelper.sol"; // ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣾⣿⣷⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣼⣿⣿⣷⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ @@ -25,6 +27,7 @@ import {QVHelper} from "contracts/core/libraries/QVHelper.sol"; // allo.gitcoin.co contract QVSimple is CoreBaseStrategy, RecipientsExtension { using QVHelper for QVHelper.VotingState; + using Transfer for address; /// ====================== /// ======= Storage ====== @@ -178,7 +181,7 @@ contract QVSimple is CoreBaseStrategy, RecipientsExtension { revert RECIPIENT_ERROR(recipientId); } - _transferAmount(pool.token, recipient.recipientAddress, amount); + pool.token.transferAmount(recipient.recipientAddress, amount); paidOut[recipientId] = true; diff --git a/contracts/strategies/RFPSimple.sol b/contracts/strategies/RFPSimple.sol index aad80bc59..a239ba29f 100644 --- a/contracts/strategies/RFPSimple.sol +++ b/contracts/strategies/RFPSimple.sol @@ -2,13 +2,14 @@ pragma solidity 0.8.19; // Interfaces -import {IAllo} from "../core/interfaces/IAllo.sol"; +import {IAllo} from "contracts/core/interfaces/IAllo.sol"; // Core Contracts -import {CoreBaseStrategy} from "./CoreBaseStrategy.sol"; -import {MilestonesExtension} from "../extensions/contracts/MilestonesExtension.sol"; -import {RecipientsExtension} from "../extensions/contracts/RecipientsExtension.sol"; +import {CoreBaseStrategy} from "contracts/strategies/CoreBaseStrategy.sol"; +import {MilestonesExtension} from "contracts/extensions/contracts/MilestonesExtension.sol"; +import {RecipientsExtension} from "contracts/extensions/contracts/RecipientsExtension.sol"; // Internal Libraries -import {Metadata} from "../core/libraries/Metadata.sol"; +import {Transfer} from "contracts/core/libraries/Transfer.sol"; +import {Metadata} from "contracts/core/libraries/Metadata.sol"; // ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣾⣿⣷⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣼⣿⣿⣷⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ // ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣿⣿⣿⣿⣷⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣼⣿⣿⣿⣿⣿⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ @@ -28,6 +29,8 @@ import {Metadata} from "../core/libraries/Metadata.sol"; /// @title RFP Simple Strategy /// @notice Strategy for Request for Proposal (RFP) allocation with milestone submission and management. contract RFPSimple is CoreBaseStrategy, MilestonesExtension, RecipientsExtension { + using Transfer for address; + /// ================================ /// ========== Storage ============= /// ================================ @@ -131,7 +134,7 @@ contract RFPSimple is CoreBaseStrategy, MilestonesExtension, RecipientsExtension IAllo.Pool memory pool = allo.getPool(poolId); address recipientAddress = _recipients[acceptedRecipientId].recipientAddress; - _transferAmount(pool.token, recipientAddress, amount); + pool.token.transferAmount(recipientAddress, amount); emit Distributed(acceptedRecipientId, abi.encode(recipientAddress, amount, milestoneIds, _sender)); } diff --git a/contracts/strategies/_poc/direct-grants-simple/DirectGrantsSimpleStrategy.sol b/contracts/strategies/_poc/direct-grants-simple/DirectGrantsSimpleStrategy.sol index bd183dd43..7e153b961 100644 --- a/contracts/strategies/_poc/direct-grants-simple/DirectGrantsSimpleStrategy.sol +++ b/contracts/strategies/_poc/direct-grants-simple/DirectGrantsSimpleStrategy.sol @@ -9,6 +9,7 @@ import {IRegistry} from "../../../core/interfaces/IRegistry.sol"; // Core Contracts import {BaseStrategy} from "../../BaseStrategy.sol"; // Internal Libraries +import {Transfer} from "contracts/core/libraries/Transfer.sol"; import {Metadata} from "../../../core/libraries/Metadata.sol"; import {Hats} from "hats-protocol/Hats.sol"; @@ -33,6 +34,8 @@ import {Hats} from "hats-protocol/Hats.sol"; /// are set by the recipient and the pool manager can accept or reject the milestone. The pool manager /// can also reject the recipient. contract DirectGrantsSimpleStrategy is BaseStrategy, ReentrancyGuard { + using Transfer for address; + /// ================================ /// ========== Struct ============= /// ================================ @@ -427,7 +430,7 @@ contract DirectGrantsSimpleStrategy is BaseStrategy, ReentrancyGuard { poolAmount -= _amount; // Transfer the amount to the pool manager - _transferAmount(allo.getPool(poolId).token, msg.sender, _amount); + allo.getPool(poolId).token.transferAmount(msg.sender, _amount); } /// ==================================== @@ -618,7 +621,7 @@ contract DirectGrantsSimpleStrategy is BaseStrategy, ReentrancyGuard { IAllo.Pool memory pool = allo.getPool(poolId); poolAmount -= amount; - _transferAmount(pool.token, recipient.recipientAddress, amount); + pool.token.transferAmount(recipient.recipientAddress, amount); // Set the milestone status to 'Accepted' milestone.milestoneStatus = Status.Accepted; diff --git a/contracts/strategies/_poc/donation-voting/DonationVotingStrategy.sol b/contracts/strategies/_poc/donation-voting/DonationVotingStrategy.sol index 09264e0d8..009ea0ef2 100644 --- a/contracts/strategies/_poc/donation-voting/DonationVotingStrategy.sol +++ b/contracts/strategies/_poc/donation-voting/DonationVotingStrategy.sol @@ -3,7 +3,6 @@ pragma solidity 0.8.19; // External Libraries import {ReentrancyGuard} from "openzeppelin-contracts/contracts/security/ReentrancyGuard.sol"; -import {SafeTransferLib} from "solady/utils/SafeTransferLib.sol"; // Interfaces import {IAllo} from "../../../core/interfaces/IAllo.sol"; import {IRegistry} from "../../../core/interfaces/IRegistry.sol"; @@ -12,6 +11,7 @@ import {BaseStrategy} from "../../BaseStrategy.sol"; // Internal Libraries import {Metadata} from "../../../core/libraries/Metadata.sol"; import {Native} from "../../../core/libraries/Native.sol"; +import {Transfer} from "../../../core/libraries/Transfer.sol"; // ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣾⣿⣷⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣼⣿⣿⣷⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ // ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣿⣿⣿⣿⣷⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣼⣿⣿⣿⣿⣿⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ @@ -28,7 +28,9 @@ import {Native} from "../../../core/libraries/Native.sol"; // ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠙⠙⠋⠛⠙⠋⠛⠙⠋⠛⠙⠋⠃⠀⠀⠀⠀⠀⠀⠀⠀⠠⠿⠻⠟⠿⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠸⠟⠿⠟⠿⠆⠀⠸⠿⠿⠟⠯⠀⠀⠀⠸⠿⠿⠿⠏⠀⠀⠀⠀⠀⠈⠉⠻⠻⡿⣿⢿⡿⡿⠿⠛⠁⠀⠀⠀⠀⠀⠀ // allo.gitcoin.co -contract DonationVotingStrategy is BaseStrategy, ReentrancyGuard { +contract DonationVotingStrategy is BaseStrategy, ReentrancyGuard, Native { + using Transfer for address; + /// ================================ /// ========== Struct ============== /// ================================ @@ -296,7 +298,7 @@ contract DonationVotingStrategy is BaseStrategy, ReentrancyGuard { address token = singleClaim.token; - _transferAmount(token, recipient.recipientAddress, amount); + token.transferAmount(recipient.recipientAddress, amount); emit Claimed(singleClaim.recipientId, recipient.recipientAddress, amount, token); unchecked { @@ -341,7 +343,7 @@ contract DonationVotingStrategy is BaseStrategy, ReentrancyGuard { } poolAmount -= _amount; - _transferAmount(pool.token, msg.sender, _amount); + pool.token.transferAmount(msg.sender, _amount); } /// ==================================== @@ -479,16 +481,11 @@ contract DonationVotingStrategy is BaseStrategy, ReentrancyGuard { uint256 transferredAmount = amount; if (token == NATIVE) { - if (msg.value < amount) { - revert AMOUNT_MISMATCH(); - } - SafeTransferLib.safeTransferETH(address(this), amount); + if (msg.value < amount) revert ETH_MISMATCH(); } else { - uint256 balanceBefore = SafeTransferLib.balanceOf(token, address(this)); - - _transferAmount(token, address(this), amount); - - uint256 balanceAfter = SafeTransferLib.balanceOf(token, address(this)); + uint256 balanceBefore = token.getBalance(address(this)); + token.transferAmountFrom(_sender, address(this), amount); + uint256 balanceAfter = token.getBalance(address(this)); transferredAmount = balanceAfter - balanceBefore; } @@ -523,7 +520,7 @@ contract DonationVotingStrategy is BaseStrategy, ReentrancyGuard { } IAllo.Pool memory pool = allo.getPool(poolId); - _transferAmount(pool.token, recipient.recipientAddress, amount); + pool.token.transferAmount(recipient.recipientAddress, amount); emit Distributed(recipientId, recipient.recipientAddress, amount, _sender); unchecked { diff --git a/contracts/strategies/_poc/hedgey/HedgeyRFPCommitteeStrategy.sol b/contracts/strategies/_poc/hedgey/HedgeyRFPCommitteeStrategy.sol index 7075b92ca..393337ea4 100644 --- a/contracts/strategies/_poc/hedgey/HedgeyRFPCommitteeStrategy.sol +++ b/contracts/strategies/_poc/hedgey/HedgeyRFPCommitteeStrategy.sol @@ -4,9 +4,11 @@ pragma solidity 0.8.19; // Core Contracts import {RFPCommitteeStrategy} from "../../rfp-committee/RFPCommitteeStrategy.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import {IAllo} from "contracts/core/interfaces/IAllo.sol"; // Internal Libraries import {Metadata} from "../../../core/libraries/Metadata.sol"; +import {Transfer} from "contracts/core/libraries/Transfer.sol"; interface ITokenVestingPlans { function createPlan( @@ -23,6 +25,8 @@ interface ITokenVestingPlans { } contract HedgeyRFPCommitteeStrategy is RFPCommitteeStrategy { + using Transfer for address; + /// ================================ /// ========== Storage ============= /// ================================ @@ -104,17 +108,55 @@ contract HedgeyRFPCommitteeStrategy is RFPCommitteeStrategy { /// @dev Callable by the pool manager /// @param _token The token to withdraw function withdraw(address _token) external virtual override onlyPoolManager(msg.sender) onlyInactivePool { - uint256 amount = _getBalance(_token, address(this)); + uint256 amount = _token.getBalance(address(this)); // Transfer the tokens to the 'msg.sender' (pool manager calling function) - super._transferAmount(_token, msg.sender, amount); + _token.transferAmount(msg.sender, amount); } /// ==================================== /// ============ Internal ============== /// ==================================== - function _transferAmount(address _token, address _recipient, uint256 _amount) internal override { + /// @notice Distribute the upcoming milestone to acceptedRecipientId. + /// @dev '_sender' must be a pool manager to distribute. + /// @param _sender The sender of the distribution + function _distribute(address[] memory, bytes memory, address _sender) + internal + virtual + override + onlyInactivePool + onlyPoolManager(_sender) + { + // check to make sure there is a pending milestone + if (upcomingMilestone >= milestones.length) revert INVALID_MILESTONE(); + + IAllo.Pool memory pool = allo.getPool(poolId); + Milestone storage milestone = milestones[upcomingMilestone]; + Recipient memory recipient = _recipients[acceptedRecipientId]; + + // Check if the milestone is pending + if (milestone.milestoneStatus != Status.Pending) revert INVALID_MILESTONE(); + + // Calculate the amount to be distributed for the milestone + uint256 amount = (recipient.proposalBid * milestone.amountPercentage) / 1e18; + + // Get the pool, subtract the amount and transfer to the recipient + poolAmount -= amount; + _transferAmount(pool.token, recipient.recipientAddress, amount); + + // Set the milestone status to 'Accepted' + milestone.milestoneStatus = Status.Accepted; + + // Increment the upcoming milestone + upcomingMilestone++; + + // Emit events for the milestone and the distribution + emit MilestoneStatusChanged(upcomingMilestone, Status.Accepted); + emit Distributed(acceptedRecipientId, recipient.recipientAddress, amount, _sender); + } + + function _transferAmount(address _token, address _recipient, uint256 _amount) internal { IERC20(_token).approve(hedgeyContract, _amount); uint256 rate = _amount / _recipientLockupTerm[_recipient]; diff --git a/contracts/strategies/_poc/micro-grants/MicroGrantsBaseStrategy.sol b/contracts/strategies/_poc/micro-grants/MicroGrantsBaseStrategy.sol index e34150e60..a0a597193 100644 --- a/contracts/strategies/_poc/micro-grants/MicroGrantsBaseStrategy.sol +++ b/contracts/strategies/_poc/micro-grants/MicroGrantsBaseStrategy.sol @@ -10,6 +10,7 @@ import {IAllo} from "../../../core/interfaces/IAllo.sol"; import {BaseStrategy} from "../../BaseStrategy.sol"; // Internal Libraries import {Metadata} from "../../../core/libraries/Metadata.sol"; +import {Transfer} from "contracts/core/libraries/Transfer.sol"; // ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣾⣿⣷⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣼⣿⣿⣷⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ // ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣿⣿⣿⣿⣷⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣼⣿⣿⣿⣿⣿⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ @@ -27,6 +28,8 @@ import {Metadata} from "../../../core/libraries/Metadata.sol"; // allo.gitcoin.co abstract contract MicroGrantsBaseStrategy is BaseStrategy, ReentrancyGuard { + using Transfer for address; + /// ================================ /// ========== Struct ============== /// ================================ @@ -235,10 +238,10 @@ abstract contract MicroGrantsBaseStrategy is BaseStrategy, ReentrancyGuard { /// @dev Callable by the pool manager /// @param _token The token to withdraw function withdraw(address _token) external onlyPoolManager(msg.sender) onlyInactivePool { - uint256 amount = _getBalance(_token, address(this)); + uint256 amount = _token.getBalance(address(this)); // Transfer the tokens to the 'msg.sender' (pool manager calling function) - _transferAmount(_token, msg.sender, amount); + _token.transferAmount(msg.sender, amount); } /// ==================================== @@ -409,7 +412,7 @@ abstract contract MicroGrantsBaseStrategy is BaseStrategy, ReentrancyGuard { poolAmount -= amount; - _transferAmount(pool.token, recipient.recipientAddress, amount); + pool.token.transferAmount(recipient.recipientAddress, amount); emit Distributed(recipientId, recipient.recipientAddress, recipient.requestedAmount, _sender); } diff --git a/contracts/strategies/_poc/proportional-payout/ProportionalPayoutStrategy.sol b/contracts/strategies/_poc/proportional-payout/ProportionalPayoutStrategy.sol index b2ced1172..4ff6dc70a 100644 --- a/contracts/strategies/_poc/proportional-payout/ProportionalPayoutStrategy.sol +++ b/contracts/strategies/_poc/proportional-payout/ProportionalPayoutStrategy.sol @@ -5,6 +5,7 @@ import {ERC721} from "solady/tokens/ERC721.sol"; import {IAllo} from "./../../../core/interfaces/IAllo.sol"; import {BaseStrategy} from "../../BaseStrategy.sol"; import {Metadata} from "../../../core/libraries/Metadata.sol"; +import {Transfer} from "contracts/core/libraries/Transfer.sol"; // ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣾⣿⣷⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣼⣿⣿⣷⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ // ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣿⣿⣿⣿⣷⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣼⣿⣿⣿⣿⣿⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ @@ -25,6 +26,8 @@ import {Metadata} from "../../../core/libraries/Metadata.sol"; /// @notice This strategy allows the allocator to allocate votes to recipients /// @author allo-team contract ProportionalPayoutStrategy is BaseStrategy { + using Transfer for address; + /// ===================== /// ======= Events ====== /// ===================== @@ -217,7 +220,7 @@ contract ProportionalPayoutStrategy is BaseStrategy { } IAllo.Pool memory pool = allo.getPool(poolId); - _transferAmount(pool.token, recipient.recipientAddress, amount); + pool.token.transferAmount(recipient.recipientAddress, amount); paidOut[recipientId] = true; diff --git a/contracts/strategies/_poc/qv-impact-stream/QVImpactStreamStrategy.sol b/contracts/strategies/_poc/qv-impact-stream/QVImpactStreamStrategy.sol index 526f6de35..2e17b0fe4 100644 --- a/contracts/strategies/_poc/qv-impact-stream/QVImpactStreamStrategy.sol +++ b/contracts/strategies/_poc/qv-impact-stream/QVImpactStreamStrategy.sol @@ -12,6 +12,7 @@ import {IRegistry} from "../../../core/interfaces/IRegistry.sol"; import {QVBaseStrategy} from "../../qv-base/QVBaseStrategy.sol"; // Internal Libraries import {Metadata} from "../../../core/libraries/Metadata.sol"; +import {Transfer} from "contracts/core/libraries/Transfer.sol"; // ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣾⣿⣷⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣼⣿⣿⣷⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ // ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣿⣿⣿⣿⣷⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣼⣿⣿⣿⣿⣿⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ @@ -28,6 +29,8 @@ import {Metadata} from "../../../core/libraries/Metadata.sol"; // ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠙⠙⠋⠛⠙⠋⠛⠙⠋⠛⠙⠋⠃⠀⠀⠀⠀⠀⠀⠀⠀⠠⠿⠻⠟⠿⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠸⠟⠿⠟⠿⠆⠀⠸⠿⠿⠟⠯⠀⠀⠀⠸⠿⠿⠿⠏⠀⠀⠀⠀⠀⠈⠉⠻⠻⡿⣿⢿⡿⡿⠿⠛⠁⠀⠀⠀⠀⠀⠀ // allo.gitcoin.co contract QVImpactStreamStrategy is QVBaseStrategy, Multicall { + using Transfer for address; + /// ====================== /// ======= Events ======= /// ====================== @@ -217,7 +220,7 @@ contract QVImpactStreamStrategy is QVBaseStrategy, Multicall { delete payouts[recipientId]; - _transferAmount(poolToken, recipientAddress, amount); + poolToken.transferAmount(recipientAddress, amount); emit Distributed(recipientId, recipientAddress, amount, _sender); @@ -418,9 +421,8 @@ contract QVImpactStreamStrategy is QVBaseStrategy, Multicall { /// @param _recipient The recipient function recoverFunds(address _token, address _recipient) external onlyPoolManager(msg.sender) { // Get the amount of the token to transfer, which is always the entire balance of the contract address - uint256 amount = _token == NATIVE ? address(this).balance : IERC20Upgradeable(_token).balanceOf(address(this)); - + uint256 amount = _token.getBalance(address(this)); // Transfer the amount to the recipient (pool owner) - _transferAmount(_token, _recipient, amount); + _token.transferAmount(_recipient, amount); } } diff --git a/contracts/strategies/_poc/sablier-v2/LockupDynamicStrategy.sol b/contracts/strategies/_poc/sablier-v2/LockupDynamicStrategy.sol index cf230730c..f7932de1a 100644 --- a/contracts/strategies/_poc/sablier-v2/LockupDynamicStrategy.sol +++ b/contracts/strategies/_poc/sablier-v2/LockupDynamicStrategy.sol @@ -12,9 +12,11 @@ import {IAllo} from "../../../core/interfaces/IAllo.sol"; import {IRegistry} from "../../../core/interfaces/IRegistry.sol"; import {Metadata} from "../../../core/libraries/Metadata.sol"; import {BaseStrategy} from "../../BaseStrategy.sol"; +import {Transfer} from "contracts/core/libraries/Transfer.sol"; contract LockupDynamicStrategy is BaseStrategy, ReentrancyGuard { using SafeERC20 for IERC20; + using Transfer for address; /// =============================== /// ========== Errors ============= @@ -239,7 +241,7 @@ contract LockupDynamicStrategy is BaseStrategy, ReentrancyGuard { /// @param _amount The amount to be withdrawn function withdraw(uint256 _amount) external onlyPoolManager(msg.sender) { poolAmount -= _amount; - _transferAmount(allo.getPool(poolId).token, msg.sender, _amount); + allo.getPool(poolId).token.transferAmount(msg.sender, _amount); } /// ==================================== diff --git a/contracts/strategies/_poc/sablier-v2/LockupLinearStrategy.sol b/contracts/strategies/_poc/sablier-v2/LockupLinearStrategy.sol index 5f5eac7d5..d7ff1f767 100644 --- a/contracts/strategies/_poc/sablier-v2/LockupLinearStrategy.sol +++ b/contracts/strategies/_poc/sablier-v2/LockupLinearStrategy.sol @@ -12,9 +12,11 @@ import {IAllo} from "../../../core/interfaces/IAllo.sol"; import {IRegistry} from "../../../core/interfaces/IRegistry.sol"; import {Metadata} from "../../../core/libraries/Metadata.sol"; import {BaseStrategy} from "../../BaseStrategy.sol"; +import {Transfer} from "contracts/core/libraries/Transfer.sol"; contract LockupLinearStrategy is BaseStrategy, ReentrancyGuard { using SafeERC20 for IERC20; + using Transfer for address; /// =============================== /// ========== Errors ============= @@ -230,7 +232,7 @@ contract LockupLinearStrategy is BaseStrategy, ReentrancyGuard { /// @param _amount The amount to be withdrawn function withdraw(uint256 _amount) external onlyPoolManager(msg.sender) { poolAmount -= _amount; - _transferAmount(allo.getPool(poolId).token, msg.sender, _amount); + allo.getPool(poolId).token.transferAmount(msg.sender, _amount); } /// ==================================== diff --git a/contracts/strategies/_poc/sqf-superfluid/SQFSuperFluidStrategy.sol b/contracts/strategies/_poc/sqf-superfluid/SQFSuperFluidStrategy.sol index c75bf44a2..47cf309f1 100644 --- a/contracts/strategies/_poc/sqf-superfluid/SQFSuperFluidStrategy.sol +++ b/contracts/strategies/_poc/sqf-superfluid/SQFSuperFluidStrategy.sol @@ -20,11 +20,13 @@ import {IRegistry} from "../../../core/interfaces/IRegistry.sol"; // Core Contracts import {BaseStrategy} from "../../BaseStrategy.sol"; // Internal Libraries +import {Transfer} from "contracts/core/libraries/Transfer.sol"; import {Metadata} from "../../../core/libraries/Metadata.sol"; import {RecipientSuperApp} from "./RecipientSuperApp.sol"; import {RecipientSuperAppFactory} from "./RecipientSuperAppFactory.sol"; contract SQFSuperFluidStrategy is BaseStrategy, ReentrancyGuard { + using Transfer for address; using SuperTokenV1Library for ISuperToken; using FixedPointMathLib for uint256; @@ -571,13 +573,13 @@ contract SQFSuperFluidStrategy is BaseStrategy, ReentrancyGuard { if (_token == address(poolSuperToken)) { revert INVALID(); } - _transferAmount(_token, msg.sender, _amount); + _token.transferAmount(msg.sender, _amount); } /// @notice Close the stream function closeStream() external onlyPoolManager(msg.sender) { poolSuperToken.distributeFlow(address(this), gdaPool, 0, "0x"); - _transferAmount(address(poolSuperToken), msg.sender, poolSuperToken.balanceOf(address(this))); + address(poolSuperToken).transferAmount(msg.sender, poolSuperToken.balanceOf(address(this))); } /// ========================= diff --git a/contracts/strategies/_poc/wrapped-voting-nftmint/WrappedVotingNftMintStrategy.sol b/contracts/strategies/_poc/wrapped-voting-nftmint/WrappedVotingNftMintStrategy.sol index f9f5edc92..f6f6d2ee0 100644 --- a/contracts/strategies/_poc/wrapped-voting-nftmint/WrappedVotingNftMintStrategy.sol +++ b/contracts/strategies/_poc/wrapped-voting-nftmint/WrappedVotingNftMintStrategy.sol @@ -11,6 +11,7 @@ import {BaseStrategy} from "../../BaseStrategy.sol"; import {Native} from "../../../core/libraries/Native.sol"; import {NFT} from "./NFT.sol"; import {NFTFactory} from "./NFTFactory.sol"; +import {Transfer} from "contracts/core/libraries/Transfer.sol"; // ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣾⣿⣷⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣼⣿⣿⣷⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ // ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣿⣿⣿⣿⣷⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣼⣿⣿⣿⣿⣿⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ @@ -28,6 +29,8 @@ import {NFTFactory} from "./NFTFactory.sol"; // allo.gitcoin.co contract WrappedVotingNftMintStrategy is Native, BaseStrategy, ReentrancyGuard { + using Transfer for address; + /// =============================== /// ========== Events ============= /// =============================== @@ -210,7 +213,7 @@ contract WrappedVotingNftMintStrategy is Native, BaseStrategy, ReentrancyGuard { delete poolAmount; - _transferAmount(pool.token, currentWinner, poolAmount); + pool.token.transferAmount(currentWinner, poolAmount); emit Distributed(currentWinner, currentWinner, poolAmount, _sender); } diff --git a/contracts/strategies/direct-grants-lite/DirectGrantsLite.sol b/contracts/strategies/direct-grants-lite/DirectGrantsLite.sol index f104cce91..c75cdcac8 100644 --- a/contracts/strategies/direct-grants-lite/DirectGrantsLite.sol +++ b/contracts/strategies/direct-grants-lite/DirectGrantsLite.sol @@ -10,6 +10,7 @@ import {BaseStrategy} from "../BaseStrategy.sol"; // Internal Libraries import {Metadata} from "../../core/libraries/Metadata.sol"; import {Native} from "../../core/libraries/Native.sol"; +import {Transfer} from "../../core/libraries/Transfer.sol"; // ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣾⣿⣷⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣼⣿⣿⣷⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ // ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣿⣿⣿⣿⣷⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣼⣿⣿⣿⣿⣿⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ @@ -30,6 +31,8 @@ import {Native} from "../../core/libraries/Native.sol"; /// @author @thelostone-mc , @0xKurt , @codenamejason /// @notice Strategy for direct grants contract DirectGrantsLiteStrategy is Native, BaseStrategy, Multicall { + using Transfer for address; + /// ================================ /// ========== Struct ============== /// ================================ @@ -291,13 +294,13 @@ contract DirectGrantsLiteStrategy is Native, BaseStrategy, Multicall { /// @param _token The token to be withdrawn function withdraw(address _token) external onlyPoolManager(msg.sender) { // get the actual balance hold by the pool - uint256 amount = _getBalance(_token, address(this)); + uint256 amount = _token.getBalance(address(this)); // calculate the amount which is accessible uint256 accessibleAmount = amount; // transfer the amount to the pool manager - _transferAmount(_token, msg.sender, accessibleAmount); + _token.transferAmount(msg.sender, accessibleAmount); } /// ==================================== @@ -451,9 +454,6 @@ contract DirectGrantsLiteStrategy is Native, BaseStrategy, Multicall { address token = allocations[i].token; uint256 amount = allocations[i].amount; - // This will revert if the sender tries to spend more than the msg.value - if (token == NATIVE) nativeAmount -= amount; - if (recipient.recipientAddress == address(0)) { revert RECIPIENT_ERROR(recipientId); } @@ -462,7 +462,8 @@ contract DirectGrantsLiteStrategy is Native, BaseStrategy, Multicall { revert RECIPIENT_NOT_ACCEPTED(); } - _transferAmountFrom(token, TransferData({from: _sender, to: recipientAddress, amount: amount})); + if (token == NATIVE) nativeAmount -= amount; + token.transferAmountFrom(_sender, recipientAddress, amount); emit Allocated(recipientId, amount, token, _sender); @@ -471,7 +472,7 @@ contract DirectGrantsLiteStrategy is Native, BaseStrategy, Multicall { } } - if (nativeAmount > 0) _transferAmount(NATIVE, _sender, nativeAmount); + if (nativeAmount > 0) _sender.transferAmountNative(nativeAmount); } /// @notice Check if sender is profile owner or member. diff --git a/contracts/strategies/donation-voting-merkle-base/DonationVotingMerkleDistributionBaseStrategy.sol b/contracts/strategies/donation-voting-merkle-base/DonationVotingMerkleDistributionBaseStrategy.sol index ef579dbbb..352543577 100644 --- a/contracts/strategies/donation-voting-merkle-base/DonationVotingMerkleDistributionBaseStrategy.sol +++ b/contracts/strategies/donation-voting-merkle-base/DonationVotingMerkleDistributionBaseStrategy.sol @@ -13,6 +13,7 @@ import {BaseStrategy} from "../BaseStrategy.sol"; // Internal Libraries import {Metadata} from "../../core/libraries/Metadata.sol"; import {Native} from "../../core/libraries/Native.sol"; +import {Transfer} from "../../core/libraries/Transfer.sol"; // ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣾⣿⣷⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣼⣿⣿⣷⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ // ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣿⣿⣿⣿⣷⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣼⣿⣿⣿⣿⣿⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ @@ -33,6 +34,8 @@ import {Native} from "../../core/libraries/Native.sol"; /// @author @thelostone-mc , @0xKurt , @codenamejason , @0xZakk , @nfrgosselin /// @notice Strategy for donation voting allocation with a merkle distribution abstract contract DonationVotingMerkleDistributionBaseStrategy is Native, BaseStrategy, Multicall { + using Transfer for address; + /// ================================ /// ========== Struct ============== /// ================================ @@ -423,7 +426,7 @@ abstract contract DonationVotingMerkleDistributionBaseStrategy is Native, BaseSt } // get the actual balance hold by the pool - uint256 amount = _getBalance(_token, address(this)); + uint256 amount = _token.getBalance(address(this)); // get the token amount in vault which belong to the recipients uint256 tokenInVault = _tokenAmountInVault(_token); @@ -432,7 +435,7 @@ abstract contract DonationVotingMerkleDistributionBaseStrategy is Native, BaseSt uint256 accessableAmount = amount - tokenInVault; // transfer the amount to the pool manager - _transferAmount(_token, msg.sender, accessableAmount); + _token.transferAmount(msg.sender, accessableAmount); } /// @notice Internal function to return the token amount locked in vault @@ -829,7 +832,7 @@ abstract contract DonationVotingMerkleDistributionBaseStrategy is Native, BaseSt poolAmount -= amount; // Transfer the amount to the recipient - _transferAmount(pool.token, payable(recipientAddress), amount); + pool.token.transferAmount(payable(recipientAddress), amount); // Emit that the funds have been distributed to the recipient emit FundsDistributed(amount, recipientAddress, pool.token, recipientId); diff --git a/contracts/strategies/donation-voting-merkle-distribution-direct-transfer/DonationVotingMerkleDistributionDirectTransferStrategy.sol b/contracts/strategies/donation-voting-merkle-distribution-direct-transfer/DonationVotingMerkleDistributionDirectTransferStrategy.sol index dd72b1a1f..214dfc6fe 100644 --- a/contracts/strategies/donation-voting-merkle-distribution-direct-transfer/DonationVotingMerkleDistributionDirectTransferStrategy.sol +++ b/contracts/strategies/donation-voting-merkle-distribution-direct-transfer/DonationVotingMerkleDistributionDirectTransferStrategy.sol @@ -4,10 +4,10 @@ pragma solidity 0.8.19; import {ISignatureTransfer} from "permit2/ISignatureTransfer.sol"; import {DonationVotingMerkleDistributionBaseStrategy} from "../donation-voting-merkle-base/DonationVotingMerkleDistributionBaseStrategy.sol"; -import {SafeTransferLib} from "solady/utils/SafeTransferLib.sol"; import {IERC20Permit} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {IDAI} from "../donation-voting-merkle-base/IDAI.sol"; +import {Transfer} from "../../core/libraries/Transfer.sol"; // ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣾⣿⣷⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣼⣿⣿⣷⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ // ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣿⣿⣿⣿⣷⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣼⣿⣿⣿⣿⣿⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ @@ -27,6 +27,8 @@ import {IDAI} from "../donation-voting-merkle-base/IDAI.sol"; /// @author @thelostone-mc , @0xKurt , @codenamejason , @0xZakk , @nfrgosselin /// @notice Strategy for donation voting allocation with a merkle distribution contract DonationVotingMerkleDistributionDirectTransferStrategy is DonationVotingMerkleDistributionBaseStrategy { + using Transfer for address; + /// =============================== /// ======== Constructor ========== /// =============================== @@ -56,7 +58,8 @@ contract DonationVotingMerkleDistributionDirectTransferStrategy is DonationVotin address recipientAddress = _recipients[recipientId].recipientAddress; // Native or already approved if (permitType == PermitType.None) { - _transferAmountFrom(token, TransferData(_sender, recipientAddress, amount)); + if (token == NATIVE && msg.value < amount) revert ETH_MISMATCH(); + token.transferAmountFrom(_sender, recipientAddress, amount); } else if (permitType == PermitType.Permit2) { PERMIT2.permitTransferFrom( // The permit message. p2Data.permit, diff --git a/contracts/strategies/donation-voting-merkle-distribution-vault/DonationVotingMerkleDistributionVaultStrategy.sol b/contracts/strategies/donation-voting-merkle-distribution-vault/DonationVotingMerkleDistributionVaultStrategy.sol index 3ac22f3af..40147f3a3 100644 --- a/contracts/strategies/donation-voting-merkle-distribution-vault/DonationVotingMerkleDistributionVaultStrategy.sol +++ b/contracts/strategies/donation-voting-merkle-distribution-vault/DonationVotingMerkleDistributionVaultStrategy.sol @@ -6,6 +6,7 @@ import {DonationVotingMerkleDistributionBaseStrategy} from "../donation-voting-merkle-base/DonationVotingMerkleDistributionBaseStrategy.sol"; import "openzeppelin-contracts-upgradeable/contracts/security/ReentrancyGuardUpgradeable.sol"; import {SafeTransferLib} from "solady/utils/SafeTransferLib.sol"; +import {Transfer} from "../../core/libraries/Transfer.sol"; // ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣾⣿⣷⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣼⣿⣿⣷⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ // ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣿⣿⣿⣿⣷⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣼⣿⣿⣿⣿⣿⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ @@ -29,6 +30,9 @@ contract DonationVotingMerkleDistributionVaultStrategy is DonationVotingMerkleDistributionBaseStrategy, ReentrancyGuardUpgradeable { + using SafeTransferLib for address; + using Transfer for address; + /// @notice Stores the details of the allocations to claim. struct Claim { address recipientId; @@ -92,7 +96,7 @@ contract DonationVotingMerkleDistributionVaultStrategy is totalClaimableAmount[token] -= amount; // Transfer the tokens to the recipient - _transferAmount(token, recipient.recipientAddress, amount); + token.transferAmount(recipient.recipientAddress, amount); // Emit that the tokens have been claimed and sent to the recipient emit Claimed(singleClaim.recipientId, recipient.recipientAddress, amount, token); @@ -120,11 +124,11 @@ contract DonationVotingMerkleDistributionVaultStrategy is uint256 transferredAmount = amount; if (token == NATIVE) { if (msg.value < amount) { - revert AMOUNT_MISMATCH(); + revert ETH_MISMATCH(); } - SafeTransferLib.safeTransferETH(address(this), amount); + address(this).safeTransferETH(amount); } else { - uint256 balanceBefore = SafeTransferLib.balanceOf(token, address(this)); + uint256 balanceBefore = token.balanceOf(address(this)); PERMIT2.permitTransferFrom( // The permit message. p2Data.permit, @@ -137,7 +141,7 @@ contract DonationVotingMerkleDistributionVaultStrategy is p2Data.signature ); - uint256 balanceAfter = SafeTransferLib.balanceOf(token, address(this)); + uint256 balanceAfter = token.balanceOf(address(this)); transferredAmount = balanceAfter - balanceBefore; } diff --git a/contracts/strategies/easy-rpgf/EasyRPGFStrategy.sol b/contracts/strategies/easy-rpgf/EasyRPGFStrategy.sol index e12c5d9c2..02818d539 100644 --- a/contracts/strategies/easy-rpgf/EasyRPGFStrategy.sol +++ b/contracts/strategies/easy-rpgf/EasyRPGFStrategy.sol @@ -3,8 +3,11 @@ pragma solidity ^0.8.19; import {BaseStrategy} from "../BaseStrategy.sol"; import {IAllo} from "../../core/interfaces/IAllo.sol"; +import {Transfer} from "contracts/core/libraries/Transfer.sol"; contract EasyRPGFStrategy is BaseStrategy { + using Transfer for address; + error INPUT_LENGTH_MISMATCH(); error NOOP(); @@ -21,7 +24,7 @@ contract EasyRPGFStrategy is BaseStrategy { function withdraw(address _token, address _recipient) external onlyPoolManager(msg.sender) { uint256 _poolAmount = poolAmount; poolAmount = 0; - _transferAmount(_token, _recipient, _poolAmount); + _token.transferAmount(_recipient, _poolAmount); } /// @notice Distribute pool funds @@ -53,7 +56,7 @@ contract EasyRPGFStrategy is BaseStrategy { address recipientAddress = _recipientIds[i]; poolAmount -= amount; - _transferAmount(pool.token, recipientAddress, amount); + pool.token.transferAmount(recipientAddress, amount); emit Distributed(recipientAddress, recipientAddress, amount, _sender); unchecked { ++i; diff --git a/contracts/strategies/qv-base/QVBaseStrategy.sol b/contracts/strategies/qv-base/QVBaseStrategy.sol index ddb73cdd1..1ba24b474 100644 --- a/contracts/strategies/qv-base/QVBaseStrategy.sol +++ b/contracts/strategies/qv-base/QVBaseStrategy.sol @@ -8,6 +8,7 @@ import {IRegistry} from "../../core/interfaces/IRegistry.sol"; import {BaseStrategy} from "../BaseStrategy.sol"; // Internal Libraries import {Metadata} from "../../core/libraries/Metadata.sol"; +import {Transfer} from "contracts/core/libraries/Transfer.sol"; // ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣾⣿⣷⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣼⣿⣿⣷⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ // ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣿⣿⣿⣿⣷⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣼⣿⣿⣿⣿⣿⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ @@ -28,6 +29,8 @@ import {Metadata} from "../../core/libraries/Metadata.sol"; /// @notice Base strategy for quadratic voting strategies /// @author @thelostone-mc , @0xKurt , @codenamejason , @0xZakk , @nfrgosselin abstract contract QVBaseStrategy is BaseStrategy { + using Transfer for address; + /// ====================== /// ======= Events ======= /// ====================== @@ -338,10 +341,10 @@ abstract contract QVBaseStrategy is BaseStrategy { revert INVALID(); } - uint256 amount = _getBalance(_token, address(this)); + uint256 amount = _token.getBalance(address(this)); // Transfer the tokens to the 'msg.sender' (pool manager calling function) - _transferAmount(_token, msg.sender, amount); + _token.transferAmount(msg.sender, amount); } /// ==================================== @@ -505,7 +508,7 @@ abstract contract QVBaseStrategy is BaseStrategy { } IAllo.Pool memory pool = allo.getPool(poolId); - _transferAmount(pool.token, recipient.recipientAddress, amount); + pool.token.transferAmount(recipient.recipientAddress, amount); paidOut[recipientId] = true; diff --git a/contracts/strategies/rfp-simple/RFPSimpleStrategy.sol b/contracts/strategies/rfp-simple/RFPSimpleStrategy.sol index b94a9e641..3e9be436f 100644 --- a/contracts/strategies/rfp-simple/RFPSimpleStrategy.sol +++ b/contracts/strategies/rfp-simple/RFPSimpleStrategy.sol @@ -10,6 +10,7 @@ import {IRegistry} from "../../core/interfaces/IRegistry.sol"; import {BaseStrategy} from "../BaseStrategy.sol"; // Internal Libraries import {Metadata} from "../../core/libraries/Metadata.sol"; +import {Transfer} from "contracts/core/libraries/Transfer.sol"; // ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣾⣿⣷⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣼⣿⣿⣷⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ // ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣿⣿⣿⣿⣷⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣼⣿⣿⣿⣿⣿⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ @@ -30,6 +31,8 @@ import {Metadata} from "../../core/libraries/Metadata.sol"; /// @author @thelostone-mc , @0xKurt , @codenamejason , @0xZakk , @nfrgosselin /// @notice Strategy for Request for Proposal (RFP) allocation with milestone submission and management. contract RFPSimpleStrategy is BaseStrategy, ReentrancyGuard { + using Transfer for address; + /// ================================ /// ========== Struct ============== /// ================================ @@ -307,10 +310,10 @@ contract RFPSimpleStrategy is BaseStrategy, ReentrancyGuard { /// @dev Callable by the pool manager /// @param _token The token to withdraw function withdraw(address _token) external virtual onlyPoolManager(msg.sender) onlyInactivePool { - uint256 amount = _getBalance(_token, address(this)); + uint256 amount = _token.getBalance(address(this)); // Transfer the tokens to the 'msg.sender' (pool manager calling function) - _transferAmount(_token, msg.sender, amount); + _token.transferAmount(msg.sender, amount); } /// ==================================== @@ -448,7 +451,7 @@ contract RFPSimpleStrategy is BaseStrategy, ReentrancyGuard { // Get the pool, subtract the amount and transfer to the recipient poolAmount -= amount; - _transferAmount(pool.token, recipient.recipientAddress, amount); + pool.token.transferAmount(recipient.recipientAddress, amount); // Set the milestone status to 'Accepted' milestone.milestoneStatus = Status.Accepted; diff --git a/test/foundry/core/Registry.t.sol b/test/foundry/core/Registry.t.sol index a1b189682..a7f9b9d5c 100644 --- a/test/foundry/core/Registry.t.sol +++ b/test/foundry/core/Registry.t.sol @@ -446,14 +446,6 @@ contract RegistryTest is Test, RegistrySetup, Native, Errors { registry().acceptProfileOwnership(invalidProfileId); } - function testRevert_recoverFunds_INVALID_TOKEN_ADDRESS() public { - address nonExistentToken = address(0xAAA); - address recipient = address(0xBBB); - vm.expectRevert(); - vm.prank(registry_owner()); - registry().recoverFunds(nonExistentToken, recipient); - } - function testRevert_recoverFunds_ZERO_RECIPIENT() public { address nonExistentToken = address(0xAAA); vm.expectRevert(ZERO_ADDRESS.selector); diff --git a/test/foundry/integration/DirectAllocation.t.sol b/test/foundry/integration/DirectAllocation.t.sol index ded6a8d19..b8e6c27b2 100644 --- a/test/foundry/integration/DirectAllocation.t.sol +++ b/test/foundry/integration/DirectAllocation.t.sol @@ -6,6 +6,7 @@ import {Metadata} from "contracts/core/Registry.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {DirectAllocationStrategy} from "contracts/strategies/DirectAllocation.sol"; import {IntegrationBase} from "./IntegrationBase.sol"; +import {Errors} from "contracts/core/libraries/Errors.sol"; contract IntegrationDirectAllocationStrategy is IntegrationBase { IAllo public allo; @@ -33,14 +34,14 @@ contract IntegrationDirectAllocationStrategy is IntegrationBase { } function test_Revert_Register() public { - vm.expectRevert(DirectAllocationStrategy.NOT_IMPLEMENTED.selector); + vm.expectRevert(Errors.NOT_IMPLEMENTED.selector); vm.prank(address(allo)); strategy.register(new address[](0), "", address(0)); } function test_Revert_Distribute() public { - vm.expectRevert(DirectAllocationStrategy.NOT_IMPLEMENTED.selector); + vm.expectRevert(Errors.NOT_IMPLEMENTED.selector); vm.prank(address(allo)); strategy.distribute(new address[](0), "", address(0)); diff --git a/test/foundry/strategies/DirectAllocation.t.sol b/test/foundry/strategies/DirectAllocation.t.sol index d5216934e..0d5b03a50 100644 --- a/test/foundry/strategies/DirectAllocation.t.sol +++ b/test/foundry/strategies/DirectAllocation.t.sol @@ -3,6 +3,7 @@ pragma solidity 0.8.19; import {Test} from "forge-std/Test.sol"; import {DirectAllocationStrategy} from "../../../contracts/strategies/DirectAllocation.sol"; +import {Errors} from "contracts/core/libraries/Errors.sol"; contract DirectAllocationTest is Test { event Initialized(uint256 poolId, bytes data); @@ -84,7 +85,7 @@ contract DirectAllocationTest is Test { function test_DistributeRevertWhen_Called(address[] memory _recipientIds, bytes memory _data, address _sender) external { - vm.expectRevert(DirectAllocationStrategy.NOT_IMPLEMENTED.selector); + vm.expectRevert(Errors.NOT_IMPLEMENTED.selector); vm.prank(mockAlloAddress); directAllocationStrategy.distribute(_recipientIds, _data, _sender); @@ -93,14 +94,14 @@ contract DirectAllocationTest is Test { function test_RegisterRevertWhen_Called(address[] memory _recipients, bytes memory _data, address _sender) external { - vm.expectRevert(DirectAllocationStrategy.NOT_IMPLEMENTED.selector); + vm.expectRevert(Errors.NOT_IMPLEMENTED.selector); vm.prank(mockAlloAddress); directAllocationStrategy.register(_recipients, _data, _sender); } function test_ReceiveRevertWhen_Called() external { - vm.expectRevert(DirectAllocationStrategy.NOT_IMPLEMENTED.selector); + vm.expectRevert(Errors.NOT_IMPLEMENTED.selector); /// send ether to the strategy payable(address(directAllocationStrategy)).transfer(1 ether); diff --git a/test/foundry/strategies/DonationVotingStrategy.t.sol b/test/foundry/strategies/DonationVotingStrategy.t.sol index 6231759cf..970415b97 100644 --- a/test/foundry/strategies/DonationVotingStrategy.t.sol +++ b/test/foundry/strategies/DonationVotingStrategy.t.sol @@ -30,8 +30,6 @@ contract DonationVotingStrategyTest is Test, AlloSetup, RegistrySetupFull, Event address indexed recipientId, DonationVotingStrategy.Status recipientStatus, address sender ); - error AMOUNT_MISMATCH(); - bool public useRegistryAnchor; bool public metadataRequired; @@ -823,14 +821,14 @@ contract DonationVotingStrategyTest is Test, AlloSetup, RegistrySetupFull, Event strategy.allocate(allocateData, allocator); } - function testRevert_allocate_AMOUNT_MISMATCH() public { + function testRevert_allocate_ETH_MISMATCH() public { address allocator = makeAddr("allocator"); deal(address(allo()), 1e18); address recipientId = __register_accept_recipient(); vm.warp(allocationStartTime + 10); - vm.expectRevert(AMOUNT_MISMATCH.selector); + vm.expectRevert(ETH_MISMATCH.selector); bytes memory allocateData = abi.encode(recipientId, 1e18, NATIVE); vm.prank(address(allo())); diff --git a/test/foundry/strategies/QVImpactStream.t.sol b/test/foundry/strategies/QVImpactStream.t.sol index 718cdd9c6..291483952 100644 --- a/test/foundry/strategies/QVImpactStream.t.sol +++ b/test/foundry/strategies/QVImpactStream.t.sol @@ -5,7 +5,7 @@ import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {StdStorage, Test, stdStorage} from "forge-std/Test.sol"; import {IAllo} from "../../../contracts/core/interfaces/IAllo.sol"; -import {IStrategy} from "../../../contracts/core/interfaces/IStrategy.sol"; +import {IBaseStrategy} from "../../../contracts/core/interfaces/IBaseStrategy.sol"; import {IRecipientsExtension} from "../../../contracts/extensions/interfaces/IRecipientsExtension.sol"; import {QVSimple} from "../../../contracts/strategies/QVSimple.sol"; import {QVImpactStream} from "../../../contracts/strategies/QVImpactStream.sol"; @@ -149,7 +149,7 @@ contract QVImpactStreamTest is Test { function test__distributeRevertWhen_PayoutAmountForRecipientIsZero() external callWithPoolManager { IAllo.Pool memory poolData = IAllo.Pool({ profileId: keccak256(abi.encodePacked(recipient1)), - strategy: IStrategy(address(qvImpactStream)), + strategy: IBaseStrategy(address(qvImpactStream)), token: address(0), metadata: Metadata({protocol: 0, pointer: ""}), managerRole: keccak256("MANAGER_ROLE"), @@ -175,7 +175,7 @@ contract QVImpactStreamTest is Test { IAllo.Pool memory poolData = IAllo.Pool({ profileId: keccak256(abi.encodePacked(recipient1)), - strategy: IStrategy(address(qvImpactStream)), + strategy: IBaseStrategy(address(qvImpactStream)), token: address(0), metadata: Metadata({protocol: 0, pointer: ""}), managerRole: keccak256("MANAGER_ROLE"),