aboutsummaryrefslogtreecommitdiffstats
path: root/packages/connect/src/http_client.ts
blob: 85dc83c61900786dc5465f733b5568ebdf1c978d (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
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
import 'isomorphic-fetch';
import * as _ from 'lodash';
import {BigNumber} from 'bignumber.js';
import * as queryString from 'query-string';
import {assert} from '@0xproject/assert';
import {schemas} from '@0xproject/json-schemas';
import {SignedOrder} from '0x.js';
import {
    Client,
    FeesRequest,
    FeesResponse,
    OrderbookRequest,
    OrderbookResponse,
    OrdersRequest,
    TokenPairsItem,
    TokenPairsRequest,
} from './types';
import {schemas as clientSchemas} from './schemas/schemas';
import {typeConverters} from './utils/type_converters';

// TODO: move this and bigNumberConfigs in the 0x.js package into one place
BigNumber.config({
    EXPONENTIAL_AT: 1000,
});

interface RequestOptions {
    params?: object;
    payload?: object;
}

enum RequestType {
    Get = 'GET',
    Post = 'POST',
}

/**
 * This class includes all the functionality related to interacting with a set of HTTP endpoints
 * that implement the standard relayer API v0
 */
export class HttpClient implements Client {
    private apiEndpointUrl: string;
    /**
     * Instantiates a new HttpClient instance
     * @param   url    The base url for making API calls
     * @return  An instance of HttpClient
     */
    constructor(url: string) {
        assert.isHttpUrl('url', url);
        this.apiEndpointUrl = url;
    }
    /**
     * Retrieve token pair info from the API
     * @param   request     A TokenPairsRequest instance describing specific token information
     *                      to retrieve
     * @return  The resulting TokenPairsItems that match the request
     */
    public async getTokenPairsAsync(request?: TokenPairsRequest): Promise<TokenPairsItem[]> {
        if (!_.isUndefined(request)) {
            assert.doesConformToSchema('request', request, clientSchemas.relayerTokenPairsRequestSchema);
        }
        const requestOpts = {
            params: request,
        };
        const tokenPairs = await this._requestAsync('/token_pairs', RequestType.Get, requestOpts);
        assert.doesConformToSchema(
            'tokenPairs', tokenPairs, schemas.relayerApiTokenPairsResponseSchema);
        _.each(tokenPairs, (tokenPair: object) => {
            typeConverters.convertStringsFieldsToBigNumbers(tokenPair, [
                'tokenA.minAmount',
                'tokenA.maxAmount',
                'tokenB.minAmount',
                'tokenB.maxAmount',
            ]);
        });
        return tokenPairs;
    }
    /**
     * Retrieve orders from the API
     * @param   request     An OrdersRequest instance describing specific orders to retrieve
     * @return  The resulting SignedOrders that match the request
     */
    public async getOrdersAsync(request?: OrdersRequest): Promise<SignedOrder[]> {
        if (!_.isUndefined(request)) {
            assert.doesConformToSchema('request', request, clientSchemas.relayerOrdersRequestSchema);
        }
        const requestOpts = {
            params: request,
        };
        const orders = await this._requestAsync(`/orders`, RequestType.Get, requestOpts);
        assert.doesConformToSchema('orders', orders, schemas.signedOrdersSchema);
        _.each(orders, (order: object) => typeConverters.convertOrderStringFieldsToBigNumber(order));
        return orders;
    }
    /**
     * Retrieve a specific order from the API
     * @param   orderHash     An orderHash generated from the desired order
     * @return  The SignedOrder that matches the supplied orderHash
     */
    public async getOrderAsync(orderHash: string): Promise<SignedOrder> {
        assert.doesConformToSchema('orderHash', orderHash, schemas.orderHashSchema);
        const order = await this._requestAsync(`/order/${orderHash}`, RequestType.Get);
        assert.doesConformToSchema('order', order, schemas.signedOrderSchema);
        typeConverters.convertOrderStringFieldsToBigNumber(order);
        return order;
    }
    /**
     * Retrieve an orderbook from the API
     * @param   request     An OrderbookRequest instance describing the specific orderbook to retrieve
     * @return  The resulting OrderbookResponse that matches the request
     */
    public async getOrderbookAsync(request: OrderbookRequest): Promise<OrderbookResponse> {
        assert.doesConformToSchema('request', request, clientSchemas.relayerOrderBookRequestSchema);
        const requestOpts = {
            params: request,
        };
        const orderBook = await this._requestAsync('/orderbook', RequestType.Get, requestOpts);
        assert.doesConformToSchema('orderBook', orderBook, schemas.relayerApiOrderBookResponseSchema);
        typeConverters.convertOrderbookStringFieldsToBigNumber(orderBook);
        return orderBook;
    }
    /**
     * Retrieve fee information from the API
     * @param   request     A FeesRequest instance describing the specific fees to retrieve
     * @return  The resulting FeesResponse that matches the request
     */
    public async getFeesAsync(request: FeesRequest): Promise<FeesResponse> {
        assert.doesConformToSchema('request', request, schemas.relayerApiFeesPayloadSchema);
        typeConverters.convertBigNumberFieldsToStrings(request, [
            'makerTokenAmount',
            'takerTokenAmount',
            'expirationUnixTimestampSec',
            'salt',
        ]);
        const requestOpts = {
            payload: request,
        };
        const fees = await this._requestAsync('/fees', RequestType.Post, requestOpts);
        assert.doesConformToSchema('fees', fees, schemas.relayerApiFeesResponseSchema);
        typeConverters.convertStringsFieldsToBigNumbers(fees, ['makerFee', 'takerFee']);
        return fees;
    }
    /**
     * Submit a signed order to the API
     * @param   signedOrder     A SignedOrder instance to submit
     */
    public async submitOrderAsync(signedOrder: SignedOrder): Promise<void> {
        assert.doesConformToSchema('signedOrder', signedOrder, schemas.signedOrderSchema);
        const requestOpts = {
            payload: signedOrder,
        };
        await this._requestAsync('/order', RequestType.Post, requestOpts);
    }
    private async _requestAsync(path: string, requestType: RequestType, requestOptions?: RequestOptions): Promise<any> {
        const params = _.get(requestOptions, 'params');
        const payload = _.get(requestOptions, 'payload');
        let query = '';
        if (!_.isUndefined(params) && !_.isEmpty(params)) {
            const stringifiedParams = queryString.stringify(params);
            query = `?${stringifiedParams}`;
        }
        const url = `${this.apiEndpointUrl}/v0${path}${query}`;
        const headers = new Headers({
            'content-type': 'application/json',
        });

        const response = await fetch(url, {
            method: requestType,
            body: JSON.stringify(payload),
            headers,
        });
        if (!response.ok) {
            throw Error(response.statusText);
        }
        const json = await response.json();
        return json;
    }
}