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.
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:
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.
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:
The components are:
keccak256
, the standard unpadded Ethereum hashing function"\x19\x01"
, two bytes‖
, the byte-concatenation functiondomainSeparator
, the domain separatorhashStruct
, the identically named function in the EIP-712 standardorderStruct
, the order struct
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.
eth_sign signatures
This signature type is the most commonly supported signing mechanism for EOAs.
The signature of an order is computed as:
The components are:
ethSign
, using the user's private key to ECDSA-sign a message prefixed with"\x19Ethereum signed message:\n"
and its lengthorderDigest
, the order digest
Most Ethereum libraries support ethSign
signatures (web3, ethers.io).
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:
The components are:
ecdsaSign
, using the user's private key to ECDSA-sign the messageorderDigest
, 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.
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.
The components are:
traderAddress
, the address of the smart contract that signs the order‖
, the byte-concatenation functioneip1271Signature
, 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:
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:
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