-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(sdk): ERC20VariableIncentive sdk integration + tests
- Loading branch information
Showing
14 changed files
with
744 additions
and
77 deletions.
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
89 changes: 89 additions & 0 deletions
89
packages/evm/contracts/incentives/AERC20VariableIncentive.sol
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,89 @@ | ||
// SPDX-License-Identifier: GPL-3.0 | ||
pragma solidity ^0.8.24; | ||
|
||
import {LibPRNG} from "@solady/utils/LibPRNG.sol"; | ||
import {SafeTransferLib} from "@solady/utils/SafeTransferLib.sol"; | ||
|
||
import {BoostError} from "contracts/shared/BoostError.sol"; | ||
import {Incentive} from "contracts/incentives/Incentive.sol"; | ||
import {Budget} from "contracts/budgets/Budget.sol"; | ||
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; | ||
|
||
/// @title ERC20 Incentive with Variable Rewards | ||
/// @notice A modified ERC20 incentive implementation that allows claiming of variable token amounts with a spending limit | ||
abstract contract AERC20VariableIncentive is Incentive { | ||
using SafeTransferLib for address; | ||
|
||
/// @notice The address of the ERC20-like token | ||
address public asset; | ||
|
||
/// @notice The spending limit (max total claimable amount) | ||
uint256 public limit; | ||
|
||
/// @notice The total amount claimed so far | ||
uint256 public totalClaimed; | ||
|
||
/// @notice Claim the incentive with variable rewards | ||
/// @param data_ The data payload for the incentive claim `(address recipient, bytes data)` | ||
/// @return True if the incentive was successfully claimed | ||
function claim(bytes calldata data_) external override onlyOwner returns (bool) { | ||
ClaimPayload memory claim_ = abi.decode(data_, (ClaimPayload)); | ||
uint256 signedAmount = abi.decode(claim_.data, (uint256)); | ||
uint256 claimAmount; | ||
if (!_isClaimable(claim_.target)) revert NotClaimable(); | ||
|
||
if (reward == 0) { | ||
claimAmount = signedAmount; | ||
} else { | ||
// NOTE: this is assuming that the signed scalar is in ETH decimal format | ||
claimAmount = reward * signedAmount / 1e18; | ||
} | ||
|
||
if (totalClaimed + claimAmount > limit) revert ClaimFailed(); | ||
|
||
totalClaimed += claimAmount; | ||
asset.safeTransfer(claim_.target, claimAmount); | ||
|
||
emit Claimed(claim_.target, abi.encodePacked(asset, claim_.target, claimAmount)); | ||
return true; | ||
} | ||
|
||
/// @notice Check if an incentive is claimable | ||
/// @param data_ The data payload for the claim check `(address recipient, bytes data)` | ||
/// @return True if the incentive is claimable based on the data payload | ||
function isClaimable(bytes calldata data_) public view override returns (bool) { | ||
ClaimPayload memory claim_ = abi.decode(data_, (ClaimPayload)); | ||
return _isClaimable(claim_.target); | ||
} | ||
|
||
/// @notice Check if an incentive is claimable for a specific recipient | ||
/// @param recipient_ The address of the recipient | ||
/// @return True if the incentive is claimable for the recipient | ||
function _isClaimable(address recipient_) internal view returns (bool) { | ||
return totalClaimed < limit; | ||
} | ||
|
||
/// @inheritdoc Incentive | ||
function reclaim(bytes calldata data_) external override onlyOwner returns (bool) { | ||
ClaimPayload memory claim_ = abi.decode(data_, (ClaimPayload)); | ||
(uint256 amount) = abi.decode(claim_.data, (uint256)); | ||
|
||
limit -= amount; | ||
|
||
// Transfer the tokens back to the intended recipient | ||
asset.safeTransfer(claim_.target, amount); | ||
emit Claimed(claim_.target, abi.encodePacked(asset, claim_.target, amount)); | ||
|
||
return true; | ||
} | ||
|
||
/// @inheritdoc Incentive | ||
function getComponentInterface() public pure virtual override returns (bytes4) { | ||
return type(AERC20VariableIncentive).interfaceId; | ||
} | ||
|
||
/// @inheritdoc Incentive | ||
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { | ||
return interfaceId == type(AERC20VariableIncentive).interfaceId || super.supportsInterface(interfaceId); | ||
} | ||
} |
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
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
Oops, something went wrong.