Anatomy of a Hook for the EVM-based chains
During order fulfillment the DlnDestination
smart contract performs standard routine procedures, including checking that the order is still open (neither Fulfilled nor Cancelled) and that the requested amount of tokens were successfully pulled from the solver. Finally, it transfers the requested amount of tokens to the order's recipient address, OR — if the hook raw data is provided — to the DlnExternalCallAdapter
hook engine smart contract address (accessible via DlnDestination.externalCallAdapter
), immediately invoking it for hook handling.
DlnExternalCallAdapter
is responsible for proper hook raw data decoding, hook execution, and guaranties that hook behavior conforms given properties.
Hook data V1 layout
DlnExternalCallAdapter
expects the hook raw data (called externalCallEnvelope
) to be an concatenation of two encodePacked
'd data types, specifically:
first byte:
uint8 envelopeVersion
subsequent bytes:
bytes envelopeData
The envelopeVersion
determines which data structure was used to encode the envelopeData
,
Envelope v1
The envelopeVersion=1
is currently the only available version, and its corresponding structure for the envelopeData
is HookDataV1
as follows:
HookDataV1.target
defines a target smart contract address that would get called by the DlnExternalCallAdapter
hook engine smart. The target smart contract MUST implement the IExternalCallExecutor
interface with only two functions: onEtherReceived()
and onERC20Received()
— that get called (along with order details and the payload) right after the native or ERC-20 token got transferred to it:
For orders buying ERC-20 token, the DlnExternalCallAdapter
first transfers the token's amount to the hook's target
, then invokes its onERC20Received()
method:
For orders buying native blockchain currency (ether, etc), the DlnExternalCallAdapter
invokes the hook's target.onEtherReceived()
method along with the amount of native currency as a msg.value
:
Universal Hook
One the common ways to build cross-chain interactions is to make calls to arbitrary existing contracts, without the need to introduce custom intermediaries (smart contracts that act as hooks). To facilitate this need, we've build a default Universal Hook — a pre-deployed implementation of the IExternalCallExecutor
and a part of the DLN deployment — that can act as a hook's target
and is designed to transparently execute arbitrary transaction calls bypassed through its targetPayload
.
To reuse this hook, HookDataV1
's target
must be set to address(0)
(this would tell the DlnExternalCallAdapter
hook engine to switch to the universal hook as a default hook implementation), and HookDataV1
's targetPayload
must represent the following encoded data struct:
When called, the universal hook would makes a CALL
to the given to
address using the given callData
.
The transfer of native blockchain currency is performed during the call itself:
Mind that ERC-20 token transfer during transaction call made by the Universal hook behaves differently: the universal hook does not transfer the token to the to
address (like the DlnExternalCallAdapter
does when calling a hook's target
), but sets a temporary allowance instead before making a call, and reverts it back after the call:
If the payload.to
had pulled less amount than the order's outcome, the remainder is transferred to the given fallback address automatically.
If the call to the payload.to
target gets reverted, the execution bubbles up to the DlnExternalCallAdapter
hook engine who handles this failure according to the hook's properties (revert entire call if the hook is a success-required hook; gracefully ignore the failure if the hook is a success-optional hook).
Last updated