// SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.13; contract IDelegation { string public constant name = "ID management contract"; struct DelegateAdd { uint32 nonce; address delegateAddr; Role role; Permission permission; uint expiryBlock; } struct DelegateRemove { uint32 nonce; address delegateAddr; } /** * @dev Enumerated Permissions * Roles have different permissions * APPEND ONLY */ enum Permission { NOz, ANNOUNCE, OWNERSHIP_TRANSFER, DELEGATE_ADD, DELEGATE_REMOVE } /** * @dev Enumerated Roles * Roles have different permissions * APPEND ONLY */ enum Role { NONE, OWNER, ANNOUNCER } /// @notice The EIP-712 typehash for the contract's domain bytes32 public constant DOMAIN_TYPEHASH = keccak256("EIP712Domain(string name,uint256 chainId,address verifyingContract)"); /// @notice The EIP-712 typehash for the delegation struct used by the contract bytes32 public constant DELEGATION_TYPEHASH = keccak256("Delegation(address delegatee,uint256 nonce,uint256 expiry)"); mapping (address => address) public delegates; mapping (address => uint32) public nonces; mapping (address => DelegateAdd) public delegateAdd; mapping (address => DelegateRemove) public delegateRemove; event AddDelegate(address indexed delegate, Role role, Permission permission, expiryBlock); event RemoveDelegate(address indexed delegate); /** * @dev Add or change permissions for delegate */ function delegate(address newDelegate, Role role, Permission permission,uint expiryBlock) external { return _delegate(msg.sender, newDelegate, Role role, Permission permission, expiryBlock); } function _delegate(address delegator, address delegatee, Role role, Permission permission, uint expiryBlock) internal { delegateAdd[delegator].addr = delegatee; delegateAdd[delegator].nonce = nonces[delegatee]; delegateAdd[delegator].role = role; delegateAdd[delegator].permission = permission; delegateAdd[delegator].expiryBlock = expiryBlock; event AddDelegate(address indexed delegatee, Role role, Permission permission, expiryBlock); } /** * @dev Add or change permissions for delegate by EIP-712 signature */ function delegateBySig( uint8 v, bytes32 r, bytes32 s, DelegateAdd calldata change ) external { bytes32 domainSeparator = keccak256(abi.encode(DOMAIN_TYPEHASH, keccak256(bytes(name)), getChainId(), address(this))); bytes32 structHash = keccak256(abi.encode(DELEGATION_TYPEHASH, change.delegateAddr, change.nonce, change.expiryBlock, change.role, change.permission)); bytes32 digest = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); address signatory = ecrecover(digest, v, r, s); require(signatory != address(0), "delegateBySig: invalid signature"); require(nonce == nonces[signatory]++, "delegateBySig: invalid nonce"); require(block.number <= expiryBlock, "delegateBySig: signature expired"); return _delegate(signatory, change.delegateAddr, change.role, change.permission, change.expiryBlock); } /** * @dev Remove Delegate */ function delegateRemove(address address_) external { return _delegateRemove(msg.sender, address_); }; function _delegateRemove(address delegator, address address_) internal { delegateRemove[delegator].addr = address_; delegateRemove[delegator].nonce = nonces[address_]; event RemoveDelegate(address indexed address_); }; /** * @dev Remove delegate by EIP-712 signature */ function delegateRemoveBySig( Permission permission, uint256 expiryBlock, uint8 v, bytes32 r, bytes32 s, DelegateRemove calldata change ) external { bytes32 domainSeparator = keccak256(abi.encode(DOMAIN_TYPEHASH, keccak256(bytes(name)), getChainId(), address(this))); bytes32 structHash = keccak256(abi.encode(DELEGATION_TYPEHASH, change.delegateAddr, change.nonce, expiryBlock)); bytes32 digest = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); address signatory = ecrecover(digest, v, r, s); require(signatory != address(0), "delegateBySig: invalid signature"); require(nonce == nonces[signatory]++, "delegateBySig: invalid nonce"); require(block.number <= expiryBlock, "delegateBySig: signature expired"); return _delegateRemove(signatory, delegatee); }; /** * @dev Checks to see if address_ is authorized with the given permission */ function isAuthorizedTo( address address_, Permission permission, uint256 blockNumber ) external view returns (bool) { return delegateAdd[address].permission == permission && delegateAdd[address].expiryBlock >= block.number; } function getNonceForDelegate(address addr) external view returns (uint32) { return nonces[addr]; } function getChainId() internal view returns (uint) { uint256 chainId; assembly { chainId := chainid() } return chainId; } }
0.4.18