├── .babelrc ├── .circleci └── config.yml ├── .github └── issue_template.md ├── .gitignore ├── LICENSE ├── README.md ├── examples ├── example.css ├── index.html ├── test-server-sdk.js └── zabo.js ├── package-lock.json ├── package.json ├── src ├── api.js ├── constants.js ├── core │ └── SDK.js ├── err.js ├── index.d.ts ├── index.js ├── resources │ ├── accounts.js │ ├── blockchains.js │ ├── currencies.js │ ├── index.js │ ├── providers.js │ ├── teams.js │ ├── trading.js │ ├── transactions.js │ └── users.js ├── sdk.js └── utils.js ├── test ├── api.spec.js ├── index.spec.js ├── mock │ ├── api.js │ ├── dummy │ │ ├── account.json │ │ ├── address.json │ │ ├── balances.json │ │ ├── blockchains │ │ │ ├── balances.json │ │ │ ├── block.json │ │ │ ├── contract.json │ │ │ ├── token-transfers.json │ │ │ ├── tokens.json │ │ │ ├── transaction.json │ │ │ └── transactions.json │ │ ├── currencies.json │ │ ├── exchange-rates.json │ │ ├── index.js │ │ ├── providers.json │ │ ├── team.json │ │ ├── trading │ │ │ ├── order.json │ │ │ ├── orders.json │ │ │ ├── symbols.json │ │ │ └── ticker.json │ │ ├── transaction.json │ │ ├── transactions.json │ │ ├── user.json │ │ └── users.json │ └── interfaces.js ├── resources │ ├── accounts.spec.js │ ├── blockchains.spec.js │ ├── currencies.spec.js │ ├── providers.spec.js │ ├── teams.spec.js │ ├── trading.spec.js │ ├── transactions.spec.js │ └── utils.spec.js └── utils.spec.js ├── tsconfig.json └── webpack.config.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | "@babel/plugin-proposal-class-properties" 4 | ] 5 | } -------------------------------------------------------------------------------- /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | jobs: 3 | checkout: 4 | working_directory: ~/repo 5 | docker: 6 | - image: circleci/node:12 7 | 8 | steps: 9 | - restore_cache: 10 | keys: 11 | - v1-repo-{{ .Environment.CIRCLE_SHA1 }} 12 | - v1-repo- 13 | 14 | - checkout 15 | 16 | - save_cache: 17 | key: v1-repo-{{ .Environment.CIRCLE_SHA1 }} 18 | paths: 19 | - ~/repo 20 | 21 | build: 22 | working_directory: ~/repo 23 | docker: 24 | - image: circleci/node:12 25 | 26 | steps: 27 | - restore_cache: 28 | keys: 29 | - v1-repo-{{ .Environment.CIRCLE_SHA1 }} 30 | 31 | - restore_cache: 32 | keys: 33 | - v1-dependencies-{{ checksum "package-lock.json" }} 34 | - v1-dependencies- 35 | 36 | - run: 37 | name: "Install tools" 38 | command: sudo apt-get update && sudo apt-get install -y python-dev moreutils 39 | 40 | - run: npm ci 41 | 42 | - save_cache: 43 | paths: 44 | - ~/repo/node_modules 45 | - ~/repo/awsdir 46 | key: v1-dependencies-{{ checksum "package-lock.json" }} 47 | 48 | test: 49 | working_directory: ~/repo 50 | docker: 51 | - image: circleci/node:12 52 | 53 | steps: 54 | - restore_cache: 55 | keys: 56 | - v1-repo-{{ .Environment.CIRCLE_SHA1 }} 57 | 58 | - restore_cache: 59 | keys: 60 | - v1-dependencies-{{ checksum "package-lock.json" }} 61 | 62 | - restore_cache: 63 | keys: 64 | - v1-{{ .Environment.CIRCLE_BRANCH }}-{{ .Environment.CIRCLE_SHA1 }} 65 | 66 | - run: npm run test 67 | 68 | compile: 69 | working_directory: ~/repo 70 | docker: 71 | - image: circleci/node:12 72 | 73 | steps: 74 | - restore_cache: 75 | keys: 76 | - v1-repo-{{ .Environment.CIRCLE_SHA1 }} 77 | 78 | - restore_cache: 79 | keys: 80 | - v1-dependencies-{{ checksum "package-lock.json" }} 81 | 82 | - restore_cache: 83 | keys: 84 | - v1-{{ .Environment.CIRCLE_BRANCH }}-{{ .Environment.CIRCLE_SHA1 }} 85 | - v1-{{ .Environment.CIRCLE_BRANCH }}- 86 | 87 | - run: 88 | name: "Compile static files" 89 | command: npm run build 90 | 91 | - save_cache: 92 | paths: 93 | - ~/repo/dist 94 | key: v1-{{ .Environment.CIRCLE_BRANCH }}-{{ .Environment.CIRCLE_SHA1 }} 95 | 96 | deploy: 97 | docker: 98 | - image: circleci/node:12 99 | 100 | working_directory: ~/repo 101 | 102 | steps: 103 | - run: 104 | name: "Check if we want to deploy" 105 | command: | 106 | if [[ "$CIRCLE_BRANCH" =~ ^(develop|stage|master|[0-9]+\.[0-9]+\.[0-9]+)$ ]]; then 107 | echo "Continuing with build" 108 | else 109 | circleci step halt 110 | fi 111 | 112 | - run: 113 | name: "Install tools" 114 | command: | 115 | sudo apt-get update && sudo apt-get install -y jq python-dev moreutils 116 | curl "https://s3.amazonaws.com/aws-cli/awscli-bundle.zip" -o "awscli-bundle.zip" 117 | unzip awscli-bundle.zip 118 | sudo awscli-bundle/install -i /usr/local/aws -b /usr/local/bin/aws 119 | 120 | - restore_cache: 121 | keys: 122 | - v1-repo-{{ .Environment.CIRCLE_SHA1 }} 123 | 124 | - restore_cache: 125 | keys: 126 | - v1-{{ .Environment.CIRCLE_BRANCH }}-{{ .Environment.CIRCLE_SHA1 }} 127 | 128 | - run: 129 | name: "Get version" 130 | command: | 131 | echo 'export THISVER="$(npm run getv | tail -1)"' >> $BASH_ENV 132 | 133 | - run: 134 | name: "Deploy code" 135 | command: | 136 | cd dist 137 | if [ $CIRCLE_BRANCH == 'develop' ]; then 138 | aws s3 sync ./ "s3://cdn.zabo.com/develop/$THISVER" 139 | aws s3 sync ./ "s3://cdn.zabo.com/develop/latest" 140 | elif [[ "$CIRCLE_BRANCH" =~ ^(master|stage|[0-9]+\.[0-9]+\.[0-9]+)$ ]]; then 141 | if [ $CIRCLE_BRANCH == 'master' ]; then 142 | aws s3 sync ./ "s3://cdn.zabo.com/$THISVER" 143 | aws s3 sync ./ "s3://cdn.zabo.com/latest" 144 | else 145 | aws s3 sync ./ "s3://cdn.zabo.com/$CIRCLE_BRANCH" 146 | fi 147 | else 148 | echo "no build" 149 | fi 150 | 151 | workflows: 152 | version: 2 153 | build-compile-deploy: 154 | jobs: 155 | - checkout 156 | - build: 157 | requires: 158 | - checkout 159 | - test: 160 | requires: 161 | - build 162 | - compile: 163 | requires: 164 | - build 165 | - deploy: 166 | context: zabo-cdn 167 | requires: 168 | - compile 169 | - test 170 | -------------------------------------------------------------------------------- /.github/issue_template.md: -------------------------------------------------------------------------------- 1 | ## What SDK version are you using? 2 | 3 | 4 | ### What technology and versions are you using that is causing issues with the SDK? 5 | 6 | 11 | 12 | 13 | ### What did you do? 14 | 15 | 19 | 20 | ### What did you expect to see? 21 | 22 | 23 | 24 | ### What did you see instead? 25 | 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (https://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # TypeScript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Output of 'npm run build' 55 | dist/ 56 | 57 | # Yarn Integrity file 58 | .yarn-integrity 59 | 60 | # dotenv environment variables file 61 | .env 62 | 63 | # next.js build output 64 | .next 65 | 66 | .npmrc 67 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Modular, Inc 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | What is Zabo? A unified cryptocurrency API. 2 | ========================= 3 | [![CircleCI](https://circleci.com/gh/zabo-api/zabo-sdk-js/tree/master.svg?style=svg)](https://circleci.com/gh/zabo-api/zabo-sdk-js/tree/master) 4 | [![Discord](https://img.shields.io/discord/533336922970521600)](https://discord.gg/vGHYuUT) 5 | [![Discourse](https://img.shields.io/discourse/https/forum.zabo.com/status)](https://forum.zabo.com) 6 | 7 | [Zabo](https://zabo.com) is an API for connecting with cryptocurrency exchanges, wallets and protocols like Bitcoin. Instead of manually integrating with [Coinbase API](https://zabo.com/integrations/coinbase), [Binance API](https://zabo.com/integrations/binance), [Bitcoin APIs](https://zabo.com/integrations/bitcoin) or the hundreds of other cryptocurrency APIs - you can simply use Zabo for them all. 8 | 9 | We believe teams and developers should focus on building great products, not worry about the fragmented landscape of exchange APIs and blockchain protocols. 10 | 11 | For our updated list of integrations, [check out our Zabo integrations](https://zabo.com/integrations). 12 | 13 | # Zabo API Javascript (JS) SDK 14 | 15 | The Zabo SDK for JS provides convenient access to the Zabo API from applications written in browser and server-side JavaScript. 16 | 17 | Please keep in mind that [you must register](https://zabo.com/login) and receive a team id to use in your client application, or if you are using the server side functions, [generate an API keypair from your dashboard](https://zabo.com/dashboard). 18 | 19 | ## Documentation 20 | See the [Zabo API docs](https://zabo.com/docs). 21 | 22 | ## Installation 23 | For a standard browser application, add the script tag to your html file: 24 | ```html 25 | 65 | 66 | 122 | 123 | 124 | 125 | ``` 126 | 127 | Or importing as a package: 128 | ```js 129 | const Zabo = require('zabo-sdk-js') 130 | 131 | const zabo = await Zabo.init({ 132 | clientId: 'YourClientIDFromTheZaboDotComDashboard', 133 | env: 'sandbox' 134 | }) 135 | 136 | zabo.connect().onConnection(account => { 137 | console.log('account connected:', account) 138 | }).onError(error => { 139 | console.error('account connection error:', error.message) 140 | }) 141 | ``` 142 | Or using ES6 modules: 143 | ```js 144 | import Zabo from 'zabo-sdk-js' 145 | ``` 146 | 147 | ### After connecting 148 | After a user connects, the client SDK can continued to be used for the connected wallet: 149 | ```js 150 | zabo.transactions.getList({ ticker: 'ETH' }).then(history => { 151 | console.log(history) 152 | }).catch(error => { 153 | /* User has not yet connected */ 154 | console.error(error) 155 | }) 156 | ``` 157 | Or you can send the account to your server for the server-side SDK to create a unique user: 158 | ```js 159 | zabo.connect().onConnection(account => { 160 | sendToYourServer(account) 161 | }).onError(error => { 162 | console.error('account connection error:', error.message) 163 | }) 164 | 165 | // Then in your server 166 | const Zabo = require('zabo-sdk-js') 167 | let account = accountReceivedFromTheClient 168 | 169 | Zabo.init({ 170 | apiKey: 'YourPublicAPIKeyGeneratedInYourZaboDotComDashboard', 171 | secretKey: 'YourSecretAPIKey', 172 | env: 'sandbox' 173 | }).then(zabo => { 174 | zabo.users.create(account) 175 | }).catch(e => { 176 | console.log(e.message) 177 | }) 178 | ``` 179 | 180 | ### Zabo.init() Configuration 181 | While instantiating your new Zabo SDK instance, you have a few configuration options that can be changed to best suit your needs. Please note that some options are available only when running the SDK from the browser while others are available when running the SDK on your node.js code. 182 | 183 | | Key | Description | Platform | 184 | | ------------- | ------------- |----------- | 185 | | clientId | App Key acquired when registering a team in [Zabo Dashboard](https://zabo.com/login/). | Browser | 186 | | env | Zabo API environment the SDK is connecting with. Could be either `sandbox` or `live`. Only `sandbox` is available unless a `live` connection is approved. | Both | 187 | | apiKey | API Key generated via the Developer Settings section at [Zabo Dashboard](https://zabo.com/login/). | Node | 188 | | secretKey | Secret Key generated via the Developer Settings section at [Zabo Dashboard](https://zabo.com/login/). | Node | 189 | | autoConnect | Optional boolean useful if you wish to stop the SDK from fetching the team data during Zabo.init(). Defaults to `true`. | Both | 190 | | apiVersion | Optional parameter to specify the Zabo API version. Could be either `v0` or `v1`. Defaults to `v1`. | Both | 191 | 192 | ### Server vs Client 193 | The SDK can be used in either the client or server environment after a user connects their wallet, however, they have different functions available to them and utilize different authentication methods. See [the Zabo API docs](https://zabo.com/docs) for more information. 194 | 195 | 196 | ### Using Promises 197 | Every method returns a chainable promise which can be used: 198 | ```js 199 | zabo.getTeam().then(a => { 200 | console.log(a) 201 | }).catch(e => { 202 | console.log(e.message) 203 | }) 204 | ``` 205 | Or with async/await: 206 | ```js 207 | let exchangeRates = await zabo.currencies.exchangeRates() 208 | console.log(exchangeRates) 209 | ``` 210 | 211 | ## Help and Further Information 212 | Please [read our docs](https://zabo.com/docs) and reach out to us in any or all of the following forums for questions: 213 | 214 | * [Discord](https://discord.gg/vGHYuUT) 215 | * [Discourse](https://forum.zabo.com) 216 | * [Gitter](https://gitter.im/zabo-api/community) 217 | * [Email](mailto:contact@zabo.com) 218 | 219 | ## Issues 220 | If you notice any issues with our docs, this README, or the SDK, feel free to open an issue and/or a PR. We welcome community contributions! 221 | -------------------------------------------------------------------------------- /examples/example.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: Helvetica, Arial, sans-serif; 3 | } 4 | 5 | img { 6 | width: 64px; 7 | margin-right: 12px; 8 | } 9 | h2 { 10 | padding: 20px 15px 0; 11 | } 12 | 13 | .content-holder { 14 | width: 75%; 15 | margin: auto; 16 | } 17 | 18 | button { 19 | font-size: 1.2rem; 20 | font-weight: 500; 21 | line-height: 1.5em; 22 | position: relative; 23 | cursor: pointer; 24 | user-select: none; 25 | color: rgb(255, 255, 255); 26 | margin-bottom: 12px; 27 | padding: 12px 24px; 28 | border-radius: 4px; 29 | border-width: 1px; 30 | border-style: solid; 31 | border-color: rgb(234, 233, 233); 32 | border-image: initial; 33 | background: rgb(52, 101, 224); 34 | } 35 | 36 | p { 37 | padding: 20px 0; 38 | line-height: 1.5em; 39 | } 40 | 41 | .methods { 42 | display: none; 43 | padding: 10px 0 20px; 44 | } 45 | .methods button { 46 | font-size: 1rem; 47 | padding: 8px 14px; 48 | } 49 | 50 | #output { 51 | max-width: 720px; 52 | padding: 24px 20px; 53 | background: #eee; 54 | color: #333; 55 | font-size: 15px; 56 | overflow-x: hidden; 57 | } 58 | -------------------------------------------------------------------------------- /examples/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Zabo Connect Playground 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 |

Zabo Connect Playground

38 |
39 |
40 |
41 |

42 | This is a sandbox demo of Zabo Connect capabilities. Use this as a guideline to write your own 43 | production-ready code. 44 |
45 | Please visit the Zabo SDK docs for a full API documentation 46 | and 47 | more details. 48 |

49 | 50 | 51 | 52 |
53 |

Other SDK methods

54 | 55 | 56 | 57 |
58 | 59 |

Output:

