Skip to content
This repository has been archived by the owner on Jun 24, 2022. It is now read-only.

563/fix eth sign v4 signature not supported #576

Merged
merged 10 commits into from
May 27, 2021
Merged
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,7 @@ cypress/screenshots
cypress/fixtures/example.json
cypress-custom/videos
cypress-custom/screenshots
cypress-custom/fixtures/example.json
cypress-custom/fixtures/example.json

.yalc*
yalc.lock
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@
"license": "GPL-3.0-or-later",
"dependencies": {
"@web3-react/walletconnect-connector": "^6.2.0",
"@gnosis.pm/gp-v2-contracts": "^0.0.1-alpha.17",
"@gnosis.pm/gp-v2-contracts": "^0.0.1-alpha.18",
"@uniswap/default-token-list": "^2.0.0",
"react-appzi": "^1.0.4",
"react-router-hash-link": "^2.4.0"
Expand Down
81 changes: 78 additions & 3 deletions src/custom/utils/signatures.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,27 @@
import { domain as domainGp, signOrder as signOrderGp, Order, Signature } from '@gnosis.pm/gp-v2-contracts'
import {
domain as domainGp,
signOrder as signOrderGp,
EcdsaSignature,
Order,
Signature,
TypedDataV3Signer
} from '@gnosis.pm/gp-v2-contracts'
import { ChainId } from '@uniswap/sdk'

import { GP_SETTLEMENT_CONTRACT_ADDRESS } from 'constants/index'
import { TypedDataDomain, Signer } from 'ethers'
import { registerOnWindow } from './misc'

export { OrderKind } from '@gnosis.pm/gp-v2-contracts'

// For error codes, see:
// - https://eth.wiki/json-rpc/json-rpc-error-codes-improvement-proposal
// - https://www.jsonrpc.org/specification#error_object
const METAMASK_SIGNATURE_ERROR_CODE = -32603
const METHOD_NOT_FOUND_ERROR_CODE = -32601
const V4_ERROR_MSG_REGEX = /eth_signTypedData_v4 does not exist/i
const V3_ERROR_MSG_REGEX = /eth_signTypedData_v3 does not exist/i

export type UnsignedOrder = Omit<Order, 'receiver'> & { receiver: string }

export interface SignOrderParams {
Expand Down Expand Up @@ -90,7 +106,7 @@ function _getDomain(chainId: ChainId): TypedDataDomain {
return domainGp(chainId, settlementContract) // TODO: Fix types in NPM package
}

export async function signOrder(params: SignOrderParams): Promise<Signature> {
async function _signOrder(params: SignOrderParams): Promise<Signature> {
const { chainId, signer, order, signingScheme } = params

const domain = _getDomain(chainId)
Expand All @@ -103,4 +119,63 @@ export async function signOrder(params: SignOrderParams): Promise<Signature> {
return signOrderGp(domain, order, signer, getSigningSchemeLibValue(signingScheme))
}

registerOnWindow({ signature: { signOrder, getDomain: _getDomain } })
export async function signOrder(
unsignedOrder: UnsignedOrder,
chainId: ChainId,
signer: Signer,
signingMethod: 'v4' | 'v3' | 'eth_sign' = 'v4'
): Promise<{ signature: string; signingScheme: EcdsaSigningScheme }> {
const signingScheme = signingMethod === 'eth_sign' ? SigningScheme.ETHSIGN : SigningScheme.EIP712
let signature: Signature | null = null

let _signer = signer
try {
_signer = signingMethod === 'v3' ? new TypedDataV3Signer(signer) : signer
} catch (e) {
console.error('Wallet not supported:', e)
throw new Error('Wallet not supported')
}

const signatureParams: SignOrderParams = {
chainId,
signer: _signer,
order: unsignedOrder,
signingScheme
}

try {
signature = (await _signOrder(signatureParams)) as EcdsaSignature // Only ECDSA signing supported for now
} catch (e) {
if (e.code === METHOD_NOT_FOUND_ERROR_CODE) {
// Maybe the wallet returns the proper error code? We can only hope 🤞
switch (signingMethod) {
case 'v4':
return signOrder(unsignedOrder, chainId, signer, 'v3')
case 'v3':
return signOrder(unsignedOrder, chainId, signer, 'eth_sign')
default:
throw e
}
} else if (e.code === METAMASK_SIGNATURE_ERROR_CODE) {
// We tried to sign order the nice way.
// That works fine for regular MM addresses. Does not work for Hardware wallets, though.
// See https://github.com/MetaMask/metamask-extension/issues/10240#issuecomment-810552020
// So, when that specific error occurs, we know this is a problem with MM + HW.
// Then, we fallback to ETHSIGN.
return signOrder(unsignedOrder, chainId, signer, 'eth_sign')
} else if (V4_ERROR_MSG_REGEX.test(e.message)) {
// Failed with `v4`, and the wallet does not set the proper error code
return signOrder(unsignedOrder, chainId, signer, 'v3')
} else if (V3_ERROR_MSG_REGEX.test(e.message)) {
// Failed with `v3`, and the wallet does not set the proper error code
return signOrder(unsignedOrder, chainId, signer, 'eth_sign')
} else {
// Some other error signing. Let it bubble up.
console.error(e)
throw e
}
}
return { signature: signature.data.toString(), signingScheme }
}

registerOnWindow({ signature: { signOrder: _signOrder, getDomain: _getDomain } })
40 changes: 4 additions & 36 deletions src/custom/utils/trade.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,10 @@ import { ChainId, CurrencyAmount, Token } from '@uniswap/sdk'
import { isAddress, shortenAddress } from '@src/utils'
import { AddPendingOrderParams, OrderStatus, OrderKind } from 'state/orders/actions'

import { SigningScheme, signOrder, SignOrderParams, UnsignedOrder } from 'utils/signatures'
import { signOrder, UnsignedOrder } from 'utils/signatures'
import { postSignedOrder } from 'utils/operator'
import { Signer } from 'ethers'
import { APP_ID, RADIX_DECIMAL, SHORTEST_PRECISION } from 'constants/index'
import { EcdsaSignature, Signature } from '@gnosis.pm/gp-v2-contracts'

const DEFAULT_SIGNING_SCHEME = SigningScheme.EIP712
const METAMASK_SIGNATURE_ERROR_CODE = -32603

export interface PostOrderParams {
account: string
Expand Down Expand Up @@ -89,43 +85,15 @@ export async function postOrder(params: PostOrderParams): Promise<string> {
partiallyFillable: false // Always fill or kill
}

let signature: Signature | null = null
let signingScheme = DEFAULT_SIGNING_SCHEME

const signatureParams: SignOrderParams = {
chainId,
signer,
order: unsignedOrder,
signingScheme
}

try {
signature = (await signOrder(signatureParams)) as EcdsaSignature // Only ECDSA signing supported for now
} catch (e) {
if (e.code === METAMASK_SIGNATURE_ERROR_CODE) {
// We tried to sign order the nice way.
// That works fine for regular MM addresses. Does not work for Hardware wallets, though.
// See https://github.com/MetaMask/metamask-extension/issues/10240#issuecomment-810552020
// So, when that specific error occurs, we know this is a problem with MM + HW.
// Then, we fallback to ETHSIGN.
signingScheme = SigningScheme.ETHSIGN
signature = (await signOrder({ ...signatureParams, signingScheme })) as EcdsaSignature // Only ECDSA signing supported for now
} else {
// Some other error signing. Let it bubble up.
console.error(e)
throw e
}
}

const signatureData = signature.data.toString()
const { signature, signingScheme } = await signOrder(unsignedOrder, chainId, signer)
const creationTime = new Date().toISOString()

// Call API
const orderId = await postSignedOrder({
chainId,
order: {
...unsignedOrder,
signature: signatureData,
signature,
receiver,
signingScheme
}
Expand All @@ -140,7 +108,7 @@ export async function postOrder(params: PostOrderParams): Promise<string> {
id: orderId,
owner: account,
creationTime,
signature: signatureData,
signature,
status: OrderStatus.PENDING,
summary,
inputToken: sellToken,
Expand Down
8 changes: 4 additions & 4 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1674,10 +1674,10 @@
"@ethersproject/properties" "^5.1.0"
"@ethersproject/strings" "^5.1.0"

"@gnosis.pm/gp-v2-contracts@^0.0.1-alpha.17":
version "0.0.1-alpha.17"
resolved "https://registry.yarnpkg.com/@gnosis.pm/gp-v2-contracts/-/gp-v2-contracts-0.0.1-alpha.17.tgz#5c3e08b4a761d13383af631cee255cc7645aa50a"
integrity sha512-kr/tpeqvlKVXDka/aFHGFJyT9xgYjAvWK9wszlq4pyH3Qs7WIB0mj9YMdzAmVRVdvpXeLGWgTPZ88xIXD/9X6Q==
"@gnosis.pm/gp-v2-contracts@^0.0.1-alpha.18":
version "0.0.1-alpha.18"
resolved "https://registry.yarnpkg.com/@gnosis.pm/gp-v2-contracts/-/gp-v2-contracts-0.0.1-alpha.18.tgz#317ec588724713d067158293748cb1057a9d02a3"
integrity sha512-jOh0UHYA6W5q2840CKR6FWNxc0ARGs6ijSjPXbX6ytPU5c7uH5yrXM0Qvkogp4KWrnxtZQ9ptdA+nsVAgyDmRQ==

"@hapi/[email protected]":
version "2.1.4"
Expand Down