Skip to content

Commit

Permalink
Add delegate.xyz support
Browse files Browse the repository at this point in the history
  • Loading branch information
boyuanx committed Apr 18, 2024
1 parent d3859e7 commit deb73c3
Show file tree
Hide file tree
Showing 3 changed files with 342 additions and 4 deletions.
19 changes: 17 additions & 2 deletions contracts/core/TokenTableUnlockerV2.sol
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {IERC20} from "@openzeppelin/contracts/interfaces/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {ReentrancyGuardUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol";
import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
import {IDelegateRegistry} from "../libraries/IDelegateRegistry.sol";

// solhint-disable var-name-mixedcase
// solhint-disable no-inline-assembly
Expand Down Expand Up @@ -48,6 +49,8 @@ contract TokenTableUnlockerV2 is
uint256 public constant override BIPS_PRECISION = 10 ** 4; // down to 0.01%
uint256 public constant TOKEN_PRECISION = 10 ** 5;
uint256 public constant DURATION_PRECISION = 10 ** 5;
IDelegateRegistry public constant externalDelegateRegistry =
IDelegateRegistry(0x00000000000000447e69651d841bD8D104Bed493);

function _getTokenTableUnlockerV2Storage()
internal
Expand Down Expand Up @@ -156,9 +159,21 @@ contract TokenTableUnlockerV2 is
) external virtual override nonReentrant {
TokenTableUnlockerV2Storage
storage $ = _getTokenTableUnlockerV2Storage();
if (!$.claimingDelegates.contains(_msgSender()))
revert NotPermissioned();
bool callerIsClaimingDelegate = $.claimingDelegates.contains(
_msgSender()
);
for (uint256 i = 0; i < actualIds.length; i++) {
if (
!callerIsClaimingDelegate &&
!externalDelegateRegistry.checkDelegateForContract(
_msgSender(),
$.futureToken.ownerOf(actualIds[i]),
address(this),
this.delegateClaim.selector
)
) {
revert NotPermissioned();
}
_claim(actualIds[i], address(0), batchId);
}
_callHook(_msgData());
Expand Down
318 changes: 318 additions & 0 deletions contracts/libraries/IDelegateRegistry.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,318 @@
// SPDX-License-Identifier: CC0-1.0
pragma solidity >=0.8.13;

/**
* @title IDelegateRegistry
* @custom:version 2.0
* @custom:author foobar (0xfoobar)
* @notice A standalone immutable registry storing delegated permissions from one address to another
*/
interface IDelegateRegistry {
/// @notice Delegation type, NONE is used when a delegation does not exist or is revoked
enum DelegationType {
NONE,
ALL,
CONTRACT,
ERC721,
ERC20,
ERC1155
}

/// @notice Struct for returning delegations
struct Delegation {
DelegationType type_;
address to;
address from;
bytes32 rights;
address contract_;
uint256 tokenId;
uint256 amount;
}

/// @notice Emitted when an address delegates or revokes rights for their entire wallet
event DelegateAll(
address indexed from,
address indexed to,
bytes32 rights,
bool enable
);

/// @notice Emitted when an address delegates or revokes rights for a contract address
event DelegateContract(
address indexed from,
address indexed to,
address indexed contract_,
bytes32 rights,
bool enable
);

/// @notice Emitted when an address delegates or revokes rights for an ERC721 tokenId
event DelegateERC721(
address indexed from,
address indexed to,
address indexed contract_,
uint256 tokenId,
bytes32 rights,
bool enable
);

/// @notice Emitted when an address delegates or revokes rights for an amount of ERC20 tokens
event DelegateERC20(
address indexed from,
address indexed to,
address indexed contract_,
bytes32 rights,
uint256 amount
);

/// @notice Emitted when an address delegates or revokes rights for an amount of an ERC1155 tokenId
event DelegateERC1155(
address indexed from,
address indexed to,
address indexed contract_,
uint256 tokenId,
bytes32 rights,
uint256 amount
);

/// @notice Thrown if multicall calldata is malformed
error MulticallFailed();

/**
* ----------- WRITE -----------
*/

/**
* @notice Call multiple functions in the current contract and return the data from all of them if they all succeed
* @param data The encoded function data for each of the calls to make to this contract
* @return results The results from each of the calls passed in via data
*/
function multicall(
bytes[] calldata data
) external payable returns (bytes[] memory results);

/**
* @notice Allow the delegate to act on behalf of `msg.sender` for all contracts
* @param to The address to act as delegate
* @param rights Specific subdelegation rights granted to the delegate, pass an empty bytestring to encompass all rights
* @param enable Whether to enable or disable this delegation, true delegates and false revokes
* @return delegationHash The unique identifier of the delegation
*/
function delegateAll(
address to,
bytes32 rights,
bool enable
) external payable returns (bytes32 delegationHash);

/**
* @notice Allow the delegate to act on behalf of `msg.sender` for a specific contract
* @param to The address to act as delegate
* @param contract_ The contract whose rights are being delegated
* @param rights Specific subdelegation rights granted to the delegate, pass an empty bytestring to encompass all rights
* @param enable Whether to enable or disable this delegation, true delegates and false revokes
* @return delegationHash The unique identifier of the delegation
*/
function delegateContract(
address to,
address contract_,
bytes32 rights,
bool enable
) external payable returns (bytes32 delegationHash);

/**
* @notice Allow the delegate to act on behalf of `msg.sender` for a specific ERC721 token
* @param to The address to act as delegate
* @param contract_ The contract whose rights are being delegated
* @param tokenId The token id to delegate
* @param rights Specific subdelegation rights granted to the delegate, pass an empty bytestring to encompass all rights
* @param enable Whether to enable or disable this delegation, true delegates and false revokes
* @return delegationHash The unique identifier of the delegation
*/
function delegateERC721(
address to,
address contract_,
uint256 tokenId,
bytes32 rights,
bool enable
) external payable returns (bytes32 delegationHash);

/**
* @notice Allow the delegate to act on behalf of `msg.sender` for a specific amount of ERC20 tokens
* @dev The actual amount is not encoded in the hash, just the existence of a amount (since it is an upper bound)
* @param to The address to act as delegate
* @param contract_ The address for the fungible token contract
* @param rights Specific subdelegation rights granted to the delegate, pass an empty bytestring to encompass all rights
* @param amount The amount to delegate, > 0 delegates and 0 revokes
* @return delegationHash The unique identifier of the delegation
*/
function delegateERC20(
address to,
address contract_,
bytes32 rights,
uint256 amount
) external payable returns (bytes32 delegationHash);

/**
* @notice Allow the delegate to act on behalf of `msg.sender` for a specific amount of ERC1155 tokens
* @dev The actual amount is not encoded in the hash, just the existence of a amount (since it is an upper bound)
* @param to The address to act as delegate
* @param contract_ The address of the contract that holds the token
* @param tokenId The token id to delegate
* @param rights Specific subdelegation rights granted to the delegate, pass an empty bytestring to encompass all rights
* @param amount The amount of that token id to delegate, > 0 delegates and 0 revokes
* @return delegationHash The unique identifier of the delegation
*/
function delegateERC1155(
address to,
address contract_,
uint256 tokenId,
bytes32 rights,
uint256 amount
) external payable returns (bytes32 delegationHash);

/**
* ----------- CHECKS -----------
*/

/**
* @notice Check if `to` is a delegate of `from` for the entire wallet
* @param to The potential delegate address
* @param from The potential address who delegated rights
* @param rights Specific rights to check for, pass the zero value to ignore subdelegations and check full delegations only
* @return valid Whether delegate is granted to act on the from's behalf
*/
function checkDelegateForAll(
address to,
address from,
bytes32 rights
) external view returns (bool);

/**
* @notice Check if `to` is a delegate of `from` for the specified `contract_` or the entire wallet
* @param to The delegated address to check
* @param contract_ The specific contract address being checked
* @param from The cold wallet who issued the delegation
* @param rights Specific rights to check for, pass the zero value to ignore subdelegations and check full delegations only
* @return valid Whether delegate is granted to act on from's behalf for entire wallet or that specific contract
*/
function checkDelegateForContract(
address to,
address from,
address contract_,
bytes32 rights
) external view returns (bool);

/**
* @notice Check if `to` is a delegate of `from` for the specific `contract` and `tokenId`, the entire `contract_`, or the entire wallet
* @param to The delegated address to check
* @param contract_ The specific contract address being checked
* @param tokenId The token id for the token to delegating
* @param from The wallet that issued the delegation
* @param rights Specific rights to check for, pass the zero value to ignore subdelegations and check full delegations only
* @return valid Whether delegate is granted to act on from's behalf for entire wallet, that contract, or that specific tokenId
*/
function checkDelegateForERC721(
address to,
address from,
address contract_,
uint256 tokenId,
bytes32 rights
) external view returns (bool);

/**
* @notice Returns the amount of ERC20 tokens the delegate is granted rights to act on the behalf of
* @param to The delegated address to check
* @param contract_ The address of the token contract
* @param from The cold wallet who issued the delegation
* @param rights Specific rights to check for, pass the zero value to ignore subdelegations and check full delegations only
* @return balance The delegated balance, which will be 0 if the delegation does not exist
*/
function checkDelegateForERC20(
address to,
address from,
address contract_,
bytes32 rights
) external view returns (uint256);

/**
* @notice Returns the amount of a ERC1155 tokens the delegate is granted rights to act on the behalf of
* @param to The delegated address to check
* @param contract_ The address of the token contract
* @param tokenId The token id to check the delegated amount of
* @param from The cold wallet who issued the delegation
* @param rights Specific rights to check for, pass the zero value to ignore subdelegations and check full delegations only
* @return balance The delegated balance, which will be 0 if the delegation does not exist
*/
function checkDelegateForERC1155(
address to,
address from,
address contract_,
uint256 tokenId,
bytes32 rights
) external view returns (uint256);

/**
* ----------- ENUMERATIONS -----------
*/

/**
* @notice Returns all enabled delegations a given delegate has received
* @param to The address to retrieve delegations for
* @return delegations Array of Delegation structs
*/
function getIncomingDelegations(
address to
) external view returns (Delegation[] memory delegations);

/**
* @notice Returns all enabled delegations an address has given out
* @param from The address to retrieve delegations for
* @return delegations Array of Delegation structs
*/
function getOutgoingDelegations(
address from
) external view returns (Delegation[] memory delegations);

/**
* @notice Returns all hashes associated with enabled delegations an address has received
* @param to The address to retrieve incoming delegation hashes for
* @return delegationHashes Array of delegation hashes
*/
function getIncomingDelegationHashes(
address to
) external view returns (bytes32[] memory delegationHashes);

/**
* @notice Returns all hashes associated with enabled delegations an address has given out
* @param from The address to retrieve outgoing delegation hashes for
* @return delegationHashes Array of delegation hashes
*/
function getOutgoingDelegationHashes(
address from
) external view returns (bytes32[] memory delegationHashes);

/**
* @notice Returns the delegations for a given array of delegation hashes
* @param delegationHashes is an array of hashes that correspond to delegations
* @return delegations Array of Delegation structs, return empty structs for nonexistent or revoked delegations
*/
function getDelegationsFromHashes(
bytes32[] calldata delegationHashes
) external view returns (Delegation[] memory delegations);

/**
* ----------- STORAGE ACCESS -----------
*/

/**
* @notice Allows external contracts to read arbitrary storage slots
*/
function readSlot(bytes32 location) external view returns (bytes32);

/**
* @notice Allows external contracts to read an arbitrary array of storage slots
*/
function readSlots(
bytes32[] calldata locations
) external view returns (bytes32[] memory);
}
9 changes: 7 additions & 2 deletions contracts/proxy/TTUDeployerLiteZkSync.sol
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ contract TTUDeployerLiteZkSync is TTUDeployerLite {

function _deployClonesAndInitialize(
address projectToken,
address existingFutureToken,
bool isTransferable,
bool isCancelable,
bool isHookable,
Expand All @@ -30,8 +31,12 @@ contract TTUDeployerLiteZkSync is TTUDeployerLite {
ITTTrackerTokenV2 trackerToken
)
{
futureToken = new TTFutureTokenV2();
futureToken.initialize(projectToken, isTransferable);
if (existingFutureToken == address(0)) {
futureToken = new TTFutureTokenV2();
futureToken.initialize(projectToken, isTransferable);
} else {
futureToken = ITTFutureTokenV2(existingFutureToken);
}
unlocker = new TokenTableUnlockerV2();
unlocker.initialize(
projectToken,
Expand Down

0 comments on commit deb73c3

Please sign in to comment.