Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: implement Allocation extension on strategies #659

Merged
merged 24 commits into from
Sep 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
c9de4f7
feat: add AllocationExtension.sol
0xAustrian Aug 26, 2024
86e0804
feat: complete interface, add natspec
0xAustrian Aug 26, 2024
b31c7da
test: add .tree and scaffolding
0xAustrian Aug 26, 2024
de598aa
test: create mock to integrate with smock
0xAustrian Aug 26, 2024
a7284da
test: add units tests for the extension
0xAustrian Aug 27, 2024
c01bbe7
chore: add smock to CI
0xAustrian Aug 27, 2024
a8f4b66
chore: rename unit file
0xAustrian Aug 27, 2024
fee01d4
fix: minor bug in _checkBeforeAllocation
0xAustrian Aug 27, 2024
3fa7747
feat: add AllocatorsAllowlistExtension.sol
0xAustrian Aug 27, 2024
ac85ce4
test: add tests for the extension
0xAustrian Aug 28, 2024
3b6bba3
feat: bump sol version
0xAustrian Aug 28, 2024
714fb32
feat: bump sol version
0xAustrian Aug 28, 2024
8ec1383
feat: small improvement when emitting event
0xAustrian Aug 28, 2024
0d4e82b
feat: remove unchecked block from for loops
0xAustrian Aug 28, 2024
37b99a1
Merge branch 'v2.1' into feat/allocation-extension
0xAustrian Aug 29, 2024
0b8e488
fix: fmt
0xAustrian Aug 29, 2024
4c836d3
Merge branch 'feat/allocation-extension' into feat/allocators-allowli…
0xAustrian Aug 29, 2024
9229e4e
Merge branch 'v2.1' into feat/allocators-allowlist-extension
0xAustrian Sep 2, 2024
bdbacac
feat: implement Allocation extension on DonationVotingOffchain strat
0xAustrian Sep 4, 2024
0edbe1c
feat: implement allocation extension in DonationVotingOnchain
0xAustrian Sep 4, 2024
d74fb48
feat: implement allocation extensions for QVSimple and QVImpactStream
0xAustrian Sep 4, 2024
8e8956a
fix: improvements and feedback
0xAustrian Sep 4, 2024
1ba961e
Merge branch 'v2.1' into feat/implement-allocation-extensions
0xAustrian Sep 5, 2024
ef18eaa
feat: remove unused error
0xAustrian Sep 9, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {IAllo} from "contracts/core/interfaces/IAllo.sol";
// Core Contracts
import {BaseStrategy} from "strategies/BaseStrategy.sol";
import {RecipientsExtension} from "strategies/extensions/register/RecipientsExtension.sol";
import {AllocationExtension} from "strategies/extensions/allocate/AllocationExtension.sol";
// Internal Libraries
import {Transfer} from "contracts/core/libraries/Transfer.sol";
import {Native} from "contracts/core/libraries/Native.sol";
Expand All @@ -28,7 +29,7 @@ import {Native} from "contracts/core/libraries/Native.sol";
/// @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 BaseStrategy, RecipientsExtension, Native {
contract DonationVotingOffchain is BaseStrategy, RecipientsExtension, AllocationExtension, Native {
using Transfer for address;

/// ===============================
Expand All @@ -46,12 +47,6 @@ contract DonationVotingOffchain is BaseStrategy, RecipientsExtension, Native {
/// @param amount The amount of pool tokens set
event PayoutSet(address indexed recipientId, uint256 amount);

/// @notice Emitted when the allocation timestamps are updated
/// @param allocationStartTime The start time for the allocation period
/// @param allocationEndTime The end time for the allocation period
/// @param sender The sender of the transaction
event AllocationTimestampsUpdated(uint64 allocationStartTime, uint64 allocationEndTime, address sender);

/// ================================
/// ========== Errors ==============
/// ================================
Expand All @@ -60,9 +55,6 @@ contract DonationVotingOffchain is BaseStrategy, RecipientsExtension, Native {
/// @param recipientId The recipientId to which distribution was attempted.
error NOTHING_TO_DISTRIBUTE(address recipientId);

/// @notice Thrown when the timestamps being set or updated don't meet the contracts requirements.
error INVALID_TIMESTAMPS();

/// @notice Thrown when a the payout for a recipient is attempted to be overwritten.
/// @param recipientId The recipientId to which a repeated payout was attempted.
error PAYOUT_ALREADY_SET(address recipientId);
Expand Down Expand Up @@ -100,41 +92,16 @@ contract DonationVotingOffchain is BaseStrategy, RecipientsExtension, Native {
/// @notice If true, allocations are directly sent to recipients. Otherwise, they they must be claimed later.
bool public immutable DIRECT_TRANSFER;

/// @notice The start time for allocations
uint64 public allocationStartTime;
/// @notice The end time for allocations
uint64 public allocationEndTime;
/// @notice Cooldown time from allocationEndTime after which the pool manager is allowed to withdraw tokens.
uint64 public withdrawalCooldown;
/// @notice amount to be distributed. `totalPayoutAmount` get reduced with each distribution.
uint256 public totalPayoutAmount;

/// @notice token -> bool
mapping(address => bool) public allowedTokens;
/// @notice recipientId -> PayoutSummary
mapping(address => PayoutSummary) public payoutSummaries;
/// @notice recipientId -> token -> amount
mapping(address => mapping(address => uint256)) public amountAllocated;

/// ================================
/// ========== Modifier ============
/// ================================

/// @notice Modifier to check if allocation is active
/// @dev Reverts if allocation is not active
modifier onlyActiveAllocation() {
if (block.timestamp < allocationStartTime) revert ALLOCATION_NOT_ACTIVE();
if (block.timestamp > allocationEndTime) revert ALLOCATION_NOT_ACTIVE();
_;
}

/// @notice Modifier to check if allocation has ended
/// @dev Reverts if allocation has not ended
modifier onlyAfterAllocation() {
if (block.timestamp <= allocationEndTime) revert ALLOCATION_NOT_ENDED();
_;
}

/// ===============================
/// ======== Constructor ==========
/// ===============================
Expand All @@ -158,34 +125,24 @@ contract DonationVotingOffchain is BaseStrategy, RecipientsExtension, Native {
/// uint64 _allocationStartTime,
/// uint64 _allocationEndTime,
/// uint64 _withdrawalCooldown,
/// address[] _allowedTokens
/// address[] _allowedTokens,
/// bool _isUsingAllocationMetadata
/// )
function initialize(uint256 _poolId, bytes memory _data) external virtual override {
(
RecipientInitializeData memory _recipientExtensionInitializeData,
uint64 _allocationStartTime,
uint64 _allocationEndTime,
uint64 _withdrawalCooldown,
address[] memory _allowedTokens
) = abi.decode(_data, (RecipientInitializeData, uint64, uint64, uint64, address[]));

allocationStartTime = _allocationStartTime;
allocationEndTime = _allocationEndTime;
emit AllocationTimestampsUpdated(_allocationStartTime, _allocationEndTime, msg.sender);
address[] memory _allowedTokens,
bool _isUsingAllocationMetadata
) = abi.decode(_data, (RecipientInitializeData, uint64, uint64, uint64, address[], bool));

withdrawalCooldown = _withdrawalCooldown;

if (_allowedTokens.length == 0) {
// all tokens
allowedTokens[address(0)] = true;
} else {
for (uint256 i; i < _allowedTokens.length; i++) {
allowedTokens[_allowedTokens[i]] = true;
}
}

__BaseStrategy_init(_poolId);
__RecipientsExtension_init(_recipientExtensionInitializeData);
__AllocationExtension_init(_allowedTokens, _allocationStartTime, _allocationEndTime, _isUsingAllocationMetadata);

emit Initialized(_poolId, _data);
}
Expand All @@ -194,26 +151,6 @@ contract DonationVotingOffchain is BaseStrategy, RecipientsExtension, Native {
/// ======= External/Custom =======
/// ===============================

/// @notice Sets the start and end dates.
/// @dev The 'msg.sender' must be a pool manager.
/// @param _registrationStartTime The start time for the registration
/// @param _registrationEndTime The end time for the registration
/// @param _allocationStartTime The start time for the allocation
/// @param _allocationEndTime The end time for the allocation
function updatePoolTimestamps(
uint64 _registrationStartTime,
uint64 _registrationEndTime,
uint64 _allocationStartTime,
uint64 _allocationEndTime
) external onlyPoolManager(msg.sender) {
if (_allocationStartTime > _allocationEndTime) revert INVALID_TIMESTAMPS();
allocationStartTime = _allocationStartTime;
allocationEndTime = _allocationEndTime;
emit AllocationTimestampsUpdated(allocationStartTime, allocationEndTime, msg.sender);

_updatePoolTimestamps(_registrationStartTime, _registrationEndTime);
}

/// @notice Transfers the allocated tokens to recipients.
/// @dev This function is ignored if DIRECT_TRANSFER is enabled, in which case allocated tokens are not stored
/// in the contract for later claim but directly sent to recipients in `_allocate()`.
Expand Down Expand Up @@ -353,26 +290,17 @@ contract DonationVotingOffchain is BaseStrategy, RecipientsExtension, Native {
if (block.timestamp > allocationEndTime) revert POOL_INACTIVE();
}

/// @notice Checks if the timestamps are valid.
/// @param _registrationStartTime The start time for the registration
/// @param _registrationEndTime The end time for the registration
function _isPoolTimestampValid(uint64 _registrationStartTime, uint64 _registrationEndTime)
internal
view
virtual
override
{
if (_registrationStartTime > _registrationEndTime) revert INVALID_TIMESTAMPS();
if (block.timestamp > _registrationStartTime) revert INVALID_TIMESTAMPS();
// Check consistency with allocation timestamps
if (_registrationStartTime > allocationStartTime) revert INVALID_TIMESTAMPS();
if (_registrationEndTime > allocationEndTime) revert INVALID_TIMESTAMPS();
}

/// @notice Returns if the recipient is accepted
/// @param _recipientId The recipient id
/// @return If the recipient is accepted
function _isAcceptedRecipient(address _recipientId) internal view virtual returns (bool) {
return _getRecipientStatus(_recipientId) == Status.Accepted;
}

/// @notice Returns always true as all addresses are valid allocators
/// @param _allocator NOT USED
/// @return Returns always true
function _isValidAllocator(address _allocator) internal view override returns (bool) {
return true;
}
}
Loading
Loading