From e59ec4ed288040690cf75c1c937066bfe8fbb5cf Mon Sep 17 00:00:00 2001 From: Rafael Taranto Date: Sun, 30 Dec 2018 11:48:40 -0300 Subject: [PATCH] Add ZEC, BCH, and LTC to BitGO wallet plugin (#240) * Add ZEC, BCH and LTC to BitGO wallet plugin * Convert to legacyAddress on bitgo plugin --- lib/admin/config.js | 2 +- lib/plugins/wallet/bitgo/bitgo.js | 44 +++++++++++++------ package-lock.json | 73 +++++++++++++++++++++++++------ package.json | 1 + schemas/bitgo.json | 56 ++++++++++++++++++++++-- 5 files changed, 144 insertions(+), 32 deletions(-) diff --git a/lib/admin/config.js b/lib/admin/config.js index 825f08a0..155809d2 100644 --- a/lib/admin/config.js +++ b/lib/admin/config.js @@ -190,7 +190,7 @@ function fetchData () { {code: 'litecoind', display: 'litecoind', class: 'wallet', cryptos: ['LTC']}, {code: 'dashd', display: 'dashd', class: 'wallet', cryptos: ['DASH']}, {code: 'bitcoincashd', display: 'bitcoincashd', class: 'wallet', cryptos: ['BCH']}, - {code: 'bitgo', display: 'BitGo', class: 'wallet', cryptos: ['BTC']}, + {code: 'bitgo', display: 'BitGo', class: 'wallet', cryptos: ['BTC', 'ZEC', 'LTC', 'BCH']}, {code: 'bitstamp', display: 'Bitstamp', class: 'exchange', cryptos: ['BTC', 'ETH', 'LTC', 'BCH']}, {code: 'itbit', display: 'itBit', class: 'exchange', cryptos: ['BTC']}, {code: 'kraken', display: 'Kraken', class: 'exchange', cryptos: ['BTC', 'ETH', 'LTC', 'DASH', 'ZEC', 'BCH']}, diff --git a/lib/plugins/wallet/bitgo/bitgo.js b/lib/plugins/wallet/bitgo/bitgo.js index 0565ffea..d3868b8d 100644 --- a/lib/plugins/wallet/bitgo/bitgo.js +++ b/lib/plugins/wallet/bitgo/bitgo.js @@ -1,4 +1,5 @@ const BitGo = require('bitgo') +const { toLegacyAddress, toCashAddress } = require('bchaddrjs') const BN = require('../../../bn') @@ -8,35 +9,50 @@ const pjson = require('../../../../package.json') const userAgent = 'Lamassu-Server/' + pjson.version const NAME = 'BitGo' +const SUPPORTED_COINS = ['BTC', 'ZEC', 'LTC', 'BCH'] function buildBitgo (account) { const env = account.environment === 'test' ? 'test' : 'prod' return new BitGo.BitGo({ accessToken: account.token, env, userAgent: userAgent }) } -function getWallet (account) { +function getWallet (account, cryptoCode) { const bitgo = buildBitgo(account) - const coin = account.environment === 'test' ? 'tbtc' : 'btc' + const coin = account.environment === 'test' ? `t${cryptoCode.toLowerCase()}` : cryptoCode.toLowerCase() - return bitgo.coin(coin).wallets().get({ id: account.walletId }) + return bitgo.coin(coin).wallets().get({ id: account[`${cryptoCode}WalletId`] }) } function checkCryptoCode (cryptoCode) { - if (cryptoCode !== 'BTC') { + if (!SUPPORTED_COINS.includes(cryptoCode)) { return Promise.reject(new Error('Unsupported crypto: ' + cryptoCode)) } return Promise.resolve() } +function getLegacyAddress (address, cryptoCode) { + const BCH_CODES = ['BCH', 'TBCH'] + if (!BCH_CODES.includes(cryptoCode)) return address + + return toLegacyAddress(address) +} + +function getCashAddress (address, cryptoCode) { + const BCH_CODES = ['BCH', 'TBCH'] + if (!BCH_CODES.includes(cryptoCode)) return address + + return toCashAddress(address) +} + function sendCoins (account, address, cryptoAtoms, cryptoCode) { return checkCryptoCode(cryptoCode) - .then(() => getWallet(account)) + .then(() => getWallet(account, cryptoCode)) .then(wallet => { const params = { - address: address, + address: getLegacyAddress(address, cryptoCode), amount: cryptoAtoms.toNumber(), - walletPassphrase: account.walletPassphrase + walletPassphrase: account[`${cryptoCode}WalletPassphrase`] } return wallet.send(params) }) @@ -51,13 +67,13 @@ function sendCoins (account, address, cryptoAtoms, cryptoCode) { function balance (account, cryptoCode) { return checkCryptoCode(cryptoCode) - .then(() => getWallet(account)) + .then(() => getWallet(account, cryptoCode)) .then(wallet => BN(wallet._wallet.spendableBalanceString)) } function newAddress (account, info) { return checkCryptoCode(info.cryptoCode) - .then(() => getWallet(account)) + .then(() => getWallet(account, info.cryptoCode)) .then(wallet => { return wallet.createAddress() .then(result => { @@ -66,10 +82,10 @@ function newAddress (account, info) { // If a label was provided, set the label if (info.label) { return wallet.updateAddress({ address: address, label: info.label }) - .then(() => address) + .then(() => getCashAddress(address, info.cryptoCode)) } - return address + return getCashAddress(address, info.cryptoCode) }) }) } @@ -77,7 +93,7 @@ function newAddress (account, info) { function getStatus (account, toAddress, requested, cryptoCode) { const bitgo = buildBitgo(account) return checkCryptoCode(cryptoCode) - .then(() => bitgo.blockchain().getAddress({ address: toAddress })) + .then(() => bitgo.blockchain().getAddress({ address: getLegacyAddress(toAddress, cryptoCode) })) .then(rec => { if (rec.balance === 0) return { status: 'notSeen' } if (requested.gt(rec.balance)) return { status: 'insufficientFunds' } @@ -89,7 +105,7 @@ function getStatus (account, toAddress, requested, cryptoCode) { function newFunding (account, cryptoCode) { return checkCryptoCode(cryptoCode) .then(() => { - return getWallet(account) + return getWallet(account, cryptoCode) .then(wallet => { return wallet.createAddress() .then(result => { @@ -98,7 +114,7 @@ function newFunding (account, cryptoCode) { .then(() => ({ fundingPendingBalance: BN(wallet._wallet.balanceString), fundingConfirmedBalance: BN(wallet._wallet.confirmedBalanceString), - fundingAddress + fundingAddress: getCashAddress(fundingAddress, cryptoCode) })) }) }) diff --git a/package-lock.json b/package-lock.json index 19d12c21..a85e1d17 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1096,6 +1096,40 @@ } } }, + "bchaddrjs": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/bchaddrjs/-/bchaddrjs-0.3.0.tgz", + "integrity": "sha512-XtOie1INzvGsoXKzOh/CUQ4p98Z/X+MSOV1aqFZZXG8BdZofvlXwtwcNrJDlwdfOqCVCVcFBgLBkuXEOcIQ/0w==", + "requires": { + "bs58check": "^2.1.2", + "cashaddrjs": "^0.2.9" + }, + "dependencies": { + "bs58": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz", + "integrity": "sha1-vhYedsNU9veIrkBx9j806MTwpCo=", + "requires": { + "base-x": "^3.0.2" + } + }, + "bs58check": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/bs58check/-/bs58check-2.1.2.tgz", + "integrity": "sha512-0TS1jicxdU09dwJMNZtVAfzPi6Q6QeN0pM1Fkzrjn+XYHvzMKPU3pHVpva+769iNVSfIYWf7LJ6WR+BuuMf8cA==", + "requires": { + "bs58": "^4.0.0", + "create-hash": "^1.1.0", + "safe-buffer": "^5.1.2" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + } + } + }, "bcrypt-pbkdf": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz", @@ -1118,9 +1152,14 @@ "callsite": "1.0.0" } }, + "big-integer": { + "version": "1.6.40", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.40.tgz", + "integrity": "sha512-CjhtJp0BViLzP1ZkEnoywjgtFQXS2pomKjAJtIISTCnuHILkLcAXLdFLG/nxsHc4s9kJfc+82Xpg8WNyhfACzQ==" + }, "big.js": { "version": "3.1.3", - "resolved": "http://registry.npmjs.org/big.js/-/big.js-3.1.3.tgz", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-3.1.3.tgz", "integrity": "sha1-TK2iGTZS6zyp7I5VyQFWacmAaXg=" }, "bigi": { @@ -1475,7 +1514,7 @@ }, "ethereumjs-util": { "version": "4.4.1", - "resolved": "http://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-4.4.1.tgz", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-4.4.1.tgz", "integrity": "sha1-Yxa/vIoByHZ6eGIJKO1570JMP5I=", "optional": true, "requires": { @@ -1611,7 +1650,7 @@ }, "minimist": { "version": "0.2.0", - "resolved": "http://registry.npmjs.org/minimist/-/minimist-0.2.0.tgz", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.2.0.tgz", "integrity": "sha1-Tf/lJdriuGTGbC4jxicdev3s784=" }, "moment": { @@ -1980,7 +2019,7 @@ }, "buffer-equals": { "version": "1.0.4", - "resolved": "http://registry.npmjs.org/buffer-equals/-/buffer-equals-1.0.4.tgz", + "resolved": "https://registry.npmjs.org/buffer-equals/-/buffer-equals-1.0.4.tgz", "integrity": "sha1-A1O1T9B/2VZBcGca5vZrnPENJ/U=" }, "buffer-writer": { @@ -2137,6 +2176,14 @@ } } }, + "cashaddrjs": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/cashaddrjs/-/cashaddrjs-0.2.9.tgz", + "integrity": "sha512-DhJF4iAH0/RM3UjHDHKRxzs09YGL9px+oTyizzydanhC7jTyM2aJ+aLKA96vZGTTWayvvr2iDF2l13lpqXiRFg==", + "requires": { + "big-integer": "^1.6.34" + } + }, "chalk": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", @@ -3006,14 +3053,14 @@ "dependencies": { "domelementtype": { "version": "1.1.3", - "resolved": "http://registry.npmjs.org/domelementtype/-/domelementtype-1.1.3.tgz", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.1.3.tgz", "integrity": "sha1-vSh3PiZCiBrsUVRJJCmcXNgiGFs=" } } }, "domelementtype": { "version": "1.3.0", - "resolved": "http://registry.npmjs.org/domelementtype/-/domelementtype-1.3.0.tgz", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.0.tgz", "integrity": "sha1-sXrtguirWeUt2cGbF1bg/BhyBMI=" }, "domhandler": { @@ -3623,7 +3670,7 @@ "dependencies": { "doctrine": { "version": "1.5.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz", + "resolved": "http://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz", "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=", "dev": true, "requires": { @@ -3884,7 +3931,7 @@ "dependencies": { "ethereumjs-util": { "version": "4.5.0", - "resolved": "http://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-4.5.0.tgz", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-4.5.0.tgz", "integrity": "sha1-PpQosxfuvaPXJg2FT93alUsfG8Y=", "optional": true, "requires": { @@ -5911,7 +5958,7 @@ }, "http-proxy": { "version": "1.11.1", - "resolved": "http://registry.npmjs.org/http-proxy/-/http-proxy-1.11.1.tgz", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.11.1.tgz", "integrity": "sha1-cd9VdX6ALVjqgQ3yJEAZ3aBa6F0=", "requires": { "eventemitter3": "1.x.x", @@ -9433,7 +9480,7 @@ "dependencies": { "babel-runtime": { "version": "5.8.38", - "resolved": "http://registry.npmjs.org/babel-runtime/-/babel-runtime-5.8.38.tgz", + "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-5.8.38.tgz", "integrity": "sha1-HAsC62MxL18If/IEUIJ7QlydTBk=", "requires": { "core-js": "^1.0.0" @@ -9446,7 +9493,7 @@ }, "core-js": { "version": "1.2.7", - "resolved": "http://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz", "integrity": "sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY=" }, "elliptic": { @@ -9568,7 +9615,7 @@ }, "sanitize-html": { "version": "1.13.0", - "resolved": "http://registry.npmjs.org/sanitize-html/-/sanitize-html-1.13.0.tgz", + "resolved": "https://registry.npmjs.org/sanitize-html/-/sanitize-html-1.13.0.tgz", "integrity": "sha1-TuF8vsUWv+MvLOZoaladfmtPNjE=", "requires": { "htmlparser2": "^3.9.0", @@ -11195,7 +11242,7 @@ "dependencies": { "base-x": { "version": "1.1.0", - "resolved": "http://registry.npmjs.org/base-x/-/base-x-1.1.0.tgz", + "resolved": "https://registry.npmjs.org/base-x/-/base-x-1.1.0.tgz", "integrity": "sha1-QtPXF0dPnqAiB/bRqh9CaRPut6w=" } } diff --git a/package.json b/package.json index 2ec9ed0c..3a8ea9fe 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,7 @@ "@fczbkk/uuid4": "^3.0.0", "axios": "^0.16.1", "base-x": "^3.0.2", + "bchaddrjs": "^0.3.0", "bignumber.js": "^4.1.0", "bip39": "^2.3.1", "bitcoind-rpc": "^0.7.0", diff --git a/schemas/bitgo.json b/schemas/bitgo.json index 34e9c2df..eca20d26 100644 --- a/schemas/bitgo.json +++ b/schemas/bitgo.json @@ -11,16 +11,64 @@ "value": "" }, { - "code": "walletId", - "display": "Wallet ID", + "code": "BTCWalletId", + "display": "BTC Wallet ID", "fieldType": "string", "secret": false, "required": true, "value": "" }, { - "code": "walletPassphrase", - "display": "Wallet passphrase", + "code": "BTCWalletPassphrase", + "display": "BTC Wallet passphrase", + "fieldType": "password", + "secret": true, + "required": true, + "value": "" + }, + { + "code": "LTCWalletId", + "display": "LTC Wallet ID", + "fieldType": "string", + "secret": false, + "required": true, + "value": "" + }, + { + "code": "LTCWalletPassphrase", + "display": "LTC Wallet passphrase", + "fieldType": "password", + "secret": true, + "required": true, + "value": "" + }, + { + "code": "ZECWalletId", + "display": "ZEC Wallet ID", + "fieldType": "string", + "secret": false, + "required": true, + "value": "" + }, + { + "code": "ZECWalletPassphrase", + "display": "ZEC Wallet passphrase", + "fieldType": "password", + "secret": true, + "required": true, + "value": "" + }, + { + "code": "BCHWalletId", + "display": "BCH Wallet ID", + "fieldType": "string", + "secret": false, + "required": true, + "value": "" + }, + { + "code": "BCHWalletPassphrase", + "display": "BCH Wallet passphrase", "fieldType": "password", "secret": true, "required": true,