Skip to content

Commit

Permalink
simplifies internal request headers and signature logic
Browse files Browse the repository at this point in the history
  • Loading branch information
ivoputzer committed Jan 7, 2019
1 parent 78c1926 commit a706ad9
Show file tree
Hide file tree
Showing 8 changed files with 231 additions and 181 deletions.
10 changes: 10 additions & 0 deletions auth.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/* eslint-disable camelcase */

const crypto = require('crypto')

exports.signatureFor = ({ timestamp, method, path = String.prototype, body = String.prototype }, { npm_config_coinbase_pro_api_secret = String.prototype } = process.env, { createHmac } = crypto) => {
const buffer = Buffer.from(npm_config_coinbase_pro_api_secret, 'base64')
return createHmac('sha256', buffer)
.update(timestamp + method.toUpperCase() + path + body)
.digest('base64')
}
42 changes: 42 additions & 0 deletions client.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/* eslint-disable camelcase */

const https = require('https')

const info = require('./package')
const auth = require('./auth')

exports.request = (options, env = process.env, { request } = https, { hostnameFor, headersFor, toString } = exports) => {
return new Promise((resolve, reject) => {
options.hostname = hostnameFor(env)
options.headers = headersFor(options, env)

request(options, resolve)
.on('error', reject)
.end(options.body)
})
}

exports.toString = (stream, chunks = [], encoding = 'utf8', { concat } = Buffer) => {
return new Promise((resolve, reject) => {
stream.on('data', (data) => chunks.push(data))
stream.on('error', reject)
stream.on('end', (_) => resolve(concat(chunks).toString(encoding)))
})
}

exports.hostnameFor = ({ npm_config_coinbase_pro_api_hostname, npm_config_coinbase_pro_api_sandbox } = process.env, { coinbase_pro_api_sandbox_hostname, coinbase_pro_api_hostname } = info.config) => {
return npm_config_coinbase_pro_api_hostname || (['true', '1'].includes(npm_config_coinbase_pro_api_sandbox) ? coinbase_pro_api_sandbox_hostname : coinbase_pro_api_hostname)
}

exports.headersFor = (options, { npm_config_coinbase_pro_api_key = String.prototype, npm_config_coinbase_pro_api_passphrase = String.prototype, npm_config_coinbase_pro_api_secret = String.prototype } = process.env, { signatureFor } = auth, { name, version } = info) => {
const timestamp = 1e-3 * Date.now() // fixme: not sure if timestamp should be created here
return {
'User-Agent': name + '/' + version,
'Accept': 'application/json',
'Content-Type': 'application/json',
'CB-ACCESS-KEY': npm_config_coinbase_pro_api_key,
'CB-ACCESS-PASSPHRASE': npm_config_coinbase_pro_api_passphrase,
'CB-ACCESS-SIGN': signatureFor(Object.assign(options, { timestamp }), { npm_config_coinbase_pro_api_secret }),
'CB-ACCESS-TIMESTAMP': timestamp
}
}
90 changes: 45 additions & 45 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,114 +1,114 @@
const querystring = require('querystring')
const client = require('./lib/client')
const client = require('./client')

exports.getProducts = (query = {}, { parse } = JSON, { stringify } = querystring, { request } = client) => request({
exports.getProducts = (query = {}, { parse } = JSON, { stringify } = querystring, { env } = process, { toString, request } = client) => request({
method: 'get',
path: '/products?' + stringify(query)
}).then(parse)
}, env).then(toString).then(parse)

exports.getProductOrderBook = (productId, query = {}, { parse } = JSON, { stringify } = querystring, { request } = client) => request({
exports.getProductOrderBook = (productId, query = {}, { parse } = JSON, { stringify } = querystring, { env } = process, { toString, request } = client) => request({
method: 'get',
path: '/products/' + productId + '/book?' + stringify(query)
}).then(parse)
}, env).then(toString).then(parse)

exports.getProductTicker = (productId, query = {}, { parse } = JSON, { stringify } = querystring, { request } = client) => request({
exports.getProductTicker = (productId, query = {}, { parse } = JSON, { stringify } = querystring, { env } = process, { toString, request } = client) => request({
method: 'get',
path: '/products/' + productId + '/ticker?' + stringify(query)
}).then(parse)
}, env).then(toString).then(parse)

