@0x/order-utils
: Add getOrderHash()
, getExchangeTransactionHash()
, getExchangeProxyTransactionHash()
This commit is contained in:
parent
aced474dc5
commit
829a353b14
@ -5,6 +5,10 @@
|
||||
{
|
||||
"note": "Add ERC20 Transformer utils and export useful constants.",
|
||||
"pr": 2604
|
||||
},
|
||||
{
|
||||
"note": "Add `getOrderHash()`, `getExchangeTransactionHash()`, `getExchangeProxyTransactionHash()`",
|
||||
"pr": 2610
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -64,6 +64,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@0x/assert": "^3.0.8",
|
||||
"@0x/contract-addresses": "^4.10.0",
|
||||
"@0x/contract-wrappers": "^13.7.0",
|
||||
"@0x/json-schemas": "^5.0.8",
|
||||
"@0x/utils": "^5.5.0",
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { getContractAddressesForChainOrThrow } from '@0x/contract-addresses';
|
||||
import { BigNumber, NULL_ADDRESS, NULL_BYTES } from '@0x/utils';
|
||||
import { MethodAbi } from 'ethereum-types';
|
||||
|
||||
@ -150,6 +151,27 @@ export const constants = {
|
||||
{ name: 'transactionSignature', type: 'bytes' },
|
||||
],
|
||||
},
|
||||
MAINNET_EXCHANGE_PROXY_DOMAIN: {
|
||||
name: 'ZeroEx',
|
||||
version: '1.0.0',
|
||||
chainId: 1,
|
||||
verifyingContract: getContractAddressesForChainOrThrow(1).exchangeProxy,
|
||||
},
|
||||
EXCHANGE_PROXY_MTX_SCEHMA: {
|
||||
name: 'MetaTransactionData',
|
||||
parameters: [
|
||||
{ name: 'signer', type: 'address' },
|
||||
{ name: 'sender', type: 'address' },
|
||||
{ name: 'minGasPrice', type: 'uint256' },
|
||||
{ name: 'maxGasPrice', type: 'uint256' },
|
||||
{ name: 'expirationTime', type: 'uint256' },
|
||||
{ name: 'salt', type: 'uint256' },
|
||||
{ name: 'callData', type: 'bytes' },
|
||||
{ name: 'value', type: 'uint256' },
|
||||
{ name: 'feeToken', type: 'address' },
|
||||
{ name: 'feeAmount', type: 'uint256' },
|
||||
],
|
||||
},
|
||||
ERC20_METHOD_ABI,
|
||||
ERC721_METHOD_ABI,
|
||||
MULTI_ASSET_METHOD_ABI,
|
||||
|
@ -5,11 +5,12 @@ import {
|
||||
EIP712Object,
|
||||
EIP712TypedData,
|
||||
EIP712Types,
|
||||
ExchangeProxyMetaTransaction,
|
||||
Order,
|
||||
SignedZeroExTransaction,
|
||||
ZeroExTransaction,
|
||||
} from '@0x/types';
|
||||
import { hexUtils, signTypedDataUtils } from '@0x/utils';
|
||||
import { BigNumber, hexUtils, signTypedDataUtils } from '@0x/utils';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import { constants } from './constants';
|
||||
@ -131,4 +132,18 @@ export const eip712Utils = {
|
||||
);
|
||||
return typedData;
|
||||
},
|
||||
createExchangeProxyMetaTransactionTypedData(mtx: ExchangeProxyMetaTransaction): EIP712TypedData {
|
||||
return eip712Utils.createTypedData(
|
||||
constants.EXCHANGE_PROXY_MTX_SCEHMA.name,
|
||||
{
|
||||
MetaTransactionData: constants.EXCHANGE_PROXY_MTX_SCEHMA.parameters,
|
||||
},
|
||||
_.mapValues(
|
||||
_.omit(mtx, 'domain'),
|
||||
// tslint:disable-next-line: custom-no-magic-numbers
|
||||
v => (BigNumber.isBigNumber(v) ? v.toString(10) : v),
|
||||
) as EIP712Object,
|
||||
_domain,
|
||||
);
|
||||
},
|
||||
};
|
||||
|
29
packages/order-utils/src/hash_utils.ts
Normal file
29
packages/order-utils/src/hash_utils.ts
Normal file
@ -0,0 +1,29 @@
|
||||
import { ExchangeProxyMetaTransaction, Order, ZeroExTransaction } from '@0x/types';
|
||||
import { hexUtils, signTypedDataUtils } from '@0x/utils';
|
||||
|
||||
import { eip712Utils } from './eip712_utils';
|
||||
import { orderHashUtils } from './order_hash_utils';
|
||||
import { transactionHashUtils } from './transaction_hash_utils';
|
||||
|
||||
/**
|
||||
* Compute the EIP712 hash of an order.
|
||||
*/
|
||||
export function getOrderHash(order: Order): string {
|
||||
return orderHashUtils.getOrderHash(order);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the EIP712 hash of an Exchange meta-transaction.
|
||||
*/
|
||||
export function getExchangeMetaTransactionHash(tx: ZeroExTransaction): string {
|
||||
return transactionHashUtils.getTransactionHash(tx);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the EIP712 hash of an Exchange Proxy meta-transaction.
|
||||
*/
|
||||
export function getExchangeProxyMetaTransactionHash(mtx: ExchangeProxyMetaTransaction): string {
|
||||
return hexUtils.toHex(
|
||||
signTypedDataUtils.generateTypedDataHash(eip712Utils.createExchangeProxyMetaTransactionTypedData(mtx)),
|
||||
);
|
||||
}
|
@ -49,6 +49,8 @@ export {
|
||||
ZeroExTransaction,
|
||||
SignedZeroExTransaction,
|
||||
ValidatorSignature,
|
||||
ExchangeProxyMetaTransaction,
|
||||
SignedExchangeProxyMetaTransaction,
|
||||
} from '@0x/types';
|
||||
|
||||
export {
|
||||
@ -77,6 +79,8 @@ export {
|
||||
decodeAffiliateFeeTransformerData,
|
||||
} from './transformer_data_encoders';
|
||||
|
||||
export { getOrderHash, getExchangeMetaTransactionHash, getExchangeProxyMetaTransactionHash } from './hash_utils';
|
||||
|
||||
import { constants } from './constants';
|
||||
export const NULL_ADDRESS = constants.NULL_ADDRESS;
|
||||
export const NULL_BYTES = constants.NULL_BYTES;
|
||||
|
@ -1,14 +1,16 @@
|
||||
import { schemas } from '@0x/json-schemas';
|
||||
import {
|
||||
ECSignature,
|
||||
ExchangeProxyMetaTransaction,
|
||||
Order,
|
||||
SignatureType,
|
||||
SignedExchangeProxyMetaTransaction,
|
||||
SignedOrder,
|
||||
SignedZeroExTransaction,
|
||||
ValidatorSignature,
|
||||
ZeroExTransaction,
|
||||
} from '@0x/types';
|
||||
import { providerUtils } from '@0x/utils';
|
||||
import { hexUtils, providerUtils } from '@0x/utils';
|
||||
import { Web3Wrapper } from '@0x/web3-wrapper';
|
||||
import { SupportedProvider } from 'ethereum-types';
|
||||
import * as ethUtil from 'ethereumjs-util';
|
||||
@ -16,6 +18,7 @@ import * as _ from 'lodash';
|
||||
|
||||
import { assert } from './assert';
|
||||
import { eip712Utils } from './eip712_utils';
|
||||
import { getExchangeProxyMetaTransactionHash } from './hash_utils';
|
||||
import { orderHashUtils } from './order_hash_utils';
|
||||
import { transactionHashUtils } from './transaction_hash_utils';
|
||||
import { TypedDataError } from './types';
|
||||
@ -187,6 +190,96 @@ export const signatureUtils = {
|
||||
}
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Signs an Exchange Proxy meta-transaction and returns a SignedExchangeProxyMetaTransaction.
|
||||
* First `eth_signTypedData` is requested then a fallback to `eth_sign` if not
|
||||
* available on the supplied provider.
|
||||
* @param supportedProvider Web3 provider to use for all JSON RPC requests
|
||||
* @param transaction The ExchangeProxyMetaTransaction to sign.
|
||||
* @param signerAddress The hex encoded Ethereum address you wish to sign it with. This address
|
||||
* must be available via the supplied Provider.
|
||||
* @return A SignedExchangeProxyMetaTransaction containing the order and
|
||||
* elliptic curve signature with Signature Type.
|
||||
*/
|
||||
async ecSignExchangeProxyMetaTransactionAsync(
|
||||
supportedProvider: SupportedProvider,
|
||||
transaction: ExchangeProxyMetaTransaction,
|
||||
signerAddress: string,
|
||||
): Promise<SignedExchangeProxyMetaTransaction> {
|
||||
assert.doesConformToSchema('transaction', transaction, schemas.zeroExTransactionSchema, [schemas.hexSchema]);
|
||||
try {
|
||||
const signedTransaction = await signatureUtils.ecSignTypedDataExchangeProxyMetaTransactionAsync(
|
||||
supportedProvider,
|
||||
transaction,
|
||||
signerAddress,
|
||||
);
|
||||
return signedTransaction;
|
||||
} catch (err) {
|
||||
// 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. Unfortunately for signers with a different error message,
|
||||
// they will receive two signature requests.
|
||||
if (err.message.includes('User denied message signature')) {
|
||||
throw err;
|
||||
}
|
||||
const transactionHash = getExchangeProxyMetaTransactionHash(transaction);
|
||||
const signatureHex = await signatureUtils.ecSignHashAsync(
|
||||
supportedProvider,
|
||||
transactionHash,
|
||||
signerAddress,
|
||||
);
|
||||
const signedTransaction = {
|
||||
...transaction,
|
||||
signature: signatureHex,
|
||||
};
|
||||
return signedTransaction;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Signs an Exchange Proxy meta-transaction using `eth_signTypedData` and
|
||||
* returns a SignedZeroExTransaction.
|
||||
* @param supportedProvider Web3 provider to use for all JSON RPC requests
|
||||
* @param transaction The Exchange Proxy transaction to sign.
|
||||
* @param signerAddress The hex encoded Ethereum address you wish
|
||||
* to sign it with. This address must be available via the supplied Provider.
|
||||
* @return A SignedExchangeProxyMetaTransaction containing the
|
||||
* ExchangeProxyMetaTransaction and elliptic curve signature with Signature Type.
|
||||
*/
|
||||
async ecSignTypedDataExchangeProxyMetaTransactionAsync(
|
||||
supportedProvider: SupportedProvider,
|
||||
transaction: ExchangeProxyMetaTransaction,
|
||||
signerAddress: string,
|
||||
): Promise<SignedExchangeProxyMetaTransaction> {
|
||||
const provider = providerUtils.standardizeOrThrow(supportedProvider);
|
||||
assert.isETHAddressHex('signerAddress', signerAddress);
|
||||
assert.doesConformToSchema('transaction', transaction, schemas.zeroExTransactionSchema, [schemas.hexSchema]);
|
||||
const web3Wrapper = new Web3Wrapper(provider);
|
||||
await assert.isSenderAddressAsync('signerAddress', signerAddress, web3Wrapper);
|
||||
const normalizedSignerAddress = signerAddress.toLowerCase();
|
||||
const typedData = eip712Utils.createExchangeProxyMetaTransactionTypedData(transaction);
|
||||
try {
|
||||
const signature = await web3Wrapper.signTypedDataAsync(normalizedSignerAddress, typedData);
|
||||
const ecSignatureRSV = parseSignatureHexAsRSV(signature);
|
||||
const signatureHex = hexUtils.concat(
|
||||
ecSignatureRSV.v,
|
||||
ecSignatureRSV.r,
|
||||
ecSignatureRSV.s,
|
||||
SignatureType.EIP712,
|
||||
);
|
||||
return {
|
||||
...transaction,
|
||||
signature: signatureHex,
|
||||
};
|
||||
} catch (err) {
|
||||
// Detect if Metamask to transition users to the MetamaskSubprovider
|
||||
if ((provider as any).isMetaMask) {
|
||||
throw new Error(TypedDataError.InvalidMetamaskSigner);
|
||||
} else {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Signs a hash using `eth_sign` and returns its elliptic curve signature and signature type.
|
||||
* @param supportedProvider Web3 provider to use for all JSON RPC requests
|
||||
@ -245,12 +338,7 @@ export const signatureUtils = {
|
||||
* @return Hex encoded string of signature (v,r,s) with Signature Type
|
||||
*/
|
||||
convertECSignatureToSignatureHex(ecSignature: ECSignature): string {
|
||||
const signatureBuffer = Buffer.concat([
|
||||
ethUtil.toBuffer(ecSignature.v),
|
||||
ethUtil.toBuffer(ecSignature.r),
|
||||
ethUtil.toBuffer(ecSignature.s),
|
||||
]);
|
||||
const signatureHex = `0x${signatureBuffer.toString('hex')}`;
|
||||
const signatureHex = hexUtils.concat(ecSignature.v, ecSignature.r, ecSignature.s);
|
||||
const signatureWithType = signatureUtils.convertToSignatureWithType(signatureHex, SignatureType.EthSign);
|
||||
return signatureWithType;
|
||||
},
|
||||
|
Loading…
x
Reference in New Issue
Block a user