I posted this on the ENS discourse as well:
One example of a valid implementation of balanceOf
would thus be:
function balanceOf(address addr) public view returns(uint balance) {
revert OffchainLookup(
address(this),
[url],
abi.encodeWithSelector(Gateway.getSignedBalance.selector, addr),
ContractName.balanceOfWithProof.selector,
abi.encode(addr)
);
}
Note that in this example the contract is returning addr
in both callData
and extraData
, because it is required both by the gateway (in order to look up the data) and the callback function (in order to verify it). The contract cannot simply pass it to the gateway and rely on it being returned in the response, as this would give the gateway an opportunity to respond with an answer to a different query than the one that was initially issued.
Doesn’t this open the door for poor/naive implementations where devs will fail to read the full spec and just rely on the correct response being returned? I notice this is pointed out below in the Security Considerations as well, but I’m worried about a hasty implementer missing this. What if the original callData
was also passed to the callback as well?
(bytes originalCallData, bytes response, bytes extraData)
That way the callback is always guaranteed to receive the original data. And then extraData
can still be used for anything that your callback needs but that you don’t want to send to the gateway (or other arbitrary contextual data).
Or, do you think that would be inappropriate/superfluous for most cases? It seems like it would be helpful for the current balanceOfWithProof
example at least. But it doesn’t eliminate the problem: If verification is needed, implementers would still need to actually do that verification against originalCallData
.
Admittedly this also means that you would probably be passing additional information to the callback function that it wouldn’t ever need, like Gateway.getSignedBalance.selector
in this case.
So yeah, now that I’ve typed this out I can see the pros and cons, either way the dev is going to need to be aware of the security best practices here, and passing the original calldata might just be more overhead. Curious to hear your thoughts though!