exports.getProductTrades = (productId, query = {}, { parse } = JSON, { stringify } = querystring, { request } = client) => request({
exports.getProductTrades = (productId, query = {}, { parse } = JSON, { stringify } = querystring, { env } = process, { toString, request } = client) => request({
method: 'get',
path: '/products/' + productId + '/trades?' + stringify(query)
}).then(parse)
}, env).then(toString).then(parse)

exports.getProductHistoricRates = (productId, query = {}, { parse } = JSON, { stringify } = querystring, { request } = client) => request({
exports.getProductHistoricRates = (productId, query = {}, { parse } = JSON, { stringify } = querystring, { env } = process, { toString, request } = client) => request({
method: 'get',
path: '/products/' + productId + '/candles?' + stringify(query)
}).then(parse)
}, env).then(toString).then(parse)

exports.getProduct24HrStats = (productId, query = {}, { parse } = JSON, { stringify } = querystring, { request } = client) => request({
exports.getProduct24HrStats = (productId, query = {}, { parse } = JSON, { stringify } = querystring, { env } = process, { toString, request } = client) => request({
method: 'get',
path: '/products/' + productId + '/stats?' + stringify(query)
}).then(parse)
}, env).then(toString).then(parse)

exports.getCurrencies = (query = {}, { parse } = JSON, { stringify } = querystring, { request } = client) => request({
exports.getCurrencies = (query = {}, { parse } = JSON, { stringify } = querystring, { env } = process, { toString, request } = client) => request({
method: 'get',
path: '/currencies?' + stringify(query)
}).then(parse)
}, env).then(toString).then(parse)

exports.getTime = (query = {}, { parse } = JSON, { stringify } = querystring, { request } = client) => request({
exports.getTime = (query = {}, { parse } = JSON, { stringify } = querystring, { env } = process, { toString, request } = client) => request({
method: 'get',
path: '/time?' + stringify(query)
}).then(parse)
}, env).then(toString).then(parse)

exports.getCoinbaseAccounts = (query = {}, { parse } = JSON, { stringify } = querystring, { request } = client) => request({
exports.getCoinbaseAccounts = (query = {}, { parse } = JSON, { stringify } = querystring, { env } = process, { toString, request } = client) => request({
method: 'get',
path: '/coinbase-accounts?' + stringify(query)
}).then(parse)
}, env).then(toString).then(parse)

exports.getPaymentMethods = (query = {}, { parse } = JSON, { stringify } = querystring, { request } = client) => request({
exports.getPaymentMethods = (query = {}, { parse } = JSON, { stringify } = querystring, { env } = process, { toString, request } = client) => request({
method: 'get',
path: '/payment-methods?' + stringify(query)
}).then(parse)
}, env).then(toString).then(parse)

exports.getAccounts = (query = {}, { parse } = JSON, { stringify } = querystring, { request } = client) => request({
exports.getAccounts = (query = {}, { parse } = JSON, { stringify } = querystring, { env } = process, { toString, request } = client) => request({
method: 'get',
path: '/accounts?' + stringify(query)
}).then(parse)
}, env).then(toString).then(parse)

exports.getAccount = (accountId, query = {}, { parse } = JSON, { stringify } = querystring, { request } = client) => request({
exports.getAccount = (accountId, query = {}, { parse } = JSON, { stringify } = querystring, { env } = process, { toString, request } = client) => request({
method: 'get',
path: '/accounts/' + accountId + '?' + stringify(query)
}).then(parse)
}, env).then(toString).then(parse)

exports.getAccountHistory = (accountId, query = {}, { parse } = JSON, { stringify } = querystring, { request } = client) => request({
exports.getAccountHistory = (accountId, query = {}, { parse } = JSON, { stringify } = querystring, { env } = process, { toString, request } = client) => request({
method: 'get',
path: '/accounts/' + accountId + '/ledger?' + stringify(query)
}).then(parse)
}, env).then(toString).then(parse)

exports.getAccountTransfers = (accountId, query = {}, { parse } = JSON, { stringify } = querystring, { request } = client) => request({
exports.getAccountTransfers = (accountId, query = {}, { parse } = JSON, { stringify } = querystring, { env } = process, { toString, request } = client) => request({
method: 'get',
path: '/accounts/' + accountId + '/transfers?' + stringify(query)
}).then(parse)
}, env).then(toString).then(parse)