60 | 61 |
62 | 63 |
64 |
65 | 66 | 67 | 68 | 69 | 155 | 156 | 157 | 158 | -------------------------------------------------------------------------------- /examples/test-server-sdk.js: -------------------------------------------------------------------------------- 1 | const Zabo = require('../src/index') 2 | 3 | async function main () { 4 | const zabo = await Zabo.init({ 5 | apiKey: '', 6 | secretKey: '', 7 | env: 'sandbox', 8 | }) 9 | 10 | console.log('status:', zabo.status) 11 | console.log('appId:', zabo.data.id) 12 | 13 | zabo.users.create({ 14 | id: '', 15 | token: '' 16 | }).then(user => { 17 | console.log('user:', user) 18 | return zabo.users.getList() // HAS PAGINATION 19 | }).then(users => { 20 | console.log('users:', users) 21 | }).catch(err => { 22 | console.error('err:', err) 23 | }) 24 | } 25 | 26 | main() 27 | -------------------------------------------------------------------------------- /examples/zabo.js: -------------------------------------------------------------------------------- 1 | ../dist/zabo.js -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "zabo-sdk-js", 3 | "version": "1.3.2", 4 | "description": "Zabo SDK for JS", 5 | "main": "dist/index.js", 6 | "types": "./src/index.d.ts", 7 | "scripts": { 8 | "getv": "echo $npm_package_version", 9 | "test": "NODE_ENV=test ./node_modules/mocha/bin/mocha -r jsdom-global/register --recursive test/ --exit --timeout 10000", 10 | "test:ethereum": "TEST_TYPE=ethereum npm run test", 11 | "prepublishOnly": "npm run build && npm run build:types", 12 | "build": "PACKAGE_VERSION=$npm_package_version npx webpack --config webpack.config.js --progress --colors", 13 | "build:local": "NODE_ENV=local npm run build", 14 | "build:types": "tsc", 15 | "examples": "npm run build && ln -sf ../dist/zabo.js examples/zabo.js && live-server --host=localhost --port=8080 examples/" 16 | }, 17 | "repository": { 18 | "type": "git", 19 | "url": "git+https://github.com/zabo-api/zabo-sdk-js.git" 20 | }, 21 | "keywords": [ 22 | "zabo", 23 | "cryptocurrency", 24 | "wallets", 25 | "bitcoin", 26 | "ethereum", 27 | "metamask", 28 | "ledger", 29 | "coinbase", 30 | "binance", 31 | "bitfinex", 32 | "kraken", 33 | "api" 34 | ], 35 | "author": "Zabo", 36 | "license": "MIT", 37 | "bugs": { 38 | "url": "https://github.com/zabo-api/zabo-sdk-js/issues" 39 | }, 40 | "homepage": "https://github.com/zabo-api/zabo-sdk-js#readme", 41 | "devDependencies": { 42 | "@babel/core": "^7.7.2", 43 | "@babel/plugin-proposal-class-properties": "^7.7.4", 44 | "babel-loader": "^8.0.6", 45 | "jsdom": "15.2.1", 46 | "jsdom-global": "3.0.2", 47 | "mocha": "^6.2.0", 48 | "should": "^13.2.3", 49 | "standard": "^14.3.1", 50 | "typescript": "^4.2.4", 51 | "webpack": "^4.41.2", 52 | "webpack-cli": "^3.3.3" 53 | }, 54 | "dependencies": { 55 | "axios": "^0.21.1", 56 | "create-hmac": "^1.1.7" 57 | }, 58 | "engines": { 59 | "node": ">=12" 60 | }, 61 | "browser": { 62 | "net": false, 63 | "fs": false, 64 | "child_process": false 65 | }, 66 | "standard": { 67 | "env": [ 68 | "jest" 69 | ], 70 | "ignore": [ 71 | "/assets", 72 | "/examples" 73 | ] 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/api.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @Copyright (c) 2019-present, Zabo & Modular, Inc. All rights reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | * @description: Zabo API communication library 17 | */ 18 | 19 | 'use strict' 20 | 21 | const axios = require('axios') 22 | const constants = require('./constants') 23 | const utils = require('./utils') 24 | const resources = require('./resources') 25 | 26 | const { SDKError } = require('./err') 27 | 28 | const CONNECTION_SUCCESS = 'CONNECTION_SUCCESS' 29 | const CONNECTION_FAILURE = 'CONNECTION_FAILURE' 30 | 31 | /** 32 | * Main API class definition 33 | */ 34 | class API { 35 | constructor (options) { 36 | Object.assign(this, options) 37 | 38 | if (!this.env) { 39 | throw new SDKError( 40 | 400, '[Zabo] Please provide an \'env\' value when initializing Zabo. More details at: https://zabo.com/docs' 41 | ) 42 | } 43 | 44 | const urls = constants(this.baseUrl, this.connectUrl, this.apiVersion)[this.env] 45 | this.baseUrl = urls.API_BASE_URL 46 | this.axios = axios.create() 47 | this.axios.defaults.baseURL = this.baseUrl 48 | 49 | if (utils.isNode()) { 50 | this.axios.defaults.headers.common['X-Zabo-Key'] = this.apiKey 51 | resources(this, true).then(resources => { this.resources = resources }) 52 | } else { 53 | this.connectUrl = urls.CONNECT_BASE_URL 54 | resources(this, false).then(resources => { this.resources = resources }) 55 | } 56 | 57 | this._onConnectorMessage = this._onMessage.bind(this, 'connector') 58 | this._onSocketMessage = this._onMessage.bind(this, 'socket') 59 | 60 | this._isConnecting = false 61 | this._isWaitingForConnector = false 62 | } 63 | 64 | /** 65 | * Connect to the Zabo API. 66 | */ 67 | async connect ({ provider, params } = {}) { 68 | let appId = null 69 | 70 | if (utils.isNode()) { 71 | const res = await this.request('GET', '/teams/id') 72 | appId = res.id 73 | 74 | if (!appId) { 75 | throw new SDKError(500, '[Zabo] Something went wrong on our end. Please note the time and let us know') 76 | } 77 | 78 | this.resources.teams.setId(appId) 79 | return appId 80 | } else { 81 | if (provider && typeof provider !== 'string') { 82 | throw new SDKError(400, '[Zabo] `provider` must be a string. More details at: https://zabo.com/docs/#preselected-provider-connections') 83 | } 84 | 85 | if (params && typeof params !== 'object') { 86 | throw new SDKError(400, '[Zabo] `params` must be an object. More details at: https://zabo.com/docs/#new-account-connections') 87 | } 88 | 89 | this._isConnecting = true 90 | 91 | try { 92 | await window.fetch(`${this.connectUrl}/health-check`) 93 | 94 | const connectParams = { 95 | client_id: this.clientId, 96 | origin: encodeURIComponent(window.location.host), 97 | zabo_env: this.env, 98 | zabo_version: this.apiVersion || process.env.PACKAGE_VERSION, 99 | ...(params || {}) 100 | } 101 | 102 | const teamSession = await this.resources.teams.getSession() 103 | if (teamSession) { 104 | connectParams.otp = teamSession.one_time_password 105 | } 106 | 107 | let url = `${this.connectUrl}/connect` 108 | url += (provider && typeof provider === 'string') ? `/${provider}` : '' 109 | url += `?${new URLSearchParams(connectParams).toString()}` 110 | 111 | this.iframe = this._appendIframe('zabo-connect-widget') 112 | this.connector = window.open(url, this.iframe.name) 113 | this.connector.focus() 114 | 115 | this._isWaitingForConnector = true 116 | this._watchConnector(teamSession) 117 | } catch (err) { 118 | this._triggerCallback(CONNECTION_FAILURE, { error_type: 500, message: 'Connection refused' }) 119 | } 120 | } 121 | } 122 | 123 | /** 124 | * Send an API request. 125 | * @param {String} method HTTP method to use for the request. 126 | * @param {String} path The endpoint for the API. 127 | * @param {Object} data Additional data to send for the request. 128 | * @param {Boolean} isPublic If the endpoint requires authentication. 129 | * @returns 130 | */ 131 | async request (method, path, data, isPublic = false) { 132 | const request = this._buildRequest(method, path, data, isPublic) 133 | 134 | try { 135 | const response = await this.axios(request) 136 | 137 | if (response.data && response.data.list_cursor) { 138 | return utils.createPaginator(response.data, this) 139 | } 140 | return response.data 141 | } catch (err) { 142 | if (err.response) { 143 | throw new SDKError(err.response.status, err.response.data.message, err.response.data.request_id) 144 | } 145 | throw new SDKError(500, err.message) 146 | } 147 | } 148 | 149 | _buildRequest (method, path, data, isPublic) { 150 | const timestamp = Date.now() 151 | const url = this.baseUrl + path 152 | const body = data ? JSON.stringify(data) : '' 153 | let headers = {} 154 | 155 | if (utils.isNode()) { 156 | const signature = utils.generateHMACSignature(this.secretKey, url, body, timestamp) 157 | headers = { 158 | 'X-Zabo-Sig': signature, 159 | 'X-Zabo-Timestamp': timestamp 160 | } 161 | } else if (!isPublic) { 162 | headers = { Authorization: 'Bearer ' + utils.getZaboSession() } 163 | } 164 | method = method.toLowerCase() 165 | 166 | return { method, url, data, headers } 167 | } 168 | 169 | _watchConnector (teamSession) { 170 | this._setListeners(teamSession) 171 | 172 | // Connector timeout (10 minutes) 173 | const connectorTimeout = setTimeout(() => { 174 | this._closeConnector() 175 | this._triggerCallback(CONNECTION_FAILURE, { error_type: 400, message: 'Connection timeout' }) 176 | }, 10 * 60 * 1000) 177 | 178 | // Watch interval 179 | const watchInterval = setInterval(() => { 180 | if (this._isWaitingForConnector) { 181 | if (this.connector.closed) { 182 | this._closeConnector() // Ensure that the connector has been destroyed 183 | this._triggerCallback(CONNECTION_FAILURE, { error_type: 400, message: 'Connection closed' }) 184 | } 185 | } else { 186 | this._removeListeners() 187 | clearInterval(watchInterval) 188 | clearTimeout(connectorTimeout) 189 | 190 | this._closeConnector() 191 | } 192 | }, 1000) 193 | } 194 | 195 | _setListeners (teamSession) { 196 | // Listen to postMessage 197 | window.addEventListener('message', this._onConnectorMessage, false) 198 | 199 | // Listen to WebSocket 200 | if (window.WebSocket && teamSession) { 201 | let wsUrl = new URL(this.baseUrl) 202 | wsUrl.protocol = 'wss:' 203 | wsUrl = wsUrl.toString() + '/ws' 204 | wsUrl += `?client_id=${this.clientId}` 205 | wsUrl += `&otp=${teamSession.one_time_password}` 206 | 207 | try { 208 | this.ws = new window.WebSocket(wsUrl) 209 | this.ws.onmessage = this._onSocketMessage 210 | } catch (err) { 211 | console.warn('[Zabo] Error establishing WebSocket connection.', err.message) 212 | } 213 | } 214 | } 215 | 216 | _removeListeners () { 217 | window.removeEventListener('message', this._onConnectorMessage, false) 218 | 219 | if (this.ws) { 220 | this.ws.close() 221 | this.ws = null 222 | } 223 | } 224 | 225 | _onMessage (emitter, { origin, data }) { 226 | try { 227 | data = JSON.parse(data) 228 | } catch (err) {} 229 | 230 | if (data.zabo) { 231 | if (origin !== this.connectUrl && !(/\.zabo\.com$/).test(new URL(origin).hostname)) { 232 | throw new SDKError(401, '[Zabo] Unauthorized attempt to call SDK from origin: ' + origin) 233 | } 234 | 235 | switch (data.eventName) { 236 | case 'connectSuccess': { 237 | if (emitter === 'connector') { 238 | this._isWaitingForConnector = false 239 | } 240 | 241 | if (data.account && data.account.token) { 242 | this._setAccountSession({ 243 | key: 'zabosession', 244 | value: data.account.token, 245 | exp_time: data.account.exp_time 246 | }) 247 | } 248 | 249 | if (this.resources.accounts && this.resources.transactions) { 250 | this.resources.accounts._setAccount(data.account) 251 | this.resources.transactions._setAccount(data.account) 252 | this.resources.trading._setAccount(data.account) 253 | } 254 | 255 | this._triggerCallback(CONNECTION_SUCCESS, data.account) 256 | break 257 | } 258 | 259 | case 'connectError': { 260 | if (emitter === 'connector') { 261 | this._isWaitingForConnector = false 262 | } 263 | 264 | this._triggerCallback(CONNECTION_FAILURE, data.error) 265 | break 266 | } 267 | 268 | case 'connectClose': { 269 | this._closeConnector() 270 | break 271 | } 272 | 273 | default: { 274 | if (this._onEvent) { 275 | this._onEvent(data.eventName, data.metadata || {}) 276 | } 277 | } 278 | } 279 | } 280 | } 281 | 282 | _setAccountSession (cookie) { 283 | utils.setCookie(cookie.key, cookie.value, cookie.exp_time) 284 | return true 285 | } 286 | 287 | _appendIframe (name) { 288 | let iframe = document.getElementsByName(name)[0] 289 | 290 | if (!iframe) { 291 | iframe = document.createElement('iframe') 292 | iframe.setAttribute('style', 'position:fixed; top:0; left:0; right:0; bottom:0; z-index:2147483647;') 293 | iframe.style.width = '100%' 294 | iframe.style.height = '100%' 295 | iframe.frameBorder = 0 296 | iframe.allow = 'usb *; hid *' 297 | iframe.name = name 298 | 299 | document.body.appendChild(iframe) 300 | } 301 | 302 | iframe.style.display = 'block' 303 | return iframe 304 | } 305 | 306 | _closeConnector () { 307 | this._isWaitingForConnector = false 308 | 309 | if (!this.connector.closed) { 310 | this.connector.close() 311 | } 312 | 313 | if (this.iframe) { 314 | this.iframe.style.display = 'none' 315 | this.iframe.src = '' 316 | } 317 | } 318 | 319 | _triggerCallback (type, data) { 320 | if (this._isConnecting) { 321 | this._isConnecting = false 322 | 323 | if (type === CONNECTION_SUCCESS && this._onConnection) { 324 | this._onConnection(data) 325 | } 326 | 327 | if (type === CONNECTION_FAILURE && this._onError) { 328 | this._onError(data) 329 | } 330 | } 331 | } 332 | } 333 | 334 | /** 335 | * Export API functions. 336 | * @type {API} 337 | */ 338 | module.exports = API 339 | -------------------------------------------------------------------------------- /src/constants.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @Copyright (c) 2019-present, Zabo, All rights reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | 'use strict' 18 | 19 | const DEFAULT_API_HOST = 'https://api.zabo.com' 20 | const DEFAULT_CONNECT_PATH = 'https://connect.zabo.com' 21 | const DEFAULT_API_VERSION = 'v1' 22 | 23 | module.exports = (host, connectHost, apiVersion) => { 24 | host = host || DEFAULT_API_HOST 25 | connectHost = connectHost || DEFAULT_CONNECT_PATH 26 | apiVersion = apiVersion || DEFAULT_API_VERSION 27 | 28 | return { 29 | sandbox: { 30 | API_BASE_URL: `${host}/sandbox-${apiVersion}`, 31 | CONNECT_BASE_URL: connectHost 32 | }, 33 | live: { 34 | API_BASE_URL: `${host}/${apiVersion}`, 35 | CONNECT_BASE_URL: connectHost 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/core/SDK.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @Copyright (c) 2019-present, Zabo, All rights reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | 'use strict' 18 | 19 | const utils = require('../utils') 20 | const { SDKError } = require('../err') 21 | 22 | /** 23 | * Zabo main SDK class definition. 24 | */ 25 | class ZaboSDK { 26 | constructor () { 27 | this.status = 'offline' 28 | this.api = null 29 | this.autoConnect = true 30 | 31 | /** 32 | * Account functions. 33 | * @type {import('../resources/accounts').AccountsAPI} 34 | */ 35 | this.accounts = undefined 36 | 37 | /** 38 | * Blockchain functions. 39 | * @type {import('../resources/blockchains').BlockchainsAPI} 40 | */ 41 | this.blockchains = undefined 42 | 43 | /** 44 | * Currency functions. 45 | * @type {import('../resources/currencies').CurrenciesAPI} 46 | */ 47 | this.currencies = undefined 48 | 49 | /** 50 | * Provider functions. 51 | * @type {import('../resources/providers').ProvidersAPI} 52 | */ 53 | this.providers = undefined 54 | 55 | /** 56 | * Trading functions. Client-side only. 57 | * @type {import('../resources/trading').TradingAPI} 58 | */ 59 | this.trading = undefined 60 | 61 | /** 62 | * Transaction functions. 63 | * @type {import('../resources/transactions').TransactionsAPI} 64 | */ 65 | this.transactions = undefined 66 | 67 | /** 68 | * User functions. 69 | * @type {import('../resources/users').UsersAPI} 70 | */ 71 | this.users = undefined 72 | } 73 | 74 | async initAPI () { 75 | return null 76 | } 77 | 78 | async init (o) { 79 | this.env = this.checkZaboEnv(o.env) 80 | this.apiVersion = this.checkApiVersion(o.apiVersion) 81 | 82 | if (typeof o.autoConnect !== 'undefined') { 83 | this.autoConnect = o.autoConnect 84 | } else { 85 | this.autoConnect = true 86 | } 87 | 88 | if (utils.isNode()) { 89 | if (!o.apiKey || !o.secretKey || typeof o.apiKey !== 'string' || typeof o.secretKey !== 'string') { 90 | return this.throwConnectError(401, '[Zabo] Please provide a valid Zabo app API and Secret keys. More details at: https://zabo.com/docs#app-server-authentication') 91 | } 92 | 93 | await this.initAPI({ 94 | baseUrl: o.baseUrl, 95 | apiKey: o.apiKey, 96 | secretKey: o.secretKey 97 | }) 98 | 99 | if (this.autoConnect) { 100 | this.status = 'connecting' 101 | await this.api.connect() 102 | this.status = 'online' 103 | 104 | if (!this.api.resources.teams.id) { 105 | return this.throwConnectError(400, '[Zabo] Unable to connect with Zabo API. Please check your credentials and try again. More details at: https://zabo.com/docs') 106 | } 107 | 108 | return this.api.resources.teams.get() 109 | } 110 | } else { 111 | if (!o.clientId || typeof o.clientId !== 'string') { 112 | throw new SDKError(400, '[Zabo] Please provide a valid Zabo app clientId. More details at: https://zabo.com/docs') 113 | } 114 | 115 | await this.initAPI({ 116 | baseUrl: o.baseUrl, 117 | connectUrl: o.connectUrl, 118 | clientId: o.clientId 119 | }) 120 | 121 | try { 122 | if (utils.getZaboSession()) { 123 | const account = await this.accounts.get() 124 | this.transactions._setAccount(account) 125 | this.trading._setAccount(account) 126 | } 127 | } catch (err) { 128 | console.info('[Zabo] No account connected yet.') 129 | } 130 | 131 | return this.api.resources.teams.get() 132 | } 133 | } 134 | 135 | throwConnectError (code, message) { 136 | this.status = 'offline' 137 | throw new SDKError(code, message) 138 | } 139 | 140 | async setEndpointAliases () { 141 | while (!this.api.resources) { 142 | await utils.sleep(500) 143 | } 144 | 145 | const { teams, ...apiResources } = this.api.resources 146 | Object.assign(this, apiResources) 147 | } 148 | 149 | checkZaboEnv (env) { 150 | env = env ? env.toLowerCase() : null 151 | 152 | const acceptedEnvs = ['sandbox', 'live'] 153 | if (!env || !acceptedEnvs.includes(env)) { 154 | return this.throwConnectError(400, '[Zabo] Please provide a valid env, should be \'sandbox\' or \'live\'. More details at: https://zabo.com/docs') 155 | } 156 | 157 | return env 158 | } 159 | 160 | checkApiVersion (version) { 161 | if (version && !['v0', 'v1'].includes(version)) { 162 | return this.throwConnectError(400, '[Zabo] Please provide a valid apiVersion, should be \'v0\' or \'v1\'. More details at: https://zabo.com/docs') 163 | } 164 | 165 | return version 166 | } 167 | 168 | connect (config = {}) { 169 | if (this.api && (utils.isBrowser() || utils.isReactNative())) { 170 | this.api.connect(config) 171 | return this 172 | } 173 | 174 | this.status = 'connecting' 175 | 176 | return this.api.connect().then(appId => { 177 | this.status = 'online' 178 | return appId 179 | }).catch(err => { 180 | throw err 181 | }) 182 | } 183 | 184 | buildConnectUrl (config = {}) { 185 | return this.api.buildUrl(config) 186 | } 187 | 188 | onConnection (fn) { 189 | if (typeof fn !== 'function') { return } 190 | this.api._onConnection = fn.bind(this) 191 | return this 192 | } 193 | 194 | onError (fn) { 195 | if (typeof fn !== 'function') { return } 196 | this.api._onError = fn.bind(this) 197 | return this 198 | } 199 | 200 | onEvent (fn) { 201 | if (typeof fn !== 'function') { return } 202 | this.api._onEvent = fn.bind(this) 203 | return this 204 | } 205 | 206 | getTeam () { 207 | return this.api.resources.teams.get() 208 | } 209 | 210 | get data () { 211 | return this.api.resources.teams.data 212 | } 213 | } 214 | 215 | // Export ZaboSDK class 216 | module.exports = ZaboSDK 217 | -------------------------------------------------------------------------------- /src/err.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @Copyright (c) 2019-present, Zabo & Modular, Inc. All rights reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | 'use strict' 18 | 19 | class SDKError extends Error { 20 | constructor (type, msg, requestId, ...params) { 21 | super(msg, ...params) 22 | 23 | // Maintains proper stack trace for where our error was thrown (only available on V8) 24 | if (Error.captureStackTrace) { 25 | Error.captureStackTrace(this, SDKError) 26 | } 27 | 28 | this.error_type = type 29 | this.request_id = requestId 30 | 31 | return this 32 | } 33 | } 34 | 35 | module.exports = { 36 | SDKError 37 | } 38 | -------------------------------------------------------------------------------- /src/index.d.ts: -------------------------------------------------------------------------------- 1 | declare const _exports: ZaboClass; 2 | export = _exports 3 | export type ZaboClass = Zabo; 4 | /** 5 | * Zabo main class definition. 6 | */ 7 | declare class Zabo { 8 | /** 9 | * Initialize the Zabo SDK. 10 | * @param {{ 11 | * clientId?: String 12 | * env: 'live' | 'sandbox' 13 | * apiKey?: String 14 | * secretKey?: String 15 | * autoConnect?: Boolean 16 | * apiVersion?: 'v0' | 'v1' | {} 17 | * }} config Zabo initialization config. 18 | * @returns {typeof Promise} The Zabo SDK. 19 | */ 20 | init(config?: { 21 | clientId?: string; 22 | env: 'live' | 'sandbox'; 23 | apiKey?: string; 24 | secretKey?: string; 25 | autoConnect?: boolean; 26 | apiVersion?: 'v0' | 'v1' | {}; 27 | }): Promise; 28 | /** 29 | * Get an instance of the ZaboSDK. 30 | * @returns {typeof import('../dist/@types/sdk')} An instance of ZaboSDK. 31 | */ 32 | get instance(): typeof import('../dist/@types/sdk'); 33 | get version(): any; 34 | } 35 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @Copyright (c) 2019-present, Zabo, All rights reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | 'use strict' 18 | 19 | const sdk = require('./sdk') 20 | 21 | /** 22 | * Zabo main class definition. 23 | */ 24 | class Zabo { 25 | /** 26 | * Initialize the Zabo SDK. 27 | * @param {{ 28 | * clientId?: String 29 | * env: 'live' | 'sandbox' 30 | * apiKey?: String 31 | * secretKey?: String 32 | * autoConnect?: Boolean 33 | * apiVersion?: 'v0' | 'v1' | {} 34 | * }} config Zabo initialization config. 35 | * @returns {Promise} The Zabo SDK. 36 | */ 37 | async init (config = {}) { 38 | await sdk.init(config) 39 | return sdk 40 | } 41 | 42 | /** 43 | * Get an instance of the ZaboSDK. 44 | * @returns {any} An instance of ZaboSDK. 45 | */ 46 | get instance () { 47 | return sdk 48 | } 49 | 50 | get version () { 51 | return process.env.PACKAGE_VERSION 52 | } 53 | } 54 | 55 | /** 56 | * @typedef {Zabo} ZaboClass 57 | * @type {ZaboClass} 58 | */ 59 | module.exports = new Zabo() 60 | -------------------------------------------------------------------------------- /src/resources/accounts.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @Copyright (c) 2019-present, Zabo & Modular, Inc. All rights reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | * @description: Zabo account-related functions 17 | */ 18 | 19 | 'use strict' 20 | 21 | const { SDKError } = require('../err') 22 | 23 | /** 24 | * @typedef {{ 25 | * data?: [import('./users').Balance] 26 | * request_id?: String 27 | * }} GetAccountBalancesResp 28 | */ 29 | 30 | /** 31 | * Accounts API. 32 | */ 33 | class Accounts { 34 | constructor (api) { 35 | /** @private */ 36 | this.api = api 37 | this.id = null 38 | this.data = null 39 | } 40 | 41 | /** 42 | * @private 43 | */ 44 | _setAccount (account) { 45 | this.id = account.id 46 | this.data = account 47 | } 48 | 49 | async get () { 50 | try { 51 | const response = await this.api.request('GET', '/sessions') 52 | this._setAccount(response) 53 | 54 | return this.data 55 | } catch (err) { 56 | throw new SDKError(err.error_type, err.message, err.request_id) 57 | } 58 | } 59 | 60 | async create ({ clientId, credentials, provider, origin } = {}) { 61 | if (!clientId) { 62 | throw new SDKError( 63 | 400, 64 | `[Zabo] Unable to connect with the Zabo API. Make sure you have registered your app at https://zabo.com and that you entered a valid 'clientId' value. 65 | More details at: https://zabo.com/docs` 66 | ) 67 | } 68 | const data = { 69 | client_id: clientId, 70 | provider_name: provider, 71 | credentials, 72 | origin 73 | } 74 | 75 | try { 76 | const account = await this.api.request('POST', '/accounts', data) 77 | this._setAccount(account) 78 | return account 79 | } catch (err) { 80 | throw new SDKError(err.error_type, err.message, err.request_id) 81 | } 82 | } 83 | 84 | /** 85 | * Returns the user balances for the requested currencies. When requesting balances from the client, 86 | * the request should be made in the context of the connected account. When requesting from an 87 | * application server, requests should be made in the context of a user. See documentation about 88 | * users. Cryptocurrencies available to your app can be queried. If no currencies are specified, 89 | * then all available currencies will be returned. 90 | * @param {{ 91 | * tickers?: [String] 92 | * }} param0 Request parameters. 93 | * @returns {Promise} API response. 94 | */ 95 | async getBalances ({ tickers } = {}) { 96 | if (!this.id) { 97 | throw new SDKError(401, '[Zabo] Account not yet connected. See: https://zabo.com/docs#connecting-a-user') 98 | } 99 | 100 | let url = `/accounts/${this.id}/balances` 101 | 102 | if (tickers) { 103 | if (Array.isArray(tickers)) { 104 | tickers = tickers.join(',') 105 | } 106 | url = `${url}?tickers=${tickers}` 107 | } 108 | 109 | try { 110 | return this.api.request('GET', url) 111 | } catch (err) { 112 | throw new SDKError(err.error_type, err.message, err.request_id) 113 | } 114 | } 115 | 116 | /** 117 | * This endpoint will create and return a deposit address for the specified account. 118 | * If the currency is not supported by the connected provider, you will receive an 'unsupported' error. 119 | * See Unsupported Functions for more information. 120 | * @param {String} ticker Three-letter identifier for the currency this deposit address should be used for. 121 | * @returns {Promise} API response. 122 | */ 123 | async createDepositAddress (ticker) { 124 | if (!this.id) { 125 | throw new SDKError(401, '[Zabo] Account not yet connected. See: https://zabo.com/docs#connecting-a-user') 126 | } else if (!ticker || typeof ticker !== 'string') { 127 | throw new SDKError(400, '[Zabo] Missing or invalid `ticker` parameter. See: https://zabo.com/docs#create-a-deposit-address') 128 | } 129 | 130 | const providersWithStaticDepositAddresses = [ 131 | 'metamask', 132 | 'ledger', 133 | 'hedera', 134 | 'address-only', 135 | 'binance' 136 | ] 137 | 138 | for (const provider of providersWithStaticDepositAddresses) { 139 | if (provider === this.data.provider.name) { 140 | console.warn(`[Zabo] Provider '${provider}' does not support dynamic address generation. Fallbacking to accounts.getDepositAddresses()... More details: https://zabo.com/docs#get-deposit-addresses`) 141 | return this.getDepositAddresses(ticker) 142 | } 143 | } 144 | 145 | try { 146 | return this.api.request('POST', `/accounts/${this.id}/deposit-addresses?ticker=${ticker}`) 147 | } catch (err) { 148 | throw new SDKError(err.error_type, err.message, err.request_id) 149 | } 150 | } 151 | 152 | /** 153 | * This endpoint will retrieve all deposit addresses for the specified account. 154 | * If the currency is not supported by the connected provider, you will receive 155 | * an 'unsupported' error. See Unsupported Functions for more information. 156 | * @param {String} ticker Three-letter identifier for the currency this deposit address should be used for. 157 | * @returns {Promise} 158 | */ 159 | async getDepositAddresses (ticker) { 160 | if (!this.id) { 161 | throw new SDKError(401, '[Zabo] Account not yet connected. See: https://zabo.com/docs#connecting-a-user') 162 | } else if (!ticker || typeof ticker !== 'string') { 163 | throw new SDKError(400, '[Zabo] Invalid `ticker` parameter. See: https://zabo.com/docs#get-deposit-addresses') 164 | } 165 | 166 | try { 167 | return this.api.request('GET', `/accounts/${this.id}/deposit-addresses?ticker=${ticker}`) 168 | } catch (err) { 169 | throw new SDKError(err.error_type, err.message, err.request_id) 170 | } 171 | } 172 | } 173 | 174 | /** 175 | * @typedef {Accounts} AccountsAPI 176 | * @type {(api) => AccountsAPI} 177 | */ 178 | module.exports = (api) => { 179 | return new Accounts(api) 180 | } 181 | -------------------------------------------------------------------------------- /src/resources/blockchains.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @Copyright (c) 2019-present, Zabo & Modular, Inc. All rights reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | 'use strict' 18 | 19 | const utils = require('../utils') 20 | const { SDKError } = require('../err') 21 | 22 | /** 23 | * @typedef {{ 24 | * hex: String 25 | * nonce?: Number 26 | * name?: String 27 | * }} Address 28 | * 29 | * @typedef {{ 30 | * contract?: Contract 31 | * ticker?: String 32 | * name?: String 33 | * decimals?: Number 34 | * total_supply?: String 35 | * is_erc20: Boolean 36 | * }} Token 37 | * 38 | * @typedef {{ 39 | * number?: Number 40 | * hash?: String 41 | * size?: Number 42 | * gas_limit?: Number 43 | * gas_used?: Number 44 | * transaction_count?: Number 45 | * timestamp?: Number 46 | * version?: Number 47 | * nonce?: String 48 | * }} Block 49 | * 50 | * @typedef {{ 51 | * address?: Address 52 | * bytecode?: String 53 | * }} Contract 54 | * 55 | * @typedef {{ 56 | * data?: [{ 57 | * contract?: Contract 58 | * ticker?: String 59 | * name?: String 60 | * decimals?: Number 61 | * total_supply?: String 62 | * is_erc20: Boolean 63 | * }] 64 | * request_id?: String 65 | * }} GetTokensResp 66 | * 67 | * @typedef {{ 68 | * token?: Token 69 | * address?: Address 70 | * balance?: String 71 | * }} TokenBalance 72 | * 73 | * @typedef {{ 74 | * data?: [TokenBalance] | Number 75 | * request_id?: String 76 | * }} GetBalancesResp 77 | * 78 | * @typedef {{ 79 | * hash?: String 80 | * block_number?: Number 81 | * from_address?: Address 82 | * to_address?: Address 83 | * value?: String 84 | * gas?: Number 85 | * gas_price?: String 86 | * gas_used?: Number 87 | * input?: String 88 | * status?: Number 89 | * protocol_information?: any 90 | * value_transfers?: any 91 | * }} ETHTransaction 92 | * 93 | * @typedef {{ 94 | * node: { 95 | * output_script?: String 96 | * output_script_type?: String 97 | * addresses?: [{ 98 | * address?: Address 99 | * index?: Number 100 | * }], 101 | * input_script?: String 102 | * input_sequence?: String 103 | * required_signatures?: Number 104 | * output_value?: Number 105 | * }, 106 | * output_transaction?: { 107 | * hash?: String 108 | * block_number?: Number 109 | * outputs?: any 110 | * inputs?: any 111 | * size?: Number 112 | * lock_time?: Number 113 | * is_coinbase?: Boolean 114 | * }, 115 | * output_index?: Number 116 | * input_transaction?: any 117 | * input_index?: any 118 | * }} BTCNode 119 | * 120 | * @typedef {{ 121 | * outputs?: [BTCNode] 122 | * inputs?: [BTCNode] 123 | * }} BTCTransaction 124 | * 125 | * @typedef {ETHTransaction & BTCTransaction} TransactionData 126 | * 127 | * @typedef {{ 128 | * data?: [TransactionData] 129 | * request_id?: String 130 | * }} GetTransactionsResp 131 | * 132 | * @typedef {{ 133 | * request_id?: String 134 | * } & TransactionData} GetTransactionResp 135 | * 136 | * @typedef {{ 137 | * data?: [{ 138 | * transaction: ETHTransaction, 139 | * token: Token, 140 | * from_address: Address, 141 | * to_address: Address, 142 | * value: String 143 | * }] 144 | * request_id?: String 145 | * }} GetTokenTransfersResp 146 | * 147 | * @typedef {{ 148 | * data?: [{ 149 | * transaction: ETHTransaction, 150 | * token: Token, 151 | * from_address: Address, 152 | * to_address: Address, 153 | * value: String 154 | * }] 155 | * request_id?: String 156 | * }} GetTokenTransferResp 157 | */ 158 | 159 | class Blockchains { 160 | constructor (api) { 161 | /** @private */ 162 | this.api = api 163 | } 164 | 165 | /** 166 | * Fetches information regarding the requested block number. 167 | * If the endpoint is called without a block number, then the latest block Zabo has will be returned. 168 | * **NOTE:** Zabo lags the head of the blockchain by 10 blocks. 169 | * @param {'ethereum' | 'bitcoin' | {}} blockchain The blockchain name. 170 | * @param {Number} blockNumber Block number. 171 | * @returns {Promise} API response. 172 | */ 173 | async getBlock (blockchain, blockNumber) { 174 | utils.validateEnumParameter('blockchain', blockchain, ['bitcoin', 'ethereum']) 175 | 176 | let url = `/blockchains/${blockchain}/blocks` 177 | if (typeof blockNumber !== 'undefined') { 178 | url += '/' + blockNumber 179 | } 180 | 181 | try { 182 | return this.api.request('GET', url) 183 | } catch (err) { 184 | throw new SDKError(err.error_type, err.message, err.request_id) 185 | } 186 | } 187 | 188 | /** 189 | * This function returns the address and bytecode for the contract at a given address. 190 | * The address is required, and there must be a smart contract deployed at the given address. 191 | * @param {('ethereum' | {})} blockchain The blockchain name. 192 | * @param {String} address The address for the contract. 193 | * @returns {Promise} API response. 194 | */ 195 | async getContract (blockchain, address) { 196 | utils.validateEnumParameter('blockchain', blockchain, ['ethereum']) 197 | 198 | if (typeof address === 'undefined') { 199 | throw new SDKError(400, '[Zabo] Missing `address` parameter. See: https://zabo.com/docs#get-a-smart-contract') 200 | } 201 | 202 | try { 203 | return this.api.request('GET', `/blockchains/${blockchain}/contracts/${address}`) 204 | } catch (err) { 205 | throw new SDKError(err.error_type, err.message, err.request_id) 206 | } 207 | } 208 | 209 | /** 210 | * This function returns a list of tokens on the Ethereum blockchain in general, 211 | * or a specific token if the name is provided. 212 | * Names are not unique so, if a name is provided in the path, a list is still 213 | * returned for all tokens that share the same name. 214 | * **NOTE:** The name is case-sensitive! 215 | * @param {'ethereum' | {}} blockchain The blockchain name. 216 | * @param {String?} tokenName The name of the token. 217 | * @returns {Promise} API response. 218 | */ 219 | async getTokens (blockchain, tokenName) { 220 | utils.validateEnumParameter('blockchain', blockchain, ['ethereum']) 221 | 222 | let url = `/blockchains/${blockchain}/tokens` 223 | if (typeof tokenName !== 'undefined') { 224 | url += '/' + tokenName 225 | } 226 | 227 | try { 228 | return this.api.request('GET', url) 229 | } catch (err) { 230 | throw new SDKError(err.error_type, err.message, err.request_id) 231 | } 232 | } 233 | 234 | /** 235 | * This function returns a list of balances of the assets which the given address 236 | * or extended public key holds. If the response is for a Bitcoin network request, 237 | * then the data response is simply the address', or xpub key's, balance in satoshis. 238 | * @param {'ethereum' | 'bitcoin' | {}} blockchain The blockchain name. 239 | * @param {String} address The blockchain address. 240 | * @returns {Promise} API response. 241 | */ 242 | async getBalances (blockchain, address) { 243 | utils.validateEnumParameter('blockchain', blockchain, ['bitcoin', 'ethereum']) 244 | 245 | if (typeof address === 'undefined') { 246 | throw new SDKError(400, '[Zabo] Missing `address` parameter. See: https://zabo.com/docs#get-balances-for-address-or-xpub') 247 | } 248 | 249 | try { 250 | return this.api.request('GET', `/blockchains/${blockchain}/addresses/${address}/balances`) 251 | } catch (err) { 252 | throw new SDKError(err.error_type, err.message, err.request_id) 253 | } 254 | } 255 | 256 | /** 257 | * This function returns a list of transactions executed by the given address or extended public key. 258 | * @param {'ethereum' | 'bitcoin' | {}} blockchain The blockchain name. 259 | * @param {String} address The blockchain address. 260 | * @returns {Promise} API response. 261 | */ 262 | async getTransactions (blockchain, address) { 263 | utils.validateEnumParameter('blockchain', blockchain, ['bitcoin', 'ethereum']) 264 | 265 | if (typeof address === 'undefined') { 266 | throw new SDKError(400, '[Zabo] Missing `address` parameter. See: https://zabo.com/docs#get-transactions-for-address-or-xpub') 267 | } 268 | 269 | try { 270 | return this.api.request('GET', `/blockchains/${blockchain}/addresses/${address}/transactions`) 271 | } catch (err) { 272 | throw new SDKError(err.error_type, err.message, err.request_id) 273 | } 274 | } 275 | 276 | /** 277 | * This function returns a single transaction object related to the hash included in the request. 278 | * @param {'ethereum' | 'bitcoin' | {}} blockchain The blockchain name. 279 | * @param {String} transactionHash The transaction hash. 280 | * @returns {Promise} API response. 281 | */ 282 | async getTransaction (blockchain, transactionHash) { 283 | utils.validateEnumParameter('blockchain', blockchain, ['bitcoin', 'ethereum']) 284 | 285 | if (typeof transactionHash === 'undefined') { 286 | throw new SDKError(400, '[Zabo] Missing `transactionHash` parameter. See: https://zabo.com/docs#get-transaction-by-hash') 287 | } 288 | 289 | try { 290 | return this.api.request('GET', `/blockchains/${blockchain}/transactions/${transactionHash}`) 291 | } catch (err) { 292 | throw new SDKError(err.error_type, err.message, err.request_id) 293 | } 294 | } 295 | 296 | /** 297 | * This function returns a list of token transfers directly involving the given address or extended public key. 298 | * @param {'ethereum' | {}} blockchain The blockchain name. 299 | * @param {String} address The blockchain address. 300 | * @returns {Promise} API response. 301 | */ 302 | async getTokenTransfers (blockchain, address) { 303 | utils.validateEnumParameter('blockchain', blockchain, ['ethereum']) 304 | 305 | if (typeof address === 'undefined') { 306 | throw new SDKError(400, '[Zabo] Missing `address` parameter. See: https://zabo.com/docs#get-token-transfers-for-address-or-xpub') 307 | } 308 | 309 | try { 310 | return this.api.request('GET', `/blockchains/${blockchain}/addresses/${address}/token-transfers`) 311 | } catch (err) { 312 | throw new SDKError(err.error_type, err.message, err.request_id) 313 | } 314 | } 315 | 316 | /** 317 | * This function returns the token transfers which executed as a result of the given transaction hash. 318 | * @param {'ethereum' | {}} blockchain The blockchain name. 319 | * @param {String} transactionHash The transaction hash. 320 | * @returns {Promise} API response. 321 | */ 322 | async getTokenTransfer (blockchain, transactionHash) { 323 | utils.validateEnumParameter('blockchain', blockchain, ['ethereum']) 324 | 325 | if (typeof transactionHash === 'undefined') { 326 | throw new SDKError(400, '[Zabo] Missing `transactionHash` parameter. See: https://zabo.com/docs#get-a-token-transfer-by-hash') 327 | } 328 | 329 | try { 330 | return this.api.request('GET', `/blockchains/${blockchain}/token-transfers/${transactionHash}`) 331 | } catch (err) { 332 | throw new SDKError(err.error_type, err.message, err.request_id) 333 | } 334 | } 335 | } 336 | 337 | /** 338 | * @typedef {Blockchains} BlockchainsAPI 339 | * @type {(api) => BlockchainsAPI} 340 | */ 341 | module.exports = (api) => { 342 | return new Blockchains(api) 343 | } 344 | -------------------------------------------------------------------------------- /src/resources/currencies.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @Copyright (c) 2019-present, Zabo & Modular, Inc. All rights reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | 'use strict' 18 | 19 | const utils = require('../utils') 20 | const { SDKError } = require('../err') 21 | 22 | /** 23 | * @typedef {{ 24 | * ticker?: String 25 | * name?: String 26 | * type?: String 27 | * priority?: Number 28 | * logo?: String 29 | * decimals?: Number 30 | * supporting_providers?: [String] 31 | * address?: String 32 | * resource_type?: String 33 | * }} Currency 34 | * 35 | * @typedef {{ 36 | * from?: String 37 | * to?: String 38 | * rate?: String 39 | * timestamp?: Number 40 | * resource_type?: String 41 | * }} ExchangeRate 42 | * 43 | * @typedef {{ 44 | * data?: [Currency] 45 | * request_id?: String 46 | * }} GetListCurrenciesResp 47 | * 48 | * @typedef {{ 49 | * request_id?: String 50 | * } & Currency} GetOneCurrencyResp 51 | * 52 | * @typedef {{ 53 | * data?: [ExchangeRate] 54 | * request_id?: String 55 | * }} GetExchangeRatesResp 56 | */ 57 | 58 | /** 59 | * Currencies API. 60 | */ 61 | class Currencies { 62 | constructor (api) { 63 | /** @private */ 64 | this.api = api 65 | } 66 | 67 | /** 68 | * This endpoint will return the full list of currencies available in the system. 69 | * Use the providers endpoint to see the currencies supported by each provider. 70 | * @param {{ 71 | * limit?: Number 72 | * cursor?: String 73 | * }} param0 Request parameters. 74 | * @returns {Promise} API response. 75 | */ 76 | async getList ({ limit = 25, cursor = '' } = {}) { 77 | utils.validateListParameters(limit) 78 | 79 | try { 80 | return this.api.request('GET', `/currencies?limit=${limit}&cursor=${cursor}`) 81 | } catch (err) { 82 | throw new SDKError(err.error_type, err.message, err.request_id) 83 | } 84 | } 85 | 86 | /** 87 | * This endpoint provides information about a specific currency. 88 | * @param {String} ticker Identifier for this currency or asset. 89 | * @returns {Promise} API response. 90 | */ 91 | async getOne (ticker) { 92 | if (!ticker) { 93 | throw new SDKError(400, '[Zabo] Missing `ticker` input. See: https://zabo.com/docs#get-specific-currency') 94 | } 95 | 96 | try { 97 | return this.api.request('GET', `/currencies/${ticker}`) 98 | } catch (err) { 99 | throw new SDKError(err.error_type, err.message, err.request_id) 100 | } 101 | } 102 | 103 | /** 104 | * This function returns a list of exchange rates for the available cryptocurrencies/assets 105 | * for a given fiat currency. Currently, USD is the only fiat currency available. 106 | * Any supported assets can be used for the tickers parameter. This parameter is optional 107 | * and, if left out, all supported cryptocurrencies/assets will be returned. 108 | * @param {{ 109 | * toCrypto?: Boolean 110 | * limit?: Number 111 | * cursor?: String 112 | * tickers?: Array | String 113 | * }} param0 Request parameters. 114 | * @returns {Promise} API response. 115 | */ 116 | async getExchangeRates ({ toCrypto = false, limit = 25, cursor = '', tickers = '' } = {}) { 117 | utils.validateListParameters(limit) 118 | 119 | let url = `/exchange-rates?to_crypto=${!!toCrypto}&limit=${limit}&cursor=${cursor}` 120 | 121 | if (tickers) { 122 | if (Array.isArray(tickers)) { 123 | tickers = tickers.join(',') 124 | } 125 | url = `${url}&tickers=${tickers}` 126 | } 127 | 128 | return this.api.request('GET', url) 129 | } 130 | } 131 | 132 | /** 133 | * @typedef {Currencies} CurrenciesAPI 134 | * @type {(api) => CurrenciesAPI} 135 | */ 136 | module.exports = (api) => { 137 | return new Currencies(api) 138 | } 139 | -------------------------------------------------------------------------------- /src/resources/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @Copyright (c) 2019-present, Zabo & Modular, Inc. All rights reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /** 18 | * The API endpoints. 19 | * @param {import('../api')} api An API object. 20 | * @param {Boolean} isNode Is the SDK running on Node.js. 21 | * @returns {any} 22 | */ 23 | module.exports = async (api, isNode) => { 24 | if (isNode) { 25 | return { 26 | users: require('./users')(api), 27 | teams: require('./teams')(api), 28 | blockchains: require('./blockchains')(api), 29 | currencies: require('./currencies')(api), 30 | transactions: require('./transactions')(api), 31 | providers: require('./providers')(api) 32 | } 33 | } 34 | 35 | const resources = { 36 | teams: require('./teams')(api), 37 | accounts: require('./accounts')(api), 38 | trading: require('./trading')(api), 39 | currencies: require('./currencies')(api), 40 | transactions: require('./transactions')(api), 41 | providers: require('./providers')(api) 42 | } 43 | 44 | return resources 45 | } 46 | -------------------------------------------------------------------------------- /src/resources/providers.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @Copyright (c) 2019-present, Zabo & Modular, Inc. All rights reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | 'use strict' 18 | 19 | const utils = require('../utils') 20 | const { SDKError } = require('../err') 21 | 22 | /** 23 | * @typedef {{ 24 | * data?: [import('./users').Provider] 25 | * request_id?: String 26 | * }} GetListProvidersResp 27 | * 28 | * @typedef {{ 29 | * request_id?: String 30 | * } & import('./users').Provider} GetOneProviderResp 31 | */ 32 | 33 | /** 34 | * Providers API. 35 | */ 36 | class Providers { 37 | constructor (api) { 38 | /** @private */ 39 | this.api = api 40 | } 41 | 42 | /** 43 | * This endpoint will return the list of all providers available for an application as 44 | * well as the scopes and currencies available for that particular provider. 45 | * @param {{ 46 | * limit?: Number 47 | * cursor?: String 48 | * }} param0 Request parameters. 49 | * @returns {Promise} API response. 50 | */ 51 | async getList ({ limit = 25, cursor = '' } = {}) { 52 | utils.validateListParameters(limit, cursor) 53 | 54 | try { 55 | return this.api.request('GET', `/providers?limit=${limit}&cursor=${cursor}`) 56 | } catch (err) { 57 | throw new SDKError(err.error_type, err.message, err.request_id) 58 | } 59 | } 60 | 61 | /** 62 | * This endpoint will return the requested provider resource. 63 | * **Note:** The provider name is the 'computer' name for the provider, not the display name. 64 | * @param {String} name Name of the provider. 65 | * @returns {Promise} API response. 66 | */ 67 | async getOne (name) { 68 | if (!name) { 69 | throw new SDKError(400, '[Zabo] Missing `name` input. See: https://zabo.com/docs#get-a-provider') 70 | } 71 | 72 | try { 73 | return this.api.request('GET', `/providers/${name}`) 74 | } catch (err) { 75 | throw new SDKError(err.error_type, err.message, err.request_id) 76 | } 77 | } 78 | } 79 | 80 | /** 81 | * @typedef {Providers} ProvidersAPI 82 | * @type {(api) => ProvidersAPI} 83 | */ 84 | module.exports = (api) => { 85 | return new Providers(api) 86 | } 87 | -------------------------------------------------------------------------------- /src/resources/teams.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @Copyright (c) 2019-present, Zabo & Modular, Inc. All rights reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | 'use strict' 18 | 19 | const utils = require('../utils') 20 | const { SDKError } = require('../err') 21 | 22 | /** 23 | * Teams API. 24 | */ 25 | class Teams { 26 | constructor (api) { 27 | /** @private */ 28 | this.api = api 29 | this.id = null 30 | this.data = null 31 | } 32 | 33 | setId (id) { 34 | if (!utils.uuidValidate(id)) { 35 | throw new SDKError(400, '[Zabo] Team id must be a valid UUID version 4. See: https://zabo.com/docs') 36 | } 37 | 38 | this.id = id 39 | } 40 | 41 | async get () { 42 | if (utils.isNode()) { 43 | if (!this.id) { 44 | throw new SDKError(401, '[Zabo] SDK has not initialized properly.') 45 | } 46 | 47 | try { 48 | this.data = await this.api.request('GET', `/teams/${this.id}`) 49 | } catch (err) { 50 | throw new SDKError(err.error_type, err.message, err.request_id) 51 | } 52 | } else { 53 | if (!this.api.clientId) { 54 | throw new SDKError(401, '[Zabo] SDK has not initialized properly.') 55 | } 56 | 57 | try { 58 | this.data = await this.api.request('GET', `/teams/info?client_id=${this.api.clientId}`, '', true) 59 | } catch (err) { 60 | throw new SDKError(err.error_type, err.message, err.request_id) 61 | } 62 | } 63 | 64 | return this.data 65 | } 66 | 67 | async getSession () { 68 | const session = this.data && this.data.session 69 | 70 | if (session && new Date(session.expires_at) > Date.now()) { 71 | return session 72 | } 73 | 74 | const team = await this.get() 75 | return team.session 76 | } 77 | } 78 | 79 | /** 80 | * @typedef {Teams} TeamsAPI 81 | * @type {(api) => TeamsAPI} 82 | */ 83 | module.exports = (api, appId) => { 84 | return new Teams(api, appId) 85 | } 86 | -------------------------------------------------------------------------------- /src/resources/trading.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @Copyright (c) 2019-present, Zabo & Modular, Inc. All rights reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | 'use strict' 18 | 19 | const utils = require('../utils') 20 | const { SDKError } = require('../err') 21 | 22 | /** 23 | * @typedef {{ 24 | * id?: String 25 | * base_currency?: String 26 | * quote_currency?: String 27 | * base_amount?: String 28 | * buy_or_sell?: 'buy' | 'sell' 29 | * quote_amount?: String 30 | * price?: String 31 | * time_in_force?: String 32 | * ttl?: Number 33 | * provide_liquidity_only?: Boolean 34 | * type?: 'limit' | 'market' 35 | * status?: String 36 | * created_at?: Number 37 | * done_at?: Number 38 | * done_reason?: String 39 | * filled_size?: String 40 | * fill_fees?: String 41 | * settled?: Boolean 42 | * request_id?: String 43 | * }} Order 44 | * 45 | * @typedef {{ 46 | * data?: [{ 47 | * base_currency?: String 48 | * quote_currency?: String 49 | * }] 50 | * request_id?: String 51 | * }} GetSymbolsResp 52 | * 53 | * @typedef {{ 54 | * last_price?: String 55 | * last_size?: String 56 | * ask?: String 57 | * ask_size?: String 58 | * bid?: String 59 | * bid_size?: String 60 | * volume?: String 61 | * timestamp?: Number 62 | * request_id?: String 63 | * }} GetTickerInfoResp 64 | * 65 | * @typedef {{ 66 | * data?: [Order] 67 | * request_id?: String 68 | * }} GetOrdersResp 69 | * 70 | * @typedef {{ 71 | * data?: Order 72 | * request_id?: String 73 | * }} GetOrderResp 74 | * 75 | * @typedef {Pick} CreateOrderResp 76 | * 77 | * @typedef {{ 78 | * data?: [String] 79 | * request_id?: String 80 | * }} CancelOrdersResp 81 | * 82 | * @typedef {CancelOrdersResp} CancelOrderResp 83 | */ 84 | 85 | /** 86 | * Trading API. 87 | */ 88 | class Trading { 89 | constructor (api) { 90 | /** @private */ 91 | this.api = api 92 | this.account = null 93 | } 94 | 95 | /** 96 | * @private 97 | */ 98 | _setAccount (account) { 99 | this.account = account 100 | } 101 | 102 | /** 103 | * This function returns the trading tickers available at the given account's provider. 104 | * These pairs can be used in the remaining calls to the Zabo Trading API. 105 | * @returns {Promise} API response. 106 | */ 107 | async getSymbols () { 108 | if (!this.account || !this.account.id) { 109 | throw new SDKError(400, '[Zabo] Not connected. See: https://zabo.com/docs#connecting-a-user') 110 | } 111 | 112 | try { 113 | return this.api.request('GET', `/accounts/${this.account.id}/trading-symbols`) 114 | } catch (err) { 115 | throw new SDKError(err.error_type, err.message, err.request_id) 116 | } 117 | } 118 | 119 | /** 120 | * This function returns the current market information available for the currency pair, 121 | * at the provider, for the given account. 122 | * @param {{ 123 | * baseCurrency: String 124 | * quoteCurrency: String 125 | * }} param0 Request parameters. 126 | * @returns {Promise} API response. 127 | */ 128 | async getTickerInfo ({ baseCurrency, quoteCurrency } = {}) { 129 | if (!this.account || !this.account.id) { 130 | throw new SDKError(400, '[Zabo] Not connected. See: https://zabo.com/docs#connecting-a-user') 131 | } else if (!baseCurrency) { 132 | throw new SDKError(400, '[Zabo] Missing `baseCurrency` parameter. See: https://zabo.com/docs/#get-ticker-info') 133 | } else if (!quoteCurrency) { 134 | throw new SDKError(400, '[Zabo] Missing `quoteCurrency` parameter. See: https://zabo.com/docs/#get-ticker-info') 135 | } 136 | 137 | try { 138 | return this.api.request('GET', `/accounts/${this.account.id}/tickers/${baseCurrency}-${quoteCurrency}`) 139 | } catch (err) { 140 | throw new SDKError(err.error_type, err.message, err.request_id) 141 | } 142 | } 143 | 144 | /** 145 | * This function returns all active orders for the given account. 146 | * @returns {Promise} API response. 147 | */ 148 | async getOrders () { 149 | if (!this.account || !this.account.id) { 150 | throw new SDKError(400, '[Zabo] Not connected. See: https://zabo.com/docs#connecting-a-user') 151 | } 152 | 153 | try { 154 | return this.api.request('GET', `/accounts/${this.account.id}/orders`) 155 | } catch (err) { 156 | throw new SDKError(err.error_type, err.message, err.request_id) 157 | } 158 | } 159 | 160 | /** 161 | * This function returns the specific order for the given order id. 162 | * @param {{ 163 | * orderId: String 164 | * }} param0 Request parameters. 165 | * @returns {Promise} API response. 166 | */ 167 | async getOrder ({ orderId } = {}) { 168 | if (!this.account || !this.account.id) { 169 | throw new SDKError(400, '[Zabo] Not connected. See: https://zabo.com/docs#connecting-a-user') 170 | } else if (!orderId) { 171 | throw new SDKError(400, '[Zabo] Missing `orderId` parameter. See: https://zabo.com/docs/#get-an-order') 172 | } else if (!utils.uuidValidate(orderId)) { 173 | throw new SDKError(400, '[Zabo] `orderId` must be a valid UUID v4. See: https://zabo.com/docs/#get-an-order') 174 | } 175 | 176 | try { 177 | return this.api.request('GET', `/accounts/${this.account.id}/orders/${orderId}`) 178 | } catch (err) { 179 | throw new SDKError(err.error_type, err.message, err.request_id) 180 | } 181 | } 182 | 183 | /** 184 | * This function creates a new trade order. 185 | * @param {{ 186 | * baseCurrency: String 187 | * baseAmount?: String 188 | * quoteCurrency: String 189 | * quoteAmount?: String 190 | * buyOrSell: 'buy' | 'sell' 191 | * priceLimit?: String 192 | * timeInForce?: String 193 | * ttl?: Number 194 | * provideLiquidityOnly?: Boolean 195 | * }} param0 Request parameters. 196 | * @returns {Promise} API response. 197 | */ 198 | async createOrder ({ baseCurrency, quoteCurrency, buyOrSell, priceLimit, baseAmount, quoteAmount, provideLiquidityOnly, timeInForce, ttl } = {}) { 199 | if (!this.account || !this.account.id) { 200 | throw new SDKError(400, '[Zabo] Not connected. See: https://zabo.com/docs#connecting-a-user') 201 | } else if (!baseCurrency) { 202 | throw new SDKError(400, '[Zabo] Missing `baseCurrency` parameter. See: https://zabo.com/docs/#place-new-order') 203 | } else if (!quoteCurrency) { 204 | throw new SDKError(400, '[Zabo] Missing `quoteCurrency` parameter. See: https://zabo.com/docs/#place-new-order') 205 | } else if (typeof baseAmount !== 'undefined' && baseAmount <= 0) { 206 | throw new SDKError(400, '[Zabo] `baseAmount` must be greater than 0. See: https://zabo.com/docs/#place-new-order') 207 | } else if (typeof quoteAmount !== 'undefined' && quoteAmount <= 0) { 208 | throw new SDKError(400, '[Zabo] `quoteAmount` must be greater than 0. See: https://zabo.com/docs/#place-new-order') 209 | } else if (typeof priceLimit !== 'undefined' && priceLimit <= 0) { 210 | throw new SDKError(400, '[Zabo] `priceLimit` must be greater than 0. See: https://zabo.com/docs/#place-new-order') 211 | } 212 | 213 | utils.validateEnumParameter('buyOrSell', buyOrSell, ['buy', 'sell']) 214 | utils.validateEnumParameter('timeInForce', timeInForce, ['GTC', 'GTT', 'IOC', 'FOK'], true) 215 | 216 | try { 217 | return this.api.request('POST', `/accounts/${this.account.id}/orders`, { 218 | base_currency: baseCurrency, 219 | quote_currency: quoteCurrency, 220 | buy_or_sell: buyOrSell, 221 | price_limit: priceLimit, 222 | base_amount: baseAmount, 223 | quote_amount: quoteAmount, 224 | provide_liquidity_only: provideLiquidityOnly, 225 | time_in_force: timeInForce, 226 | ttl: ttl 227 | }) 228 | } catch (err) { 229 | throw new SDKError(err.error_type, err.message, err.request_id) 230 | } 231 | } 232 | 233 | /** 234 | * This function cancels all open orders. 235 | * @returns {Promise} API response. 236 | */ 237 | async cancelOrders () { 238 | if (!this.account || !this.account.id) { 239 | throw new SDKError(400, '[Zabo] Not connected. See: https://zabo.com/docs#connecting-a-user') 240 | } 241 | 242 | try { 243 | return this.api.request('DELETE', `/accounts/${this.account.id}/orders`) 244 | } catch (err) { 245 | throw new SDKError(err.error_type, err.message, err.request_id) 246 | } 247 | } 248 | 249 | /** 250 | * This function cancels the order with the given order id. 251 | * @param {{ 252 | * orderId: String 253 | * }} param0 Request parameters. 254 | * @returns {Promise} API response. 255 | */ 256 | async cancelOrder ({ orderId } = {}) { 257 | if (!this.account || !this.account.id) { 258 | throw new SDKError(400, '[Zabo] Not connected. See: https://zabo.com/docs#connecting-a-user') 259 | } else if (!orderId) { 260 | throw new SDKError(400, '[Zabo] Missing `orderId` parameter. See: https://zabo.com/docs/#cancel-an-order') 261 | } else if (!utils.uuidValidate(orderId)) { 262 | throw new SDKError(400, '[Zabo] `orderId` must be a valid UUID v4. See: https://zabo.com/docs/#cancel-an-order') 263 | } 264 | 265 | try { 266 | return this.api.request('DELETE', `/accounts/${this.account.id}/orders/${orderId}`) 267 | } catch (err) { 268 | throw new SDKError(err.error_type, err.message, err.request_id) 269 | } 270 | } 271 | } 272 | 273 | /** 274 | * @typedef {Trading} TradingAPI 275 | * @type {(api) => TradingAPI} 276 | */ 277 | module.exports = (api) => { 278 | return new Trading(api) 279 | } 280 | -------------------------------------------------------------------------------- /src/resources/transactions.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @Copyright (c) 2019-present, Zabo & Modular, Inc. All rights reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | 'use strict' 18 | 19 | const utils = require('../utils') 20 | const { SDKError } = require('../err') 21 | 22 | /** 23 | * @typedef {{ 24 | * direction?: 'sent' | 'received' 25 | * ticker?: String 26 | * provider_ticker?: String 27 | * amount?: String 28 | * asset_is_verified?: Boolean 29 | * fiat_ticker?: String 30 | * fiat_value?: String 31 | * fiat_asset_is_verified?: Boolean 32 | * other_parties?: [String] 33 | * }} Part 34 | * 35 | * @typedef {{ 36 | * type?: String 37 | * ticker?: String 38 | * provider_ticker?: String 39 | * amount?: String 40 | * asset_is_verified?: Boolean 41 | * fiat_ticker?: String 42 | * fiat_value?: String 43 | * fiat_asset_is_verified?: Boolean 44 | * resource_type?: String 45 | * }} Fee 46 | * 47 | * @typedef {{ 48 | * id?: String 49 | * status?: String 50 | * transaction_type?: String 51 | * parts?: [Part] 52 | * fees?: [Fee] 53 | * misc?: [String] 54 | * fiat_calculated_at?: Number 55 | * initiated_at?: Number 56 | * confirmed_at?: Number 57 | * resource_type?: String 58 | * }} Transaction 59 | * 60 | * @typedef {{ 61 | * data?: [Transaction] 62 | * delay?: Number 63 | * last_updated_at?: Number 64 | * has_errors?: Boolean 65 | * request_id?: String 66 | * }} GetListTransactionsResp 67 | */ 68 | 69 | /** 70 | * Transactions API. 71 | */ 72 | class Transactions { 73 | constructor (api) { 74 | /** @private */ 75 | this.api = api 76 | this.account = null 77 | } 78 | 79 | /** 80 | * @private 81 | */ 82 | _setAccount (account) { 83 | this.account = account 84 | } 85 | 86 | /** 87 | * getOne fetches a specific transaction for the given account. 88 | * @param {{ 89 | * userId?: String 90 | * accountId?: String 91 | * txId: String 92 | * ticker?: String 93 | * }} param0 Transaction request object. 94 | * @returns {Promise} A transaction. 95 | */ 96 | async getOne ({ userId, accountId, txId, ticker } = {}) { 97 | if (utils.isNode()) { 98 | if (!userId) { 99 | throw new SDKError(400, '[Zabo] Missing `userId` parameter. See: https://zabo.com/docs#get-a-specific-transaction') 100 | } else if (!utils.uuidValidate(userId)) { 101 | throw new SDKError(400, '[Zabo] `userId` must be a valid UUID v4. See: https://zabo.com/docs#get-a-specific-transaction') 102 | } else if (!accountId) { 103 | throw new SDKError(400, '[Zabo] Missing `accountId` parameter. See: https://zabo.com/docs#get-a-specific-transaction') 104 | } else if (!utils.uuidValidate(accountId)) { 105 | throw new SDKError(400, '[Zabo] `accountId` must be a valid UUID v4. See: https://zabo.com/docs#get-a-specific-transaction') 106 | } else if (!txId) { 107 | throw new SDKError(400, '[Zabo] Missing `txId` parameter. See: https://zabo.com/docs#get-a-specific-transaction') 108 | } 109 | 110 | try { 111 | return this.api.request('GET', `/users/${userId}/accounts/${accountId}/transactions/${txId}`) 112 | } catch (err) { 113 | throw new SDKError(err.error_type, err.message, err.request_id) 114 | } 115 | } 116 | 117 | if (!this.account.id) { 118 | throw new SDKError(400, '[Zabo] Not connected. See: https://zabo.com/docs#connecting-a-user') 119 | } else if (!txId) { 120 | throw new SDKError(400, '[Zabo] Missing `txId` parameter. See: https://zabo.com/docs#get-a-specific-transaction') 121 | } 122 | 123 | try { 124 | return this.api.request('GET', `/accounts/${this.account.id}/transactions/${txId}`) 125 | } catch (err) { 126 | throw new SDKError(err.error_type, err.message, err.request_id) 127 | } 128 | } 129 | 130 | /** 131 | * getList fetches a list of transaction for the given account. 132 | * @param {{ 133 | * userId: String 134 | * accountId?: String 135 | * ticker?: String 136 | * limit?: Number 137 | * cursor?: String 138 | * }} param0 Transactions request object. 139 | * @returns {Promise} An API response with transactions within `data`. 140 | */ 141 | async getList ({ userId, accountId, ticker = '', limit = 25, cursor = '' } = {}) { 142 | utils.validateListParameters(limit) 143 | 144 | let url = null 145 | 146 | if (utils.isNode()) { 147 | if (!userId) { 148 | throw new SDKError(400, '[Zabo] Missing `userId` parameter. See: https://zabo.com/docs#get-transaction-history') 149 | } else if (!utils.uuidValidate(userId)) { 150 | throw new SDKError(400, '[Zabo] `userId` must be a valid UUID v4. See: https://zabo.com/docs#get-transaction-history') 151 | } else if (!accountId) { 152 | throw new SDKError(400, '[Zabo] Missing `accountId` parameter. See: https://zabo.com/docs#get-transaction-history') 153 | } else if (!utils.uuidValidate(accountId)) { 154 | throw new SDKError(400, '[Zabo] `accountId` must be a valid UUID v4. See: https://zabo.com/docs#get-transaction-history') 155 | } 156 | 157 | url = `/users/${userId}/accounts/${accountId}/transactions?limit=${limit}&cursor=${cursor}` 158 | if (ticker !== '') { 159 | url = `${url}&ticker=${ticker}` 160 | } 161 | } else { 162 | if (!this.account.id) { 163 | throw new SDKError(400, '[Zabo] Not connected. See: https://zabo.com/docs#connecting-a-user') 164 | } 165 | 166 | url = `/accounts/${this.account.id}/transactions?limit=${limit}&cursor=${cursor}` 167 | if (ticker !== '') { 168 | url = `${url}&ticker=${ticker}` 169 | } 170 | } 171 | 172 | try { 173 | return this.api.request('GET', url) 174 | } catch (err) { 175 | throw new SDKError(err.error_type, err.message, err.request_id) 176 | } 177 | } 178 | } 179 | 180 | /** 181 | * @typedef {Transactions} TransactionsAPI 182 | * @type {(api) => TransactionsAPI} 183 | */ 184 | module.exports = (api) => { 185 | return new Transactions(api) 186 | } 187 | -------------------------------------------------------------------------------- /src/resources/users.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @Copyright (c) 2019-present, Zabo & Modular, Inc. All rights reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | 'use strict' 18 | 19 | const utils = require('../utils') 20 | const { SDKError } = require('../err') 21 | 22 | /** 23 | * @typedef {{ 24 | * id?: String, 25 | * name?: String 26 | * }} Application 27 | * 28 | * @typedef {{ 29 | * name?: String 30 | * display_name?: String 31 | * logo?: String 32 | * auth_type?: String 33 | * available_currencies?: [{ 34 | * type?: String 35 | * list?: [String] 36 | * resource_type?: String 37 | * }] 38 | * available_scopes?: [ 39 | * { 40 | * name?: String 41 | * display_name?: String 42 | * description?: String 43 | * } 44 | * ] 45 | * status?: any 46 | * is_beta?: Boolean 47 | * connect_notice?: String 48 | * status_notice?: String 49 | * resource_type?: String 50 | * }} Provider 51 | * 52 | * @typedef {{ 53 | * id?: String 54 | * blockchain?: String 55 | * provider?: Provider 56 | * last_connected?: Number 57 | * created_at?: Number 58 | * updated_at?: Number 59 | * resource_type?: String 60 | * balances?: [Balance] 61 | * }} Account 62 | * 63 | * @typedef {{ 64 | * id?: String, 65 | * accounts?: [Account] 66 | * created_at?: Number 67 | * updated_at?: Number 68 | * resource_type?: String 69 | * }} User 70 | * 71 | * @typedef {{ 72 | * request_id?: String 73 | * } & User} CreateUserResp 74 | * 75 | * @typedef {{ 76 | * id?: String 77 | * application?: Application 78 | * accounts?: [Account] 79 | * created_at?: Number 80 | * updated_at?: Number 81 | * resource_type?: String 82 | * request_id?: String 83 | * }} AddAccountResp 84 | * 85 | * @typedef {AddAccountResp} RemoveAccountResp 86 | * 87 | * @typedef {{ 88 | * request_id?: String 89 | * } & User} GetOneUserResp 90 | * 91 | * @typedef {{ 92 | * data?: [User] 93 | * total?: Number 94 | * request_id?: String 95 | * }} GetListUsersResp 96 | * 97 | * @typedef {{ 98 | * ticker?: String 99 | * provider_ticker?: String 100 | * name?: String 101 | * asset_is_verified?: Boolean 102 | * asset_type?: String 103 | * amount?: String 104 | * decimals?: Number 105 | * fiat_ticker?: String 106 | * fiat_value?: String 107 | * fiat_asset_is_verified?: Boolean 108 | * logo?: String 109 | * updated_at?: Number 110 | * misc?: [String] 111 | * resource_type?: String 112 | * }} Balance 113 | * 114 | * @typedef {{ 115 | * request_id?: String 116 | * } & Account} GetAccountResp 117 | * 118 | * @typedef {{ 119 | * data?: [Balance] 120 | * delay?: Number 121 | * request_id?: String 122 | * }} GetBalancesResp 123 | * 124 | * @typedef {{ 125 | * asset?: import('./currencies').Currency 126 | * provider_ticker?: String 127 | * address?: String 128 | * resource_type?: String 129 | * request_id?: String 130 | * }} CreateDepositAddressResp 131 | * 132 | * @typedef {{ 133 | * data?: [ 134 | * { 135 | * ticker?: String 136 | * provider_ticker?: String 137 | * address?: String 138 | * resource_type?: String 139 | * } 140 | * ] 141 | * request_id?: String 142 | * }} GetDepositAddressesResp 143 | */ 144 | 145 | class Users { 146 | constructor (api) { 147 | /** @private */ 148 | this.api = api 149 | } 150 | 151 | /** 152 | * This function creates a new user for your application. A user connects their cryptocurrency 153 | * wallet via the Zabo Client API, and then you can create a user from your server. 154 | * From there, your application server can have access this user's account data. 155 | * @param {any} account Account data. 156 | * @returns {Promise} API response. 157 | */ 158 | async create (account = {}) { 159 | if (!account.id) { 160 | throw new SDKError(400, '[Zabo] Missing `id` property in account object. See: https://zabo.com/docs#create-a-user') 161 | } else if (!account.token) { 162 | throw new SDKError(400, '[Zabo] Missing `token` property in account object. See: https://zabo.com/docs#create-a-user') 163 | } 164 | 165 | try { 166 | return this.api.request('POST', '/users', account) 167 | } catch (err) { 168 | throw new SDKError(err.error_type, err.message, err.request_id) 169 | } 170 | } 171 | 172 | /** 173 | * This function inserts an additional account into a given user object. 174 | * Useful when your application makes it possible for the same user to connect with multiple providers. 175 | * @param {any} user The user object received from zabo.users.create() response. 176 | * @param {any} account The account object received when the user connected. This object must contain a valid token. 177 | * @returns {Promise} API response. 178 | */ 179 | async addAccount (user = {}, account = {}) { 180 | if (!user.id) { 181 | throw new SDKError(400, '[Zabo] Missing `id` property in user object. See: https://zabo.com/docs#add-account-to-existing-user') 182 | } else if (!account.id) { 183 | throw new SDKError(400, '[Zabo] Missing `id` property in account object. See: https://zabo.com/docs#add-account-to-existing-user') 184 | } else if (!account.token) { 185 | throw new SDKError(400, '[Zabo] Missing `token` property in account object. See: https://zabo.com/docs#add-account-to-existing-user') 186 | } 187 | 188 | try { 189 | return this.api.request('POST', `/users/${user.id}/accounts`, account) 190 | } catch (err) { 191 | throw new SDKError(err.error_type, err.message, err.request_id) 192 | } 193 | } 194 | 195 | /** 196 | * This function removes a defined account from a given user object. Use this function when a user 197 | * doesn't want to have any specific provider account linked to your application anymore. 198 | * @param {{ userId: String, accountId: String }} param0 The user & account IDs. 199 | * @returns {Promise} API response. 200 | */ 201 | async removeAccount ({ userId, accountId }) { 202 | if (!userId) { 203 | throw new SDKError(400, '[Zabo] Missing `id` property in user object. See: https://zabo.com/docs#remove-account-from-user') 204 | } else if (!accountId) { 205 | throw new SDKError(400, '[Zabo] Missing `id` property in account object. See: https://zabo.com/docs#remove-account-from-user') 206 | } 207 | 208 | try { 209 | return this.api.request('DELETE', `/users/${userId}/accounts/${accountId}`) 210 | } catch (err) { 211 | throw new SDKError(err.error_type, err.message, err.request_id) 212 | } 213 | } 214 | 215 | /** 216 | * This function returns the user requested. The user object contains the user's unique id 217 | * and accounts information, including the provider used during connection with your app. 218 | * @param {String} id The user's ID. 219 | * @returns {Promise} API response. 220 | */ 221 | async getOne (id) { 222 | if (!id) { 223 | throw new SDKError(400, '[Zabo] Missing `id` input. See: https://zabo.com/docs#get-a-user') 224 | } 225 | 226 | try { 227 | return this.api.request('GET', `/users/${id}`) 228 | } catch (err) { 229 | throw new SDKError(err.error_type, err.message, err.request_id) 230 | } 231 | } 232 | 233 | /** 234 | * This function returns all users registered with the application. You must have authorization to the users. 235 | * @param {{ limit?: Number, cursor?: String }} param0 Cursor location. 236 | * @returns {Promise} API response. 237 | */ 238 | async getList ({ limit = 25, cursor = '' } = {}) { 239 | utils.validateListParameters(limit, cursor) 240 | 241 | try { 242 | return this.api.request('GET', `/users?limit=${limit}&cursor=${cursor}`) 243 | } catch (err) { 244 | throw new SDKError(err.error_type, err.message, err.request_id) 245 | } 246 | } 247 | 248 | /** 249 | * This function returns the full account object for a particular user requested. 250 | * @param {{ userId: String, accountId: String }} param0 The user & account IDs. 251 | * @returns {Promise} API response. 252 | */ 253 | async getAccount ({ userId, accountId } = {}) { 254 | if (!userId) { 255 | throw new SDKError(400, '[Zabo] Missing `userId` parameter. See: https://zabo.com/docs#get-a-user-account') 256 | } else if (!accountId) { 257 | throw new SDKError(400, '[Zabo] Missing `accountId` parameter. See: https://zabo.com/docs#get-a-user-account') 258 | } 259 | 260 | try { 261 | return this.api.request('GET', `/users/${userId}/accounts/${accountId}`) 262 | } catch (err) { 263 | throw new SDKError(err.error_type, err.message, err.request_id) 264 | } 265 | } 266 | 267 | /** 268 | * Returns the user balances for the requested currencies. When requesting balances from the client, 269 | * the request should be made in the context of the connected account. When requesting from an application server, 270 | * requests should be made in the context of a user. Cryptocurrencies available to your app can be queried 271 | * using the currencies function documented below. If no currencies are specified, then all 272 | * available currencies will be returned. 273 | * @param {{ 274 | * userId?: String 275 | * accountId?: String 276 | * tickers?: String 277 | * }} param0 Request parameters. 278 | * @returns {Promise} API response. 279 | */ 280 | async getBalances ({ userId, accountId, tickers } = {}) { 281 | if (!userId) { 282 | throw new SDKError(400, '[Zabo] Missing `userId` parameter. See: https://zabo.com/docs#get-a-specific-balance') 283 | } else if (!accountId) { 284 | throw new SDKError(400, '[Zabo] Missing `accountId` parameter. See: https://zabo.com/docs#get-a-specific-balance') 285 | } 286 | 287 | let url = `/users/${userId}/accounts/${accountId}/balances` 288 | 289 | if (tickers) { 290 | if (Array.isArray(tickers)) { 291 | tickers = tickers.join(',') 292 | } 293 | url = `${url}?tickers=${tickers}` 294 | } 295 | 296 | try { 297 | return this.api.request('GET', url) 298 | } catch (err) { 299 | throw new SDKError(err.error_type, err.message, err.request_id) 300 | } 301 | } 302 | 303 | /** 304 | * This endpoint will create and return a deposit address for the specified account. If the currency 305 | * is not supported by the connected provider, you will receive an 'unsupported' error. 306 | * @param {{ 307 | * userId: String 308 | * accountId: String 309 | * ticker: String 310 | * }} param0 Request parameters. 311 | * @returns {Promise} API response. 312 | */ 313 | async createDepositAddress ({ userId, accountId, ticker } = {}) { 314 | if (!userId) { 315 | throw new SDKError(400, '[Zabo] Missing `userId` parameter. See: https://zabo.com/docs#create-a-deposit-address') 316 | } else if (!accountId) { 317 | throw new SDKError(400, '[Zabo] Missing `accountId` parameter. See: https://zabo.com/docs#create-a-deposit-address') 318 | } else if (!ticker || typeof ticker !== 'string') { 319 | throw new SDKError(400, '[Zabo] Missing or invalid `ticker` parameter. See: https://zabo.com/docs#create-a-deposit-address') 320 | } 321 | 322 | try { 323 | return this.api.request('POST', `/users/${userId}/accounts/${accountId}/deposit-addresses?ticker=${ticker}`) 324 | } catch (err) { 325 | throw new SDKError(err.error_type, err.message, err.request_id) 326 | } 327 | } 328 | 329 | /** 330 | * This endpoint will retrieve all deposit addresses for the specified account. If the currency 331 | * is not supported by the connected provider, you will receive an 'unsupported' error. 332 | * @param {{ 333 | * userId: String 334 | * accountId: String 335 | * ticker: String 336 | * }} param0 Request parameters. 337 | * @returns {Promise} API response. 338 | */ 339 | async getDepositAddresses ({ userId, accountId, ticker } = {}) { 340 | if (!userId) { 341 | throw new SDKError(400, '[Zabo] Missing `userId` parameter. See: https://zabo.com/docs#get-deposit-addresses') 342 | } else if (!accountId) { 343 | throw new SDKError(400, '[Zabo] Missing `accountId` parameter. See: https://zabo.com/docs#get-deposit-addresses') 344 | } else if (!ticker || typeof ticker !== 'string') { 345 | throw new SDKError(400, '[Zabo] Missing or invalid `ticker` parameter. See: https://zabo.com/docs#get-deposit-addresses') 346 | } 347 | 348 | try { 349 | return this.api.request('GET', `/users/${userId}/accounts/${accountId}/deposit-addresses?ticker=${ticker}`) 350 | } catch (err) { 351 | throw new SDKError(err.error_type, err.message, err.request_id) 352 | } 353 | } 354 | } 355 | 356 | /** 357 | * @typedef {Users} UsersAPI 358 | * @type {(api) => UsersAPI} 359 | */ 360 | module.exports = (api) => { 361 | return new Users(api) 362 | } 363 | -------------------------------------------------------------------------------- /src/sdk.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @Copyright (c) 2019-present, Zabo, All rights reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | 'use strict' 18 | 19 | const API = require('./api') 20 | const ZaboSDK = require('./core/SDK') 21 | 22 | /** 23 | * SDK main class definition. 24 | */ 25 | class SDK extends ZaboSDK { 26 | /** 27 | * Initialize the Zabo SDK. 28 | * @param {Object} params Zabo initialization config. 29 | */ 30 | async initAPI (params) { 31 | this.api = new API({ 32 | apiVersion: this.apiVersion, 33 | env: this.env, 34 | ...params 35 | }) 36 | 37 | await this.setEndpointAliases() 38 | } 39 | } 40 | 41 | module.exports = new SDK() 42 | -------------------------------------------------------------------------------- /src/utils.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @Copyright (c) 2019-present, Zabo & Modular, Inc. All rights reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | 'use strict' 18 | 19 | const createHmac = require('create-hmac') 20 | const { SDKError } = require('./err') 21 | 22 | const uuidPattern = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[0-9a-f]{4}-[0-9a-f]{12}$/i 23 | 24 | function generateHMACSignature (secretKey, url, body, timestamp) { 25 | const text = timestamp + url + body 26 | return createHmac('sha256', secretKey).update(text).digest('hex') 27 | } 28 | 29 | function setCookie (name, value, expires, path) { 30 | const valueStr = value ? encodeURIComponent(value) : '' 31 | const expiresStr = expires ? ('; expires=' + expires) : '' 32 | const pathStr = '; path=' + (path || '/') 33 | document.cookie = encodeURIComponent(name) + '=' + valueStr + expiresStr + pathStr 34 | } 35 | 36 | function getCookie (name) { 37 | const regexp = new RegExp('(?:(?:^|.*;)\\s*' + encodeURIComponent(name).replace(/[-.+*]/g, '\\$&') + '\\s*\\=\\s*([^;]*).*$)|^.*$') 38 | return decodeURIComponent(document.cookie.replace(regexp, '$1')) || null 39 | } 40 | 41 | function deleteCookie (name, path) { 42 | setCookie(name, null, 'Thu, 01 Jan 1970 00:00:01 GMT', path) 43 | } 44 | 45 | function getZaboSession () { 46 | return getCookie('zabosession') 47 | } 48 | 49 | function uuidValidate (uuid) { 50 | if (typeof uuid !== 'string') { 51 | return false 52 | } 53 | 54 | uuid = uuid.toLowerCase() 55 | 56 | if (!uuidPattern.test(uuid)) { 57 | return false 58 | } 59 | 60 | switch (uuid.charAt(14) | 0) { 61 | case 1: 62 | case 2: 63 | return true 64 | case 3: 65 | case 4: 66 | case 5: 67 | return ['8', '9', 'a', 'b'].indexOf(uuid.charAt(19)) !== -1 68 | default: 69 | return false 70 | } 71 | } 72 | 73 | function validateListParameters (limit, cursor) { 74 | if (limit && limit > 50) { 75 | throw new SDKError(400, '[Zabo] Values for `limit` must be 50 or below. See: https://zabo.com/docs#pagination') 76 | } 77 | if (cursor && !uuidValidate(cursor)) { 78 | throw new SDKError(400, '[Zabo] `cursor` must be a valid UUID version 4. See: https://zabo.com/docs#pagination') 79 | } 80 | } 81 | 82 | function validateEnumParameter (name, value, options, optional = false) { 83 | if (!optional && typeof value === 'undefined') { 84 | throw new SDKError(400, `[Zabo] Missing \`${name}\` parameter.`) 85 | } else if (!options.includes(value)) { 86 | throw new SDKError(400, `[Zabo] Invalid \`${name}\` parameter. Available options: "${options.join('", "')}".`) 87 | } 88 | } 89 | 90 | function Paginator (obj) { 91 | Object.assign(this, obj || {}) 92 | } 93 | 94 | function createPaginator (payload, api) { 95 | const { list_cursor = {}, ...data } = payload || {} 96 | 97 | Paginator.prototype.hasMore = list_cursor.has_more 98 | Paginator.prototype.limit = list_cursor.limit 99 | Paginator.prototype.next = async function () { 100 | if (list_cursor.has_more && list_cursor.next_uri) { 101 | return api.request('GET', list_cursor.next_uri) 102 | } 103 | 104 | return new Paginator() 105 | } 106 | 107 | return new Paginator(data) 108 | } 109 | 110 | function sleep (milliseconds) { 111 | return new Promise(resolve => setTimeout(resolve, milliseconds)) 112 | } 113 | 114 | const isBrowser = () => { 115 | return (typeof window !== 'undefined' && ({}).toString.call(window) === '[object Window]') 116 | } 117 | 118 | const isReactNative = () => { 119 | return typeof navigator !== 'undefined' && navigator.product === 'ReactNative' 120 | } 121 | 122 | const isNode = () => { 123 | return typeof global !== 'undefined' && ({}).toString.call(global) === '[object global]' 124 | } 125 | 126 | module.exports = { 127 | generateHMACSignature, 128 | setCookie, 129 | getCookie, 130 | deleteCookie, 131 | getZaboSession, 132 | uuidValidate, 133 | validateListParameters, 134 | validateEnumParameter, 135 | createPaginator, 136 | sleep, 137 | isBrowser, 138 | isNode, 139 | isReactNative 140 | } 141 | -------------------------------------------------------------------------------- /test/api.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const crypto = require('crypto') 4 | const sdk = require('../src/sdk.js') 5 | const constants = require('../src/constants.js') 6 | require('should') 7 | 8 | const URLS = constants() 9 | 10 | describe('Zabo SDK API', () => { 11 | it('should be instantiated during zabo.init()', async function () { 12 | await sdk.init({ 13 | apiKey: 'some-api-key', 14 | secretKey: 'some-secret-key', 15 | env: 'sandbox', 16 | autoConnect: false 17 | }).should.be.ok() 18 | 19 | sdk.api.should.be.ok() 20 | sdk.api.connect.should.be.a.Function() 21 | sdk.api.resources.should.be.an.Object() 22 | 23 | sdk.api.resources.should.have.property('teams') 24 | sdk.api.resources.should.have.property('transactions') 25 | sdk.api.resources.should.have.property('providers') 26 | sdk.api.resources.should.have.property('currencies') 27 | }) 28 | 29 | it('should build a proper request object', function () { 30 | const request = sdk.api._buildRequest('GET', '/sessions') 31 | 32 | request.should.be.instanceof(Object) 33 | 34 | request.method.should.equal('get') 35 | request.url.should.equal(URLS.sandbox.API_BASE_URL + '/sessions') 36 | 37 | request.headers.should.have.property('X-Zabo-Sig') 38 | request.headers.should.have.property('X-Zabo-Timestamp') 39 | }) 40 | 41 | it('should embed a valid timestamp in the request headers', function () { 42 | const request = sdk.api._buildRequest('GET', '/sessions') 43 | 44 | request.headers.should.have.property('X-Zabo-Timestamp') 45 | const date = new Date(request.headers['X-Zabo-Timestamp']) 46 | 47 | date.should.be.instanceof(Date) 48 | date.getTime().should.be.above(0) 49 | }) 50 | 51 | it('should embed a valid HMAC signature in the request headers', function () { 52 | const request = sdk.api._buildRequest('GET', '/users?limit=25') 53 | 54 | request.should.be.instanceof(Object) 55 | 56 | request.method.should.equal('get') 57 | request.url.should.equal(URLS.sandbox.API_BASE_URL + '/users?limit=25') 58 | 59 | const timestamp = request.headers['X-Zabo-Timestamp'] 60 | const text = timestamp + request.url 61 | const sig = crypto.createHmac('sha256', sdk.api.secretKey).update(text).digest('hex') 62 | 63 | request.headers.should.have.property('X-Zabo-Sig') 64 | request.headers['X-Zabo-Sig'].should.equal(sig) 65 | }) 66 | }) 67 | -------------------------------------------------------------------------------- /test/index.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const sdk = require('../src/sdk.js') 4 | require('should') 5 | 6 | describe('Zabo SDK Module', () => { 7 | it('should init properly', async function () { 8 | await sdk.init({ 9 | apiKey: 'some-api-key', 10 | secretKey: 'some-secret-key', 11 | env: 'sandbox' 12 | }).should.be.rejected() 13 | 14 | sdk.status.should.be.ok() 15 | sdk.status.should.equal('connecting') 16 | }) 17 | 18 | it('init() should throw an error if an argument is missing', async function () { 19 | this.timeout(1000) 20 | 21 | await sdk.init({ 22 | apiKey: 'some-api-key', 23 | env: 'sandbox' 24 | }).should.be.rejected() 25 | 26 | sdk.status.should.equal('offline') 27 | }) 28 | 29 | it('should just init and not try to connect if autoConnect is set to false', async function () { 30 | await sdk.init({ 31 | apiKey: 'some-api-key', 32 | secretKey: 'some-secret-key', 33 | env: 'sandbox', 34 | autoConnect: false 35 | }).catch(err => err).should.be.ok() 36 | 37 | sdk.status.should.be.ok() 38 | sdk.status.should.equal('offline') 39 | }) 40 | 41 | it('should enable zabo.connect() to be called when autoConnect is set to false', async function () { 42 | await sdk.init({ 43 | apiKey: 'some-api-key', 44 | secretKey: 'some-secret-key', 45 | env: 'sandbox', 46 | autoConnect: false 47 | }).catch(err => err).should.be.ok() 48 | 49 | sdk.connect.should.be.a.Function() 50 | }) 51 | 52 | it('setEndpointAliases() should append API endpoints on the main SDK instance', async function () { 53 | this.timeout(1000) 54 | 55 | sdk.init({ 56 | apiKey: 'some-api-key', 57 | secretKey: 'some-secret-key', 58 | env: 'sandbox', 59 | autoConnect: false 60 | }).catch(err => err) 61 | 62 | sdk.status.should.be.ok() 63 | 64 | // users 65 | sdk.users.should.have.property('create') 66 | sdk.users.should.have.property('getOne') 67 | sdk.users.should.have.property('getList') 68 | sdk.users.should.have.property('getBalances') 69 | 70 | // providers 71 | sdk.providers.should.have.property('getList') 72 | sdk.providers.should.have.property('getOne') 73 | 74 | // currencies 75 | sdk.currencies.should.have.property('getOne') 76 | sdk.currencies.should.have.property('getList') 77 | sdk.currencies.should.have.property('getExchangeRates') 78 | 79 | // transactions 80 | sdk.transactions.should.have.property('getOne') 81 | sdk.transactions.should.have.property('getList') 82 | }) 83 | }) 84 | -------------------------------------------------------------------------------- /test/mock/api.js: -------------------------------------------------------------------------------- 1 | const dummy = require('./dummy') 2 | const Currencies = require('../../src/resources/currencies') 3 | const Users = require('../../src/resources/users') 4 | 5 | class MockApi { 6 | constructor () { 7 | this.clientId = 'some-client-id' 8 | this.resources = { 9 | currencies: Currencies(this), 10 | users: Users(this) 11 | } 12 | } 13 | 14 | request (method, path, data = {}) { 15 | const map = { 16 | GET: [{ 17 | regexp: /\/blockchains\/.+\/blocks\/.+/, 18 | data: dummy.blockchainsBlock 19 | }, { 20 | regexp: /\/blockchains\/.+\/blocks/, 21 | data: dummy.blockchainsBlock 22 | }, { 23 | regexp: /\/blockchains\/.+\/contracts\/.+/, 24 | data: dummy.blockchainsContract 25 | }, { 26 | regexp: /\/blockchains\/.+\/tokens\/.+/, 27 | data: dummy.blockchainsTokens 28 | }, { 29 | regexp: /\/blockchains\/.+\/tokens/, 30 | data: dummy.blockchainsTokens 31 | }, { 32 | regexp: /\/blockchains\/.+\/addresses\/.+\/balances/, 33 | data: dummy.blockchainsBalances 34 | }, { 35 | regexp: /\/blockchains\/.+\/transactions\/.+/, 36 | data: dummy.blockchainsTransaction 37 | }, { 38 | regexp: /\/blockchains\/.+\/addresses\/.+\/transactions/, 39 | data: dummy.blockchainsTransactions 40 | }, { 41 | regexp: /\/blockchains\/.+\/token-transfers\/.+/, 42 | data: dummy.blockchainsTokenTransfers 43 | }, { 44 | regexp: /\/blockchains\/.+\/addresses\/.+\/token-transfers/, 45 | data: dummy.blockchainsTokenTransfers 46 | }, { 47 | regexp: /\/teams\/.+/, 48 | data: dummy.team 49 | }, { 50 | regexp: /\/sessions/, 51 | data: dummy.account 52 | }, { 53 | regexp: /\/accounts\/.+\/trading-symbols/, 54 | data: dummy.tradingSymbols 55 | }, { 56 | regexp: /\/accounts\/.+\/tickers\/.+/, 57 | data: dummy.tradingTicker 58 | }, { 59 | regexp: /\/accounts\/.+\/orders\/.+/, 60 | data: dummy.tradingOrder 61 | }, { 62 | regexp: /\/accounts\/.+\/orders/, 63 | data: dummy.tradingOrders 64 | }, { 65 | regexp: /\/accounts\/.+\/balances/, 66 | data: dummy.balances 67 | }, { 68 | regexp: /\/accounts\/.+\/deposit-addresses/, 69 | data: [dummy.address] 70 | }, { 71 | regexp: /\/currencies\/.+/, 72 | data: dummy.currencies.data.find(c => path.includes(c.ticker)) 73 | }, { 74 | regexp: /\/currencies/, 75 | data: dummy.currencies 76 | }, { 77 | regexp: /\/providers\/.+/, 78 | data: dummy.providers.data.find(p => path.includes(p.name)) 79 | }, { 80 | regexp: /\/providers/, 81 | data: dummy.providers 82 | }, { 83 | regexp: /\/transactions\/.+/, 84 | data: dummy.transactions.data.find(tx => path.includes(tx.id)) 85 | }, { 86 | regexp: /\/transactions/, 87 | data: dummy.transactions 88 | }, { 89 | regexp: /\/users\/.*\/accounts\/.+/, 90 | data: dummy.user.accounts.find(a => path.includes(a.id)) || dummy.account 91 | }, { 92 | regexp: /\/users\/.+/, 93 | data: dummy.users.data.find(u => path.includes(u.id)) 94 | }, { 95 | regexp: /\/users/, 96 | data: dummy.users 97 | }, { 98 | regexp: /\/exchange-rates/, 99 | data: dummy.exchangeRates 100 | }], 101 | POST: [{ 102 | regexp: /\/accounts\/.+\/deposit-addresses/, 103 | data: dummy.address 104 | }, { 105 | regexp: /\/accounts\/.+\/orders/, 106 | data: dummy.tradingOrder 107 | }, { 108 | regexp: /\/users\/.+\/accounts\/.+\/transactions/, 109 | data: { 110 | ...dummy.transaction, 111 | amount: data.amount, 112 | currency: data.currency, 113 | other_parties: [data.to_address] 114 | } 115 | }, { 116 | regexp: /\/accounts\/.+\/transactions/, 117 | data: dummy.transaction 118 | }, { 119 | regexp: /\/users\/.+\/accounts/, 120 | data: { 121 | ...dummy.user, 122 | accounts: [ 123 | ...dummy.user.accounts, 124 | data 125 | ] 126 | } 127 | }, { 128 | regexp: /\/accounts/, 129 | data: { 130 | ...dummy.account, 131 | provider: { 132 | ...dummy.account.provider, 133 | name: data.provider_name 134 | } 135 | } 136 | }, { 137 | regexp: /\/users/, 138 | data: dummy.user 139 | }], 140 | DELETE: [{ 141 | regexp: /\/users\/.+\/accounts\/.+/, 142 | data: { 143 | ...dummy.user, 144 | accounts: dummy.user.accounts.filter(a => !path.includes(a.id)) 145 | } 146 | }, { 147 | regexp: /\/accounts\/.+\/orders\/.+/, 148 | data: { 149 | data: [path.split('orders/')[1]] 150 | } 151 | }, { 152 | regexp: /\/accounts\/.+\/orders/, 153 | data: { 154 | data: dummy.tradingOrders.data.map(o => o.id) 155 | } 156 | }] 157 | } 158 | 159 | const responses = map[method] || [] 160 | const response = responses.find(r => r.regexp.test(path)) 161 | 162 | return new Promise(resolve => 163 | setTimeout(() => resolve(response && response.data), 100) 164 | ) 165 | } 166 | } 167 | 168 | module.exports = new MockApi() 169 | -------------------------------------------------------------------------------- /test/mock/dummy/account.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "663925bd-205d-479e-895f-27ec3891e8b3", 3 | "provider": { 4 | "name": "coinbase", 5 | "display_name": "Coinbase", 6 | "logo": "https://cdn.zabo.com/assets/providers/coinbase.png", 7 | "type": "oauth", 8 | "scopes": [ 9 | "read_transaction_history", 10 | "read_balances", 11 | "get_deposit_addresses", 12 | "create_deposit_address" 13 | ], 14 | "resource_type": "provider" 15 | }, 16 | "balances": [ 17 | { 18 | "ticker": "RANDOMZABO", 19 | "provider_ticker": "RANDOMZABO", 20 | "name": "", 21 | "asset_is_verified": false, 22 | "asset_type": "", 23 | "amount": "10438.480388", 24 | "decimals": 0, 25 | "fiat_ticker": "", 26 | "fiat_value": "", 27 | "fiat_asset_is_verified": false, 28 | "logo": "", 29 | "updated_at": 1614609696650, 30 | "misc": null, 31 | "resource_type": "balance" 32 | }, 33 | { 34 | "ticker": "ETH", 35 | "provider_ticker": "ETH", 36 | "name": "Ether", 37 | "asset_is_verified": true, 38 | "asset_type": "account", 39 | "amount": "1.159320000047394086", 40 | "decimals": 18, 41 | "fiat_ticker": "USD", 42 | "fiat_value": "448.10", 43 | "fiat_asset_is_verified": true, 44 | "logo": "https://cdn.zabo.com/assets/currencies/ETH.png", 45 | "updated_at": 1614609696650, 46 | "misc": null, 47 | "resource_type": "balance" 48 | }, 49 | { 50 | "ticker": "XYZ", 51 | "provider_ticker": "XYZ", 52 | "name": "", 53 | "asset_is_verified": false, 54 | "asset_type": "", 55 | "amount": "10438.480388", 56 | "decimals": 0, 57 | "fiat_ticker": "", 58 | "fiat_value": "", 59 | "fiat_asset_is_verified": false, 60 | "logo": "", 61 | "updated_at": 1614609696650, 62 | "misc": null, 63 | "resource_type": "balance" 64 | }, 65 | { 66 | "ticker": "BTC", 67 | "provider_ticker": "BTC", 68 | "name": "Bitcoin", 69 | "asset_is_verified": true, 70 | "asset_type": "UTXO", 71 | "amount": "0.1932", 72 | "decimals": 8, 73 | "fiat_ticker": "USD", 74 | "fiat_value": "2210.93", 75 | "fiat_asset_is_verified": true, 76 | "logo": "https://cdn.zabo.com/assets/currencies/BTC.png", 77 | "updated_at": 1614609696650, 78 | "misc": null, 79 | "resource_type": "balance" 80 | } 81 | ], 82 | "blockchain": null, 83 | "created_at": 1614609696539, 84 | "updated_at": 1614609696539, 85 | "resource_type": "account" 86 | } -------------------------------------------------------------------------------- /test/mock/dummy/address.json: -------------------------------------------------------------------------------- 1 | { 2 | "ticker": "BTC", 3 | "provider_ticker": "BTC", 4 | "address": "1JdH9enMREoU9f6v6TscKxZEzkfohvpqvR", 5 | "resource_type": "deposit_address" 6 | } -------------------------------------------------------------------------------- /test/mock/dummy/balances.json: -------------------------------------------------------------------------------- 1 | { 2 | "data": [ 3 | { 4 | "ticker": "RANDOMZABO", 5 | "provider_ticker": "RANDOMZABO", 6 | "name": "", 7 | "asset_is_verified": false, 8 | "asset_type": "", 9 | "amount": "10438.480388", 10 | "decimals": 0, 11 | "fiat_ticker": "", 12 | "fiat_value": "", 13 | "fiat_asset_is_verified": false, 14 | "logo": "", 15 | "updated_at": 1614609696650, 16 | "misc": null, 17 | "resource_type": "balance" 18 | }, 19 | { 20 | "ticker": "ETH", 21 | "provider_ticker": "ETH", 22 | "name": "Ether", 23 | "asset_is_verified": true, 24 | "asset_type": "account", 25 | "amount": "1.159320000047394086", 26 | "decimals": 18, 27 | "fiat_ticker": "USD", 28 | "fiat_value": "448.10", 29 | "fiat_asset_is_verified": true, 30 | "logo": "https://cdn.zabo.com/assets/currencies/ETH.png", 31 | "updated_at": 1614609696650, 32 | "misc": null, 33 | "resource_type": "balance" 34 | }, 35 | { 36 | "ticker": "XYZ", 37 | "provider_ticker": "XYZ", 38 | "name": "", 39 | "asset_is_verified": false, 40 | "asset_type": "", 41 | "amount": "10438.480388", 42 | "decimals": 0, 43 | "fiat_ticker": "", 44 | "fiat_value": "", 45 | "fiat_asset_is_verified": false, 46 | "logo": "", 47 | "updated_at": 1614609696650, 48 | "misc": null, 49 | "resource_type": "balance" 50 | }, 51 | { 52 | "ticker": "BTC", 53 | "provider_ticker": "BTC", 54 | "name": "Bitcoin", 55 | "asset_is_verified": true, 56 | "asset_type": "UTXO", 57 | "amount": "0.1932", 58 | "decimals": 8, 59 | "fiat_ticker": "USD", 60 | "fiat_value": "2210.93", 61 | "fiat_asset_is_verified": true, 62 | "logo": "https://cdn.zabo.com/assets/currencies/BTC.png", 63 | "updated_at": 1614609696650, 64 | "misc": null, 65 | "resource_type": "balance" 66 | } 67 | ] 68 | } -------------------------------------------------------------------------------- /test/mock/dummy/blockchains/balances.json: -------------------------------------------------------------------------------- 1 | { 2 | "data": [ 3 | { 4 | "token": { 5 | "contract": { 6 | "address": { 7 | "hex": "0x0000000000000000000000000000000000000000", 8 | "nonce": 0, 9 | "name": null 10 | }, 11 | "bytecode": "" 12 | }, 13 | "ticker": "ETH", 14 | "name": "Ether", 15 | "decimals": 18, 16 | "total_supply": null, 17 | "is_erc20": false 18 | }, 19 | "address": { 20 | "hex": "0xa43D8937C77644eb0DAa242A8F5F7eA3cB83F1ce", 21 | "nonce": 62, 22 | "name": null 23 | }, 24 | "balance": "22657836000003000" 25 | } 26 | ] 27 | } -------------------------------------------------------------------------------- /test/mock/dummy/blockchains/block.json: -------------------------------------------------------------------------------- 1 | { 2 | "number": 10658732, 3 | "hash": "0xdcb7ad1f4beb3f13117a2a6e104cb0bf4b291f1d6c7c79395710370601fe4b2d", 4 | "size": 39651, 5 | "gas_limit": 12499929, 6 | "gas_used": 12482105, 7 | "transaction_count": 183, 8 | "timestamp": 1597416299 9 | } -------------------------------------------------------------------------------- /test/mock/dummy/blockchains/contract.json: -------------------------------------------------------------------------------- 1 | { 2 | "address": { 3 | "hex": "0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f", 4 | "nonce": 1, 5 | "name": null 6 | }, 7 | "bytecode": "0x60806040526004361061006d576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680633659cfe6146100775780634f1ef286146100ba5780635c60da1b146101085780638f2839701461015f578063f851a440146101a2575b6100756101f9565b005b34801561008357600080fd5b506100b8600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610213565b005b610106600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001908201803590602001919091929391929390505050610268565b005b34801561011457600080fd5b5061011d610308565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34801561016b57600080fd5b506101a0600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610360565b005b3480156101ae57600080fd5b506101b761051e565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b610201610576565b61021161020c610651565b610682565b565b61021b6106a8565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141561025c57610257816106d9565b610265565b6102646101f9565b5b50565b6102706106a8565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614156102fa576102ac836106d9565b3073ffffffffffffffffffffffffffffffffffffffff163483836040518083838082843782019150509250505060006040518083038185875af19250505015156102f557600080fd5b610303565b6103026101f9565b5b505050565b60006103126106a8565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614156103545761034d610651565b905061035d565b61035c6101f9565b5b90565b6103686106a8565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141561051257600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614151515610466576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260368152602001807f43616e6e6f74206368616e6765207468652061646d696e206f6620612070726f81526020017f787920746f20746865207a65726f20616464726573730000000000000000000081525060400191505060405180910390fd5b7f7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f61048f6106a8565b82604051808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019250505060405180910390a161050d81610748565b61051b565b61051a6101f9565b5b50565b60006105286106a8565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141561056a576105636106a8565b9050610573565b6105726101f9565b5b90565b61057e6106a8565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614151515610647576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260328152602001807f43616e6e6f742063616c6c2066616c6c6261636b2066756e6374696f6e20667281526020017f6f6d207468652070726f78792061646d696e000000000000000000000000000081525060400191505060405180910390fd5b61064f610777565b565b6000807f7050c9e0f4ca769c69bd3a8ef740bc37934f8e2c036e5a723fd8ee048ed3f8c36001029050805491505090565b3660008037600080366000845af43d6000803e80600081146106a3573d6000f35b3d6000fd5b6000807f10d6a54a4754c8869d6886b5f5d7fbfa5b4522237ea5c60d11bc4e7a1ff9390b6001029050805491505090565b6106e281610779565b7fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b81604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390a150565b60007f10d6a54a4754c8869d6886b5f5d7fbfa5b4522237ea5c60d11bc4e7a1ff9390b60010290508181555050565b565b60006107848261084b565b151561081e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603b8152602001807f43616e6e6f742073657420612070726f787920696d706c656d656e746174696f81526020017f6e20746f2061206e6f6e2d636f6e74726163742061646472657373000000000081525060400191505060405180910390fd5b7f7050c9e0f4ca769c69bd3a8ef740bc37934f8e2c036e5a723fd8ee048ed3f8c360010290508181555050565b600080823b9050600081119150509190505600a165627a7a72305820a4a547cfc7202c5acaaae74d428e988bc62ad5024eb0165532d3a8f91db4ed240029" 8 | } -------------------------------------------------------------------------------- /test/mock/dummy/blockchains/token-transfers.json: -------------------------------------------------------------------------------- 1 | { 2 | "list_cursor": { 3 | "limit": 25, 4 | "has_more": false, 5 | "self_uri": "/blockchains/ethereum/addresses/0x4ae694344e7e4e5820c62aa9816b7aa61210eaba/token-transfers", 6 | "next_uri": "" 7 | }, 8 | "data": [ 9 | { 10 | "transaction": { 11 | "hash": "0x23170b29a16c3ed89a2d7514c2d6d0796e39a29184c52a0bb5aed6b404b78a83", 12 | "block_number": 10609471, 13 | "from_address": { 14 | "hex": "0x4ae694344e7e4e5820c62aa9816b7aa61210eaba", 15 | "nonce": 4 16 | }, 17 | "to_address": { 18 | "hex": "0xe41d2489571d322189246dafa5ebde1f4699f498", 19 | "nonce": 1 20 | }, 21 | "value": "0", 22 | "gas": 77012, 23 | "gas_price": "42000000000", 24 | "gas_used": 23506, 25 | "input": "0xa9059cbb0000000000000000000000006...", 26 | "status": 1 27 | }, 28 | "token": { 29 | "contract": { 30 | "address": { 31 | "hex": "0xtheTokenAddress", 32 | "nonce": 1, 33 | "name": null 34 | } 35 | }, 36 | "symbol": "ABC", 37 | "name": "No Zabo Tokens", 38 | "decimals": 18, 39 | "total_supply": "100000000000000000000000000", 40 | "is_erc20": true 41 | }, 42 | "from_address": { 43 | "hex": "0x4ae694344e7e4e5820c62aa9816b7aa61210eaba", 44 | "nonce": 4 45 | }, 46 | "to_address": { 47 | "hex": "0xe41d2489571d322189246dafa5ebde1f4699f498", 48 | "nonce": 1 49 | }, 50 | "value": "12823000000000000000000" 51 | } 52 | ] 53 | } -------------------------------------------------------------------------------- /test/mock/dummy/blockchains/tokens.json: -------------------------------------------------------------------------------- 1 | { 2 | "list_cursor": { 3 | "limit": 25, 4 | "has_more": false, 5 | "self_uri": "/blockchains/ethereum/tokens?cursor=Wrapped%20Ether&limit=25", 6 | "next_uri": "" 7 | }, 8 | "data": [ 9 | { 10 | "contract": { 11 | "address": { 12 | "hex": "0xf13e9402fb894eBCF5b8249C211F6f09E95d9a73", 13 | "nonce": 0, 14 | "name": "USD Coin" 15 | }, 16 | "bytecode": "" 17 | }, 18 | "ticker": "USDC", 19 | "name": "USD Coin", 20 | "decimals": 6, 21 | "total_supply": "7857963744689550", 22 | "is_erc20": true 23 | } 24 | ] 25 | } -------------------------------------------------------------------------------- /test/mock/dummy/blockchains/transaction.json: -------------------------------------------------------------------------------- 1 | { 2 | "hash": "0x23170b29a16c3ed89a2d7514c2d6d0796e39a29184c52a0bb5aed6b404b78a83", 3 | "block_number": 10609471, 4 | "from_address": { 5 | "hex": "0x4ae694344e7e4e5820c62aa9816b7aa61210eaba", 6 | "nonce": 4 7 | }, 8 | "to_address": { 9 | "hex": "0xe41d2489571d322189246dafa5ebde1f4699f498", 10 | "nonce": 1 11 | }, 12 | "value": "0", 13 | "gas": 77012, 14 | "gas_price": "42000000000", 15 | "gas_used": 23506, 16 | "input": "0xa9059cbb0000000000000000000000006...", 17 | "status": 1 18 | } -------------------------------------------------------------------------------- /test/mock/dummy/blockchains/transactions.json: -------------------------------------------------------------------------------- 1 | { 2 | "list_cursor": { 3 | "limit": 25, 4 | "has_more": false, 5 | "self_uri": "/blockchains/ethereum/addresses/0x4ae694344e7e4e5820c62aa9816b7aa61210eaba/transactions", 6 | "next_uri": "" 7 | }, 8 | "data": [ 9 | { 10 | "hash": "0x23170b29a16c3ed89a2d7514c2d6d0796e39a29184c52a0bb5aed6b404b78a83", 11 | "block_number": 10609471, 12 | "from_address": { 13 | "hex": "0x4ae694344e7e4e5820c62aa9816b7aa61210eaba", 14 | "nonce": 4 15 | }, 16 | "to_address": { 17 | "hex": "0xe41d2489571d322189246dafa5ebde1f4699f498", 18 | "nonce": 1 19 | }, 20 | "value": "0", 21 | "gas": 77012, 22 | "gas_price": "42000000000", 23 | "gas_used": 23506, 24 | "input": "0xa9059cbb0000000000000000000000006...", 25 | "status": 1 26 | } 27 | ] 28 | } -------------------------------------------------------------------------------- /test/mock/dummy/currencies.json: -------------------------------------------------------------------------------- 1 | { 2 | "list_cursor": { 3 | "limit": 25, 4 | "has_more": false, 5 | "self_uri": "/currencies?limit=25", 6 | "next_uri": "" 7 | }, 8 | "data": [ 9 | { 10 | "ticker": "BTC", 11 | "name": "Bitcoin", 12 | "type": "UTXO", 13 | "logo": "https://cdn.zabo.com/assets/currencies/BTC.png", 14 | "decimals": 8, 15 | "address": null, 16 | "supporting_providers": [ 17 | "gateio", 18 | "kucoin", 19 | "bitmex", 20 | "bittrex", 21 | "binanceUS", 22 | "bybit", 23 | "coinbase", 24 | "testnet", 25 | "blockcypher", 26 | "bitstamp", 27 | "gemini", 28 | "liquid", 29 | "coinbasePro", 30 | "nomics", 31 | "huobi" 32 | ], 33 | "resource_type": "currency" 34 | }, 35 | { 36 | "ticker": "ETH", 37 | "name": "Ether", 38 | "type": "account", 39 | "logo": "https://cdn.zabo.com/assets/currencies/ETH.png", 40 | "decimals": 18, 41 | "address": null, 42 | "supporting_providers": [ 43 | "gateio", 44 | "bittrex", 45 | "binanceUS", 46 | "bybit", 47 | "binance", 48 | "coinbase", 49 | "testnet", 50 | "bitstamp", 51 | "liquid", 52 | "nomics", 53 | "zaboethereum" 54 | ], 55 | "resource_type": "currency" 56 | }, 57 | { 58 | "ticker": "USDT", 59 | "name": "USD coin", 60 | "type": "ERC20", 61 | "logo": "https://cdn.zabo.com/assets/currencies/USDT.png", 62 | "decimals": 6, 63 | "address": "0xdac17f958d2ee523a2206206994597c13d831ec7", 64 | "supporting_providers": [ 65 | "gateio" 66 | ], 67 | "resource_type": "currency" 68 | }, 69 | { 70 | "ticker": "XRP", 71 | "name": "Ripple", 72 | "type": "account", 73 | "logo": "https://cdn.zabo.com/assets/currencies/XRP.png", 74 | "decimals": 6, 75 | "address": null, 76 | "supporting_providers": [ 77 | "coinbase", 78 | "bitstamp", 79 | "nomics" 80 | ], 81 | "resource_type": "currency" 82 | }, 83 | { 84 | "ticker": "LINK", 85 | "name": "ChainLink Token", 86 | "type": "ERC20", 87 | "logo": "https://cdn.zabo.com/assets/currencies/LINK.png", 88 | "decimals": 18, 89 | "address": "0x1dff79CfcE03751D254fd2322F52FEe0107bFC86", 90 | "supporting_providers": [ 91 | "coinbase", 92 | "nomics", 93 | "zaboethereum" 94 | ], 95 | "resource_type": "currency" 96 | }, 97 | { 98 | "ticker": "BCH", 99 | "name": "Bitcoin Cash", 100 | "type": "UTXO", 101 | "logo": "https://cdn.zabo.com/assets/currencies/BCH.png", 102 | "decimals": 8, 103 | "address": null, 104 | "supporting_providers": [ 105 | "coinbase", 106 | "testnet", 107 | "bitstamp", 108 | "liquid", 109 | "nomics", 110 | "zaboethereum" 111 | ], 112 | "resource_type": "currency" 113 | }, 114 | { 115 | "ticker": "LTC", 116 | "name": "Litecoin", 117 | "type": "UTXO", 118 | "logo": "https://cdn.zabo.com/assets/currencies/LTC.png", 119 | "decimals": 8, 120 | "address": null, 121 | "supporting_providers": [ 122 | "gateio", 123 | "kucoin", 124 | "coinbase", 125 | "bitstamp", 126 | "nomics" 127 | ], 128 | "resource_type": "currency" 129 | }, 130 | { 131 | "ticker": "BNB", 132 | "name": "Binance Coin", 133 | "type": "ERC20", 134 | "logo": "https://cdn.zabo.com/assets/currencies/BNB.png", 135 | "decimals": 18, 136 | "address": "0xB8c77482e45F1F44dE1745F52C74426C631bDD52", 137 | "supporting_providers": [ 138 | "nomics" 139 | ], 140 | "resource_type": "currency" 141 | }, 142 | { 143 | "ticker": "EOS", 144 | "name": "EOS", 145 | "type": "account", 146 | "logo": "https://cdn.zabo.com/assets/currencies/EOS.png", 147 | "decimals": 4, 148 | "address": null, 149 | "supporting_providers": [ 150 | "binanceUS", 151 | "bybit", 152 | "coinbase", 153 | "nomics" 154 | ], 155 | "resource_type": "currency" 156 | }, 157 | { 158 | "ticker": "XTZ", 159 | "name": "Tezos", 160 | "type": "account", 161 | "logo": "https://cdn.zabo.com/assets/currencies/XTZ.png", 162 | "decimals": 8, 163 | "address": null, 164 | "supporting_providers": [ 165 | "coinbase", 166 | "nomics" 167 | ], 168 | "resource_type": "currency" 169 | }, 170 | { 171 | "ticker": "XLM", 172 | "name": "Stellar Lumens", 173 | "type": "account", 174 | "logo": "https://cdn.zabo.com/assets/currencies/XLM.png", 175 | "decimals": 7, 176 | "address": null, 177 | "supporting_providers": [ 178 | "coinbase", 179 | "nomics" 180 | ], 181 | "resource_type": "currency" 182 | }, 183 | { 184 | "ticker": "ATOM", 185 | "name": "Cosmos", 186 | "type": "account", 187 | "logo": "https://cdn.zabo.com/assets/currencies/ATOM.png", 188 | "decimals": 8, 189 | "address": null, 190 | "supporting_providers": [ 191 | "nomics" 192 | ], 193 | "resource_type": "currency" 194 | }, 195 | { 196 | "ticker": "USDC", 197 | "name": "USD//C", 198 | "type": "ERC20", 199 | "logo": "https://cdn.zabo.com/assets/currencies/USDC.png", 200 | "decimals": 6, 201 | "address": "0x7fAe10a97e6bE2cB84747D32dFe3444cbd799DbE", 202 | "supporting_providers": [ 203 | "coinbase", 204 | "nomics", 205 | "zaboethereum" 206 | ], 207 | "resource_type": "currency" 208 | }, 209 | { 210 | "ticker": "SNX", 211 | "name": "Synthetix Network Token", 212 | "type": "ERC20", 213 | "logo": "https://cdn.zabo.com/assets/currencies/SNX.png", 214 | "decimals": 18, 215 | "address": "0x924Ba5bfF703f20EA28EC31B5dDa82AaF4a9E020", 216 | "supporting_providers": [ 217 | "zaboethereum" 218 | ], 219 | "resource_type": "currency" 220 | }, 221 | { 222 | "ticker": "DASH", 223 | "name": "DASH", 224 | "type": "UTXO", 225 | "logo": "https://cdn.zabo.com/assets/currencies/DASH.png", 226 | "decimals": 8, 227 | "address": null, 228 | "supporting_providers": [ 229 | "nomics" 230 | ], 231 | "resource_type": "currency" 232 | }, 233 | { 234 | "ticker": "ZEC", 235 | "name": "Zcash", 236 | "type": "UTXO", 237 | "logo": "https://cdn.zabo.com/assets/currencies/ZEC.png", 238 | "decimals": 8, 239 | "address": null, 240 | "supporting_providers": [ 241 | "coinbase", 242 | "nomics" 243 | ], 244 | "resource_type": "currency" 245 | }, 246 | { 247 | "ticker": "ETC", 248 | "name": "Ethereum Classic", 249 | "type": "account", 250 | "logo": "https://cdn.zabo.com/assets/currencies/ETC.png", 251 | "decimals": 18, 252 | "address": null, 253 | "supporting_providers": [ 254 | "coinbasePro", 255 | "nomics" 256 | ], 257 | "resource_type": "currency" 258 | }, 259 | { 260 | "ticker": "MKR", 261 | "name": "Maker", 262 | "type": "ERC20", 263 | "logo": "https://cdn.zabo.com/assets/currencies/MKR.png", 264 | "decimals": 18, 265 | "address": "0xA85f49574CDa7EC6D48c9b74f0D4A092eE453242", 266 | "supporting_providers": [ 267 | "coinbase", 268 | "nomics", 269 | "zaboethereum" 270 | ], 271 | "resource_type": "currency" 272 | }, 273 | { 274 | "ticker": "BAT", 275 | "name": "Basic Attention Token", 276 | "type": "ERC20", 277 | "logo": "https://cdn.zabo.com/assets/currencies/BAT.png", 278 | "decimals": 18, 279 | "address": "0x180716c2a99429Bc4f19a10ea28693bA44a6B26f", 280 | "supporting_providers": [ 281 | "gateio", 282 | "nomics", 283 | "zaboethereum" 284 | ], 285 | "resource_type": "currency" 286 | }, 287 | { 288 | "ticker": "REN", 289 | "name": "Republic Token", 290 | "type": "ERC20", 291 | "logo": "https://cdn.zabo.com/assets/currencies/REN.png", 292 | "decimals": 18, 293 | "address": "0xb93962880ecD52536E487Df46165c77a0887Edbe", 294 | "supporting_providers": [ 295 | "zaboethereum" 296 | ], 297 | "resource_type": "currency" 298 | }, 299 | { 300 | "ticker": "THETA", 301 | "name": "Theta Token", 302 | "type": "ERC20", 303 | "logo": "https://cdn.zabo.com/assets/currencies/THETA.png", 304 | "decimals": 18, 305 | "address": "0x3883f5e181fccaf8410fa61e12b59bad963fb645", 306 | "supporting_providers": [ 307 | "nomics" 308 | ], 309 | "resource_type": "currency" 310 | }, 311 | { 312 | "ticker": "AMPL", 313 | "name": "Ampleforth", 314 | "type": "ERC20", 315 | "logo": "https://cdn.zabo.com/assets/currencies/AMPL.png", 316 | "decimals": 9, 317 | "address": "0x021c724e5737f0B883C8898EceCF4DCE89E62a75", 318 | "supporting_providers": [ 319 | "zaboethereum" 320 | ], 321 | "resource_type": "currency" 322 | }, 323 | { 324 | "ticker": "DAI", 325 | "name": "Dai", 326 | "type": "ERC20", 327 | "logo": "https://cdn.zabo.com/assets/currencies/DAI.png", 328 | "decimals": 18, 329 | "address": "0x6B175474E89094C44Da98b954EedeAC495271d0F", 330 | "supporting_providers": [ 331 | "gateio", 332 | "zaboethereum" 333 | ], 334 | "resource_type": "currency" 335 | }, 336 | { 337 | "ticker": "ZRX", 338 | "name": "0x Protocol Token", 339 | "type": "ERC20", 340 | "logo": "https://cdn.zabo.com/assets/currencies/ZRX.png", 341 | "decimals": 18, 342 | "address": "0x00BEF1E237958621D07Ce5a7E1C01B0c9c172677", 343 | "supporting_providers": [ 344 | "nomics", 345 | "zaboethereum" 346 | ], 347 | "resource_type": "currency" 348 | }, 349 | { 350 | "ticker": "TUSD", 351 | "name": "TrueUSD", 352 | "type": "ERC20", 353 | "logo": "https://cdn.zabo.com/assets/currencies/TUSD.png", 354 | "decimals": 18, 355 | "address": "0x8a5f7a5dC251baEC7F23810D5C79E200bb7192b7", 356 | "supporting_providers": [ 357 | "gateio", 358 | "binance", 359 | "nomics", 360 | "zaboethereum" 361 | ], 362 | "resource_type": "currency" 363 | } 364 | ], 365 | "request_id": "8d891edf-b2ed-4661-a630-eed46367532d" 366 | } -------------------------------------------------------------------------------- /test/mock/dummy/exchange-rates.json: -------------------------------------------------------------------------------- 1 | { 2 | "list_cursor": { 3 | "limit": 25, 4 | "has_more": false, 5 | "self_uri": "/exchange-rates?to_crypto=false&limit=25", 6 | "next_uri": "" 7 | }, 8 | "data": [ 9 | { 10 | "from": "BTC", 11 | "to": "USD", 12 | "rate": "9300.00", 13 | "timestamp": 1571767336000, 14 | "resource_type": "exchange_rate" 15 | }, 16 | { 17 | "from": "ETH", 18 | "to": "USD", 19 | "rate": "183.00", 20 | "timestamp": 1571767336000, 21 | "resource_type": "exchange_rate" 22 | }, 23 | { 24 | "from": "XRP", 25 | "to": "USD", 26 | "rate": "0.30", 27 | "timestamp": 1571767336000, 28 | "resource_type": "exchange_rate" 29 | }, 30 | { 31 | "from": "LINK", 32 | "to": "USD", 33 | "rate": "0", 34 | "timestamp": 1571767336000, 35 | "resource_type": "exchange_rate" 36 | }, 37 | { 38 | "from": "BCH", 39 | "to": "USD", 40 | "rate": "450.00", 41 | "timestamp": 1571767336000, 42 | "resource_type": "exchange_rate" 43 | }, 44 | { 45 | "from": "LTC", 46 | "to": "USD", 47 | "rate": "55.00", 48 | "timestamp": 1571767336000, 49 | "resource_type": "exchange_rate" 50 | }, 51 | { 52 | "from": "BNB", 53 | "to": "USD", 54 | "rate": "0", 55 | "timestamp": 1571767336000, 56 | "resource_type": "exchange_rate" 57 | }, 58 | { 59 | "from": "EOS", 60 | "to": "USD", 61 | "rate": "3.00", 62 | "timestamp": 1571767336000, 63 | "resource_type": "exchange_rate" 64 | }, 65 | { 66 | "from": "XTZ", 67 | "to": "USD", 68 | "rate": "0.90", 69 | "timestamp": 1571767336000, 70 | "resource_type": "exchange_rate" 71 | }, 72 | { 73 | "from": "XLM", 74 | "to": "USD", 75 | "rate": "0.64", 76 | "timestamp": 1571767336000, 77 | "resource_type": "exchange_rate" 78 | }, 79 | { 80 | "from": "ATOM", 81 | "to": "USD", 82 | "rate": "3.00", 83 | "timestamp": 1571767336000, 84 | "resource_type": "exchange_rate" 85 | }, 86 | { 87 | "from": "USDC", 88 | "to": "USD", 89 | "rate": "0", 90 | "timestamp": 1571767336000, 91 | "resource_type": "exchange_rate" 92 | }, 93 | { 94 | "from": "DASH", 95 | "to": "USD", 96 | "rate": "69.00", 97 | "timestamp": 1571767336000, 98 | "resource_type": "exchange_rate" 99 | }, 100 | { 101 | "from": "ZEC", 102 | "to": "USD", 103 | "rate": "37.00", 104 | "timestamp": 1571767336000, 105 | "resource_type": "exchange_rate" 106 | }, 107 | { 108 | "from": "ETC", 109 | "to": "USD", 110 | "rate": "3.00", 111 | "timestamp": 1571767336000, 112 | "resource_type": "exchange_rate" 113 | }, 114 | { 115 | "from": "MKR", 116 | "to": "USD", 117 | "rate": "0", 118 | "timestamp": 1571767336000, 119 | "resource_type": "exchange_rate" 120 | }, 121 | { 122 | "from": "BAT", 123 | "to": "USD", 124 | "rate": "0", 125 | "timestamp": 1571767336000, 126 | "resource_type": "exchange_rate" 127 | }, 128 | { 129 | "from": "THETA", 130 | "to": "USD", 131 | "rate": "0", 132 | "timestamp": 1571767336000, 133 | "resource_type": "exchange_rate" 134 | }, 135 | { 136 | "from": "ZRX", 137 | "to": "USD", 138 | "rate": "0", 139 | "timestamp": 1571767336000, 140 | "resource_type": "exchange_rate" 141 | }, 142 | { 143 | "from": "TUSD", 144 | "to": "USD", 145 | "rate": "0", 146 | "timestamp": 1571767336000, 147 | "resource_type": "exchange_rate" 148 | }, 149 | { 150 | "from": "ICX", 151 | "to": "USD", 152 | "rate": "0", 153 | "timestamp": 1571767336000, 154 | "resource_type": "exchange_rate" 155 | }, 156 | { 157 | "from": "PAX", 158 | "to": "USD", 159 | "rate": "0", 160 | "timestamp": 1571767336000, 161 | "resource_type": "exchange_rate" 162 | }, 163 | { 164 | "from": "HBAR", 165 | "to": "USD", 166 | "rate": "0", 167 | "timestamp": 1571767336000, 168 | "resource_type": "exchange_rate" 169 | }, 170 | { 171 | "from": "HYN", 172 | "to": "USD", 173 | "rate": "0", 174 | "timestamp": 1571767336000, 175 | "resource_type": "exchange_rate" 176 | }, 177 | { 178 | "from": "WETH", 179 | "to": "USD", 180 | "rate": "0", 181 | "timestamp": 1571767336000, 182 | "resource_type": "exchange_rate" 183 | } 184 | ], 185 | "request_id": "52101631-5674-455d-b138-e99deb1ca6b6" 186 | } -------------------------------------------------------------------------------- /test/mock/dummy/index.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | account: require('./account.json'), 3 | address: require('./address.json'), 4 | team: require('./team.json'), 5 | balances: require('./balances.json'), 6 | currencies: require('./currencies.json'), 7 | exchangeRates: require('./exchange-rates.json'), 8 | transaction: require('./transaction.json'), 9 | transactions: require('./transactions.json'), 10 | user: require('./user.json'), 11 | users: require('./users.json'), 12 | providers: require('./providers.json'), 13 | // Blockchains 14 | blockchainsBlock: require('./blockchains/block.json'), 15 | blockchainsBalances: require('./blockchains/balances.json'), 16 | blockchainsContract: require('./blockchains/contract.json'), 17 | blockchainsTokenTransfers: require('./blockchains/token-transfers.json'), 18 | blockchainsTokens: require('./blockchains/tokens.json'), 19 | blockchainsTransaction: require('./blockchains/transaction.json'), 20 | blockchainsTransactions: require('./blockchains/transactions.json'), 21 | // Trading 22 | tradingOrder: require('./trading/order.json'), 23 | tradingOrders: require('./trading/orders.json'), 24 | tradingSymbols: require('./trading/symbols.json'), 25 | tradingTicker: require('./trading/ticker.json') 26 | } 27 | -------------------------------------------------------------------------------- /test/mock/dummy/team.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "b5cfb0d8-58de-4786-9545-3d38521d7d2b", 3 | "name": "Your App Name", 4 | "redirect_uris": [], 5 | "request_id": "d2b8cec7-47e6-47ac-8bc0-e45236354285" 6 | } -------------------------------------------------------------------------------- /test/mock/dummy/trading/order.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "4545edd1-c9bb-4a50-9304-dd737f2c37c1", 3 | "base_currency": "ETH", 4 | "quote_currency": "USD", 5 | "base_amount": "0.02000000", 6 | "buy_or_sell": "buy", 7 | "quote_amount": "", 8 | "price": "1000.00000000", 9 | "time_in_force": "GTC", 10 | "ttl": 0, 11 | "provide_liquidity_only": false, 12 | "type": "limit", 13 | "status": "NEW", 14 | "done_at": 0, 15 | "done_reason": "", 16 | "filled_size": "0.00000000", 17 | "fill_fees": "0.0000000000000000", 18 | "settled": false, 19 | "created_at": 1600967765356, 20 | "request_id": "9fa5155e-0189-40b0-a9be-718705a842f3" 21 | } -------------------------------------------------------------------------------- /test/mock/dummy/trading/orders.json: -------------------------------------------------------------------------------- 1 | { 2 | "data": [ 3 | { 4 | "id": "4545edd1-c9bb-4a50-9304-dd737f2c37c1", 5 | "base_currency": "ETH", 6 | "quote_currency": "USD", 7 | "base_amount": "0.02000000", 8 | "buy_or_sell": "buy", 9 | "quote_amount": "", 10 | "price": "1000.00000000", 11 | "time_in_force": "GTC", 12 | "ttl": 0, 13 | "provide_liquidity_only": false, 14 | "type": "limit", 15 | "status": "NEW", 16 | "created_at": 1600967765356, 17 | "done_at": 0, 18 | "done_reason": "", 19 | "filled_size": "0.00000000", 20 | "fill_fees": "0.0000000000000000", 21 | "settled": false 22 | } 23 | ] 24 | } -------------------------------------------------------------------------------- /test/mock/dummy/trading/symbols.json: -------------------------------------------------------------------------------- 1 | { 2 | "data": [ 3 | { 4 | "base_currency": "BTC", 5 | "quote_currency": "USD" 6 | }, 7 | { 8 | "base_currency": "LTC", 9 | "quote_currency": "BTC" 10 | } 11 | ] 12 | } -------------------------------------------------------------------------------- /test/mock/dummy/trading/ticker.json: -------------------------------------------------------------------------------- 1 | { 2 | "last_price": "1058.45", 3 | "last_size": "5.0398", 4 | "ask": "1298.09", 5 | "ask_size": "1.03847", 6 | "bid": "1057.99", 7 | "bid_size": "3827.03847", 8 | "volume": "493744.38324983", 9 | "timestamp": 1597416299, 10 | "request_id": "ee556fef-c83c-45a5-866c-ff30d8a60c4d" 11 | } -------------------------------------------------------------------------------- /test/mock/dummy/transaction.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "932e2040-32ce-4f3a-a67b-f1f37bcb74ba", 3 | "status": "completed", 4 | "transaction_type": "withdrawal", 5 | "parts": [ 6 | { 7 | "direction": "sent", 8 | "ticker": "ETH", 9 | "provider_ticker": "ETH", 10 | "amount": "3.7062262", 11 | "asset_is_verified": true, 12 | "fiat_ticker": "USD", 13 | "fiat_value": "1432.52", 14 | "fiat_asset_is_verified": true, 15 | "other_parties": [ 16 | "0xbdfa60A3ec7F0786DaFB3B86949A617c7b81f3A7" 17 | ] 18 | } 19 | ], 20 | "fees": [ 21 | { 22 | "type": "network", 23 | "ticker": "ETH", 24 | "provider_ticker": "ETH", 25 | "amount": "0.0185311", 26 | "asset_is_verified": true, 27 | "fiat_ticker": "", 28 | "fiat_value": "", 29 | "fiat_asset_is_verified": false, 30 | "resource_type": "transaction_fee" 31 | } 32 | ], 33 | "misc": [], 34 | "fiat_calculated_at": 1589390065699, 35 | "initiated_at": 1589390064699, 36 | "confirmed_at": 1589390065699, 37 | "resource_type": "transaction" 38 | } -------------------------------------------------------------------------------- /test/mock/dummy/user.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "17c0f27e-9916-4ace-b8ca-18d1be2cff43", 3 | "team": { 4 | "id": "b5cfb0d8-58de-4786-9545-3d38521d7d2b", 5 | "name": "Your App Name" 6 | }, 7 | "accounts": [ 8 | { 9 | "id": "8b0a037f-67cb-44b7-ac2a-18e7a54561a8", 10 | "address": "0x667dd163f1a0e6ef3e7bbe8e0676f62146d5662d", 11 | "provider": { 12 | "name": "metamask", 13 | "display_name": "MetaMask", 14 | "logo": "https://cdn.zabo.com/assets/providers/metamask.png", 15 | "type": "private_key", 16 | "scopes": ["read_balances", "read_transactions", "send_crypto", "receive_crypto"] 17 | } 18 | }, 19 | { 20 | "id": "5ab071ba-1171-43e1-a3a5-3cf43d2d4dc8", 21 | "address": "b134235a-088a-489c-aa36-dc0841094db8", 22 | "provider":{ 23 | "name":"hedera", 24 | "display_name":"Hedera Wallet", 25 | "logo":"https://cdn.zabo.com/assets/providers/hedera.png", 26 | "type":"private_key", 27 | "scopes":[ 28 | "read_balances", 29 | "read_transaction_history", 30 | "send_crypto" 31 | ] 32 | }, 33 | "balances": [ 34 | { 35 | "balance": "10", 36 | "currency": "HBAR", 37 | "type": "account", 38 | "updated_at": 1560134750000 39 | } 40 | ], 41 | "created_at": 1560134750000, 42 | "updated_at": 1560134750000, 43 | "resource_type": "account" 44 | } 45 | ], 46 | "created_at": 1420069800000, 47 | "updated_at": 1420069800000, 48 | "resource_type": "user" 49 | } -------------------------------------------------------------------------------- /test/mock/dummy/users.json: -------------------------------------------------------------------------------- 1 | { 2 | "list_cursor": { 3 | "limit": 25, 4 | "has_more": false, 5 | "self_uri": "/users?limit=25", 6 | "next_uri": "" 7 | }, 8 | "data": [ 9 | { 10 | "id": "17c0f27e-9916-4ace-b8ca-18d1be2cff43", 11 | "accounts": [ 12 | { 13 | "id": "8b0a037f-67cb-44b7-ac2a-18e7a54561a8", 14 | "address": "0x667dd163f1a0e6ef3e7bbe8e0676f62146d5662d", 15 | "provider": { 16 | "name": "metamask", 17 | "display_name": "MetaMask", 18 | "logo": "https://cdn.zabo.com/assets/providers/metamask.png", 19 | "type": "private_key", 20 | "scopes": ["read_balances", "read_transactions", "send_crypto", "receive_crypto"] 21 | } 22 | } 23 | ] 24 | } 25 | ] 26 | } -------------------------------------------------------------------------------- /test/mock/interfaces.js: -------------------------------------------------------------------------------- 1 | module.exports = (addresses = []) => { 2 | window.ethereum = { 3 | isMetaMask: true, 4 | enable: () => addresses 5 | } 6 | 7 | window.web3 = { 8 | eth: { 9 | sendTransaction: (tx, cb) => cb(null, 'tx-hash') 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /test/resources/accounts.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const sdk = require('../../src/sdk.js') 4 | const mockApi = require('../mock/api.js') 5 | require('should') 6 | 7 | describe('Zabo SDK Accounts Resource', () => { 8 | let accounts 9 | 10 | it('should not be instantiated during zabo.init() running outside a browser', async function () { 11 | await sdk.init({ 12 | apiKey: 'some-api-key', 13 | secretKey: 'some-secret-key', 14 | env: 'sandbox', 15 | autoConnect: false 16 | }).catch(err => err).should.be.ok() 17 | 18 | sdk.api.should.be.ok() 19 | sdk.api.connect.should.be.a.Function() 20 | 21 | sdk.api.resources.should.not.have.property('accounts') 22 | 23 | accounts = await require('../../src/resources/accounts')(mockApi) 24 | 25 | accounts.should.have.property('get') 26 | accounts.should.have.property('create') 27 | accounts.should.have.property('getBalances') 28 | }) 29 | 30 | it('accounts.getBalances() should fail if an account has not connected yet', async function () { 31 | const response = await accounts.getBalances({ tickers: 'BTC' }).should.be.rejected() 32 | 33 | response.should.be.an.Error() 34 | response.error_type.should.be.equal(401) 35 | response.message.should.containEql('connected') 36 | }) 37 | 38 | it('accounts.get() should return an account and cache account data', async function () { 39 | const account = await accounts.get() 40 | 41 | account.should.be.ok() 42 | account.should.have.properties(['id', 'provider', 'balances']) 43 | 44 | accounts.data.should.be.eql(account) 45 | accounts.id.should.be.equal(account.id) 46 | }) 47 | 48 | it('accounts.create() should create and return a new account', async function () { 49 | const data = { 50 | clientId: 'some-client-id', 51 | credentials: ['some-credentials'], 52 | provider: 'some-provider', 53 | origin: 'some-origin' 54 | } 55 | 56 | const account = await accounts.create(data) 57 | 58 | account.should.be.ok() 59 | account.should.have.properties(['id', 'provider', 'balances']) 60 | account.provider.name.should.have.equal(data.provider) 61 | }) 62 | 63 | it('accounts.getBalances() should return balances for the required tickers', async function () { 64 | const tickers = ['BTC', 'ETH'] 65 | const balances = await accounts.getBalances({ tickers }) 66 | 67 | balances.data.should.be.ok() 68 | balances.data.should.be.an.Array() 69 | 70 | const currencyTickers = balances.data.map(b => b.ticker) 71 | currencyTickers.should.containDeep(tickers) 72 | }) 73 | 74 | it('accounts.createDepositAddress() should create and return an address', async function () { 75 | const resp = await accounts.createDepositAddress('BTC') 76 | 77 | resp.should.be.ok() 78 | resp.should.have.properties(['ticker', 'provider_ticker', 'address']) 79 | }) 80 | 81 | it('accounts.getDepositAddresses() should return a list of addresses', async function () { 82 | const resp = await accounts.getDepositAddresses('BTC') 83 | 84 | resp.should.be.ok() 85 | resp.should.be.an.Array() 86 | resp[0].should.have.properties(['ticker', 'provider_ticker', 'address']) 87 | }) 88 | }) 89 | -------------------------------------------------------------------------------- /test/resources/blockchains.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const sdk = require('../../src/sdk.js') 4 | const mockApi = require('../mock/api.js') 5 | require('should') 6 | 7 | describe('Zabo SDK Blockchains Resource', () => { 8 | let blockchains 9 | 10 | it('should be instantiated during zabo.init()', async function () { 11 | await sdk.init({ 12 | apiKey: 'some-api-key', 13 | secretKey: 'some-secret-key', 14 | env: 'sandbox', 15 | autoConnect: false 16 | }).catch(err => err).should.be.ok() 17 | 18 | sdk.api.should.be.ok() 19 | sdk.api.connect.should.be.a.Function() 20 | 21 | sdk.api.resources.should.have.property('blockchains') 22 | 23 | blockchains = await require('../../src/resources/blockchains')(mockApi) 24 | 25 | blockchains.should.have.property('getBlock') 26 | blockchains.should.have.property('getContract') 27 | blockchains.should.have.property('getTokens') 28 | blockchains.should.have.property('getBalances') 29 | blockchains.should.have.property('getTransactions') 30 | blockchains.should.have.property('getTransaction') 31 | blockchains.should.have.property('getTokenTransfers') 32 | blockchains.should.have.property('getTokenTransfer') 33 | }) 34 | 35 | // blockchains.getBlock() 36 | it('blockchains.getBlock() should fail if `blockchain` is not provided', async function () { 37 | const response = await blockchains.getBlock() 38 | .should.be.rejected() 39 | 40 | response.should.be.an.Error() 41 | response.error_type.should.be.equal(400) 42 | response.message.should.containEql('blockchain') 43 | }) 44 | 45 | it('blockchains.getBlock() should fail if `blockchain` is invalid', async function () { 46 | const response = await blockchains.getBlock('invalidBlockchain') 47 | .should.be.rejected() 48 | 49 | response.should.be.an.Error() 50 | response.error_type.should.be.equal(400) 51 | response.message.should.containEql('blockchain') 52 | }) 53 | 54 | it('blockchains.getBlock() should return one block', async function () { 55 | const response = await blockchains.getBlock('ethereum', 0) 56 | 57 | response.should.be.ok() 58 | response.should.have.properties(['number', 'hash', 'size', 'gas_limit', 'gas_used', 'transaction_count', 'timestamp']) 59 | }) 60 | 61 | it('blockchains.getBlock() should return latest block if `blockNumber` is missing', async function () { 62 | const response = await blockchains.getBlock('ethereum') 63 | 64 | response.should.be.ok() 65 | response.should.have.properties(['number', 'hash', 'size', 'gas_limit', 'gas_used', 'transaction_count', 'timestamp']) 66 | }) 67 | 68 | // blockchains.getContract() 69 | it('blockchains.getContract() should fail if `blockchain` is not provided', async function () { 70 | const response = await blockchains.getContract() 71 | .should.be.rejected() 72 | 73 | response.should.be.an.Error() 74 | response.error_type.should.be.equal(400) 75 | response.message.should.containEql('blockchain') 76 | }) 77 | 78 | it('blockchains.getContract() should fail if `blockchain` is invalid', async function () { 79 | const response = await blockchains.getContract('invalidBlockchain') 80 | .should.be.rejected() 81 | 82 | response.should.be.an.Error() 83 | response.error_type.should.be.equal(400) 84 | response.message.should.containEql('blockchain') 85 | }) 86 | 87 | it('blockchains.getContract() should fail if `blockchain` is not supported', async function () { 88 | const response = await blockchains.getContract('bitcoin') 89 | .should.be.rejected() 90 | 91 | response.should.be.an.Error() 92 | response.error_type.should.be.equal(400) 93 | response.message.should.containEql('blockchain') 94 | }) 95 | 96 | it('blockchains.getContract() should fail if `address` is not provided', async function () { 97 | const response = await blockchains.getContract('ethereum') 98 | .should.be.rejected() 99 | 100 | response.should.be.an.Error() 101 | response.error_type.should.be.equal(400) 102 | response.message.should.containEql('address') 103 | }) 104 | 105 | it('blockchains.getContract() should return one contract', async function () { 106 | const response = await blockchains.getContract('ethereum', '0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f') 107 | 108 | response.should.be.ok() 109 | response.should.have.properties(['address', 'bytecode']) 110 | }) 111 | 112 | // blockchains.getTokens() 113 | it('blockchains.getTokens() should fail if `blockchain` is not provided', async function () { 114 | const response = await blockchains.getTokens() 115 | .should.be.rejected() 116 | 117 | response.should.be.an.Error() 118 | response.error_type.should.be.equal(400) 119 | response.message.should.containEql('blockchain') 120 | }) 121 | 122 | it('blockchains.getTokens() should fail if `blockchain` is invalid', async function () { 123 | const response = await blockchains.getTokens('invalidBlockchain') 124 | .should.be.rejected() 125 | 126 | response.should.be.an.Error() 127 | response.error_type.should.be.equal(400) 128 | response.message.should.containEql('blockchain') 129 | }) 130 | 131 | it('blockchains.getTokens() should fail if `blockchain` is not supported', async function () { 132 | const response = await blockchains.getTokens('bitcoin') 133 | .should.be.rejected() 134 | 135 | response.should.be.an.Error() 136 | response.error_type.should.be.equal(400) 137 | response.message.should.containEql('blockchain') 138 | }) 139 | 140 | it('blockchains.getTokens() should return the list of tokens', async function () { 141 | const response = await blockchains.getTokens('ethereum') 142 | 143 | response.should.be.ok() 144 | response.data.should.be.an.Array() 145 | response.data[0].should.have.properties(['contract', 'ticker', 'name', 'decimals', 'total_supply', 'is_erc20']) 146 | }) 147 | 148 | it('blockchains.getTokens() should return the list of tokens by name', async function () { 149 | const response = await blockchains.getTokens('ethereum', 'Wrapped Ether') 150 | 151 | response.should.be.ok() 152 | response.data.should.be.an.Array() 153 | response.data[0].should.have.properties(['contract', 'ticker', 'name', 'decimals', 'total_supply', 'is_erc20']) 154 | }) 155 | 156 | // blockchains.getBalances() 157 | it('blockchains.getBalances() should fail if `blockchain` is not provided', async function () { 158 | const response = await blockchains.getBalances() 159 | .should.be.rejected() 160 | 161 | response.should.be.an.Error() 162 | response.error_type.should.be.equal(400) 163 | response.message.should.containEql('blockchain') 164 | }) 165 | 166 | it('blockchains.getBalances() should fail if `blockchain` is invalid', async function () { 167 | const response = await blockchains.getBalances('invalidBlockchain') 168 | .should.be.rejected() 169 | 170 | response.should.be.an.Error() 171 | response.error_type.should.be.equal(400) 172 | response.message.should.containEql('blockchain') 173 | }) 174 | 175 | it('blockchains.getBalances() should fail if `address` is not provided', async function () { 176 | const response = await blockchains.getBalances('ethereum') 177 | .should.be.rejected() 178 | 179 | response.should.be.an.Error() 180 | response.error_type.should.be.equal(400) 181 | response.message.should.containEql('address') 182 | }) 183 | 184 | it('blockchains.getBalances() should return balances for the given address', async function () { 185 | const response = await blockchains.getBalances('ethereum', '0x62a6ebC0ac598819CCad368788F6d2357FaE0d9e') 186 | 187 | response.should.be.ok() 188 | response.data.should.be.an.Array() 189 | response.data[0].should.have.properties(['token', 'address', 'balance']) 190 | }) 191 | 192 | // blockchains.getTransactions() 193 | it('blockchains.getTransactions() should fail if `blockchain` is not provided', async function () { 194 | const response = await blockchains.getTransactions() 195 | .should.be.rejected() 196 | 197 | response.should.be.an.Error() 198 | response.error_type.should.be.equal(400) 199 | response.message.should.containEql('blockchain') 200 | }) 201 | 202 | it('blockchains.getTransactions() should fail if `blockchain` is invalid', async function () { 203 | const response = await blockchains.getTransactions('invalidBlockchain') 204 | .should.be.rejected() 205 | 206 | response.should.be.an.Error() 207 | response.error_type.should.be.equal(400) 208 | response.message.should.containEql('blockchain') 209 | }) 210 | 211 | it('blockchains.getTransactions() should fail if `address` is not provided', async function () { 212 | const response = await blockchains.getTransactions('ethereum') 213 | .should.be.rejected() 214 | 215 | response.should.be.an.Error() 216 | response.error_type.should.be.equal(400) 217 | response.message.should.containEql('address') 218 | }) 219 | 220 | it('blockchains.getTransactions() should return transactions for the given address', async function () { 221 | const response = await blockchains.getTransactions('ethereum', '0x4ae694344e7e4e5820c62aa9816b7aa61210eaba') 222 | 223 | response.should.be.ok() 224 | response.data.should.be.an.Array() 225 | response.data[0].should.have.properties(['hash', 'to_address', 'value', 'gas', 'gas_price', 'gas_used', 'input', 'status']) 226 | }) 227 | 228 | // blockchains.getTransaction() 229 | it('blockchains.getTransaction() should fail if `blockchain` is not provided', async function () { 230 | const response = await blockchains.getTransaction() 231 | .should.be.rejected() 232 | 233 | response.should.be.an.Error() 234 | response.error_type.should.be.equal(400) 235 | response.message.should.containEql('blockchain') 236 | }) 237 | 238 | it('blockchains.getTransaction() should fail if `blockchain` is invalid', async function () { 239 | const response = await blockchains.getTransaction('invalidBlockchain') 240 | .should.be.rejected() 241 | 242 | response.should.be.an.Error() 243 | response.error_type.should.be.equal(400) 244 | response.message.should.containEql('blockchain') 245 | }) 246 | 247 | it('blockchains.getTransaction() should fail if `transactionHash` is not provided', async function () { 248 | const response = await blockchains.getTransaction('ethereum') 249 | .should.be.rejected() 250 | 251 | response.should.be.an.Error() 252 | response.error_type.should.be.equal(400) 253 | response.message.should.containEql('transactionHash') 254 | }) 255 | 256 | it('blockchains.getTransaction() should return one transaction', async function () { 257 | const response = await blockchains.getTransaction('ethereum', '0x23170b29a16c3ed89a2d7514c2d6d0796e39a29184c52a0bb5aed6b404b78a83') 258 | 259 | response.should.be.ok() 260 | response.should.have.properties(['hash', 'block_number', 'from_address', 'to_address', 'value', 'gas', 'gas_price', 'gas_used', 'input', 'status']) 261 | }) 262 | 263 | // blockchains.getTokenTransfers() 264 | it('blockchains.getTokenTransfers() should fail if `blockchain` is not provided', async function () { 265 | const response = await blockchains.getTokenTransfers() 266 | .should.be.rejected() 267 | 268 | response.should.be.an.Error() 269 | response.error_type.should.be.equal(400) 270 | response.message.should.containEql('blockchain') 271 | }) 272 | 273 | it('blockchains.getTokenTransfers() should fail if `blockchain` is invalid', async function () { 274 | const response = await blockchains.getTokenTransfers('invalidBlockchain') 275 | .should.be.rejected() 276 | 277 | response.should.be.an.Error() 278 | response.error_type.should.be.equal(400) 279 | response.message.should.containEql('blockchain') 280 | }) 281 | 282 | it('blockchains.getTokenTransfers() should fail if `blockchain` is not supported', async function () { 283 | const response = await blockchains.getTokenTransfers('bitcoin') 284 | .should.be.rejected() 285 | 286 | response.should.be.an.Error() 287 | response.error_type.should.be.equal(400) 288 | response.message.should.containEql('blockchain') 289 | }) 290 | 291 | it('blockchains.getTokenTransfers() should fail if `address` is not provided', async function () { 292 | const response = await blockchains.getTokenTransfers('ethereum') 293 | .should.be.rejected() 294 | 295 | response.should.be.an.Error() 296 | response.error_type.should.be.equal(400) 297 | response.message.should.containEql('address') 298 | }) 299 | 300 | it('blockchains.getTokenTransfers() should return token transfers for the given address', async function () { 301 | const response = await blockchains.getTokenTransfers('ethereum', '0x4ae694344e7e4e5820c62aa9816b7aa61210eaba') 302 | 303 | response.should.be.ok() 304 | response.data.should.be.an.Array() 305 | response.data[0].should.have.properties(['transaction', 'token', 'from_address', 'to_address', 'value']) 306 | }) 307 | 308 | // blockchains.getTokenTransfer() 309 | it('blockchains.getTokenTransfer() should fail if `blockchain` is not provided', async function () { 310 | const response = await blockchains.getTokenTransfer() 311 | .should.be.rejected() 312 | 313 | response.should.be.an.Error() 314 | response.error_type.should.be.equal(400) 315 | response.message.should.containEql('blockchain') 316 | }) 317 | 318 | it('blockchains.getTokenTransfer() should fail if `blockchain` is invalid', async function () { 319 | const response = await blockchains.getTokenTransfer('invalidBlockchain') 320 | .should.be.rejected() 321 | 322 | response.should.be.an.Error() 323 | response.error_type.should.be.equal(400) 324 | response.message.should.containEql('blockchain') 325 | }) 326 | 327 | it('blockchains.getTokenTransfer() should fail if `blockchain` is not supported', async function () { 328 | const response = await blockchains.getTokenTransfer('bitcoin') 329 | .should.be.rejected() 330 | 331 | response.should.be.an.Error() 332 | response.error_type.should.be.equal(400) 333 | response.message.should.containEql('blockchain') 334 | }) 335 | 336 | it('blockchains.getTokenTransfer() should fail if `transactionHash` is not provided', async function () { 337 | const response = await blockchains.getTokenTransfer('ethereum') 338 | .should.be.rejected() 339 | 340 | response.should.be.an.Error() 341 | response.error_type.should.be.equal(400) 342 | response.message.should.containEql('transactionHash') 343 | }) 344 | 345 | it('blockchains.getTokenTransfer() should return the token transfers for the given transaction hash', async function () { 346 | const response = await blockchains.getTokenTransfer('ethereum', '0x23170b29a16c3ed89a2d7514c2d6d0796e39a29184c52a0bb5aed6b404b78a83') 347 | 348 | response.should.be.ok() 349 | response.data.should.be.an.Array() 350 | response.data[0].should.have.properties(['transaction', 'token', 'from_address', 'to_address', 'value']) 351 | }) 352 | }) 353 | -------------------------------------------------------------------------------- /test/resources/currencies.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const sdk = require('../../src/sdk.js') 4 | const mockApi = require('../mock/api.js') 5 | require('should') 6 | 7 | describe('Zabo SDK Currencies Resource', () => { 8 | let currencies 9 | 10 | it('should be instantiated during zabo.init()', async function () { 11 | await sdk.init({ 12 | apiKey: 'some-api-key', 13 | secretKey: 'some-secret-key', 14 | env: 'sandbox', 15 | autoConnect: false 16 | }).catch(err => err).should.be.ok() 17 | 18 | sdk.api.should.be.ok() 19 | sdk.api.connect.should.be.a.Function() 20 | 21 | sdk.api.resources.should.have.property('currencies') 22 | 23 | currencies = await require('../../src/resources/currencies')(mockApi) 24 | 25 | currencies.should.have.property('getList') 26 | currencies.should.have.property('getOne') 27 | currencies.should.have.property('getExchangeRates') 28 | }) 29 | 30 | it('currencies.getList() should fail if an invalid `limit` is provided', async function () { 31 | const response = await currencies.getList({ limit: 51 }).should.be.rejected() 32 | 33 | response.should.be.an.Error() 34 | 35 | response.error_type.should.be.equal(400) 36 | response.message.should.containEql('limit') 37 | }) 38 | 39 | it('currencies.getOne() should fail if a ticker is not provided', async function () { 40 | const response = await currencies.getOne().should.be.rejected() 41 | 42 | response.should.be.an.Error() 43 | response.error_type.should.be.equal(400) 44 | response.message.should.containEql('ticker') 45 | }) 46 | 47 | it('currencies.getExchangeRates() should fail if an invalid `limit` is provided', async function () { 48 | const response = await currencies.getExchangeRates({ 49 | tickers: 'BTC', 50 | limit: 51 51 | }).should.be.rejected() 52 | 53 | response.should.be.an.Error() 54 | 55 | response.error_type.should.be.equal(400) 56 | response.message.should.containEql('limit') 57 | }) 58 | 59 | it('currencies.getList() should return the list of currencies', async function () { 60 | const list = await currencies.getList() 61 | 62 | list.should.be.ok() 63 | list.data.should.be.an.Array() 64 | list.data[0].should.have.properties(['ticker', 'name', 'type', 'logo']) 65 | }) 66 | 67 | it('currencies.getOne() should return one currency', async function () { 68 | const currency = await currencies.getOne('BTC') 69 | 70 | currency.should.be.ok() 71 | currency.should.have.properties(['ticker', 'name', 'type', 'logo']) 72 | currency.ticker.should.be.eql('BTC') 73 | }) 74 | 75 | it('currencies.getExchangeRates() should return a list of exchange rates', async function () { 76 | const list = await currencies.getExchangeRates() 77 | 78 | list.should.be.ok() 79 | list.data.should.be.an.Array() 80 | list.data[0].should.have.properties(['from', 'to', 'rate']) 81 | }) 82 | }) 83 | -------------------------------------------------------------------------------- /test/resources/providers.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const sdk = require('../../src/sdk.js') 4 | const mockApi = require('../mock/api.js') 5 | require('should') 6 | 7 | describe('Zabo SDK Providers Resource', () => { 8 | let providers 9 | 10 | it('should be instantiated during zabo.init()', async function () { 11 | await sdk.init({ 12 | apiKey: 'some-api-key', 13 | secretKey: 'some-secret-key', 14 | env: 'sandbox', 15 | autoConnect: false 16 | }).catch(err => err).should.be.ok() 17 | 18 | sdk.api.should.be.ok() 19 | sdk.api.connect.should.be.a.Function() 20 | 21 | sdk.api.resources.should.have.property('providers') 22 | 23 | providers = await require('../../src/resources/providers')(mockApi) 24 | 25 | providers.should.have.property('getList') 26 | providers.should.have.property('getOne') 27 | }) 28 | 29 | it('providers.getList() should fail if an invalid `limit` is provided', async function () { 30 | const response = await providers.getList({ limit: 51 }).should.be.rejected() 31 | 32 | response.should.be.an.Error() 33 | 34 | response.error_type.should.be.equal(400) 35 | response.message.should.containEql('limit') 36 | }) 37 | 38 | it('providers.getList() should fail if an invalid `cursor` is provided', async function () { 39 | const response = await providers.getList({ cursor: 'not_a_valid_id' }).should.be.rejected() 40 | 41 | response.should.be.an.Error() 42 | 43 | response.error_type.should.be.equal(400) 44 | response.message.should.containEql('cursor') 45 | }) 46 | 47 | it('providers.getOne() should fail if a provider name is not provided', async function () { 48 | const response = await providers.getOne().should.be.rejected() 49 | 50 | response.should.be.an.Error() 51 | response.error_type.should.be.equal(400) 52 | response.message.should.containEql('name') 53 | }) 54 | 55 | it('providers.getList() should return the list of provider', async function () { 56 | const list = await providers.getList() 57 | 58 | list.should.be.ok() 59 | list.data.should.be.an.Array() 60 | list.data[0].should.have.properties(['name', 'display_name', 'logo', 'auth_type', 'available_scopes', 'available_currencies']) 61 | }) 62 | 63 | it('providers.getOne() should return one provider', async function () { 64 | const provider = await providers.getOne('metamask') 65 | 66 | provider.should.be.ok() 67 | provider.should.have.properties(['name', 'display_name', 'logo', 'auth_type', 'available_scopes', 'available_currencies']) 68 | provider.name.should.be.eql('metamask') 69 | }) 70 | }) 71 | -------------------------------------------------------------------------------- /test/resources/teams.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const should = require('should') 4 | const sdk = require('../../src/sdk.js') 5 | const mockApi = require('../mock/api.js') 6 | 7 | describe('Zabo SDK Teams Resource', () => { 8 | let teams 9 | 10 | it('should be instantiated during zabo.init()', async function () { 11 | await sdk.init({ 12 | apiKey: 'some-api-key', 13 | secretKey: 'some-secret-key', 14 | env: 'sandbox', 15 | autoConnect: false 16 | }).catch(err => err).should.be.ok() 17 | 18 | sdk.api.should.be.ok() 19 | sdk.api.connect.should.be.a.Function() 20 | 21 | sdk.api.resources.should.have.property('teams') 22 | 23 | teams = await require('../../src/resources/teams')(mockApi) 24 | 25 | teams.should.have.property('get') 26 | }) 27 | 28 | it('teams.get() should fail if an team id has not been set', async function () { 29 | const response = await teams.get().should.be.rejected() 30 | 31 | response.should.be.an.Error() 32 | response.error_type.should.be.equal(401) 33 | }) 34 | 35 | it('teams.setId() should fail if an invalid team id is provided', function () { 36 | should(() => { 37 | teams.setId('not a valid id') 38 | }).throw(Error) 39 | }) 40 | 41 | it('teams.setId() should set id if a valid team id is provided', function () { 42 | const uuid = 'b5cfb0d8-58de-4786-9545-3d38521d7d2b' 43 | 44 | should(() => { 45 | teams.setId(uuid) 46 | }).not.throw(Error) 47 | 48 | teams.id.should.equal(uuid) 49 | }) 50 | 51 | it('teams.get() should return an team and cache team data [server-side]', async function () { 52 | const team = await teams.get() 53 | 54 | team.should.be.ok() 55 | team.should.have.properties(['id', 'name']) 56 | 57 | teams.data.should.be.eql(team) 58 | teams.id.should.be.equal(team.id) 59 | }) 60 | 61 | it('teams.get() should return team and cache team data [client-side]', async function () { 62 | // Mock DOM 63 | require('jsdom-global')() 64 | const originalGlobal = global 65 | global = undefined // eslint-disable-line 66 | 67 | const team = await teams.get() 68 | 69 | team.should.be.ok() 70 | team.should.have.properties(['id', 'name']) 71 | 72 | teams.data.should.be.eql(team) 73 | teams.id.should.be.equal(team.id) 74 | 75 | // Undo mock DOM 76 | global = originalGlobal // eslint-disable-line 77 | }) 78 | 79 | it('teams.getSession() should return a valid session [client-side]', async function () { 80 | // Mock DOM 81 | require('jsdom-global')() 82 | const originalGlobal = global 83 | global = undefined // eslint-disable-line 84 | 85 | const session = await teams.getSession() 86 | 87 | // Backwards compatible 88 | if (session) { 89 | console.log(session) 90 | session.should.be.ok() 91 | session.should.have.properties(['one_time_password', 'expires_at']) 92 | new Date(session.expires_at).should.be.above(Date.now()) 93 | } 94 | 95 | // Undo mock DOM 96 | global = originalGlobal // eslint-disable-line 97 | }) 98 | }) 99 | -------------------------------------------------------------------------------- /test/resources/trading.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const mockApi = require('../mock/api.js') 4 | require('should') 5 | 6 | const ORDER_PROPERTIES = [ 7 | 'id', 8 | 'base_currency', 9 | 'quote_currency', 10 | 'base_amount', 11 | 'buy_or_sell', 12 | 'quote_amount', 13 | 'price', 14 | 'time_in_force', 15 | 'ttl', 16 | 'provide_liquidity_only', 17 | 'type', 18 | 'status', 19 | 'done_at', 20 | 'done_reason', 21 | 'filled_size', 22 | 'fill_fees', 23 | 'settled', 24 | 'created_at' 25 | ] 26 | 27 | describe('Zabo SDK Trading Resource', () => { 28 | let trading 29 | 30 | it('should be instantiated and expose the methods', async function () { 31 | trading = await require('../../src/resources/trading')(mockApi) 32 | 33 | trading.should.have.property('getSymbols') 34 | trading.should.have.property('getTickerInfo') 35 | trading.should.have.property('getOrders') 36 | trading.should.have.property('getOrder') 37 | trading.should.have.property('createOrder') 38 | trading.should.have.property('cancelOrders') 39 | trading.should.have.property('cancelOrder') 40 | }) 41 | 42 | // trading.getSymbols() 43 | it('trading.getSymbols() should fail if account not connected', async function () { 44 | trading._setAccount(null) 45 | 46 | const response = await trading.getSymbols() 47 | .should.be.rejected() 48 | 49 | response.should.be.an.Error() 50 | response.error_type.should.be.equal(400) 51 | response.message.should.containEql('Not connected') 52 | 53 | // mock account data 54 | trading._setAccount({ id: 'fake-account' }) 55 | }) 56 | 57 | it('trading.getSymbols() should return the list of symbols', async function () { 58 | const list = await trading.getSymbols() 59 | 60 | list.should.be.ok() 61 | list.data.should.be.an.Array() 62 | list.data[0].should.have.properties(['base_currency', 'quote_currency']) 63 | }) 64 | 65 | // trading.getTickerInfo() 66 | it('trading.getTickerInfo() should fail if account not connected', async function () { 67 | trading._setAccount(null) 68 | 69 | const response = await trading.getTickerInfo() 70 | .should.be.rejected() 71 | 72 | response.should.be.an.Error() 73 | response.error_type.should.be.equal(400) 74 | response.message.should.containEql('Not connected') 75 | 76 | // mock account data 77 | trading._setAccount({ id: 'fake-account' }) 78 | }) 79 | 80 | it('trading.getTickerInfo() should fail if `baseCurrency` is missing', async function () { 81 | const response = await trading.getTickerInfo({ quoteCurrency: 'USD' }) 82 | .should.be.rejected() 83 | 84 | response.should.be.an.Error() 85 | response.error_type.should.be.equal(400) 86 | response.message.should.containEql('Missing') 87 | response.message.should.containEql('baseCurrency') 88 | }) 89 | 90 | it('trading.getTickerInfo() should fail if `quoteCurrency` is missing', async function () { 91 | const response = await trading.getTickerInfo({ baseCurrency: 'BTC' }) 92 | .should.be.rejected() 93 | 94 | response.should.be.an.Error() 95 | response.error_type.should.be.equal(400) 96 | response.message.should.containEql('Missing') 97 | response.message.should.containEql('quoteCurrency') 98 | }) 99 | 100 | it('trading.getTickerInfo() should return ticker information', async function () { 101 | const response = await trading.getTickerInfo({ baseCurrency: 'BTC', quoteCurrency: 'USD' }) 102 | 103 | response.should.be.ok() 104 | response.should.have.properties(['last_price', 'last_size', 'ask', 'ask_size', 'bid', 'bid_size', 'volume', 'timestamp']) 105 | }) 106 | 107 | // trading.getOrders() 108 | it('trading.getOrders() should fail if account not connected', async function () { 109 | trading._setAccount(null) 110 | 111 | const response = await trading.getOrders() 112 | .should.be.rejected() 113 | 114 | response.should.be.an.Error() 115 | response.error_type.should.be.equal(400) 116 | response.message.should.containEql('Not connected') 117 | 118 | // mock account data 119 | trading._setAccount({ id: 'fake-account' }) 120 | }) 121 | 122 | it('trading.getOrders() should return the list of orders', async function () { 123 | const list = await trading.getOrders() 124 | 125 | list.should.be.ok() 126 | list.data.should.be.an.Array() 127 | list.data[0].should.have.properties(ORDER_PROPERTIES) 128 | }) 129 | 130 | // trading.getOrder() 131 | it('trading.getOrder() should fail if account not connected', async function () { 132 | trading._setAccount(null) 133 | 134 | const response = await trading.getOrder() 135 | .should.be.rejected() 136 | 137 | response.should.be.an.Error() 138 | response.error_type.should.be.equal(400) 139 | response.message.should.containEql('Not connected') 140 | 141 | // mock account data 142 | trading._setAccount({ id: 'fake-account' }) 143 | }) 144 | 145 | it('trading.getOrder() should fail if `orderId` is missing', async function () { 146 | const response = await trading.getOrder({}) 147 | .should.be.rejected() 148 | 149 | response.should.be.an.Error() 150 | response.error_type.should.be.equal(400) 151 | response.message.should.containEql('Missing') 152 | response.message.should.containEql('orderId') 153 | }) 154 | 155 | it('trading.getOrder() should fail if `orderId` is invalid', async function () { 156 | const response = await trading.getOrder({ orderId: 'not-an-uuid' }) 157 | .should.be.rejected() 158 | 159 | response.should.be.an.Error() 160 | response.error_type.should.be.equal(400) 161 | response.message.should.containEql('orderId') 162 | response.message.should.containEql('UUID') 163 | }) 164 | 165 | it('trading.getOrder() should return the order', async function () { 166 | const response = await trading.getOrder({ orderId: 'c1bea143-15b1-4ae5-a33f-a25f32937559' }) 167 | 168 | response.should.be.ok() 169 | response.should.have.properties(ORDER_PROPERTIES) 170 | }) 171 | 172 | // trading.createOrder() 173 | it('trading.createOrder() should fail if account not connected', async function () { 174 | trading._setAccount(null) 175 | 176 | const response = await trading.createOrder() 177 | .should.be.rejected() 178 | 179 | response.should.be.an.Error() 180 | response.error_type.should.be.equal(400) 181 | response.message.should.containEql('Not connected') 182 | 183 | // mock account data 184 | trading._setAccount({ id: 'fake-account' }) 185 | }) 186 | 187 | it('trading.createOrder() should fail if `baseCurrency` is missing', async function () { 188 | const response = await trading.createOrder({ 189 | quoteCurrency: 'USD', 190 | buyOrSell: 'buy', 191 | baseAmount: '0.001', 192 | priceLimit: '11537.56', 193 | timeInForce: 'GTC', 194 | provideLiquidityOnly: true 195 | }) 196 | .should.be.rejected() 197 | 198 | response.should.be.an.Error() 199 | response.error_type.should.be.equal(400) 200 | response.message.should.containEql('Missing') 201 | response.message.should.containEql('baseCurrency') 202 | }) 203 | 204 | it('trading.createOrder() should fail if `quoteCurrency` is missing', async function () { 205 | const response = await trading.createOrder({ 206 | baseCurrency: 'BTC', 207 | buyOrSell: 'buy', 208 | baseAmount: '0.001', 209 | priceLimit: '11537.56', 210 | timeInForce: 'GTC', 211 | provideLiquidityOnly: true 212 | }) 213 | .should.be.rejected() 214 | 215 | response.should.be.an.Error() 216 | response.error_type.should.be.equal(400) 217 | response.message.should.containEql('Missing') 218 | response.message.should.containEql('quoteCurrency') 219 | }) 220 | 221 | it('trading.createOrder() should fail if `baseAmount` is 0', async function () { 222 | const response = await trading.createOrder({ 223 | baseCurrency: 'BTC', 224 | quoteCurrency: 'USD', 225 | buyOrSell: 'buy', 226 | baseAmount: '0', 227 | timeInForce: 'GTC' 228 | }) 229 | .should.be.rejected() 230 | 231 | response.should.be.an.Error() 232 | response.error_type.should.be.equal(400) 233 | response.message.should.containEql('greater') 234 | response.message.should.containEql('baseAmount') 235 | }) 236 | 237 | it('trading.createOrder() should fail if `baseAmount` is negative', async function () { 238 | const response = await trading.createOrder({ 239 | baseCurrency: 'BTC', 240 | quoteCurrency: 'USD', 241 | buyOrSell: 'buy', 242 | baseAmount: '-1', 243 | timeInForce: 'GTC' 244 | }) 245 | .should.be.rejected() 246 | 247 | response.should.be.an.Error() 248 | response.error_type.should.be.equal(400) 249 | response.message.should.containEql('greater') 250 | response.message.should.containEql('baseAmount') 251 | }) 252 | 253 | it('trading.createOrder() should fail if `quoteAmount` is 0', async function () { 254 | const response = await trading.createOrder({ 255 | baseCurrency: 'BTC', 256 | quoteCurrency: 'USD', 257 | buyOrSell: 'buy', 258 | quoteAmount: '0', 259 | timeInForce: 'GTC' 260 | }) 261 | .should.be.rejected() 262 | 263 | response.should.be.an.Error() 264 | response.error_type.should.be.equal(400) 265 | response.message.should.containEql('greater') 266 | response.message.should.containEql('quoteAmount') 267 | }) 268 | 269 | it('trading.createOrder() should fail if `quoteAmount` is negative', async function () { 270 | const response = await trading.createOrder({ 271 | baseCurrency: 'BTC', 272 | quoteCurrency: 'USD', 273 | buyOrSell: 'buy', 274 | quoteAmount: '-1', 275 | timeInForce: 'GTC' 276 | }) 277 | .should.be.rejected() 278 | 279 | response.should.be.an.Error() 280 | response.error_type.should.be.equal(400) 281 | response.message.should.containEql('greater') 282 | response.message.should.containEql('quoteAmount') 283 | }) 284 | 285 | it('trading.createOrder() should fail if `priceLimit` is 0', async function () { 286 | const response = await trading.createOrder({ 287 | baseCurrency: 'BTC', 288 | quoteCurrency: 'USD', 289 | buyOrSell: 'buy', 290 | priceLimit: '0', 291 | timeInForce: 'GTC' 292 | }) 293 | .should.be.rejected() 294 | 295 | response.should.be.an.Error() 296 | response.error_type.should.be.equal(400) 297 | response.message.should.containEql('greater') 298 | response.message.should.containEql('priceLimit') 299 | }) 300 | 301 | it('trading.createOrder() should fail if `priceLimit` is negative', async function () { 302 | const response = await trading.createOrder({ 303 | baseCurrency: 'BTC', 304 | quoteCurrency: 'USD', 305 | buyOrSell: 'buy', 306 | priceLimit: '-1', 307 | timeInForce: 'GTC' 308 | }) 309 | .should.be.rejected() 310 | 311 | response.should.be.an.Error() 312 | response.error_type.should.be.equal(400) 313 | response.message.should.containEql('greater') 314 | response.message.should.containEql('priceLimit') 315 | }) 316 | 317 | it('trading.createOrder() should fail if `buyOrSell` is missing', async function () { 318 | const response = await trading.createOrder({ 319 | baseCurrency: 'BTC', 320 | quoteCurrency: 'USD', 321 | baseAmount: '0.001', 322 | priceLimit: '11537.56', 323 | timeInForce: 'GTC', 324 | provideLiquidityOnly: true 325 | }) 326 | .should.be.rejected() 327 | 328 | response.should.be.an.Error() 329 | response.error_type.should.be.equal(400) 330 | response.message.should.containEql('Missing') 331 | response.message.should.containEql('buyOrSell') 332 | }) 333 | 334 | it('trading.createOrder() should fail if `buyOrSell` is not valid', async function () { 335 | const response = await trading.createOrder({ 336 | baseCurrency: 'BTC', 337 | quoteCurrency: 'USD', 338 | buyOrSell: 'trade', 339 | baseAmount: '0.001', 340 | priceLimit: '11537.56', 341 | timeInForce: 'GTC', 342 | provideLiquidityOnly: true 343 | }) 344 | .should.be.rejected() 345 | 346 | response.should.be.an.Error() 347 | response.error_type.should.be.equal(400) 348 | response.message.should.containEql('Invalid') 349 | response.message.should.containEql('buyOrSell') 350 | }) 351 | 352 | it('trading.createOrder() should fail if `timeInForce` is not valid', async function () { 353 | const response = await trading.createOrder({ 354 | baseCurrency: 'BTC', 355 | quoteCurrency: 'USD', 356 | buyOrSell: 'buy', 357 | baseAmount: '0.001', 358 | priceLimit: '11537.56', 359 | timeInForce: 'ABC', 360 | provideLiquidityOnly: true 361 | }) 362 | .should.be.rejected() 363 | 364 | response.should.be.an.Error() 365 | response.error_type.should.be.equal(400) 366 | response.message.should.containEql('Invalid') 367 | response.message.should.containEql('timeInForce') 368 | }) 369 | 370 | it('trading.createOrder() should create and return the order', async function () { 371 | const response = await trading.createOrder({ 372 | baseCurrency: 'BTC', 373 | quoteCurrency: 'USD', 374 | buyOrSell: 'buy', 375 | baseAmount: '0.001', 376 | priceLimit: '11537.56', 377 | timeInForce: 'GTC', 378 | provideLiquidityOnly: true 379 | }) 380 | 381 | response.should.be.ok() 382 | response.should.have.properties(ORDER_PROPERTIES) 383 | }) 384 | 385 | // trading.cancelOrders() 386 | it('trading.cancelOrders() should fail if account not connected', async function () { 387 | trading._setAccount(null) 388 | 389 | const response = await trading.cancelOrders() 390 | .should.be.rejected() 391 | 392 | response.should.be.an.Error() 393 | response.error_type.should.be.equal(400) 394 | response.message.should.containEql('Not connected') 395 | 396 | // mock account data 397 | trading._setAccount({ id: 'fake-account' }) 398 | }) 399 | 400 | it('trading.cancelOrders() should cancel all orders', async function () { 401 | const response = await trading.cancelOrders() 402 | 403 | response.should.be.ok() 404 | response.data.should.be.an.Array() 405 | }) 406 | 407 | // trading.cancelOrder() 408 | it('trading.cancelOrder() should fail if account not connected', async function () { 409 | trading._setAccount(null) 410 | 411 | const response = await trading.cancelOrder() 412 | .should.be.rejected() 413 | 414 | response.should.be.an.Error() 415 | response.error_type.should.be.equal(400) 416 | response.message.should.containEql('Not connected') 417 | 418 | // mock account data 419 | trading._setAccount({ id: 'fake-account' }) 420 | }) 421 | 422 | it('trading.cancelOrder() should fail if `orderId` is missing', async function () { 423 | const response = await trading.cancelOrder({}) 424 | .should.be.rejected() 425 | 426 | response.should.be.an.Error() 427 | response.error_type.should.be.equal(400) 428 | response.message.should.containEql('Missing') 429 | response.message.should.containEql('orderId') 430 | }) 431 | 432 | it('trading.cancelOrder() should fail if `orderId` is invalid', async function () { 433 | const response = await trading.cancelOrder({ orderId: 'not-an-uuid' }) 434 | .should.be.rejected() 435 | 436 | response.should.be.an.Error() 437 | response.error_type.should.be.equal(400) 438 | response.message.should.containEql('orderId') 439 | response.message.should.containEql('UUID') 440 | }) 441 | 442 | it('trading.cancelOrder() should cancel the order', async function () { 443 | const data = { orderId: 'c1bea143-15b1-4ae5-a33f-a25f32937559' } 444 | const response = await trading.cancelOrder(data) 445 | 446 | response.should.be.ok() 447 | response.data.should.be.an.Array() 448 | response.data.should.containEql(data.orderId) 449 | }) 450 | }) 451 | -------------------------------------------------------------------------------- /test/resources/transactions.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const sdk = require('../../src/sdk.js') 4 | const mockApi = require('../mock/api.js') 5 | require('should') 6 | 7 | describe('Zabo SDK Transactions Resource', () => { 8 | let transactions 9 | 10 | it('should be instantiated during zabo.init()', async function () { 11 | await sdk.init({ 12 | apiKey: 'some-api-key', 13 | secretKey: 'some-secret-key', 14 | env: 'sandbox', 15 | autoConnect: false 16 | }).catch(err => err).should.be.ok() 17 | 18 | sdk.api.should.be.ok() 19 | sdk.api.connect.should.be.a.Function() 20 | 21 | sdk.api.resources.should.have.property('transactions') 22 | 23 | transactions = await require('../../src/resources/transactions')(mockApi) 24 | 25 | transactions.should.have.property('getOne') 26 | transactions.should.have.property('getList') 27 | }) 28 | 29 | it('transactions.getOne() should fail if `userId` is not provided', async function () { 30 | const response = await transactions.getOne({ 31 | accountId: '7a1e6a76-f7d0-4b8c-8c16-8972041c970a', 32 | txId: 'b2a020df-1057-4847-8aaf-eb1f524e3518' 33 | }).should.be.rejected() 34 | 35 | response.should.be.an.Error() 36 | response.error_type.should.be.equal(400) 37 | response.message.should.containEql('userId') 38 | }) 39 | 40 | it('transactions.getOne() should fail if `accountId` is not provided', async function () { 41 | const response = await transactions.getOne({ 42 | userId: '35b6b5dd-90a4-478e-b7b4-8712370f3333', 43 | txId: 'b2a020df-1057-4847-8aaf-eb1f524e3518' 44 | }).should.be.rejected() 45 | 46 | response.should.be.an.Error() 47 | response.error_type.should.be.equal(400) 48 | response.message.should.containEql('accountId') 49 | }) 50 | 51 | it('transactions.getOne() should fail if `txId` is not provided', async function () { 52 | const response = await transactions.getOne({ 53 | userId: '35b6b5dd-90a4-478e-b7b4-8712370f3333', 54 | accountId: '7a1e6a76-f7d0-4b8c-8c16-8972041c970a' 55 | }).should.be.rejected() 56 | 57 | response.should.be.an.Error() 58 | response.error_type.should.be.equal(400) 59 | response.message.should.containEql('txId') 60 | }) 61 | 62 | it('transactions.getList() should fail if `userId` is not provided', async function () { 63 | const response = await transactions.getList({ 64 | accountId: '7a1e6a76-f7d0-4b8c-8c16-8972041c970a', 65 | ticker: 'ETH', 66 | limit: 10 67 | }).should.be.rejected() 68 | 69 | response.should.be.an.Error() 70 | 71 | response.error_type.should.be.equal(400) 72 | response.message.should.containEql('userId') 73 | }) 74 | 75 | it('transactions.getList() should fail if `accountId` is not provided', async function () { 76 | const response = await transactions.getList({ 77 | userId: '35b6b5dd-90a4-478e-b7b4-8712370f3333', 78 | ticker: 'ETH', 79 | limit: 10 80 | }).should.be.rejected() 81 | 82 | response.should.be.an.Error() 83 | 84 | response.error_type.should.be.equal(400) 85 | response.message.should.containEql('accountId') 86 | }) 87 | 88 | it('transactions.getList() should fail if an invalid `limit` is provided', async function () { 89 | const response = await transactions.getList({ limit: 51 }).should.be.rejected() 90 | 91 | response.should.be.an.Error() 92 | 93 | response.error_type.should.be.equal(400) 94 | response.message.should.containEql('limit') 95 | }) 96 | 97 | it('transactions.getList() should return the list of transactions', async function () { 98 | const data = { 99 | userId: '35b6b5dd-90a4-478e-b7b4-8712370f3333', 100 | accountId: 'ff0dc466-547b-45f6-a34c-f45463489e2f' 101 | } 102 | const list = await transactions.getList(data) 103 | 104 | list.should.be.ok() 105 | list.data.should.be.an.Array() 106 | list.data[0].should.have.properties(['id', 'status', 'transaction_type', 'parts', 'fees', 'misc', 'fiat_calculated_at', 'initiated_at', 'confirmed_at']) 107 | }) 108 | 109 | it('transactions.getOne() should return one transaction', async function () { 110 | const data = { 111 | userId: '35b6b5dd-90a4-478e-b7b4-8712370f3333', 112 | accountId: 'ff0dc466-547b-45f6-a34c-f45463489e2f', 113 | txId: '932e2040-32ce-4f3a-a67b-f1f37bcb74ba' 114 | } 115 | const tx = await transactions.getOne(data) 116 | 117 | tx.should.be.ok() 118 | tx.should.have.properties(['id', 'status', 'transaction_type', 'parts', 'fees', 'misc', 'fiat_calculated_at', 'initiated_at', 'confirmed_at']) 119 | tx.id.should.be.eql(data.txId) 120 | }) 121 | }) 122 | -------------------------------------------------------------------------------- /test/resources/utils.spec.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zabo-api/zabo-sdk-js/9c7fb8ed2f4fdd0966c960eccec055bd23d6ed64/test/resources/utils.spec.js -------------------------------------------------------------------------------- /test/utils.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const utils = require('../src/utils.js') 4 | require('should') 5 | 6 | describe('Zabo SDK Utils', () => { 7 | it('uuidValidate() accepts valid uuidv1', function () { 8 | utils.uuidValidate('23d57c30-afe7-11e4-ab7d-12e3f512a338').should.be.true() 9 | }) 10 | 11 | it('uuidValidate() accepts valid uuidv4', function () { 12 | utils.uuidValidate('09bb1d8c-4965-4788-94f7-31b151eaba4e').should.be.true() 13 | }) 14 | 15 | it('uuidValidate() denies if invalid', function () { 16 | utils.uuidValidate().should.be.false() 17 | utils.uuidValidate(null).should.be.false() 18 | utils.uuidValidate(1).should.be.false() 19 | utils.uuidValidate({}).should.be.false() 20 | utils.uuidValidate(false).should.be.false() 21 | utils.uuidValidate('foo').should.be.false() 22 | utils.uuidValidate('uuuuuuuu-uuuu-uuuu-uuuu-uuuuuuuuuuuu').should.be.false() 23 | }) 24 | }) 25 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": ["./src/**/*.js"], 3 | "exclude": [ 4 | "./src/index.d.ts", 5 | "./src/index.js", 6 | "node_modules", 7 | "dist" 8 | ], 9 | "compilerOptions": { 10 | "lib": ["ES6"], 11 | "allowJs": true, 12 | "declaration": true, 13 | "emitDeclarationOnly": true, 14 | "outDir": "./dist/@types" 15 | } 16 | } -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const webpack = require('webpack') 3 | 4 | module.exports = () => { 5 | const plugins = [ 6 | // Create global constants. 7 | new webpack.DefinePlugin({ 8 | 'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV), 9 | 'process.env.PACKAGE_VERSION': JSON.stringify(process.env.PACKAGE_VERSION) 10 | }), 11 | // Add banner to the top of each generated chunk. 12 | new webpack.BannerPlugin({ 13 | banner: ` 14 | @Copyright (c) 2019-present, Zabo & Modular, Inc. All rights reserved. 15 | 16 | Licensed under the Apache License, Version 2.0 (the "License"); 17 | you may not use this file except in compliance with the License. 18 | You may obtain a copy of the License at 19 | 20 | http://www.apache.org/licenses/LICENSE-2.0 21 | 22 | Unless required by applicable law or agreed to in writing, software 23 | distributed under the License is distributed on an "AS IS" BASIS, 24 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 25 | See the License for the specific language governing permissions and 26 | limitations under the License. 27 | 28 | @Version: ${JSON.stringify(process.env.PACKAGE_VERSION)} 29 | ` 30 | }) 31 | ] 32 | 33 | // Compile for usage in a browser-like environment. 34 | // Output: "./dist/zabo.js" 35 | const browserConfig = { 36 | mode: 'production', 37 | target: 'web', 38 | devtool: 'source-map', 39 | entry: './src/index.js', 40 | output: { 41 | library: 'Zabo', 42 | filename: 'zabo.js', 43 | path: path.resolve(__dirname, 'dist'), 44 | libraryTarget: 'window' 45 | }, 46 | module: { 47 | rules: [ 48 | { 49 | test: /\.js?$/, 50 | exclude: /(node_modules)/, 51 | use: 'babel-loader' 52 | } 53 | ] 54 | }, 55 | plugins 56 | } 57 | 58 | // Compile for usage in a Node.js-like environment (uses Node.js require to load chunks). 59 | // Output: "./dist/index.js" 60 | const modulesConfig = { 61 | mode: 'production', 62 | target: 'node', 63 | node: { process: false }, 64 | entry: './src/index.js', 65 | output: { 66 | library: 'Zabo', 67 | filename: 'index.js', 68 | path: path.resolve(__dirname, 'dist'), 69 | libraryTarget: 'umd', 70 | umdNamedDefine: true, 71 | // TODO: Hack (for Webpack 4+) to enable create UMD build which can be required by Node without throwing error for window being undefined (https://github.com/webpack/webpack/issues/6522) 72 | globalObject: "(typeof self !== 'undefined' ? self : this)" 73 | }, 74 | module: { 75 | rules: [ 76 | { 77 | test: /\.js?$/, 78 | exclude: /(node_modules)/, 79 | use: 'babel-loader' 80 | } 81 | ] 82 | }, 83 | plugins 84 | } 85 | 86 | return [browserConfig, modulesConfig] 87 | } 88 | --------------------------------------------------------------------------------