-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
0614b1f
commit 15f77b4
Showing
8 changed files
with
420 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
{ | ||
"solidity.packageDefaultDependenciesContractsDirectory": "src", | ||
"solidity.packageDefaultDependenciesDirectory": "lib", | ||
"solidity.compileUsingRemoteVersion": "v0.8.20", | ||
"[solidity]": { | ||
"editor.defaultFormatter": "JuanBlanco.solidity" | ||
}, | ||
"solidity.formatter": "forge", | ||
"search.exclude": { "lib": true }, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
//SPDX-License-Identifier GPL-3.0-or-later | ||
pragma solidity 0.8.20; | ||
|
||
import "interfaces/IIncentivizedLocker.sol"; | ||
import { | ||
IQuestDistributor, | ||
IDelegationDistributor, | ||
IVotiumDistributor, | ||
IHiddenHandDistributor | ||
} from "warlord/interfaces/external/incentives/IIncentivesDistributors.sol"; | ||
import { Errors } from "utils/Errors.sol"; | ||
import { SafeTransferLib } from "solady/utils/SafeTransferLib.sol"; | ||
import { ERC20 } from "solady/tokens/ERC20.sol"; | ||
import { ReentrancyGuard } from "solmate/utils/ReentrancyGuard.sol"; | ||
|
||
/** | ||
* @title Incentivized Locker contract | ||
* @author Paladin | ||
* @notice Locker contract capable of claiming vote rewards from different sources | ||
*/ | ||
abstract contract AIncentiveClaimer is IIncentivizedLocker, ReentrancyGuard { | ||
/** | ||
* @notice Claims voting rewards from Quest | ||
* @param distributor Address of the contract distributing the rewards | ||
* @param questID ID of the Quest to claim rewards from | ||
* @param period Timestamp of the Quest period to claim | ||
* @param index Index in the Merkle Tree | ||
* @param account Address claiming the rewards | ||
* @param amount Amount to claim | ||
* @param merkleProof Merkle Proofs for the claim | ||
*/ | ||
function claimQuestRewards( | ||
address distributor, | ||
uint256 questID, | ||
uint256 period, | ||
uint256 index, | ||
address account, | ||
uint256 amount, | ||
bytes32[] calldata merkleProof | ||
) external nonReentrant { | ||
IQuestDistributor _distributor = IQuestDistributor(distributor); | ||
ERC20 _token = ERC20(_distributor.questRewardToken(questID)); | ||
|
||
_distributor.claim(questID, period, index, account, amount, merkleProof); | ||
} | ||
|
||
/** | ||
* @notice Claims voting rewards from the Paladin Delegation address | ||
* @param distributor Address of the contract distributing the rewards | ||
* @param token Address of the reward token to claim | ||
* @param index Index in the Merkle Tree | ||
* @param account Address claiming the rewards | ||
* @param amount Amount to claim | ||
* @param merkleProof Merkle Proofs for the claim | ||
*/ | ||
function claimDelegationRewards( | ||
address distributor, | ||
address token, | ||
uint256 index, | ||
address account, | ||
uint256 amount, | ||
bytes32[] calldata merkleProof | ||
) external nonReentrant { | ||
IDelegationDistributor(distributor).claim(token, index, account, amount, merkleProof); | ||
} | ||
|
||
// TODO mayybe delete this function for liquis | ||
/** | ||
* @notice Claims voting rewards from Votium | ||
* @param distributor Address of the contract distributing the rewards | ||
* @param token Address of the reward token to claim | ||
* @param index Index in the Merkle Tree | ||
* @param account Address claiming the rewards | ||
* @param amount Amount to claim | ||
* @param merkleProof Merkle Proofs for the claim | ||
*/ | ||
function claimVotiumRewards( | ||
address distributor, | ||
address token, | ||
uint256 index, | ||
address account, | ||
uint256 amount, | ||
bytes32[] calldata merkleProof | ||
) external nonReentrant { | ||
IVotiumDistributor(distributor).claim(token, index, account, amount, merkleProof); | ||
} | ||
|
||
/** | ||
* @notice Claims voting rewards from HiddenHand | ||
* @param distributor Address of the contract distributing the rewards | ||
* @param claimParams Parameters for claims | ||
*/ | ||
function claimHiddenHandRewards(address distributor, IHiddenHandDistributor.Claim[] calldata claimParams) | ||
external | ||
nonReentrant | ||
{ | ||
require(claimParams.length == 1); | ||
|
||
IHiddenHandDistributor _distributor = IHiddenHandDistributor(distributor); | ||
address token = _distributor.rewards(claimParams[0].identifier).token; | ||
|
||
uint256 initialBalance = ERC20(token).balanceOf(address(this)); | ||
|
||
_distributor.claim(claimParams); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,241 @@ | ||
// SPDX-License-Identifier: GPL-3.0-or-later | ||
pragma solidity 0.8.20; | ||
|
||
import { SafeTransferLib } from "solady/utils/SafeTransferLib.sol"; | ||
import { AIncentiveClaimer } from "./AIncentiveClaimer.sol"; | ||
import { AProtocolClaimer } from "./AProtocolClaimer.sol"; | ||
import { Ownable } from "solady/auth/Ownable.sol"; | ||
import { ERC20 } from "solady/tokens/ERC20.sol"; | ||
|
||
abstract contract APounder is ERC20, AIncentiveClaimer, AProtocolClaimer, Ownable { | ||
address public asset; | ||
address public swapper; | ||
/** | ||
* @notice Address of the voting power delegate | ||
*/ | ||
address public delegate; | ||
|
||
// The amount of CVX that needs to remain unlocked for redemptions | ||
uint256 public outstandingRedemptions; | ||
|
||
// The amount of new CVX deposits that is awaiting lock | ||
uint256 public pendingLocks; | ||
|
||
/** | ||
* @notice Event emitted when the delegate is updated | ||
*/ | ||
event SetDelegate(address newDelegatee); | ||
|
||
constructor(address initialSwapper, address definitiveAsset) { | ||
asset = definitiveAsset; | ||
swapper = initialSwapper; | ||
} | ||
|
||
/** | ||
* @dev Updates the Delegatee & delegates the voting power | ||
* @param _delegatee Address of the delegatee | ||
*/ | ||
function _setDelegate(address _delegatee) internal virtual; | ||
|
||
/** | ||
* @notice Updates the Delegatee & delegates the voting power | ||
* @param _delegatee Address of the delegatee | ||
*/ | ||
function setDelegate(address _delegatee) external onlyOwner { | ||
delegate = _delegatee; | ||
_setDelegate(_delegatee); | ||
|
||
emit SetDelegate(_delegatee); | ||
} | ||
|
||
function _lock(uint256 amount) internal virtual; | ||
|
||
/** | ||
* @dev Locks the tokens in the vlToken contract | ||
* @param amount Amount to lock | ||
*/ | ||
function lock() external { | ||
_processUnlock(); | ||
|
||
uint256 balance = asset.balanceOf(address(this)); | ||
bool balanceGreaterThanRedemptions = balance > outstandingRedemptions; | ||
|
||
// Lock CVX if the balance is greater than outstanding redemptions or if there are pending locks | ||
if (balanceGreaterThanRedemptions || pendingLocks != 0) { | ||
uint256 balanceRedemptionsDifference = balanceGreaterThanRedemptions | ||
? balance - outstandingRedemptions | ||
: 0; | ||
|
||
// Lock amount is the greater of the two: balanceRedemptionsDifference or pendingLocks | ||
// balanceRedemptionsDifference is greater if there is unlocked CVX that isn't reserved for redemptions + deposits | ||
// pendingLocks is greater if there are more new deposits than unlocked CVX that is reserved for redemptions | ||
_lock( | ||
balanceRedemptionsDifference > pendingLocks | ||
? balanceRedemptionsDifference | ||
: pendingLocks | ||
); | ||
|
||
pendingLocks = 0; | ||
} | ||
} | ||
|
||
/** | ||
* @dev Processes the unlock of tokens | ||
*/ | ||
function _processUnlock() internal virtual; | ||
|
||
function deposit(uint256 amount, address receiver) external { | ||
if (amount == 0) revert ZeroAmount(); | ||
if (receiver == address(0)) revert ZeroAddress(); | ||
|
||
pendingLocks += amount; | ||
|
||
// Transfer the tokens to the contract | ||
SafeTransferLib.safeTransferFrom(asset, msg.sender, address(this), amount); | ||
|
||
_mint(receiver, amount); | ||
} | ||
|
||
function _initiateRedemption( | ||
ICvxLocker.LockedBalance memory lockData, | ||
Futures f, | ||
uint256 assets, | ||
address receiver, | ||
uint256 feeMin, | ||
uint256 feeMax | ||
) internal returns (uint256 feeAmount) { | ||
if (assets == 0) revert ZeroAmount(); | ||
if (receiver == address(0)) revert ZeroAddress(); | ||
|
||
uint256 unlockTime = lockData.unlockTime; | ||
|
||
// Used for calculating the fee and conditionally adding a round | ||
uint256 waitTime = unlockTime - block.timestamp; | ||
|
||
if (feeMax != 0) { | ||
uint256 feePercent = feeMax - | ||
(((feeMax - feeMin) * waitTime) / MAX_REDEMPTION_TIME); | ||
|
||
feeAmount = (assets * feePercent) / FEE_DENOMINATOR; | ||
} | ||
|
||
uint256 postFeeAmount = assets - feeAmount; | ||
|
||
// Increment redemptions for this unlockTime to prevent over-redeeming | ||
redemptions[unlockTime] += postFeeAmount; | ||
|
||
// Check if there is any sufficient allowance after factoring in redemptions by others | ||
if (redemptions[unlockTime] > lockData.amount) | ||
revert InsufficientRedemptionAllowance(); | ||
|
||
// Track assets that needs to remain unlocked for redemptions | ||
outstandingRedemptions += postFeeAmount; | ||
|
||
// Mint upxCVX with unlockTime as the id - validates `to` | ||
upxCvx.mint(receiver, unlockTime, postFeeAmount, UNUSED_1155_DATA); | ||
|
||
return feeAmount; | ||
} | ||
|
||
function initiateRedemptions( | ||
uint256[] calldata lockIndexes, | ||
Futures f, | ||
uint256[] calldata assets, | ||
address receiver | ||
) external whenNotPaused nonReentrant { | ||
uint256 lockLen = lockIndexes.length; | ||
|
||
if (lockLen == 0) revert EmptyArray(); | ||
if (lockLen != assets.length) revert MismatchedArrayLengths(); | ||
|
||
emit InitiateRedemptions(lockIndexes, f, assets, receiver); | ||
|
||
(, , , ICvxLocker.LockedBalance[] memory lockData) = cvxLocker | ||
.lockedBalances(address(this)); | ||
uint256 totalAssets; | ||
uint256 feeAmount; | ||
uint256 feeMin = fees[Fees.RedemptionMin]; | ||
uint256 feeMax = fees[Fees.RedemptionMax]; | ||
|
||
for (uint256 i; i < lockLen; ++i) { | ||
totalAssets += assets[i]; | ||
feeAmount += _initiateRedemption( | ||
lockData[lockIndexes[i]], | ||
f, | ||
assets[i], | ||
receiver, | ||
feeMin, | ||
feeMax | ||
); | ||
} | ||
|
||
// Burn pxCVX - reverts if sender balance is insufficient | ||
pxCvx.burn(msg.sender, totalAssets - feeAmount); | ||
|
||
if (feeAmount != 0) { | ||
// Allow PirexFees to distribute fees directly from sender | ||
pxCvx.operatorApprove(msg.sender, address(pirexFees), feeAmount); | ||
|
||
// Distribute fees | ||
pirexFees.distributeFees(msg.sender, address(pxCvx), feeAmount); | ||
} | ||
} | ||
|
||
function _redeem( | ||
uint256[] calldata unlockTimes, | ||
uint256[] calldata assets, | ||
address receiver, | ||
bool legacy | ||
) internal { | ||
uint256 unlockLen = unlockTimes.length; | ||
|
||
if (unlockLen == 0) revert EmptyArray(); | ||
if (unlockLen != assets.length) revert MismatchedArrayLengths(); | ||
if (receiver == address(0)) revert ZeroAddress(); | ||
|
||
emit Redeem(unlockTimes, assets, receiver, legacy); | ||
|
||
uint256 totalAssets; | ||
|
||
for (uint256 i; i < unlockLen; ++i) { | ||
uint256 asset = assets[i]; | ||
|
||
if (!legacy && unlockTimes[i] > block.timestamp) | ||
revert BeforeUnlock(); | ||
if (asset == 0) revert ZeroAmount(); | ||
|
||
totalAssets += asset; | ||
} | ||
|
||
// Perform unlocking and locking procedure to ensure enough CVX is available | ||
if (!legacy) { | ||
_lock(); | ||
} | ||
|
||
// Subtract redemption amount from outstanding CVX amount | ||
outstandingRedemptions -= totalAssets; | ||
|
||
// Reverts if sender has an insufficient upxCVX balance for any `unlockTime` id | ||
upxCvx.burnBatch(msg.sender, unlockTimes, assets); | ||
|
||
// Validates `to` | ||
asset.safeTransfer(receiver, totalAssets); | ||
} | ||
|
||
function redeem( | ||
uint256[] calldata unlockTimes, | ||
uint256[] calldata assets, | ||
address receiver | ||
) external whenNotPaused nonReentrant { | ||
if (upxCvxDeprecated) revert RedeemClosed(); | ||
|
||
_redeem(unlockTimes, assets, receiver, false); | ||
} | ||
|
||
/** | ||
@notice Manually unlock CVX in the case of a mass unlock | ||
*/ | ||
function unlock() external onlyOwner { | ||
_processUnlock(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
// SPDX-License-Identifier: GPL-3.0-or-later | ||
pragma solidity 0.8.20; | ||
|
||
abstract contract AProtocolClaimer { | ||
event ProtocolRewardsHarvest(); | ||
|
||
/** | ||
* @dev Harvest rewards & send them to the Controller | ||
*/ | ||
function _harvest() internal virtual; | ||
|
||
/** | ||
* @notice Harvest rewards | ||
*/ | ||
function harvest() external { | ||
emit ProtocolRewardsHarvest(); | ||
_harvest(); | ||
} | ||
} |
Oops, something went wrong.