aboutsummaryrefslogtreecommitdiffstats
path: root/packages/order-utils/src/order_hash.ts
blob: 1cde72a73b65dfdda5042363690aacb3594456d1 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
import { schemas, SchemaValidator } from '@0xproject/json-schemas';
import { Order, SignedOrder, SolidityTypes } from '@0xproject/types';
import { BigNumber } from '@0xproject/utils';
import BN = require('bn.js');
import * as ethABI from 'ethereumjs-abi';
import * as ethUtil from 'ethereumjs-util';
import * as _ from 'lodash';

import { assert } from './assert';

const INVALID_TAKER_FORMAT = 'instance.taker is not of a type(s) string';

/**
 * Converts BigNumber instance to BN
 * The only reason we convert to BN is to remain compatible with `ethABI.soliditySHA3` that
 * expects values of Solidity type `uint` to be passed as type `BN`.
 * We do not use BN anywhere else in the codebase.
 */
function bigNumberToBN(value: BigNumber): BN {
    return new BN(value.toString(), 10);
}

/**
 * Computes the orderHash for a supplied order.
 * @param   order   An object that conforms to the Order or SignedOrder interface definitions.
 * @return  The resulting orderHash from hashing the supplied order.
 */
export function getOrderHashHex(order: Order | SignedOrder): string {
    try {
        assert.doesConformToSchema('order', order, schemas.orderSchema);
    } catch (error) {
        if (_.includes(error.message, INVALID_TAKER_FORMAT)) {
            const errMsg =
                'Order taker must be of type string. If you want anyone to be able to fill an order - pass ZeroEx.NULL_ADDRESS';
            throw new Error(errMsg);
        }
        throw error;
    }
    const orderParts = [
        { value: order.exchangeContractAddress, type: SolidityTypes.Address },
        { value: order.maker, type: SolidityTypes.Address },
        { value: order.taker, type: SolidityTypes.Address },
        { value: order.makerTokenAddress, type: SolidityTypes.Address },
        { value: order.takerTokenAddress, type: SolidityTypes.Address },
        { value: order.feeRecipient, type: SolidityTypes.Address },
        {
            value: bigNumberToBN(order.makerTokenAmount),
            type: SolidityTypes.Uint256,
        },
        {
            value: bigNumberToBN(order.takerTokenAmount),
            type: SolidityTypes.Uint256,
        },
        {
            value: bigNumberToBN(order.makerFee),
            type: SolidityTypes.Uint256,
        },
        {
            value: bigNumberToBN(order.takerFee),
            type: SolidityTypes.Uint256,
        },
        {
            value: bigNumberToBN(order.expirationUnixTimestampSec),
            type: SolidityTypes.Uint256,
        },
        { value: bigNumberToBN(order.salt), type: SolidityTypes.Uint256 },
    ];
    const types = _.map(orderParts, o => o.type);
    const values = _.map(orderParts, o => o.value);
    const hashBuff = ethABI.soliditySHA3(types, values);
    const hashHex = ethUtil.bufferToHex(hashBuff);
    return hashHex;
}

/**
 * Checks if the supplied hex encoded order hash is valid.
 * Note: Valid means it has the expected format, not that an order with the orderHash exists.
 * Use this method when processing orderHashes submitted as user input.
 * @param   orderHash    Hex encoded orderHash.
 * @return  Whether the supplied orderHash has the expected format.
 */
export function isValidOrderHash(orderHash: string): boolean {
    // Since this method can be called to check if any arbitrary string conforms to an orderHash's
    // format, we only assert that we were indeed passed a string.
    assert.isString('orderHash', orderHash);
    const schemaValidator = new SchemaValidator();
    const isValid = schemaValidator.validate(orderHash, schemas.orderHashSchema).valid;
    return isValid;
}