diff --git a/contracts/extension/Ownable.sol b/contracts/extension/Ownable.sol index bdfb98d47..0a46ea6b8 100644 --- a/contracts/extension/Ownable.sol +++ b/contracts/extension/Ownable.sol @@ -46,7 +46,7 @@ abstract contract Ownable is IOwnable { } /// @dev Lets a contract admin set a new owner for the contract. The new owner must be a contract admin. - function _setupOwner(address _newOwner) internal { + function _setupOwner(address _newOwner) internal virtual { address _prevOwner = _owner; _owner = _newOwner; diff --git a/contracts/prebuilts/unaudited/airdrop/Airdrop.sol b/contracts/prebuilts/unaudited/airdrop/Airdrop.sol index 9fba1dae6..117eee5ec 100644 --- a/contracts/prebuilts/unaudited/airdrop/Airdrop.sol +++ b/contracts/prebuilts/unaudited/airdrop/Airdrop.sol @@ -24,6 +24,10 @@ import "../../../eip/interface/IERC20.sol"; import "../../../eip/interface/IERC721.sol"; import "../../../eip/interface/IERC1155.sol"; +interface IEIP1271 { + function isValidSignature(bytes32 hash, bytes memory signature) external view returns (bytes4); +} + contract Airdrop is EIP712, Initializable, Ownable { using ECDSA for bytes32; @@ -40,6 +44,9 @@ contract Airdrop is EIP712, Initializable, Ownable { /// @dev Mapping from request UID => whether the request is processed. mapping(bytes32 => bool) private processed; + /// @dev Flag to indicate if signature verification should be EIP-1271 (based on whether owner is a contract) + bool private check1271; + struct AirdropContentERC20 { address recipient; uint256 amount; @@ -98,6 +105,8 @@ contract Airdrop is EIP712, Initializable, Ownable { "AirdropRequestERC1155(bytes32 uid,address tokenAddress,uint256 expirationTimestamp,AirdropContentERC1155[] contents)AirdropContentERC1155(address recipient,uint256 tokenId,uint256 amount)" ); + bytes4 private constant EIP1271_MAGIC_VALUE = 0x1626ba7e; + address private constant NATIVE_TOKEN_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; /*/////////////////////////////////////////////////////////////// @@ -406,6 +415,12 @@ contract Airdrop is EIP712, Initializable, Ownable { tokenMerkleRoot[_token] = _tokenMerkleRoot; } + function _setupOwner(address _newOwner) internal override { + super._setupOwner(_newOwner); + + check1271 = _isContract(_newOwner) ? true : false; + } + /*/////////////////////////////////////////////////////////////// Miscellaneous //////////////////////////////////////////////////////////////*/ @@ -426,6 +441,15 @@ contract Airdrop is EIP712, Initializable, Ownable { return false; } + function _isContract(address account) internal view returns (bool) { + if (account == address(0)) return false; + bytes32 codehash; + assembly { + codehash := extcodehash(account) + } + return (codehash != 0x0 && codehash != 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470); + } + function _canSetOwner() internal view virtual override returns (bool) { return msg.sender == owner(); } @@ -485,6 +509,11 @@ contract Airdrop is EIP712, Initializable, Ownable { ); bytes32 digest = _hashTypedData(structHash); + + if (check1271) { + return IEIP1271(owner()).isValidSignature(digest, signature) == EIP1271_MAGIC_VALUE; + } + address recovered = digest.recover(signature); return recovered == owner(); } @@ -499,6 +528,11 @@ contract Airdrop is EIP712, Initializable, Ownable { ); bytes32 digest = _hashTypedData(structHash); + + if (check1271) { + return IEIP1271(owner()).isValidSignature(digest, signature) == EIP1271_MAGIC_VALUE; + } + address recovered = digest.recover(signature); return recovered == owner(); } @@ -513,6 +547,11 @@ contract Airdrop is EIP712, Initializable, Ownable { ); bytes32 digest = _hashTypedData(structHash); + + if (check1271) { + return IEIP1271(owner()).isValidSignature(digest, signature) == EIP1271_MAGIC_VALUE; + } + address recovered = digest.recover(signature); return recovered == owner(); }