Protocol Hooks
Protocol Hooks are Magna's offering to extend our escrow smart contract functionality at the protocol-level. This involves implementing our post-claim hook interface, which is a single function.
Last updated
Was this helpful?
Protocol Hooks are Magna's offering to extend our escrow smart contract functionality at the protocol-level. This involves implementing our post-claim hook interface, which is a single function.
Last updated
Was this helpful?
Was this helpful?
pragma solidity ^0.8.24;
import {IERC20} from "@openzeppelin/interfaces/IERC20.sol";
/// @dev the address for denoting whether direct claims are allowed
IPostClaimHandler constant DIRECT_CLAIM_HANDLER = IPostClaimHandler(address(0));
/// @notice Interface for post claim handlers
interface IPostClaimHandler {
/**
* @notice Implementing this method provides a way to claim vesting tokens and execute some custom action afterwards
* @dev Implementors can assume that 'amount' amount of 'claimToken' has already been transferred to this contract address.
* Implementors should:
* 1. check if the calling contract is the vesting contract, and revert otherwise
* 2. revert the transaction, if for any reasons this contract cannot execute the custom actions
* @param claimToken Address of the vesting token.
* @param amount The amount of vesting tokens that were claimed and transferred to this contract address.
* @param originalBeneficiary The address of the user who was the original owner of the vesting tokens at the time the vesting contract was created.
* @param withdrawalAddress The latest owner of the vesting tokens which might be the same as the 'originalBeneficiary' in case no ownership transfer took place.
* @param allocationId The allocation id from which the withdrawn amount was taken.
* @param extraData Any abi encoded extra data that is necessary for the custom action. For example in case of a custom staking action, the user could state his
* staking preference by providing extraData.
*/
function handlePostClaim(
IERC20 claimToken,
uint256 amount,
address originalBeneficiary,
address withdrawalAddress,
string memory allocationId,
bytes memory extraData
) external;
}pragma solidity =0.8.24;
import {IPostClaimHandler} from "../../src/interfaces/IPostClaimHandler.sol";
import {IERC20} from "@openzeppelin/interfaces/IERC20.sol";
import {SafeERC20} from "@openzeppelin/token/ERC20/utils/SafeERC20.sol";
import {Context} from "@openzeppelin/utils/Context.sol";
error InvalidCaller();
contract ClaimAndBurnHandler is IPostClaimHandler, Context {
using SafeERC20 for IERC20;
// some token implementation reject sending tokens to 0x00.00 address, so using 0xcc..cc here which is just as much unrecoverable as 0x00.00
address private constant BURN_ADDRESS = 0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC;
address public immutable vesterContract;
IERC20 public immutable burnToken;
address donationRecipient;
constructor(address vesterContract_, IERC20 burnToken_, address donationRecipient_) {
vesterContract = vesterContract_;
burnToken = burnToken_;
donationRecipient = donationRecipient_;
}
function handlePostClaim(
IERC20 claimToken,
uint256 amount,
address originalBeneficiary,
address withdrawalAddress,
string memory allocationId,
bytes memory extraData
) external {
if (_msgSender() != vesterContract) {
revert InvalidCaller();
}
bool sendDonation = abi.decode(extraData, (bool));
// 1. burn token is transferred from withdrawalAddress instead of originalBeneficiary for safety reasons
// 2. instead of calling the burn function which might not be implemented for all IERC20 tokens, tokens are sent to an unrecoverable BURN_ADDRESS address
burnToken.safeTransferFrom(withdrawalAddress, BURN_ADDRESS, amount);
if (sendDonation) {
uint256 donationAmount = amount / 10;
claimToken.safeTransfer(donationRecipient, donationAmount);
claimToken.safeTransfer(withdrawalAddress, amount - donationAmount);
} else {
claimToken.safeTransfer(withdrawalAddress, amount);
}
}
}