-
Notifications
You must be signed in to change notification settings - Fork 465
[0x.js][order-utils][web3-wrapper] Expose eth_signTypedData functionality for order signing #1102
Conversation
9336467
to
20ca573
Compare
20ca573
to
da49112
Compare
Coverage decreased (-0.4%) to 84.87% when pulling 8620cbc213556b3961087f1eedf3ba7283494081 on feature/0x.js/eip712-sign-typed-data into 119f8c9 on development. |
b867dce
to
15a17fd
Compare
7115285
to
050cc17
Compare
Create a helper back in EIP712Utils for code cleanup. Moved constants in order-utils into the constants object
050cc17
to
75d274f
Compare
Report a developer friendly error in this event to educate them on the compatability wrapper MetamaskSubprovider
pad32Buffer(buffer: Buffer): Buffer { | ||
const bufferPadded = ethUtil.setLengthLeft(buffer, EIP712_VALUE_LENGTH); | ||
return bufferPadded; | ||
createOrderTypedData: (order: Order): EIP712TypedData => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm, slight preference for createTypedDataOrder
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have a slight preference for important information first when reading the code.
createTypedDataOrder
createTypedDataZeroExTransaction
vs
createOrderTypedData
createZeroExTransactionTypedData
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, add method devdoc comment for this method.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Already added
packages/0x.js/CHANGELOG.json
Outdated
"pr": 1102 | ||
}, | ||
{ | ||
"note": "Added `MetamaskSubprovider` to handle inconsistencies with signing.", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
inconsistencies in Metamask's signing JSON RPC endpoints
const eip721MessageBuffer = eip712Utils.createEIP712Message(executeTransactionHashBuff, exchangeAddress); | ||
const messageHex = `0x${eip721MessageBuffer.toString('hex')}`; | ||
const typedData = eip712Utils.createZeroExTransactionTypedData(executeTransactionData, exchangeAddress); | ||
const eip712MessageBuffer = signTypedDataUtils.signTypedDataHash(typedData); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rename signTypedDataHash
to generateTypedDataHash
. There is no signing
going on and the current method name suggests as much.
pad32Buffer(buffer: Buffer): Buffer { | ||
const bufferPadded = ethUtil.setLengthLeft(buffer, EIP712_VALUE_LENGTH); | ||
return bufferPadded; | ||
createOrderTypedData: (order: Order): EIP712TypedData => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, add method devdoc comment for this method.
const bufferPadded = ethUtil.setLengthLeft(buffer, EIP712_VALUE_LENGTH); | ||
return bufferPadded; | ||
createOrderTypedData: (order: Order): EIP712TypedData => { | ||
const normalizedOrder = _.mapValues(order, value => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's first assert that the Order
passed in conforms to the Order schema.
message: EIP712Object, | ||
exchangeAddress: string, | ||
): EIP712TypedData => { | ||
const typedData = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add assertions for the params. This is a public, exported method.
} | ||
}, | ||
/** | ||
* Signs an order using `eth_signTypedData` and returns its elliptic curve signature and signature type. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not true any more: returns its elliptic curve signature and signature type
} catch (err) { | ||
// Detect if Metamask to transition users to the MetamaskSubprovider | ||
if ((provider as any).isMetaMask) { | ||
throw new Error(`Unsupported Provider, please use MetamaskSubprovider: ${err.message}`); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's be more explicit: MetaMask provider must be wrapped in a MetamaskSubprovider (from the '@0xproject/subproviders' package) in order to work with this method.
const signature = await web3Wrapper.signMessageAsync(normalizedSignerAddress, msgHashHex); | ||
}, | ||
/** | ||
* Signs a hash and returns its elliptic curve signature and signature type. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's mention eth_sign
here to mirror the other method description.
throw new Error(OrderError.InvalidSignature); | ||
// Detect if Metamask to transition users to the MetamaskSubprovider | ||
if ((provider as any).isMetaMask) { | ||
throw new Error('Unsupported Provider, please use MetamaskSubprovider.'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Copy above error message.
packages/website/ts/blockchain.ts
Outdated
@@ -161,7 +161,7 @@ export class Blockchain { | |||
// We catch all requests involving a users account and send it to the injectedWeb3 | |||
// instance. All other requests go to the public hosted node. | |||
const provider = new Web3ProviderEngine(); | |||
provider.addProvider(new SignerSubprovider(injectedWeb3.currentProvider)); | |||
provider.addProvider(new MetamaskSubprovider(injectedWeb3.currentProvider)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wait, we only want to do this IF provider.isMetamask
is true. Let's add that check. Website should also work with other injected signers.
8620cbc
to
c979949
Compare
In web3 wrapper when a response contains an error field we throw this rather than return response.result which is often undefined. In Signature Utils we handle the error thrown when a user rejects the signing dialogue to prevent double signing. Exposed the ZeroExTransaction JSON schema. In Website only use the MetamaskSubprovider if we can detect the provider is Metamask
c979949
to
9e8031d
Compare
packages/order-utils/CHANGELOG.json
Outdated
"version": "2.0.0", | ||
"changes": [ | ||
{ | ||
"note": "Added `ecSignOrderAsync` to first sign an order as EIP712 and fallback to EthSign", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's replace as EIP712 and fallback to EthSign
with via 'eth_signTypedData' and fallback to 'eth_sign'
// HACK: We are unable to handle specific errors thrown since provider is not an object | ||
// under our control. It could be Metamask Web3, Ethers, or any general RPC provider. | ||
// We check for a user denying the signature request in a way that supports Metamask and | ||
// Coinbase Wallet |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add: . Unfortunately for signers with a different error message, they will receive two signature requests.
// Detect if Metamask to transition users to the MetamaskSubprovider | ||
if ((provider as any).isMetaMask) { | ||
throw new Error( | ||
`MetaMask provider must be wrapped in a MetamaskSubprovider (from the '@0xproject/subproviders' package) in order to work with this method.`, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's add this to OrderError
enum.
Description
TODO
ecSignOrderHashAsync
toecSignHashAsync
for use in executeTransaction etceth_signTypedData
andeth_sign
if signTypedData is unsupported.eth_signTypedData
Note: MM now has another inconsistency with
eth_signTypedData
. It has published 4.12 and named the correct spec compliant implementationeth_signTypedData_v3
. It also requires the typedData parameter to be JSON encoded (inconsistent with Ganache).I propose we quarantine all the MM inconsistency in a Subprovider called MetamaskSubprovider. This allows us to handle the inconsistent
eth_sign
behaviour (no prefix is added) as well as handle theeth_signTypedData
inconsistency. Removing this leaky abstraction from the rest of our code as much as possible (SignerType removed).This subprovider can be used standalone or as a web3ProviderEngine subprovider.
After merge and publish
Testing instructions
Ganache and MM share the same implementation of
eth_signTypedData
. We test against this implementation inside of ganache. The output ofeth_signTypedData
is the same as a signed message of our orderHash without the prefix (since our orderHash is EIP712).Types of changes
Checklist:
[WIP]
if necessary.[sol-cov] Fixed bug
.