The purpose of the Forwarder
contract is to simplify UX for takers who wish to purchase assets with ETH. The Forwarder
handles the wrapping of ETH to WETH, user allowances, and abstraction of ZRX fees. The current Forwarder
contract is only compatible with open orderbook relayers and is heavily relied upon by 0x Instant.
One of the stated goals of the TEC (Trade Execution Coordinator) is to allow for “contract fillable liquidity” and aggregation of orders while still mitigating front-running and accidental trade collisions. The UX, communication process, and API is very different from that of an open orderbook relayer, however. In addition, the TEC contract makes heavy use of 0x transactions in order to allow contracts to fill orders on behalf of the taker, while still transferring assets directly to and from the taker’s account. This means that any taker using a TEC must have the takerAsset
in their account with the required allowance set. In the case of the Forwarder
contract, requiring the taker to sign a 0x transaction is therefore not an option.
Instead, a TEC-compatible Forwarder
must continue to use a similar flow to the existing Forwarder
contract. The TEC Forwarder
is the taker and is still responsible for wrapping ETH and setting allowances. It can produce valid signatures if it implements an isValidSignature
function that is used with the Wallet
signature type. One potential implementation of this function is simple:
function isValidSignature(
bytes32 hash,
bytes calldata signature
)
external
pure
returns (bool)
{
return true;
}
With this implementation anyone can create a valid 0x transaction on behalf of the Forwarder
, send it to the TEC server, and then submit the signed approval message to the TEC Forwarder
contract (along with the ETH required to fill the order(s)). The TEC Forwarder
would have a preset WETH allowance, will wrap the sent in ETH, forward the 0x transaction and signed approval to the TEC contract, and then refund any remaining ETH to the sender. The only signature required by the sender is the signature on the actual Ethereum transaction.
This approach maintains a very similar UX to the existing Forwarder
with additional race condition mitigation. However, it is more susceptible to griefing by the taker. How does the TEC know where the Ethereum transaction is actually originating if they are simply approving a 0x transaction with an empty signature that is tied to the TEC Forwarder
? How can this transaction be validated? It is also no longer an option for the TEC to submit the transaction on behalf of the taker since the taker is the Forwarder
contract and never has a balance (except mid-transaction). There is essentially no recourse if the taker decides not to follow through with the transaction.
The alternative requires an extra signature, but allows for blacklisting addresses that grief. The implementation of isValidSignature
simply needs to change to:
function isValidSignature(
bytes32 hash,
bytes calldata signature
)
external
view
returns (bool)
{
uint8 v = uint8(signature[0]);
bytes32 r = signature.readBytes32(1);
bytes32 s = signature.readBytes32(33);
address recovered = ecrecover(
hash,
v,
r,
s
);
return tx.origin == recovered;
}
Curious to hear everyone’s thoughts. More thoughts on 0x transaction validation and recourse against taker griefing to come.