mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #8642 from ethereum/interfaceid
Add support for interfaceId.
This commit is contained in:
commit
a371910674
@ -1,11 +1,11 @@
|
|||||||
### 0.6.7 (unreleased)
|
### 0.6.7 (unreleased)
|
||||||
|
|
||||||
Language Features:
|
Language Features:
|
||||||
|
* Add support for EIP 165 interface identifiers with `type(I).interfaceId`.
|
||||||
|
|
||||||
|
|
||||||
Compiler Features:
|
Compiler Features:
|
||||||
|
|
||||||
|
|
||||||
Bugfixes:
|
Bugfixes:
|
||||||
* SMTChecker: Fix internal error when fixed points are used.
|
* SMTChecker: Fix internal error when fixed points are used.
|
||||||
* SMTChecker: Fix internal error when using array slices.
|
* SMTChecker: Fix internal error when using array slices.
|
||||||
|
@ -319,3 +319,12 @@ available for a contract type ``C``:
|
|||||||
regular calls.
|
regular calls.
|
||||||
The same restrictions as with ``.creationCode`` also apply for this
|
The same restrictions as with ``.creationCode`` also apply for this
|
||||||
property.
|
property.
|
||||||
|
|
||||||
|
In addition to the properties above, the following properties are available
|
||||||
|
for an interface type ``I``:
|
||||||
|
|
||||||
|
``type(I).interfaceId``:
|
||||||
|
A ``bytes4`` value containing the `EIP-165 <https://eips.ethereum.org/EIPS/eip-165>`_
|
||||||
|
interface identifier of the given interface ``I``. This identifier is defined as the ``XOR`` of all
|
||||||
|
function selectors defined within the interface itself - excluding all inherited functions.
|
||||||
|
|
||||||
|
@ -2592,6 +2592,8 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess)
|
|||||||
}
|
}
|
||||||
else if (magicType->kind() == MagicType::Kind::MetaType && memberName == "name")
|
else if (magicType->kind() == MagicType::Kind::MetaType && memberName == "name")
|
||||||
annotation.isPure = true;
|
annotation.isPure = true;
|
||||||
|
else if (magicType->kind() == MagicType::Kind::MetaType && memberName == "interfaceId")
|
||||||
|
annotation.isPure = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
@ -356,6 +356,7 @@ void ViewPureChecker::endVisit(MemberAccess const& _memberAccess)
|
|||||||
{MagicType::Kind::MetaType, "creationCode"},
|
{MagicType::Kind::MetaType, "creationCode"},
|
||||||
{MagicType::Kind::MetaType, "runtimeCode"},
|
{MagicType::Kind::MetaType, "runtimeCode"},
|
||||||
{MagicType::Kind::MetaType, "name"},
|
{MagicType::Kind::MetaType, "name"},
|
||||||
|
{MagicType::Kind::MetaType, "interfaceId"},
|
||||||
};
|
};
|
||||||
set<MagicMember> static const payableMembers{
|
set<MagicMember> static const payableMembers{
|
||||||
{MagicType::Kind::Message, "value"}
|
{MagicType::Kind::Message, "value"}
|
||||||
|
@ -98,9 +98,9 @@ bool ContractDefinition::derivesFrom(ContractDefinition const& _base) const
|
|||||||
return util::contains(annotation().linearizedBaseContracts, &_base);
|
return util::contains(annotation().linearizedBaseContracts, &_base);
|
||||||
}
|
}
|
||||||
|
|
||||||
map<util::FixedHash<4>, FunctionTypePointer> ContractDefinition::interfaceFunctions() const
|
map<util::FixedHash<4>, FunctionTypePointer> ContractDefinition::interfaceFunctions(bool _includeInheritedFunctions) const
|
||||||
{
|
{
|
||||||
auto exportedFunctionList = interfaceFunctionList();
|
auto exportedFunctionList = interfaceFunctionList(_includeInheritedFunctions);
|
||||||
|
|
||||||
map<util::FixedHash<4>, FunctionTypePointer> exportedFunctions;
|
map<util::FixedHash<4>, FunctionTypePointer> exportedFunctions;
|
||||||
for (auto const& it: exportedFunctionList)
|
for (auto const& it: exportedFunctionList)
|
||||||
@ -176,14 +176,16 @@ vector<EventDefinition const*> const& ContractDefinition::interfaceEvents() cons
|
|||||||
return *m_interfaceEvents;
|
return *m_interfaceEvents;
|
||||||
}
|
}
|
||||||
|
|
||||||
vector<pair<util::FixedHash<4>, FunctionTypePointer>> const& ContractDefinition::interfaceFunctionList() const
|
vector<pair<util::FixedHash<4>, FunctionTypePointer>> const& ContractDefinition::interfaceFunctionList(bool _includeInheritedFunctions) const
|
||||||
{
|
{
|
||||||
if (!m_interfaceFunctionList)
|
if (!m_interfaceFunctionList[_includeInheritedFunctions])
|
||||||
{
|
{
|
||||||
set<string> signaturesSeen;
|
set<string> signaturesSeen;
|
||||||
m_interfaceFunctionList = make_unique<vector<pair<util::FixedHash<4>, FunctionTypePointer>>>();
|
m_interfaceFunctionList[_includeInheritedFunctions] = make_unique<vector<pair<util::FixedHash<4>, FunctionTypePointer>>>();
|
||||||
for (ContractDefinition const* contract: annotation().linearizedBaseContracts)
|
for (ContractDefinition const* contract: annotation().linearizedBaseContracts)
|
||||||
{
|
{
|
||||||
|
if (_includeInheritedFunctions == false && contract != this)
|
||||||
|
continue;
|
||||||
vector<FunctionTypePointer> functions;
|
vector<FunctionTypePointer> functions;
|
||||||
for (FunctionDefinition const* f: contract->definedFunctions())
|
for (FunctionDefinition const* f: contract->definedFunctions())
|
||||||
if (f->isPartOfExternalInterface())
|
if (f->isPartOfExternalInterface())
|
||||||
@ -201,12 +203,12 @@ vector<pair<util::FixedHash<4>, FunctionTypePointer>> const& ContractDefinition:
|
|||||||
{
|
{
|
||||||
signaturesSeen.insert(functionSignature);
|
signaturesSeen.insert(functionSignature);
|
||||||
util::FixedHash<4> hash(util::keccak256(functionSignature));
|
util::FixedHash<4> hash(util::keccak256(functionSignature));
|
||||||
m_interfaceFunctionList->emplace_back(hash, fun);
|
m_interfaceFunctionList[_includeInheritedFunctions]->emplace_back(hash, fun);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return *m_interfaceFunctionList;
|
return *m_interfaceFunctionList[_includeInheritedFunctions];
|
||||||
}
|
}
|
||||||
|
|
||||||
TypePointer ContractDefinition::type() const
|
TypePointer ContractDefinition::type() const
|
||||||
|
@ -489,8 +489,8 @@ public:
|
|||||||
|
|
||||||
/// @returns a map of canonical function signatures to FunctionDefinitions
|
/// @returns a map of canonical function signatures to FunctionDefinitions
|
||||||
/// as intended for use by the ABI.
|
/// as intended for use by the ABI.
|
||||||
std::map<util::FixedHash<4>, FunctionTypePointer> interfaceFunctions() const;
|
std::map<util::FixedHash<4>, FunctionTypePointer> interfaceFunctions(bool _includeInheritedFunctions = true) const;
|
||||||
std::vector<std::pair<util::FixedHash<4>, FunctionTypePointer>> const& interfaceFunctionList() const;
|
std::vector<std::pair<util::FixedHash<4>, FunctionTypePointer>> const& interfaceFunctionList(bool _includeInheritedFunctions = true) const;
|
||||||
|
|
||||||
/// @returns a list of all declarations in this contract
|
/// @returns a list of all declarations in this contract
|
||||||
std::vector<Declaration const*> declarations() const { return filteredNodes<Declaration>(m_subNodes); }
|
std::vector<Declaration const*> declarations() const { return filteredNodes<Declaration>(m_subNodes); }
|
||||||
@ -529,7 +529,7 @@ private:
|
|||||||
ContractKind m_contractKind;
|
ContractKind m_contractKind;
|
||||||
bool m_abstract{false};
|
bool m_abstract{false};
|
||||||
|
|
||||||
mutable std::unique_ptr<std::vector<std::pair<util::FixedHash<4>, FunctionTypePointer>>> m_interfaceFunctionList;
|
mutable std::unique_ptr<std::vector<std::pair<util::FixedHash<4>, FunctionTypePointer>>> m_interfaceFunctionList[2];
|
||||||
mutable std::unique_ptr<std::vector<EventDefinition const*>> m_interfaceEvents;
|
mutable std::unique_ptr<std::vector<EventDefinition const*>> m_interfaceEvents;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -3774,7 +3774,9 @@ MemberList::MemberMap MagicType::nativeMembers(ContractDefinition const*) const
|
|||||||
{"name", TypeProvider::stringMemory()},
|
{"name", TypeProvider::stringMemory()},
|
||||||
});
|
});
|
||||||
else
|
else
|
||||||
return {};
|
return MemberList::MemberMap({
|
||||||
|
{"interfaceId", TypeProvider::fixedBytes(4)},
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
solAssert(false, "Unknown kind of magic.");
|
solAssert(false, "Unknown kind of magic.");
|
||||||
|
@ -1582,6 +1582,15 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
|
|||||||
m_context << Instruction::DUP1 << u256(32) << Instruction::ADD;
|
m_context << Instruction::DUP1 << u256(32) << Instruction::ADD;
|
||||||
utils().storeStringData(contract.name());
|
utils().storeStringData(contract.name());
|
||||||
}
|
}
|
||||||
|
else if (member == "interfaceId")
|
||||||
|
{
|
||||||
|
TypePointer arg = dynamic_cast<MagicType const&>(*_memberAccess.expression().annotation().type).typeArgument();
|
||||||
|
ContractDefinition const& contract = dynamic_cast<ContractType const&>(*arg).contractDefinition();
|
||||||
|
uint64_t result{0};
|
||||||
|
for (auto const& function: contract.interfaceFunctionList(false))
|
||||||
|
result ^= fromBigEndian<uint64_t>(function.first.ref());
|
||||||
|
m_context << (u256{result} << (256 - 32));
|
||||||
|
}
|
||||||
else if ((set<string>{"encode", "encodePacked", "encodeWithSelector", "encodeWithSignature", "decode"}).count(member))
|
else if ((set<string>{"encode", "encodePacked", "encodeWithSelector", "encodeWithSignature", "decode"}).count(member))
|
||||||
{
|
{
|
||||||
// no-op
|
// no-op
|
||||||
|
@ -759,6 +759,10 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case FunctionType::Kind::MetaType:
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
solUnimplemented("FunctionKind " + toString(static_cast<int>(functionType->kind())) + " not yet implemented");
|
solUnimplemented("FunctionKind " + toString(static_cast<int>(functionType->kind())) + " not yet implemented");
|
||||||
}
|
}
|
||||||
@ -916,6 +920,15 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess)
|
|||||||
{
|
{
|
||||||
solUnimplementedAssert(false, "");
|
solUnimplementedAssert(false, "");
|
||||||
}
|
}
|
||||||
|
else if (member == "interfaceId")
|
||||||
|
{
|
||||||
|
TypePointer arg = dynamic_cast<MagicType const&>(*_memberAccess.expression().annotation().type).typeArgument();
|
||||||
|
ContractDefinition const& contract = dynamic_cast<ContractType const&>(*arg).contractDefinition();
|
||||||
|
uint64_t result{0};
|
||||||
|
for (auto const& function: contract.interfaceFunctionList(false))
|
||||||
|
result ^= fromBigEndian<uint64_t>(function.first.ref());
|
||||||
|
define(_memberAccess) << formatNumber(u256{result} << (256 - 32)) << "\n";
|
||||||
|
}
|
||||||
else if (set<string>{"encode", "encodePacked", "encodeWithSelector", "encodeWithSignature", "decode"}.count(member))
|
else if (set<string>{"encode", "encodePacked", "encodeWithSelector", "encodeWithSignature", "decode"}.count(member))
|
||||||
{
|
{
|
||||||
// no-op
|
// no-op
|
||||||
|
36
test/libsolidity/semanticTests/interfaceID/homer.sol
Normal file
36
test/libsolidity/semanticTests/interfaceID/homer.sol
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
interface ERC165 {
|
||||||
|
/// @notice Query if a contract implements an interface
|
||||||
|
/// @param interfaceID The interface identifier, as specified in ERC-165
|
||||||
|
/// @dev Interface identification is specified in ERC-165. This function
|
||||||
|
/// uses less than 30,000 gas.
|
||||||
|
/// @return `true` if the contract implements `interfaceID` and
|
||||||
|
/// `interfaceID` is not 0xffffffff, `false` otherwise
|
||||||
|
function supportsInterface(bytes4 interfaceID) external view returns (bool);
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Simpson {
|
||||||
|
function is2D() external returns (bool);
|
||||||
|
function skinColor() external returns (string memory);
|
||||||
|
}
|
||||||
|
|
||||||
|
contract Homer is ERC165, Simpson {
|
||||||
|
function supportsInterface(bytes4 interfaceID) external view override returns (bool) {
|
||||||
|
return
|
||||||
|
interfaceID == this.supportsInterface.selector || // ERC165
|
||||||
|
interfaceID == this.is2D.selector ^ this.skinColor.selector; // Simpson
|
||||||
|
}
|
||||||
|
|
||||||
|
function is2D() external override returns (bool) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function skinColor() external override returns (string memory) {
|
||||||
|
return "yellow";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----
|
||||||
|
// supportsInterface(bytes4): left(0x01ffc9a0) -> false
|
||||||
|
// supportsInterface(bytes4): left(0x01ffc9a7) -> true
|
||||||
|
// supportsInterface(bytes4): left(0x73b6b492) -> true
|
||||||
|
// supportsInterface(bytes4): left(0x70b6b492) -> false
|
@ -0,0 +1,36 @@
|
|||||||
|
interface ERC165 {
|
||||||
|
/// @notice Query if a contract implements an interface
|
||||||
|
/// @param interfaceID The interface identifier, as specified in ERC-165
|
||||||
|
/// @dev Interface identification is specified in ERC-165. This function
|
||||||
|
/// uses less than 30,000 gas.
|
||||||
|
/// @return `true` if the contract implements `interfaceID` and
|
||||||
|
/// `interfaceID` is not 0xffffffff, `false` otherwise
|
||||||
|
function supportsInterface(bytes4 interfaceID) external view returns (bool);
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Simpson {
|
||||||
|
function is2D() external returns (bool);
|
||||||
|
function skinColor() external returns (string memory);
|
||||||
|
}
|
||||||
|
|
||||||
|
contract Homer is ERC165, Simpson {
|
||||||
|
function supportsInterface(bytes4 interfaceID) external view override returns (bool) {
|
||||||
|
return
|
||||||
|
interfaceID == type(ERC165).interfaceId ||
|
||||||
|
interfaceID == type(Simpson).interfaceId;
|
||||||
|
}
|
||||||
|
|
||||||
|
function is2D() external override returns (bool) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function skinColor() external override returns (string memory) {
|
||||||
|
return "yellow";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----
|
||||||
|
// supportsInterface(bytes4): left(0x01ffc9a0) -> false
|
||||||
|
// supportsInterface(bytes4): left(0x01ffc9a7) -> true
|
||||||
|
// supportsInterface(bytes4): left(0x73b6b492) -> true
|
||||||
|
// supportsInterface(bytes4): left(0x70b6b492) -> false
|
@ -0,0 +1,21 @@
|
|||||||
|
interface HelloWorld {
|
||||||
|
function hello() external pure;
|
||||||
|
function world(int) external pure;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface HelloWorldWithEvent {
|
||||||
|
event Event();
|
||||||
|
function hello() external pure;
|
||||||
|
function world(int) external pure;
|
||||||
|
}
|
||||||
|
|
||||||
|
contract Test {
|
||||||
|
bytes4 public hello_world = type(HelloWorld).interfaceId;
|
||||||
|
bytes4 public hello_world_with_event = type(HelloWorldWithEvent).interfaceId;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
|
// ----
|
||||||
|
// hello_world() -> left(0xc6be8b58)
|
||||||
|
// hello_world_with_event() -> left(0xc6be8b58)
|
67
test/libsolidity/semanticTests/interfaceID/interfaces.sol
Normal file
67
test/libsolidity/semanticTests/interfaceID/interfaces.sol
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
interface HelloWorld {
|
||||||
|
function hello() external pure;
|
||||||
|
function world(int) external pure;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface HelloWorldDerived is HelloWorld {
|
||||||
|
function other() external pure;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ERC165 {
|
||||||
|
/// @notice Query if a contract implements an interface
|
||||||
|
/// @param interfaceID The interface identifier, as specified in ERC-165
|
||||||
|
/// @dev Interface identification is specified in ERC-165. This function
|
||||||
|
/// uses less than 30,000 gas.
|
||||||
|
/// @return `true` if the contract implements `interfaceID` and
|
||||||
|
/// `interfaceID` is not 0xffffffff, `false` otherwise
|
||||||
|
function supportsInterface(bytes4 interfaceID) external view returns (bool);
|
||||||
|
}
|
||||||
|
|
||||||
|
contract Test {
|
||||||
|
bytes4 public ghello_world_interfaceId = type(HelloWorld).interfaceId;
|
||||||
|
bytes4 public ERC165_interfaceId = type(ERC165).interfaceId;
|
||||||
|
|
||||||
|
function hello() public pure returns (bytes4 data){
|
||||||
|
HelloWorld i;
|
||||||
|
return i.hello.selector;
|
||||||
|
}
|
||||||
|
|
||||||
|
function world() public pure returns (bytes4 data){
|
||||||
|
HelloWorld i;
|
||||||
|
return i.world.selector;
|
||||||
|
}
|
||||||
|
|
||||||
|
function hello_world() public pure returns (bytes4 data){
|
||||||
|
// HelloWorld i;
|
||||||
|
// return i.hello.selector ^ i.world.selector; // = 0xc6be8b58
|
||||||
|
return 0xc6be8b58;
|
||||||
|
}
|
||||||
|
|
||||||
|
function hello_world_interfaceId() public pure returns (bytes4 data){
|
||||||
|
return type(HelloWorld).interfaceId;
|
||||||
|
}
|
||||||
|
|
||||||
|
function other() public pure returns (bytes4 data){
|
||||||
|
HelloWorldDerived i;
|
||||||
|
return i.other.selector;
|
||||||
|
}
|
||||||
|
|
||||||
|
function hello_world_derived_interfaceId() public pure returns (bytes4 data){
|
||||||
|
return type(HelloWorldDerived).interfaceId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
|
// ----
|
||||||
|
// hello() -> left(0x19ff1d21)
|
||||||
|
// world() -> left(0xdf419679)
|
||||||
|
//
|
||||||
|
// ERC165_interfaceId() -> left(0x01ffc9a7)
|
||||||
|
//
|
||||||
|
// hello_world() -> left(0xc6be8b58)
|
||||||
|
// hello_world_interfaceId() -> left(0xc6be8b58)
|
||||||
|
// ghello_world_interfaceId() -> left(0xc6be8b58)
|
||||||
|
//
|
||||||
|
// other() -> left(0x85295877)
|
||||||
|
// hello_world_derived_interfaceId() -> left(0x85295877)
|
47
test/libsolidity/semanticTests/interfaceID/lisa.sol
Normal file
47
test/libsolidity/semanticTests/interfaceID/lisa.sol
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
interface ERC165 {
|
||||||
|
/// @notice Query if a contract implements an interface
|
||||||
|
/// @param interfaceID The interface identifier, as specified in ERC-165
|
||||||
|
/// @dev Interface identification is specified in ERC-165. This function
|
||||||
|
/// uses less than 30,000 gas.
|
||||||
|
/// @return `true` if the contract implements `interfaceID` and
|
||||||
|
/// `interfaceID` is not 0xffffffff, `false` otherwise
|
||||||
|
function supportsInterface(bytes4 interfaceID) external view returns (bool);
|
||||||
|
}
|
||||||
|
|
||||||
|
contract ERC165MappingImplementation is ERC165 {
|
||||||
|
/// @dev You must not set element 0xffffffff to true
|
||||||
|
mapping(bytes4 => bool) internal supportedInterfaces;
|
||||||
|
|
||||||
|
constructor() internal {
|
||||||
|
supportedInterfaces[this.supportsInterface.selector] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function supportsInterface(bytes4 interfaceID) external view override returns (bool) {
|
||||||
|
return supportedInterfaces[interfaceID];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Simpson {
|
||||||
|
function is2D() external returns (bool);
|
||||||
|
function skinColor() external returns (string memory);
|
||||||
|
}
|
||||||
|
|
||||||
|
contract Lisa is ERC165MappingImplementation, Simpson {
|
||||||
|
constructor() public {
|
||||||
|
supportedInterfaces[this.is2D.selector ^ this.skinColor.selector] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function is2D() external override returns (bool) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function skinColor() external override returns (string memory) {
|
||||||
|
return "yellow";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----
|
||||||
|
// supportsInterface(bytes4): left(0x01ffc9a0) -> false
|
||||||
|
// supportsInterface(bytes4): left(0x01ffc9a7) -> true
|
||||||
|
// supportsInterface(bytes4): left(0x73b6b492) -> true
|
||||||
|
// supportsInterface(bytes4): left(0x70b6b492) -> false
|
@ -0,0 +1,47 @@
|
|||||||
|
interface ERC165 {
|
||||||
|
/// @notice Query if a contract implements an interface
|
||||||
|
/// @param interfaceID The interface identifier, as specified in ERC-165
|
||||||
|
/// @dev Interface identification is specified in ERC-165. This function
|
||||||
|
/// uses less than 30,000 gas.
|
||||||
|
/// @return `true` if the contract implements `interfaceID` and
|
||||||
|
/// `interfaceID` is not 0xffffffff, `false` otherwise
|
||||||
|
function supportsInterface(bytes4 interfaceID) external view returns (bool);
|
||||||
|
}
|
||||||
|
|
||||||
|
contract ERC165MappingImplementation is ERC165 {
|
||||||
|
/// @dev You must not set element 0xffffffff to true
|
||||||
|
mapping(bytes4 => bool) internal supportedInterfaces;
|
||||||
|
|
||||||
|
constructor() internal {
|
||||||
|
supportedInterfaces[this.supportsInterface.selector] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function supportsInterface(bytes4 interfaceID) external view override returns (bool) {
|
||||||
|
return supportedInterfaces[interfaceID];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Simpson {
|
||||||
|
function is2D() external returns (bool);
|
||||||
|
function skinColor() external returns (string memory);
|
||||||
|
}
|
||||||
|
|
||||||
|
contract Lisa is ERC165MappingImplementation, Simpson {
|
||||||
|
constructor() public {
|
||||||
|
supportedInterfaces[type(Simpson).interfaceId] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function is2D() external override returns (bool) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function skinColor() external override returns (string memory) {
|
||||||
|
return "yellow";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----
|
||||||
|
// supportsInterface(bytes4): left(0x01ffc9a0) -> false
|
||||||
|
// supportsInterface(bytes4): left(0x01ffc9a7) -> true
|
||||||
|
// supportsInterface(bytes4): left(0x73b6b492) -> true
|
||||||
|
// supportsInterface(bytes4): left(0x70b6b492) -> false
|
Loading…
Reference in New Issue
Block a user