* `@0x/contracts-zero-ex`: add limit orders feature `@0x/contracts-utils`: add `uint128` functions to `LibSafeMathV06` * `@0x/contract-addresses`: Update ganache snapshot addresses * `@0x/contracts-zero-ex`: Mask EIP712 struct hash values. * `@0x/contracts-zero-ex`: Add more limit order tests * `@0x/contracts-zero-ex`: Fix typos * `@0x/contracts-zero-ex`: Compute fee collector address after protocol fee zero check. * `@0x/contracts-zero-ex`: Remove WETH payment logic from fee collector fixin * `@0x/contracts-zero-ex`: Convert all ETH to WETH in `FeeCollector`. * `@0x/contracts-zero-ex`: Address review feedback * `@0x/contracts-zero-ex`: Export more utils * `@0x/contracts-zero-ex`: Rename `LimitOrdersFeatures`, `LibLimitOrders`, etc. into `*NativeOrders*`. `@0x/contracts-zero-ex`: Emit `protocolFeePaid` in native order fill events. `@0x/contracts-zero-ex`: Refactor to get around stack limits. `@0x/contracts-zero-ex`: Use different storage mappings for RFQ and limit order pair cancellations. * `@0x/contracts-zero-ex`: Add `getProtocolFeeMultiplier()` and `transferProtocolFeesForPools()` to `NativeOrdersFeature`. * `@0x/contracts-zero-ex`: Fix broken tests * update orders docs * `@0x/contracts-zero-ex`: Add more tests to `NativeOrdersFeature` * rebuild after rebase * `@0x/contract-addresses`: Fix changelog booboo * `@0x/contracts-zero-ex`: Add method selectors output to generated artifacts * `@0x/contracts-zero-ex`: Add maker address to order cancel events. `@0x/contracts-zreo-ex`: Remove `UpTo` suffix from order pair cancellation functions. `@0x/contracts-zreo-ex`: Address misc review comments. * `@0x/contracts-zero-ex`: More SafeMath in native orders * update orders docs Co-authored-by: Lawrence Forman <me@merklejerk.com>
142 lines
4.1 KiB
TypeScript
142 lines
4.1 KiB
TypeScript
import { SupportedProvider } from '@0x/subproviders';
|
|
import { EIP712TypedData } from '@0x/types';
|
|
import { hexUtils, providerUtils, signTypedDataUtils } from '@0x/utils';
|
|
import { Web3Wrapper } from '@0x/web3-wrapper';
|
|
import * as ethjs from 'ethereumjs-util';
|
|
|
|
/**
|
|
* Valid signature types on the Exchange Proxy.
|
|
*/
|
|
export enum SignatureType {
|
|
Illegal = 0,
|
|
Invalid = 1,
|
|
EIP712 = 2,
|
|
EthSign = 3,
|
|
}
|
|
|
|
/**
|
|
* Represents a raw EC signature.
|
|
*/
|
|
export interface ECSignature {
|
|
v: number;
|
|
r: string;
|
|
s: string;
|
|
}
|
|
|
|
/**
|
|
* A complete signature on the Exchange Proxy.
|
|
*/
|
|
export interface Signature extends ECSignature {
|
|
signatureType: SignatureType;
|
|
}
|
|
|
|
/**
|
|
* Sign a hash with the EthSign signature type on a provider.
|
|
*/
|
|
export async function ethSignHashWithProviderAsync(
|
|
hash: string,
|
|
signer: string,
|
|
provider: SupportedProvider,
|
|
): Promise<Signature> {
|
|
const w3w = new Web3Wrapper(providerUtils.standardizeOrThrow(provider));
|
|
const rpcSig = await w3w.signMessageAsync(signer, hash);
|
|
return {
|
|
...parseRpcSignature(rpcSig),
|
|
signatureType: SignatureType.EthSign,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Sign a hash with the EthSign signature type, given a private key.
|
|
*/
|
|
export function ethSignHashWithKey(hash: string, key: string): Signature {
|
|
const ethHash = hexUtils.toHex(
|
|
ethjs.sha3(hexUtils.concat(ethjs.toBuffer('\x19Ethereum Signed Message:\n32'), hash)),
|
|
);
|
|
return {
|
|
...ecSignHashWithKey(ethHash, key),
|
|
signatureType: SignatureType.EthSign,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Sign a typed data object with the EIP712 signature type on a provider.
|
|
*/
|
|
export async function eip712SignTypedDataWithProviderAsync(
|
|
data: EIP712TypedData,
|
|
signer: string,
|
|
provider: SupportedProvider,
|
|
): Promise<Signature> {
|
|
const w3w = new Web3Wrapper(providerUtils.standardizeOrThrow(provider));
|
|
const rpcSig = await w3w.signTypedDataAsync(signer, data);
|
|
return {
|
|
...parseRpcSignature(rpcSig),
|
|
signatureType: SignatureType.EIP712,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Sign a typed data object with the EIP712 signature type, given a private key.
|
|
*/
|
|
export function eip712SignTypedDataWithKey(typedData: EIP712TypedData, key: string): Signature {
|
|
const hash = hexUtils.toHex(signTypedDataUtils.generateTypedDataHash(typedData));
|
|
return {
|
|
...ecSignHashWithKey(hash, key),
|
|
signatureType: SignatureType.EIP712,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Sign an EIP712 hash with the EIP712 signature type, given a private key.
|
|
*/
|
|
export function eip712SignHashWithKey(hash: string, key: string): Signature {
|
|
return {
|
|
...ecSignHashWithKey(hash, key),
|
|
signatureType: SignatureType.EIP712,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Generate the EC signature for a hash given a private key.
|
|
*/
|
|
export function ecSignHashWithKey(hash: string, key: string): ECSignature {
|
|
const { v, r, s } = ethjs.ecsign(ethjs.toBuffer(hash), ethjs.toBuffer(key));
|
|
return {
|
|
v,
|
|
r: ethjs.bufferToHex(r),
|
|
s: ethjs.bufferToHex(s),
|
|
};
|
|
}
|
|
|
|
// Parse a hex signature returned by an RPC call into an `ECSignature`.
|
|
function parseRpcSignature(rpcSig: string): ECSignature {
|
|
if (hexUtils.size(rpcSig) !== 65) {
|
|
throw new Error(`Invalid RPC signature length: "${rpcSig}"`);
|
|
}
|
|
// Some providers encode V as 0,1 instead of 27,28.
|
|
const VALID_V_VALUES = [0, 1, 27, 28];
|
|
// Some providers return the signature packed as V,R,S and others R,S,V.
|
|
// Try to guess which encoding it is (with a slight preference for R,S,V).
|
|
let v = parseInt(rpcSig.slice(-2), 16);
|
|
if (VALID_V_VALUES.includes(v)) {
|
|
// Format is R,S,V
|
|
v = v >= 27 ? v : v + 27;
|
|
return {
|
|
r: hexUtils.slice(rpcSig, 0, 32),
|
|
s: hexUtils.slice(rpcSig, 32, 64),
|
|
v,
|
|
};
|
|
}
|
|
// Format should be V,R,S
|
|
v = parseInt(rpcSig.slice(2, 4), 16);
|
|
if (!VALID_V_VALUES.includes(v)) {
|
|
throw new Error(`Cannot determine RPC signature layout from V value: "${rpcSig}"`);
|
|
}
|
|
v = v >= 27 ? v : v + 27;
|
|
return {
|
|
v,
|
|
r: hexUtils.slice(rpcSig, 1, 33),
|
|
s: hexUtils.slice(rpcSig, 33, 65),
|
|
};
|
|
}
|