exports.getAccountHolds = (accountId, query = {}, { parse } = JSON, { stringify } = querystring, { request } = client) => request({
exports.getAccountHolds = (accountId, query = {}, { parse } = JSON, { stringify } = querystring, { env } = process, { toString, request } = client) => request({
method: 'get',
path: '/accounts/' + accountId + '/holds?' + stringify(query)
}).then(parse)
}, env).then(toString).then(parse)

exports.placeOrder = (data, { parse } = JSON, { stringify } = JSON, { request } = client) => request({
exports.placeOrder = (data, { parse } = JSON, { stringify } = JSON, { env } = process, { toString, request } = client) => request({
method: 'post',
path: '/orders',
body: stringify(data)
}).then(parse)
}, env).then(toString).then(parse)

exports.cancelOrder = (orderId, query = {}, { parse } = JSON, { stringify } = querystring, { request } = client) => request({
exports.cancelOrder = (orderId, query = {}, { parse } = JSON, { stringify } = querystring, { env } = process, { toString, request } = client) => request({
method: 'delete',
path: '/orders/' + orderId + '?' + stringify(query)
}).then(parse)
}, env).then(toString).then(parse)

exports.cancelOrders = (query = {}, { parse } = JSON, { stringify } = querystring, { request } = client) => request({
exports.cancelOrders = (query = {}, { parse } = JSON, { stringify } = querystring, { env } = process, { toString, request } = client) => request({
method: 'delete',
path: '/orders?' + stringify(query)
}).then(parse)
}, env).then(toString).then(parse)

exports.getOrders = (query = {}, { parse } = JSON, { stringify } = querystring, { request } = client) => request({
exports.getOrders = (query = {}, { parse } = JSON, { stringify } = querystring, { env } = process, { toString, request } = client) => request({
method: 'get',
path: '/orders?' + stringify(query)
}).then(parse)
}, env).then(toString).then(parse)

exports.getOrder = (orderId, query = {}, { parse } = JSON, { stringify } = querystring, { request } = client) => request({
exports.getOrder = (orderId, query = {}, { parse } = JSON, { stringify } = querystring, { env } = process, { toString, request } = client) => request({
method: 'get',
path: '/orders/' + orderId + '?' + stringify(query)
}).then(parse)
}, env).then(toString).then(parse)

exports.getFills = (query = {}, { parse } = JSON, { stringify } = querystring, { request } = client) => request({
exports.getFills = (query = {}, { parse } = JSON, { stringify } = querystring, { env } = process, { toString, request } = client) => request({
method: 'get',
path: '/fills?' + stringify(query)
}).then(parse)
}, env).then(toString).then(parse)

exports.convert = (data, { parse } = JSON, { stringify } = JSON, { request } = client) => request({
exports.convert = (data, { parse } = JSON, { stringify } = JSON, { env } = process, { toString, request } = client) => request({
method: 'post',
path: '/conversions',
body: stringify(data)
}).then(parse)
}, env).then(toString).then(parse)
58 changes: 0 additions & 58 deletions lib/client.js

This file was deleted.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "coinbase-pro-api",
"version": "0.0.2",
"version": "0.0.3",
"description": "lightweight coinbase pro api implementation",
"main": "index.js",
"config": {
Expand Down
26 changes: 26 additions & 0 deletions test/auth.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
test('coinbase-pro-api/auth', () => {
const { deepStrictEqual } = require('assert')

test('.signatureFor', () => {
const { signatureFor } = require('../auth')
const { createHmac } = require('crypto')

test('is callable', () => {
deepStrictEqual(typeof signatureFor, 'function')
})

test('creates sha256 hmac signature using crypto', () => {
const signature = signatureFor({ timestamp: 1, method: 'get' })
deepStrictEqual(signature, createHmac('sha256', Buffer.from(String.prototype, 'base64'))
.update(`1GET`)
.digest('base64'))
})

test('takes an optional secret argument', () => {
const signature = signatureFor({ timestamp: 1, method: 'get' }, { npm_config_coinbase_pro_api_secret: String.prototype })
deepStrictEqual(signature, createHmac('sha256', Buffer.from(String.prototype, 'base64'))
.update(`1GET`)
.digest('base64'))
})
})
})
Loading

0 comments on commit a706ad9

Please sign in to comment.