-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
453 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -59,3 +59,6 @@ typings/ | |
|
||
# next.js build output | ||
.next | ||
|
||
# index | ||
.tst.js |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,77 @@ | ||
# coinflux-api | ||
CoinFlux.com JavaScript API Client | ||
|
||
This is an asynchronous NodeJS client for the coinflux.com API. It exposes all the API methods found here: https://api.coinflux.com/v0/ through the ```api``` method: | ||
|
||
--- | ||
|
||
## Install | ||
|
||
```sh | ||
$ npm install pg | ||
``` | ||
|
||
--- | ||
|
||
## Example Usage: | ||
|
||
```javascript | ||
const key = '...'; // API Key | ||
const secret = '...'; // API Private Key | ||
const options = { | ||
"env": "test", //defaults to prod -> production | ||
"timeout": 20000 //optional, defaults to 20000 | ||
} | ||
const CoinFluxClient = require('coinflux-api'); | ||
const coinflux = new CoinFluxClient(key, secret, options); | ||
|
||
(async () => { | ||
// Get Ticker Info | ||
console.log(await coinflux.api('getRate', { pair : 'XBTEUR' })); | ||
|
||
// Get Ticker Info | ||
console.log(await coinflux.api('getWallet', { walletid : '0a846ac16f5842e6b48e769e6caa8942' })); | ||
})(); | ||
``` | ||
--- | ||
## __Documentation__ | ||
|
||
# __Public methods__ | ||
###getRates | ||
###getRate | ||
parameters: `pair` | ||
|
||
# __Private methods__ | ||
|
||
## __Fluxes__ | ||
### getFluxes | ||
### getFlux | ||
parameters: `fluxid` | ||
### getFluxAddresses | ||
parameters: `fluxid` | ||
### getAddress | ||
parameters: `address` | ||
|
||
## __Wallets__ | ||
### getWallets | ||
### getWallet | ||
parameters: `walletid` | ||
### getWalletHistory | ||
parameters: `walletid` | ||
### getWalletHistoryTx | ||
parameters: `walletid` and `historyid` | ||
|
||
## __Ledger__ | ||
### getLedger | ||
### getLedgerTx | ||
parameters: `ledgerid` | ||
|
||
## __Bank accounts__ | ||
### getBankAccounts | ||
### getBankAccount | ||
parameters: `accountid` | ||
|
||
--- | ||
|
||
### Credit: | ||
We used the NodeJS implementation from https://github.com/nothingisdead/npm-kraken-api as reference. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,294 @@ | ||
const axios = require('axios'); | ||
const crypto = require('crypto'); | ||
const qs = require('qs'); | ||
|
||
// Default options | ||
const defaults = { | ||
url : { | ||
prod: "https://api.coinflux.com", | ||
test: "https://apitestnet.coinflux.com" | ||
}, | ||
version : "v0", | ||
timeout : 20000, | ||
env : "prod" | ||
}; | ||
const methods = { | ||
public : ["getRates", "getRate"], | ||
private : ["getFluxes", "getFlux", "getFluxAddresses", "getAddress", "newAddress", "getWallets", "getWallet", "getLedger", "getLedgerTx", "getWalletHistory", "getWalletHistoryTx", "getBankAccounts", "getBankAccount"], | ||
get: ["getRates", "getRate", "getFluxes", "getFlux", "getFluxAddresses", "getAddress", "getWallets", "getWallet", "getLedger", "getLedgerTx", "getWalletHistory", "getWalletHistoryTx", "getBankAccounts", "getBankAccount"], | ||
post: ["newAddress"] | ||
} | ||
const paths = { | ||
getRates: "/public/rates", | ||
getRate: "/public/rates/{pair}", | ||
getFluxes: "/private/fluxes", | ||
getFlux: "/private/fluxes/{fluxid}", | ||
getFluxAddresses: "/private/fluxes/{fluxid}/addresses", | ||
getAddress: "/private/addresses/{address}", | ||
newAddress: "/private/addresses", | ||
getWallets: "/private/wallets", | ||
getWallet: "/private/wallets/{walletid}", | ||
getLedger: "/private/ledger", | ||
getLedgerTx: "/private/ledger/{ledgerid}", | ||
getWalletHistory: "/private/wallets/{walletid}/history", | ||
getWalletHistoryTx: "/private/wallets/{walletid}/history/{historyid}", | ||
getBankAccounts: "/private/bankaccounts", | ||
getBankAccount: "/private/bankaccounts/{accountid}" | ||
} | ||
|
||
// Create a signature for a request | ||
const getMessageSignature = (path, body, secret, nonce) => { | ||
const message = qs.stringify(body); | ||
const secret_buffer = Buffer.from(secret, 'hex'); | ||
const hash = new crypto.createHash('sha256'); | ||
const hmac = new crypto.createHmac('sha512', secret_buffer); | ||
const hash_digest = hash.update(nonce + message).digest('binary'); | ||
const hmac_digest = hmac.update(path + hash_digest, 'binary').digest('hex'); | ||
|
||
return hmac_digest; | ||
}; | ||
|
||
// Send an API request | ||
const rawRequest = async (url, headers, reqData, verb, timeout) => { | ||
// Set custom User-Agent string | ||
headers['user-agent'] = 'CoinFlux Javascript API Client'; | ||
|
||
const options = { headers, timeout }; | ||
|
||
Object.assign(options, { | ||
method : verb, | ||
url: url | ||
}); | ||
|
||
if (verb === 'POST') { | ||
Object.assign(options, { | ||
body : qs.stringify(reqData) | ||
}); | ||
} | ||
console.log(options); | ||
|
||
try { | ||
const {data} = await axios(options); | ||
return data; | ||
} catch (err) { | ||
err = err.response; | ||
if(Object.keys(err.data).includes("error")) { | ||
|
||
throw { | ||
statusCode: err.status, | ||
statusText: err.statusText, | ||
error: err.data.error | ||
}; | ||
} else { | ||
throw { | ||
statusCode: err.status, | ||
statusText: err.statusText, | ||
error: err.statusText | ||
}; | ||
} | ||
} | ||
|
||
|
||
}; | ||
|
||
/** | ||
* CoinFluxClient connects to the coinflux.com API | ||
* @param {String} key API Key | ||
* @param {String} secret API Secret | ||
* @param {Object} [options={}] | ||
* @param {Number} timeout Maximum timeout (in milliseconds) for all API-calls (passed to `request`) | ||
* @param {string} version API version | ||
* @param {string} env API environment [prod, test] | ||
*/ | ||
class CoinFluxClient { | ||
constructor(key, secret, options) { | ||
|
||
if (typeof options !== 'object') { | ||
options = {}; | ||
} | ||
|
||
this.config = Object.assign({ key, secret }, defaults, options); | ||
} | ||
|
||
|
||
/** | ||
* This method makes a public or private API request. | ||
* @param {String} method The API method (public or private) | ||
* @param {Object} params Arguments to pass to the api call | ||
* @param {Function} callback A callback function to be executed when the request is complete | ||
* @return {Object} The request object | ||
*/ | ||
api(method, params, callback) { | ||
// Default params to empty object | ||
if(typeof params === 'function') { | ||
callback = params; | ||
params = {}; | ||
} | ||
|
||
if(methods.public.includes(method)) { | ||
return this.publicMethod(method, params, callback); | ||
} | ||
else if(methods.private.includes(method)) { | ||
return this.privateMethod(method, params, callback); | ||
} | ||
else { | ||
throw new Error(method + ' is not a valid API method.'); | ||
} | ||
} | ||
|
||
publicMethod (method, params, callback) { | ||
if(typeof params === 'function') { | ||
callback = params; | ||
params = {}; | ||
} | ||
|
||
let path = '/' + this.config.version + paths[method]; | ||
let verb = 'GET' | ||
|
||
switch (method) { | ||
case 'getRate': | ||
if (params.pair) { | ||
path = path.replace("{pair}", params.pair) | ||
} else { | ||
throw new Error("Missing parmater: pair"); | ||
} | ||
break; | ||
} | ||
|
||
const url = this.config.url[this.config.env] + path; | ||
const response = rawRequest(url, {}, {}, verb, this.config.timeout); | ||
|
||
if(typeof callback === 'function') { | ||
response | ||
.then((result) => callback(null, result)) | ||
.catch((error) => callback(error, null)); | ||
} | ||
|
||
return response; | ||
} | ||
|
||
privateMethod (method, params, callback) { | ||
if(typeof params === 'function') { | ||
callback = params; | ||
params = {}; | ||
} | ||
|
||
let path = '/' + this.config.version + paths[method]; | ||
let verb = 'GET'; | ||
|
||
switch (method) { | ||
case 'getFlux': | ||
if (params.fluxid) { | ||
path = path.replace("{fluxid}", params.fluxid) | ||
} else { | ||
throw new Error("Missing parmater: fluxid"); | ||
} | ||
break; | ||
case 'getFluxAddresses': | ||
if (params.fluxid) { | ||
path = path.replace("{fluxid}", params.fluxid) | ||
} else { | ||
throw new Error("Missing parmater: fluxid"); | ||
} | ||
break; | ||
case 'getAddress': | ||
if (params.address) { | ||
path = path.replace("{address}", params.address) | ||
} else { | ||
throw new Error("Missing parmater: address"); | ||
} | ||
break; | ||
case 'newAddress': | ||
verb = 'POST'; | ||
if (params.fluxid) { | ||
path = path.replace("{fluxid}", params.fluxid) | ||
} else { | ||
throw new Error("Missing parmater: fluxid"); | ||
} | ||
break; | ||
case 'getWallet': | ||
if (params.walletid) { | ||
path = path.replace("{walletid}", params.walletid) | ||
} else { | ||
throw new Error("Missing parmater: walletid"); | ||
} | ||
break; | ||
case 'getLedgerTx': | ||
if (params.ledgerid) { | ||
path = path.replace("{ledgerid}", params.ledgerid) | ||
} else { | ||
throw new Error("Missing parmater: ledgerid"); | ||
} | ||
break; | ||
case 'getWalletHistory': | ||
if (params.walletid) { | ||
path = path.replace("{walletid}", params.walletid) | ||
} else { | ||
throw new Error("Missing parmater: walletid"); | ||
} | ||
break; | ||
case 'getWalletHistoryTx': | ||
if (params.walletid) { | ||
path = path.replace("{walletid}", params.walletid) | ||
} else { | ||
throw new Error("Missing parmater: walletid"); | ||
} | ||
|
||
if (params.historyid) { | ||
path = path.replace("{historyid}", params.historyid) | ||
} else { | ||
throw new Error("Missing parmater: historyid"); | ||
} | ||
break; | ||
case 'getBankAccount': | ||
if (params.accountid) { | ||
path = path.replace("{accountid}", params.accountid) | ||
} else { | ||
throw new Error("Missing parmater: accountid"); | ||
} | ||
break; | ||
} | ||
|
||
const url = this.config.url[this.config.env] + path; | ||
const nonce = new Date() * 1000; // spoof microsecond | ||
|
||
let headers = { | ||
"coinflux-api-key" : this.config.key, | ||
"coinflux-api-sign" : "", | ||
"coinflux-api-nonce" : nonce, | ||
}; | ||
|
||
let signature, response; | ||
|
||
if (methods.get.includes(method)) { | ||
signature = getMessageSignature( | ||
path, | ||
'', | ||
this.config.secret, | ||
nonce | ||
); | ||
headers["coinflux-api-sign"] = signature; | ||
response = rawRequest(url, headers, {}, verb, this.config.timeout); | ||
} else if (methods.post.includes(method)) { | ||
signature = getMessageSignature( | ||
path, | ||
params, | ||
this.config.secret, | ||
nonce | ||
); | ||
headers["coinflux-api-sign"] = signature; | ||
response = rawRequest(url, headers, params, verb, this.config.timeout); | ||
} | ||
|
||
if(typeof callback === 'function') { | ||
response | ||
.then((result) => callback(null, result)) | ||
.catch((error) => callback(error, null)); | ||
} | ||
|
||
return response; | ||
} | ||
|
||
} | ||
|
||
module.exports = CoinFluxClient; |
Oops, something went wrong.