Abstract Testnet

Contract Diff Checker

Contract Name:
ExclusiveDelegateResolver

Contract Source Code:

// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;

import {IDelegateRegistry} from "./interfaces/IDelegateRegistry.sol";

/**
 * @title ExclusiveDelegateResolver
 * @author 0xQuit
 * @notice A contract to resolve a single canonical delegated owner for a given ERC721 token
 * @dev This contract is designed to be used in conjunction with a delegate registry to resolve the most specific
 * delegation that matches the rights, with specificity being determined by delegation type in order of ERC721 >
 * CONTRACT > ALL. ERC20 and ERC1155 are not supported. If multiple delegations of the same specificity match the rights,
 * the most recent one is respected. If no delegation matches the rights, global delegations (bytes24(0) are considered,
 * but MUST have an expiration greater than 0 to avoid conflicts with pre-existing delegations.
 * If no delegation matches the rights and there are no empty delegations, the owner is returned.
 * Expirations are supported by extracting a uint40 from the final 40 bits of a given delegation's rights value.
 * If the expiration is past, the delegation is not considered to match the request.
 */
contract ExclusiveDelegateResolver {
    /// @dev The address of the Delegate Registry contract
    address public immutable DELEGATE_REGISTRY;

    /// @dev The rights value for a global delegation. These are considered only if no delegation by rights matches the request.
    bytes24 public constant GLOBAL_DELEGATION = bytes24(0);

    constructor(address delegateRegistry) {
        DELEGATE_REGISTRY = delegateRegistry;
    }

    /**
     * @notice Gets an exclusive wallet delegation, resolved through delegatexyz if possible
     * @param vault The vault address
     * @param rights The rights to check
     * @return owner The vault wallet address or delegated wallet if one exists
     * @notice returns the most recent delegation that matches the rights for an entire wallet delegation
     * (type ALL) if multiple delegations of the same specificity match the rights, the most recent one is respected.
     * If no delegation matches the rights, global delegations (bytes24(0) are considered,
     * but MUST have an expiration greater than 0 to avoid conflicts with pre-existing delegations.
     * If no delegation matches the rights and there are no empty delegations, the owner is returned.
     * Expirations are supported by extracting a uint40 from the final 40 bits of a given delegation's rights value.
     * If the expiration is past, the delegation is not considered to match the request.
     */
    function exclusiveWalletByRights(address vault, bytes24 rights) public view returns (address) {
        IDelegateRegistry.Delegation[] memory delegations =
            IDelegateRegistry(DELEGATE_REGISTRY).getOutgoingDelegations(vault);

        IDelegateRegistry.Delegation memory delegationToReturn;

        for (uint256 i = delegations.length; i > 0;) {
            unchecked {
                --i;
            }
            IDelegateRegistry.Delegation memory delegation = delegations[i];

            if (_delegationMatchesRequest(delegation, rights)) {
                if (_delegationOutranksCurrent(delegationToReturn, delegation)) {
                    // re-check rights here to ensure global delegation does not get early returned
                    if (
                        delegation.type_ == IDelegateRegistry.DelegationType.ALL && bytes24(delegation.rights) == rights
                    ) {
                        return delegation.to;
                    }
                    delegationToReturn = delegation;
                }
            }
        }

        return delegationToReturn.to == address(0) ? vault : delegationToReturn.to;
    }

    /**
     * @notice Gets all wallets that have delegated to a given wallet
     * @param wallet The wallet to check
     * @param rights The rights to check
     * @return wallets The wallets that have delegated to the given wallet
     * @notice returns all wallets that have delegated to the given wallet, filtered by the rights
     * if multiple delegations of the same specificity match the rights, the most recent one is respected.
     * If no delegation matches the rights, global delegations (bytes24(0)) are considered,
     * but MUST have an expiration greater than 0 to avoid conflicts with pre-existing delegations.
     * Expirations are supported by extracting a uint40 from the final 40 bits of a given delegation's rights value.
     * If the expiration is past, the delegation is not considered to match the request.
     */
    function delegatedWalletsByRights(address wallet, bytes24 rights) external view returns (address[] memory) {
        IDelegateRegistry.Delegation[] memory delegations =
            IDelegateRegistry(DELEGATE_REGISTRY).getIncomingDelegations(wallet);

        uint256 matchesCount = 0;
        bool[] memory matches = new bool[](delegations.length);

        for (uint256 i; i < delegations.length; ++i) {
            IDelegateRegistry.Delegation memory delegation = delegations[i];

            if (_delegationMatchesRequest(delegation, rights)) {
                if (exclusiveWalletByRights(delegation.from, rights) == wallet) {
                    matches[i] = true;
                    matchesCount++;
                }
            }
        }

        address[] memory matchesArray = new address[](matchesCount);
        uint256 matchesIndex = 0;
        // filter to the delegated wallets that match the request
        for (uint256 i; i < delegations.length; ++i) {
            if (matches[i]) {
                matchesArray[matchesIndex] = delegations[i].from;
                matchesIndex++;
            }
        }
        return matchesArray;
    }

    /**
     * @notice Gets the owner of an ERC721 token, resolved through delegatexyz if possible
     * @param contractAddress The ERC721 contract address
     * @param tokenId The token ID to check
     * @return owner The owner address or delegated owner if one exists
     * @notice returns the most specific delegation that matches the rights, with specificity being determined
     * by delegation type in order of ERC721 > CONTRACT > ALL. ERC20 and ERC1155 are not supported
     * if multiple delegations of the same specificity match the rights, the most recent one is respected.
     * If no delegation matches the rights, global delegations (bytes24(0) are considered,
     * but MUST have an expiration greater than 0 to avoid conflicts with pre-existing delegations.
     * If no delegation matches the rights and there are no empty delegations, the owner is returned.
     * Expirations are supported by extracting a uint40 from the final 40 bits of a given delegation's rights value.
     * If the expiration is past, the delegation is not considered to match the request.
     */
    function exclusiveOwnerByRights(address contractAddress, uint256 tokenId, bytes24 rights)
        external
        view
        returns (address owner)
    {
        owner = _getOwner(contractAddress, tokenId);

        IDelegateRegistry.Delegation[] memory delegations =
            IDelegateRegistry(DELEGATE_REGISTRY).getOutgoingDelegations(owner);

        IDelegateRegistry.Delegation memory delegationToReturn;

        for (uint256 i = delegations.length; i > 0;) {
            unchecked {
                --i;
            }
            IDelegateRegistry.Delegation memory delegation = delegations[i];

            if (_delegationMatchesRequest(delegation, contractAddress, tokenId, rights)) {
                if (_delegationOutranksCurrent(delegationToReturn, delegation)) {
                    // re-check rights here to ensure global ERC721 type delegations do not get early returned
                    if (
                        delegation.type_ == IDelegateRegistry.DelegationType.ERC721
                            && bytes24(delegation.rights) == rights
                    ) {
                        return delegation.to;
                    }

                    delegationToReturn = delegation;
                }
            }
        }

        return delegationToReturn.to == address(0) ? owner : delegationToReturn.to;
    }

    /**
     * @notice Decodes a rights bytes32 value into its identifier and expiration
     * @param rights The rights bytes32 value
     * @return rightsIdentifier The rights identifier
     * @return expiration The expiration timestamp
     */
    function decodeRightsExpiration(bytes32 rights) public pure returns (bytes24, uint40) {
        bytes24 rightsIdentifier = bytes24(rights);
        uint40 expiration = uint40(uint256(rights));

        return (rightsIdentifier, expiration);
    }

    /**
     * @notice Convenience function to generate a rights bytes32 rights value with an expiration
     * @param rightsIdentifier The rights identifier
     * @param expiration The expiration timestamp
     * @return rights The rights bytes32 value
     */
    function generateRightsWithExpiration(bytes24 rightsIdentifier, uint40 expiration)
        external
        pure
        returns (bytes32)
    {
        uint256 rights = uint256(uint192(rightsIdentifier)) << 64;
        return bytes32(rights | uint256(expiration));
    }

    function _delegationMatchesRequest(IDelegateRegistry.Delegation memory delegation, bytes24 rights)
        internal
        view
        returns (bool)
    {
        (bytes24 rightsIdentifier, uint40 expiration) = decodeRightsExpiration(delegation.rights);

        if (block.timestamp > expiration) {
            return false;
        } else if (rightsIdentifier != rights && rightsIdentifier != GLOBAL_DELEGATION) {
            return false;
        } else if (delegation.type_ == IDelegateRegistry.DelegationType.ALL) {
            return true;
        } else {
            return false;
        }
    }

    function _delegationMatchesRequest(
        IDelegateRegistry.Delegation memory delegation,
        address contractAddress,
        uint256 tokenId,
        bytes24 rights
    ) internal view returns (bool) {
        // Extract rights identifier (remaining 192 bits)
        (bytes24 rightsIdentifier, uint40 expiration) = decodeRightsExpiration(delegation.rights);

        if (block.timestamp > expiration) {
            return false;
        } else if (rightsIdentifier != rights && rightsIdentifier != GLOBAL_DELEGATION) {
            return false;
        } else if (delegation.type_ == IDelegateRegistry.DelegationType.ALL) {
            return true;
        } else if (delegation.type_ == IDelegateRegistry.DelegationType.CONTRACT) {
            return delegation.contract_ == contractAddress;
        } else if (delegation.type_ == IDelegateRegistry.DelegationType.ERC721) {
            return delegation.contract_ == contractAddress && delegation.tokenId == tokenId;
        } else {
            return false;
        }
    }

    function _delegationOutranksCurrent(
        IDelegateRegistry.Delegation memory currentDelegation,
        IDelegateRegistry.Delegation memory newDelegation
    ) internal pure returns (bool) {
        bytes24 currentRightsIdentifier = bytes24(currentDelegation.rights);
        bytes24 newRightsIdentifier = bytes24(newDelegation.rights);

        if (currentRightsIdentifier == newRightsIdentifier) {
            return newDelegation.type_ > currentDelegation.type_;
        } else if (currentRightsIdentifier == GLOBAL_DELEGATION) {
            return true;
        } else {
            return false;
        }
    }

    function _getOwner(address contractAddress, uint256 tokenId) internal view returns (address owner) {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            mstore(m, 0x6352211e00000000000000000000000000000000000000000000000000000000)
            mstore(add(m, 0x04), tokenId)
            let success := staticcall(gas(), contractAddress, m, 0x24, m, 0x20)
            if iszero(success) {
                mstore(0x00, 0x3204506f) // CallFailed()
                revert(0x1c, 0x04)
            }
            owner := mload(m)
        }
    }
}

// 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);
}

Please enter a contract address above to load the contract details and source code.

Context size (optional):