mirror of
https://github.com/status-im/EIPs.git
synced 2025-02-24 04:38:29 +00:00
Update caching contract
This commit is contained in:
parent
4b7b91691a
commit
6e4394d6d1
@ -86,7 +86,7 @@ Implementation note, there are several logical ways to implement this function.
|
||||
|
||||
### How to Detect if a Contract Implements ERC-165
|
||||
|
||||
1. The source contact makes a `CALL` to the destination address with input data: `0x01ffc9a701ffc9a7` value: 0 and gas 30000. This corresponds to `contract.supportsInterface("0x01ffc9a7")`.
|
||||
1. The source contact makes a static `CALL` to the destination address with input data: `0x01ffc9a701ffc9a7` value: 0 and gas 30000. This corresponds to `contract.supportsInterface("0x01ffc9a7")`.
|
||||
2. If the call fails or return false, the destination contract does not implement ERC-165.
|
||||
3. If the call returns true, a second call is made with input data `0x01ffc9a7ffffffff`.
|
||||
4. If the second call fails or returns true, the destination contract does not implement ERC-165.
|
||||
@ -110,36 +110,96 @@ Also [the ENS](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-137.md) alr
|
||||
|
||||
## Test Cases
|
||||
|
||||
XXXXXXXX HELP NEEDED XXXXXXXXX
|
||||
Following is a caching contract that detects which interfaces other contracts implement. From @fulldecent and @jbaylina.
|
||||
|
||||
```solidity
|
||||
pragma solidity ^0.4.19;
|
||||
|
||||
contract ERC165Cache {
|
||||
bytes4 constant InvalidID = 0xffffffff;
|
||||
bytes4 constant ERC165ID = 0x01ffc9a7;
|
||||
|
||||
enum ImplStatus { Unknown, No, Yes }
|
||||
mapping (address => mapping (bytes4 => ImplStatus)) cache;
|
||||
|
||||
// Return value from cache if available
|
||||
function interfaceSupported(address _contract, bytes4 _interfaceId) external returns (bool) {
|
||||
ImplStatus status = cache[_contract][_interfaceId];
|
||||
if (status == ImplStatus.Unknown) {
|
||||
return checkInterfaceSupported(_contract, _interfaceId);
|
||||
}
|
||||
return status == ImplStatus.Yes;
|
||||
}
|
||||
|
||||
// Repull result into cache
|
||||
function checkInterfaceSupported(address _contract, bytes4 _interfaceId) public returns (bool) {
|
||||
ImplStatus status = determineInterfaceImplementationStatus(_contract, _interfaceId);
|
||||
cache[_contract][_interfaceId] = status;
|
||||
return status == ImplStatus.Yes;
|
||||
}
|
||||
|
||||
function determineInterfaceImplementationStatus(address _contract, bytes4 _interfaceId) internal view returns (ImplStatus) {
|
||||
if (noThrowCall(_contract, InvalidID)) return ImplStatus.No;
|
||||
if (!noThrowCall(_contract, ERC165ID)) return ImplStatus.No;
|
||||
if (noThrowCall(_contract, _interfaceId)) return ImplStatus.Yes;
|
||||
return ImplStatus.No;
|
||||
}
|
||||
|
||||
// Update this after the Metropolis hard fork to use staticcall!
|
||||
function noThrowCall(address _contract, bytes4 _interfaceId) internal view returns (bool result) {
|
||||
bytes4 erc165ID = ERC165ID;
|
||||
assembly {
|
||||
let x := mload(0x40) // Find empty storage location using "free memory pointer"
|
||||
mstore(x, erc165ID) // Place signature at begining of empty storage
|
||||
mstore(add(x, 0x04), _interfaceId) // Place first argument directly next to signature
|
||||
call(30000, // 30k gas
|
||||
_contract, // To addr
|
||||
0, // No value
|
||||
x, // Inputs are stored at location x
|
||||
0x8, // Inputs are 8 byes long
|
||||
x, // Store output over input (saves space)
|
||||
0x20) // Outputs are 32 bytes long
|
||||
pop // Discard call return value
|
||||
result := mload(x) // Load the result
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Implementation
|
||||
|
||||
This approach uses a `view` function implementation of `supportsInterface`. The execution cost is 478 gas for any input. But contract initialization requires storing each interface (`SSTORE` is 20,000 gas).
|
||||
This approach uses a `view` function implementation of `supportsInterface`. The execution cost is 586 gas for any input. But contract initialization requires storing each interface (`SSTORE` is 20,000 gas). The `ERC165MappingImplementation` contract is generic and reusable.
|
||||
|
||||
```solidity
|
||||
pragma solidity ^0.4.19;
|
||||
|
||||
import "./ERC165.sol";
|
||||
|
||||
interface Simpson {
|
||||
function is2D() external returns (bool);
|
||||
function skinColor() external returns (string);
|
||||
}
|
||||
contract ERC165MappingImplementation is ERC165 {
|
||||
/// @dev You must not set element 0xffffffff to true
|
||||
mapping(bytes4 => bool) internal supportedInterfaces;
|
||||
|
||||
contract Lisa is ERC165, Simpson {
|
||||
mapping(bytes4 => bool) supportedInterfaces;
|
||||
|
||||
function Lisa() public {
|
||||
function ERC165MappingImplementation() internal {
|
||||
supportedInterfaces[this.supportsInterface.selector] = true;
|
||||
supportedInterfaces[this.is2D.selector ^ this.skinColor.selector] = true;
|
||||
}
|
||||
|
||||
function supportsInterface(bytes4 interfaceID) external view returns (bool) {
|
||||
return supportedInterfaces[interfaceID];
|
||||
}
|
||||
}
|
||||
|
||||
// ... is usually 2D
|
||||
// skin color is yellow
|
||||
interface Simpson {
|
||||
function is2D() external returns (bool);
|
||||
function skinColor() external returns (string);
|
||||
}
|
||||
|
||||
contract Lisa is ERC165MappingImplementation, Simpson {
|
||||
function Lisa() public {
|
||||
supportedInterfaces[this.is2D.selector ^ this.skinColor.selector] = true;
|
||||
}
|
||||
|
||||
function is2D() external returns (bool){}
|
||||
function skinColor() external returns (string){}
|
||||
}
|
||||
```
|
||||
|
||||
@ -167,10 +227,6 @@ contract Homer is ERC165, Simpson {
|
||||
|
||||
With three or more supported interfaces (including ERC165 itself as a required supported interface), the mapping table approach (for any case) costs less gas than the worst case for pure approach.
|
||||
|
||||
|
||||
|
||||
XXXXXX IN PROGRES XXXXXX [https://github.com/jbaylina/EIP165Cache](https://github.com/jbaylina/EIP165Cache)
|
||||
|
||||
## Copyright
|
||||
|
||||
Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/).
|
||||
|
Loading…
x
Reference in New Issue
Block a user