Skip to content

Commit

Permalink
Added Enforcers ERC721 and ERC115 Balance Greater Than (#452)
Browse files Browse the repository at this point in the history
  • Loading branch information
hanzel98 committed Sep 30, 2024
1 parent 44390ac commit 07acb01
Show file tree
Hide file tree
Showing 7 changed files with 845 additions and 3 deletions.
141 changes: 141 additions & 0 deletions src/enforcers/ERC1155BalanceGteEnforcer.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
// SPDX-License-Identifier: MIT AND Apache-2.0
pragma solidity 0.8.23;

import { IERC1155 } from "@openzeppelin/contracts/token/ERC1155/IERC1155.sol";

import { CaveatEnforcer } from "./CaveatEnforcer.sol";
import { ModeCode } from "../utils/Types.sol";

/**
* @title ERC1155BalanceGteEnforcer
* @dev This contract enforces that the ERC1155 token balance of a recipient for a specific token ID
* has increased by at least the specified amount after the execution, measured between the `beforeHook` and `afterHook` calls.
*/
contract ERC1155BalanceGteEnforcer is CaveatEnforcer {
////////////////////////////// State //////////////////////////////

mapping(bytes32 hashKey => uint256 balance) public balanceCache;
mapping(bytes32 hashKey => bool lock) public isLocked;

////////////////////////////// External Methods //////////////////////////////

/**
* @notice Generates the key that identifies the run. Produced by the hash of the values used.
* @param _caller Address of the sender calling the enforcer.
* @param _token ERC1155 token being compared in the beforeHook and afterHook.
* @param _recipient The address of the recipient of the token.
* @param _tokenId The ID of the ERC1155 token.
* @param _delegationHash The hash of the delegation.
* @return The hash to be used as key of the mapping.
*/
function getHashKey(
address _caller,
address _token,
address _recipient,
uint256 _tokenId,
bytes32 _delegationHash
)
external
pure
returns (bytes32)
{
return _getHashKey(_caller, _token, _recipient, _tokenId, _delegationHash);
}

////////////////////////////// Public Methods //////////////////////////////

/**
* @notice This function caches the recipient's ERC1155 token balance before the delegation is executed.
* @param _terms 104 bytes where:
* - first 20 bytes: address of the ERC1155 token,
* - next 20 bytes: address of the recipient,
* - next 32 bytes: token ID,
* - next 32 bytes: amount the balance should increase by.
* @param _delegationHash The hash of the delegation.
*/
function beforeHook(
bytes calldata _terms,
bytes calldata,
ModeCode,
bytes calldata,
bytes32 _delegationHash,
address,
address
)
public
override
{
(address token_, address recipient_, uint256 tokenId_,) = getTermsInfo(_terms);
bytes32 hashKey_ = _getHashKey(msg.sender, token_, recipient_, tokenId_, _delegationHash);
require(!isLocked[hashKey_], "ERC1155BalanceGteEnforcer:enforcer-is-locked");
isLocked[hashKey_] = true;
uint256 balance_ = IERC1155(token_).balanceOf(recipient_, tokenId_);
balanceCache[hashKey_] = balance_;
}

/**
* @notice This function enforces that the recipient's ERC1155 token balance has increased by at least the amount provided.
* @param _terms 104 bytes where:
* - first 20 bytes: address of the ERC1155 token,
* - next 20 bytes: address of the recipient,
* - next 32 bytes: token ID,
* - next 32 bytes: amount the balance should increase by.
*/
function afterHook(
bytes calldata _terms,
bytes calldata,
ModeCode,
bytes calldata,
bytes32 _delegationHash,
address,
address
)
public
override
{
(address token_, address recipient_, uint256 tokenId_, uint256 amount_) = getTermsInfo(_terms);
bytes32 hashKey_ = _getHashKey(msg.sender, token_, recipient_, tokenId_, _delegationHash);
delete isLocked[hashKey_];
uint256 balance_ = IERC1155(token_).balanceOf(recipient_, tokenId_);
require(balance_ >= balanceCache[hashKey_] + amount_, "ERC1155BalanceGteEnforcer:balance-not-gt");
}

/**
* @notice Decodes the terms used in this CaveatEnforcer.
* @param _terms Encoded data that is used during the execution hooks.
* @return token_ The address of the ERC1155 token.
* @return recipient_ The address of the recipient of the token.
* @return tokenId_ The ID of the ERC1155 token.
* @return amount_ The amount the balance should increase by.
*/
function getTermsInfo(bytes calldata _terms)
public
pure
returns (address token_, address recipient_, uint256 tokenId_, uint256 amount_)
{
require(_terms.length == 104, "ERC1155BalanceGteEnforcer:invalid-terms-length");
token_ = address(bytes20(_terms[:20]));
recipient_ = address(bytes20(_terms[20:40]));
tokenId_ = uint256(bytes32(_terms[40:72]));
amount_ = uint256(bytes32(_terms[72:]));
}

////////////////////////////// Internal Methods //////////////////////////////

/**
* @notice Generates the key that identifies the run. Produced by hashing the provided values.
*/
function _getHashKey(
address _caller,
address _token,
address _recipient,
uint256 _tokenId,
bytes32 _delegationHash
)
private
pure
returns (bytes32)
{
return keccak256(abi.encode(_caller, _token, _recipient, _tokenId, _delegationHash));
}
}
130 changes: 130 additions & 0 deletions src/enforcers/ERC721BalanceGteEnforcer.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
// SPDX-License-Identifier: MIT AND Apache-2.0
pragma solidity 0.8.23;

import { IERC721 } from "@openzeppelin/contracts/token/ERC721/IERC721.sol";

import { CaveatEnforcer } from "./CaveatEnforcer.sol";
import { ModeCode } from "../utils/Types.sol";

/**
* @title ERC721BalanceGteEnforcer
* @dev This contract enforces that the ERC721 token balance of a recipient has increased by at least the specified amount
* after the execution, measured between the `beforeHook` and `afterHook` calls, regardless of what the execution is.
*/
contract ERC721BalanceGteEnforcer is CaveatEnforcer {
////////////////////////////// State //////////////////////////////

mapping(bytes32 hashKey => uint256 balance) public balanceCache;
mapping(bytes32 hashKey => bool lock) public isLocked;

////////////////////////////// External Methods //////////////////////////////

/**
* @notice Generates the key that identifies the run. Produced by the hash of the values used.
* @param _caller Address of the sender calling the enforcer.
* @param _token ERC721 token being compared in the beforeHook and afterHook.
* @param _recipient The address of the recipient of the token.
* @param _delegationHash The hash of the delegation.
* @return The hash to be used as key of the mapping.
*/
function getHashKey(
address _caller,
address _token,
address _recipient,
bytes32 _delegationHash
)
external
pure
returns (bytes32)
{
return _getHashKey(_caller, _token, _recipient, _delegationHash);
}

////////////////////////////// Public Methods //////////////////////////////

/**
* @notice This function caches the delegator's ERC721 token balance before the delegation is executed.
* @param _terms 72 bytes where:
* - first 20 bytes: address of the ERC721 token,
* - next 20 bytes: address of the recipient,
* - next 32 bytes: amount the balance should increase by.
* @param _delegationHash The hash of the delegation.
*/
function beforeHook(
bytes calldata _terms,
bytes calldata,
ModeCode,
bytes calldata,
bytes32 _delegationHash,
address,
address
)
public
override
{
(address token_, address recipient_,) = getTermsInfo(_terms);
bytes32 hashKey_ = _getHashKey(msg.sender, token_, recipient_, _delegationHash);
require(!isLocked[hashKey_], "ERC721BalanceGteEnforcer:enforcer-is-locked");
isLocked[hashKey_] = true;
uint256 balance_ = IERC721(token_).balanceOf(recipient_);
balanceCache[hashKey_] = balance_;
}

/**
* @notice This function enforces that the delegator's ERC721 token balance has increased by at least the amount provided.
* @param _terms 72 bytes where:
* - first 20 bytes: address of the ERC721 token,
* - next 20 bytes: address of the recipient,
* - next 32 bytes: amount the balance should increase by.
*/
function afterHook(
bytes calldata _terms,
bytes calldata,
ModeCode,
bytes calldata,
bytes32 _delegationHash,
address,
address
)
public
override
{
(address token_, address recipient_, uint256 amount_) = getTermsInfo(_terms);
bytes32 hashKey_ = _getHashKey(msg.sender, token_, recipient_, _delegationHash);
delete isLocked[hashKey_];
uint256 balance_ = IERC721(token_).balanceOf(recipient_);
require(balance_ >= balanceCache[hashKey_] + amount_, "ERC721BalanceGteEnforcer:balance-not-gt");
}

/**
* @notice Decodes the terms used in this CaveatEnforcer.
* @param _terms Encoded data that is used during the execution hooks.
* @return token_ The address of the ERC721 token.
* @return recipient_ The address of the recipient of the token.
* @return amount_ The amount the balance should increase by.
*/
function getTermsInfo(bytes calldata _terms) public pure returns (address token_, address recipient_, uint256 amount_) {
require(_terms.length == 72, "ERC721BalanceGteEnforcer:invalid-terms-length");
token_ = address(bytes20(_terms[:20]));
recipient_ = address(bytes20(_terms[20:40]));
amount_ = uint256(bytes32(_terms[40:]));
}

////////////////////////////// Internal Methods //////////////////////////////

/**
* @notice Generates the key that identifies the run. Produced by the hash of the values used.
*/
function _getHashKey(
address _caller,
address _token,
address _recipient,
bytes32 _delegationHash
)
private
pure
returns (bytes32)
{
return keccak256(abi.encode(_caller, _token, _recipient, _delegationHash));
}
}
Loading

0 comments on commit 07acb01

Please sign in to comment.