Signature Schemes

CoW Protocol handles user orders that are provided off-chain. To verify that an order was approved by a user, the smart contract requires the user to provide a valid signature for that order.

The protocol supports four different signing methods:

1. EOA signatures with the [eth_sign rpc call] (https://eth.wiki/json-rpc/API#eth_sign).

2. EOA signatures with [EIP-712 typed data] (https://eips.ethereum.org/EIPS/eip-712).

3. Smart-contract [EIP-1271 signatures] (https://eips.ethereum.org/EIPS/eip-1271).

4. Pre-signing the order with an onchain transaction from the owner of the order.

Except for pre-authorization, all signing schemes involve signing an order digest that is based on the message structure of EIP-712.

The next section describes how to build the order digest, while the following sections describe each of the four signing schemes in detail.

  1. The domain separator

The domain separator is a byte string that uniquely represents a single deployment of the GPv2 contracts in a particular chain.

The domain separator is different in each of the chains where GPv2 is supported. This is done to make signatures for one network (e.g., Rinkeby) invalid in different networks (e.g., mainnet), so as to avoid network replay attacks. The same is the case for different deployments of the settlement contract in the same chain.

The domain separator is defined as prescribed by the EIP-712 standard with the following parameters:

{

  name: "Gnosis Protocol",

  version: "v2",

  chainId: /* chain ID for the current network: 1 for mainnet, 4 for Rinkeby, 100 for xDai */,

  verifyingContract: /* address of the settlement contract */

}

The actual domain separator is the result of hashing the previous struct with EIP-712's hashStruct.

You can retrieve the domain separator from the contract by calling the domainSeparator() readonly function.

  1. Computing the order digest

Signatures built with the eth_sign, EIP-712, and EIP-1271 schemes are created based on an order digest.

The order digest is a sequence of 32 bytes that uniquely describes the parameters of an order.

It is generated by encoding all information on the user order into a single struct and hashing the result as it is described in the EIP-712 standard.

It can be explicitly computed as:

orderDigest = keccak256("\x19\x01" ‖ domainSeparator ‖ hashStruct(orderStruct))

The components are:

The hashOrder function exported by the Node package @gnosis.pm/gp-v2-contracts can be used to compute the order digest without deriving each of the building blocks.

  1. eth_sign signatures

This signature type is the most commonly supported signing mechanism for EOAs.

The signature of an order is computed as:

signature = ethSign(orderDigest)

The components are:

  • ethSign, using the user's private key to ECDSA-sign a message prefixed with "\x19Ethereum signed message:\n" and its length

  • orderDigest, the order digest

Most Ethereum libraries support ethSign signatures (web3, ethers.io).

  1. EIP-712 signatures

This signing method, also known as typed structured data signing, is the recommended signing method for EOAs, since the user will be able to see the full order data that is being signed in most wallet implementations (notably Metamask signTypedData_v4 and signTypedData_v3 are compatible).

The signature is computed as:

signature = ecdsaSign(orderDigest)

The components are:

  • ecdsaSign, using the user's private key to ECDSA-sign the message

  • orderDigest, the order digest

Many Ethereum libraries have some degree of support for sign typed data without building the order digest by hand (for example, web3 and ethers.io).

In any case, you may want to read about the domain separator and the order struct.

  1. EIP-1271 signatures

This signing mechanism is the only option that provides offline signatures to orders originating from smart contracts.

In order to support smart-contract orders, the trading smart contract must implement the EIP-1271 interface.

signature = traderAddress ‖ eip1271Signature

The components are:

  • traderAddress, the address of the smart contract that signs the order

  • , the byte-concatenation function

  • eip1271Signature, any bitstring that is a valid signature for the contract for the order

For an order to be accepted, the EIP-1271 signature must be valid for the orderDigest message, that is in Solidity:

isValidSignature(orderDigest, eip1271Signature) == MAGICVALUE
  1. Pre-sign

This is the only signing method that supports both EOA and smart-contract traders.

The signature is simply the 20-bit address of the trader:

signature = traderAddress

In order for a signature to be valid, the user must have pre-approved the order on-chain.

To do that, the trader must be the sender of an on-chain call to the settlement-contract function setPreSignature on input the order uid of the order that needs to be signed. See here for information on how to compute the order uid.

Note that if an order was already filled, then presigning it does not make it tradable again.

Last updated