TEC-compatible forwarder contract

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.

3 Likes

Unless I’ve misunderstood, if the tx.origin check is only added to the Forwarding contract then this can still be abused by someone creating a contract without this?
That said the TEC could probably just blacklist entire contracts that are used for continual griefing.