Merge remote-tracking branch 'origin/release-8.1' into release-8.6
This commit is contained in:
commit
cd9c8aaba1
31 changed files with 652 additions and 160 deletions
|
|
@ -23,10 +23,8 @@ const PLUGINS = {
|
|||
BTC: require('./bitcoin.js'),
|
||||
BCH: require('./bitcoincash.js'),
|
||||
DASH: require('./dash.js'),
|
||||
ETH: require('./ethereum.js'),
|
||||
LTC: require('./litecoin.js'),
|
||||
XMR: require('./monero.js'),
|
||||
ZEC: require('./zcash.js')
|
||||
XMR: require('./monero.js')
|
||||
}
|
||||
|
||||
const BLOCKCHAIN_DIR = process.env.BLOCKCHAIN_DIR
|
||||
|
|
@ -207,10 +205,6 @@ function isInstalled (crypto) {
|
|||
|
||||
function isDisabled (crypto) {
|
||||
switch (crypto.cryptoCode) {
|
||||
case 'ETH':
|
||||
return 'Use admin\'s Infura plugin'
|
||||
case 'ZEC':
|
||||
return 'Use admin\'s BitGo plugin'
|
||||
case 'XMR':
|
||||
return isInstalled(crypto) && 'Installed' || isInstalled(_.find(it => it.code === 'zcash', cryptos)) && 'Insufficient resources. Contact support.'
|
||||
default:
|
||||
|
|
@ -220,11 +214,10 @@ function isDisabled (crypto) {
|
|||
|
||||
function run () {
|
||||
const choices = _.flow([
|
||||
_.filter(c => c.type !== 'erc-20'),
|
||||
_.filter(c => !c.hideFromInstall),
|
||||
_.map(c => {
|
||||
const name = c.code === 'ethereum' ? 'Ethereum and/or USDT' : c.display
|
||||
return {
|
||||
name,
|
||||
name: c.display,
|
||||
value: c.code,
|
||||
checked: isInstalled(c),
|
||||
disabled: isDisabled(c)
|
||||
|
|
|
|||
|
|
@ -89,6 +89,7 @@ const staticConfig = ({ currentConfigVersion, deviceId, deviceName, pq, settings
|
|||
'cashInFee',
|
||||
'cashOutCommission',
|
||||
'cryptoCode',
|
||||
'cryptoCodeDisplay',
|
||||
'cryptoNetwork',
|
||||
'cryptoUnits',
|
||||
'display',
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ const { gql } = require('apollo-server-express')
|
|||
module.exports = gql`
|
||||
type Coin {
|
||||
cryptoCode: String!
|
||||
cryptoCodeDisplay: String!
|
||||
display: String!
|
||||
minimumTx: String!
|
||||
cashInFee: String!
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ const _ = require('lodash/fp')
|
|||
|
||||
const { ALL } = require('../../plugins/common/ccxt')
|
||||
|
||||
const { BTC, BCH, DASH, ETH, LTC, USDT, ZEC, XMR } = COINS
|
||||
const { BTC, BCH, DASH, ETH, LTC, USDT, ZEC, XMR, TRX, USDT_TRON } = COINS
|
||||
const { bitpay, coinbase, itbit, bitstamp, kraken, binanceus, cex, binance } = ALL
|
||||
|
||||
const TICKER = 'ticker'
|
||||
|
|
@ -29,6 +29,7 @@ const ALL_ACCOUNTS = [
|
|||
{ code: 'bitcoind', display: 'bitcoind', class: WALLET, cryptos: [BTC] },
|
||||
{ code: 'no-layer2', display: 'No Layer 2', class: LAYER_2, cryptos: ALL_CRYPTOS },
|
||||
{ code: 'infura', display: 'Infura', class: WALLET, cryptos: [ETH, USDT] },
|
||||
{ code: 'trongrid', display: 'Trongrid', class: WALLET, cryptos: [TRX, USDT_TRON] },
|
||||
{ code: 'geth', display: 'geth (deprecated)', class: WALLET, cryptos: [ETH, USDT] },
|
||||
{ code: 'zcashd', display: 'zcashd', class: WALLET, cryptos: [ZEC] },
|
||||
{ code: 'litecoind', display: 'litecoind', class: WALLET, cryptos: [LTC] },
|
||||
|
|
|
|||
|
|
@ -34,7 +34,8 @@ const mapLanguage = lang => {
|
|||
const massageCryptos = cryptos => {
|
||||
const convert = crypto => ({
|
||||
code: crypto['cryptoCode'],
|
||||
display: crypto['display']
|
||||
display: crypto['display'],
|
||||
codeDisplay: crypto['cryptoCodeDisplay'] ?? crypto['cryptoCode']
|
||||
})
|
||||
|
||||
return _.map(convert, cryptos)
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ const typeDef = gql`
|
|||
type CryptoCurrency {
|
||||
code: String!
|
||||
display: String!
|
||||
codeDisplay: String!
|
||||
}
|
||||
|
||||
type Query {
|
||||
|
|
|
|||
|
|
@ -312,6 +312,7 @@ function plugins (settings, deviceId) {
|
|||
|
||||
return {
|
||||
cryptoCode,
|
||||
cryptoCodeDisplay: cryptoRec.cryptoCodeDisplay ?? cryptoCode,
|
||||
display: cryptoRec.display,
|
||||
isCashInOnly: Boolean(cryptoRec.isCashinOnly),
|
||||
minimumTx: BN.max(minimumTx, cashInFee),
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ const bitpay = require('../ticker/bitpay')
|
|||
const binance = require('../exchange/binance')
|
||||
const logger = require('../../logger')
|
||||
|
||||
const { BTC, BCH, DASH, ETH, LTC, ZEC, USDT } = COINS
|
||||
const { BTC, BCH, DASH, ETH, LTC, ZEC, USDT, TRX, USDT_TRON } = COINS
|
||||
|
||||
const ALL = {
|
||||
cex: cex,
|
||||
|
|
@ -20,7 +20,7 @@ const ALL = {
|
|||
itbit: itbit,
|
||||
bitpay: bitpay,
|
||||
coinbase: {
|
||||
CRYPTO: [BTC, ETH, LTC, DASH, ZEC, BCH, USDT],
|
||||
CRYPTO: [BTC, ETH, LTC, DASH, ZEC, BCH, USDT, USDT_TRON, TRX],
|
||||
FIAT: 'ALL_CURRENCIES'
|
||||
},
|
||||
binance: binance
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@ const _ = require('lodash/fp')
|
|||
const { ORDER_TYPES } = require('./consts')
|
||||
|
||||
const ORDER_TYPE = ORDER_TYPES.MARKET
|
||||
const { BTC, BCH, DASH, ETH, LTC, ZEC, USDT } = COINS
|
||||
const CRYPTO = [BTC, ETH, LTC, DASH, ZEC, BCH, USDT]
|
||||
const { BTC, BCH, DASH, ETH, LTC, ZEC, USDT, USDT_TRON } = COINS
|
||||
const CRYPTO = [BTC, ETH, LTC, DASH, ZEC, BCH, USDT, USDT_TRON]
|
||||
const FIAT = ['USD']
|
||||
const REQUIRED_CONFIG_FIELDS = ['apiKey', 'privateKey']
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ const { ORDER_TYPES } = require('./consts')
|
|||
|
||||
const ORDER_TYPE = ORDER_TYPES.MARKET
|
||||
const { BTC, ETH, LTC, BCH, USDT } = COINS
|
||||
const CRYPTO = [BTC, ETH, LTC, BCH, USDT]
|
||||
const CRYPTO = [BTC, ETH, LTC, BCH, USDT ]
|
||||
const FIAT = ['USD', 'EUR']
|
||||
const AMOUNT_PRECISION = 8
|
||||
const REQUIRED_CONFIG_FIELDS = ['key', 'secret', 'clientId']
|
||||
|
|
|
|||
|
|
@ -9,8 +9,9 @@ const DEFAULT_PRICE_PRECISION = 2
|
|||
const DEFAULT_AMOUNT_PRECISION = 8
|
||||
|
||||
function trade (side, account, tradeEntry, exchangeName) {
|
||||
const { cryptoAtoms, fiatCode, cryptoCode, tradeId } = tradeEntry
|
||||
const { cryptoAtoms, fiatCode, _cryptoCode, tradeId } = tradeEntry
|
||||
try {
|
||||
const cryptoCode = coinUtils.getEquivalentCode(_cryptoCode)
|
||||
const exchangeConfig = ALL[exchangeName]
|
||||
if (!exchangeConfig) throw Error('Exchange configuration not found')
|
||||
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@ const _ = require('lodash/fp')
|
|||
const { ORDER_TYPES } = require('./consts')
|
||||
|
||||
const ORDER_TYPE = ORDER_TYPES.MARKET
|
||||
const { BTC, BCH, DASH, ETH, LTC, USDT } = COINS
|
||||
const CRYPTO = [BTC, ETH, LTC, DASH, BCH, USDT]
|
||||
const { BTC, BCH, DASH, ETH, LTC, USDT, TRX, USDT_TRON } = COINS
|
||||
const CRYPTO = [BTC, ETH, LTC, DASH, BCH, USDT, TRX, USDT_TRON]
|
||||
const FIAT = ['USD', 'EUR']
|
||||
const REQUIRED_CONFIG_FIELDS = ['apiKey', 'privateKey']
|
||||
|
||||
|
|
|
|||
|
|
@ -1,20 +0,0 @@
|
|||
const { COINS } = require('@lamassu/coins')
|
||||
const _ = require('lodash/fp')
|
||||
|
||||
const { ORDER_TYPES } = require('./consts')
|
||||
|
||||
const ORDER_TYPE = ORDER_TYPES.MARKET
|
||||
const { BTC, BCH, ETH, LTC, USDT } = COINS
|
||||
const CRYPTO = [BTC, ETH, LTC, BCH, USDT]
|
||||
const FIAT = ['USD']
|
||||
const REQUIRED_CONFIG_FIELDS = ['apiKey', 'privateKey']
|
||||
|
||||
const loadConfig = (account) => {
|
||||
const mapper = {
|
||||
'privateKey': 'secret'
|
||||
}
|
||||
const mapped = _.mapKeys(key => mapper[key] ? mapper[key] : key)(account)
|
||||
return { ...mapped, timeout: 3000 }
|
||||
}
|
||||
|
||||
module.exports = { loadConfig, REQUIRED_CONFIG_FIELDS, CRYPTO, FIAT, ORDER_TYPE }
|
||||
|
|
@ -4,8 +4,8 @@ const { ORDER_TYPES } = require('./consts')
|
|||
const { COINS } = require('@lamassu/coins')
|
||||
|
||||
const ORDER_TYPE = ORDER_TYPES.MARKET
|
||||
const { BTC, BCH, DASH, ETH, LTC, ZEC, XMR, USDT } = COINS
|
||||
const CRYPTO = [BTC, ETH, LTC, DASH, ZEC, BCH, XMR, USDT]
|
||||
const { BTC, BCH, DASH, ETH, LTC, ZEC, XMR, USDT, TRX, USDT_TRON } = COINS
|
||||
const CRYPTO = [BTC, ETH, LTC, DASH, ZEC, BCH, XMR, USDT, TRX, USDT_TRON]
|
||||
const FIAT = ['USD', 'EUR']
|
||||
const AMOUNT_PRECISION = 6
|
||||
const REQUIRED_CONFIG_FIELDS = ['apiKey', 'privateKey']
|
||||
|
|
|
|||
|
|
@ -16,8 +16,6 @@ const BN = require('../../../bn')
|
|||
const ABI = require('../../tokens')
|
||||
const logger = require('../../../logger')
|
||||
|
||||
exports.SUPPORTED_MODULES = ['wallet']
|
||||
|
||||
const paymentPrefixPath = "m/44'/60'/0'/0'"
|
||||
const defaultPrefixPath = "m/44'/60'/1'/0'"
|
||||
let lastUsedNonces = {}
|
||||
|
|
|
|||
|
|
@ -87,10 +87,6 @@ function sweep (account, txId, cryptoCode, hdIndex, settings, operatorId) {
|
|||
throw new E.NotImplementedError()
|
||||
}
|
||||
|
||||
function isStrictAddress (cryptoCode, toAddress, settings, operatorId) {
|
||||
throw new E.NotImplementedError()
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
balance,
|
||||
sendCoins,
|
||||
|
|
@ -99,5 +95,4 @@ module.exports = {
|
|||
getStatus,
|
||||
sweep,
|
||||
supportsHd: true,
|
||||
isStrictAddress
|
||||
}
|
||||
|
|
|
|||
191
lib/plugins/wallet/tron/base.js
Normal file
191
lib/plugins/wallet/tron/base.js
Normal file
|
|
@ -0,0 +1,191 @@
|
|||
const TronWeb = require('tronweb')
|
||||
const coins = require('@lamassu/coins')
|
||||
const { default: PQueue } = require('p-queue')
|
||||
|
||||
const BN = require('../../../bn')
|
||||
|
||||
let tronWeb = null
|
||||
|
||||
const DEFAULT_PREFIX_PATH = "m/44'/195'/0'/0"
|
||||
const PAYMENT_PREFIX_PATH = "m/44'/195'/1'/0"
|
||||
|
||||
const SWEEP_QUEUE = new PQueue({
|
||||
concurrency: 3,
|
||||
interval: 250,
|
||||
})
|
||||
|
||||
function checkCryptoCode (cryptoCode) {
|
||||
if (cryptoCode === 'TRX' || coins.utils.isTrc20Token(cryptoCode)) {
|
||||
return Promise.resolve(cryptoCode)
|
||||
}
|
||||
return Promise.reject(new Error('cryptoCode must be TRX'))
|
||||
}
|
||||
|
||||
function defaultWallet (account) {
|
||||
const mnemonic = account.mnemonic
|
||||
if (!mnemonic) throw new Error('No mnemonic seed!')
|
||||
|
||||
return TronWeb.fromMnemonic(mnemonic.replace(/[\r\n]/gm, ' ').trim(), `${DEFAULT_PREFIX_PATH}/0`)
|
||||
}
|
||||
|
||||
function paymentWallet (account, index) {
|
||||
const mnemonic = account.mnemonic
|
||||
if (!mnemonic) throw new Error('No mnemonic seed!')
|
||||
|
||||
return TronWeb.fromMnemonic(mnemonic.replace(/[\r\n]/gm, ' ').trim(), `${PAYMENT_PREFIX_PATH}/${index}`)
|
||||
}
|
||||
|
||||
function newAddress (account, info, tx, settings, operatorId) {
|
||||
const wallet = paymentWallet(account, info.hdIndex)
|
||||
return Promise.resolve(wallet.address)
|
||||
}
|
||||
|
||||
function defaultAddress (account) {
|
||||
return defaultWallet(account).address
|
||||
}
|
||||
|
||||
function balance (account, cryptoCode, settings, operatorId) {
|
||||
return checkCryptoCode(cryptoCode)
|
||||
.then(code => confirmedBalance(defaultAddress(account), code))
|
||||
}
|
||||
|
||||
const confirmedBalance = (address, cryptoCode) => _balance(address, cryptoCode)
|
||||
|
||||
const _balance = async (address, cryptoCode) => {
|
||||
if (coins.utils.isTrc20Token(cryptoCode)) {
|
||||
const contractAddress = coins.utils.getTrc20Token(cryptoCode).contractAddress
|
||||
const { abi } = await tronWeb.trx.getContract(contractAddress)
|
||||
const contract = tronWeb.contract(abi.entrys, contractAddress)
|
||||
|
||||
const balance = await contract.methods.balanceOf(address).call()
|
||||
return BN(balance.toString())
|
||||
}
|
||||
|
||||
const balance = await tronWeb.trx.getBalance(address)
|
||||
return balance ? BN(balance) : BN(0)
|
||||
}
|
||||
|
||||
const sendCoins = async (account, tx) => {
|
||||
const { toAddress, cryptoAtoms, cryptoCode } = tx
|
||||
const isTrc20Token = coins.utils.isTrc20Token(cryptoCode)
|
||||
|
||||
const txFunction = isTrc20Token ? generateTrc20Tx : generateTx
|
||||
const rawTx = await txFunction(toAddress, defaultWallet(account), cryptoAtoms.toString(), cryptoCode)
|
||||
|
||||
let response = null
|
||||
|
||||
try {
|
||||
response = await tronWeb.trx.sendRawTransaction(rawTx)
|
||||
if (!response.result) throw new Error(response.code)
|
||||
} catch (err) {
|
||||
// for some reason err here is just a string
|
||||
throw new Error(err)
|
||||
}
|
||||
|
||||
const transaction = response.transaction
|
||||
const txid = transaction.txID
|
||||
const transactionInfo = tronWeb.trx.getTransactionInfo(txid)
|
||||
|
||||
if (!transactionInfo || !transactionInfo.fee) return { txid }
|
||||
|
||||
const fee = new BN(transactionInfo.fee).decimalPlaces(0)
|
||||
return { txid, fee }
|
||||
}
|
||||
|
||||
const generateTrc20Tx = async (toAddress, wallet, amount, cryptoCode) => {
|
||||
const contractAddress = coins.utils.getTrc20Token(cryptoCode).contractAddress
|
||||
const functionSelector = 'transfer(address,uint256)'
|
||||
const parameters = [
|
||||
{ type: 'address', value: tronWeb.address.toHex(toAddress) },
|
||||
{ type: 'uint256', value: amount }
|
||||
]
|
||||
|
||||
const tx = await tronWeb.transactionBuilder.triggerSmartContract(contractAddress, functionSelector, {}, parameters, wallet.address)
|
||||
|
||||
return tronWeb.trx.sign(tx.transaction, wallet.privateKey.slice(2))
|
||||
}
|
||||
|
||||
const generateTx = async (toAddress, wallet, amount) => {
|
||||
const transaction = await tronWeb.transactionBuilder.sendTrx(toAddress, amount, wallet.address)
|
||||
|
||||
const privateKey = wallet.privateKey
|
||||
|
||||
// their api return a hex string starting with 0x but expects without it
|
||||
return tronWeb.trx.sign(transaction, privateKey.slice(2))
|
||||
}
|
||||
|
||||
function newFunding (account, cryptoCode) {
|
||||
return checkCryptoCode(cryptoCode)
|
||||
.then(code => {
|
||||
const fundingAddress = defaultAddress(account)
|
||||
|
||||
return confirmedBalance(fundingAddress, code)
|
||||
.then((balance) => ({
|
||||
fundingPendingBalance: BN(0),
|
||||
fundingConfirmedBalance: balance,
|
||||
fundingAddress
|
||||
}))
|
||||
})
|
||||
}
|
||||
|
||||
function sweep (account, txId, cryptoCode, hdIndex) {
|
||||
const wallet = paymentWallet(account, hdIndex)
|
||||
const fromAddress = wallet.address
|
||||
const isTrc20Token = coins.utils.isTrc20Token(cryptoCode)
|
||||
|
||||
const txFunction = isTrc20Token ? generateTrc20Tx : generateTx
|
||||
|
||||
return SWEEP_QUEUE.add(async () => {
|
||||
const r = await confirmedBalance(fromAddress, cryptoCode)
|
||||
if (r.eq(0)) return
|
||||
const signedTx = await txFunction(defaultAddress(account), wallet, r.toString(), cryptoCode)
|
||||
let response = null
|
||||
try {
|
||||
response = await tronWeb.trx.sendRawTransaction(signedTx)
|
||||
if (!response.result) throw new Error(response.code)
|
||||
} catch (err) {
|
||||
// for some reason err here is just a string
|
||||
throw new Error(err)
|
||||
}
|
||||
return response
|
||||
})
|
||||
}
|
||||
|
||||
function connect(account) {
|
||||
if (tronWeb != null) return
|
||||
const endpoint = account.endpoint
|
||||
const apiKey = account.apiKey
|
||||
tronWeb = new TronWeb({
|
||||
fullHost: endpoint,
|
||||
headers: { "TRON-PRO-API-KEY": apiKey },
|
||||
privateKey: '01'
|
||||
})
|
||||
}
|
||||
|
||||
function getStatus (account, tx, requested, settings, operatorId) {
|
||||
const { toAddress, cryptoCode } = tx
|
||||
return checkCryptoCode(cryptoCode)
|
||||
.then(code => confirmedBalance(toAddress, code))
|
||||
.then((confirmed) => {
|
||||
if (confirmed.gte(requested)) return { receivedCryptoAtoms: confirmed, status: 'confirmed' }
|
||||
if (confirmed.gt(0)) return { receivedCryptoAtoms: confirmed, status: 'insufficientFunds' }
|
||||
return { receivedCryptoAtoms: 0, status: 'notSeen' }
|
||||
})
|
||||
}
|
||||
|
||||
function getTxHashesByAddress (cryptoCode, address) {
|
||||
throw new Error(`Transactions hash retrieval is not implemented for this coin!`)
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
balance,
|
||||
sendCoins,
|
||||
newAddress,
|
||||
getStatus,
|
||||
sweep,
|
||||
defaultAddress,
|
||||
supportsHd: true,
|
||||
newFunding,
|
||||
connect,
|
||||
getTxHashesByAddress,
|
||||
}
|
||||
12
lib/plugins/wallet/trongrid/trongrid.js
Normal file
12
lib/plugins/wallet/trongrid/trongrid.js
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
const _ = require('lodash/fp')
|
||||
const base = require('../tron/base')
|
||||
|
||||
const NAME = 'trongrid'
|
||||
|
||||
function run (account) {
|
||||
const endpoint = 'https://api.trongrid.io'
|
||||
|
||||
base.connect({ ...account, endpoint })
|
||||
}
|
||||
|
||||
module.exports = _.merge(base, { NAME, run })
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
const { utils: coinUtils } = require('@lamassu/coins')
|
||||
const _ = require('lodash/fp')
|
||||
const mem = require('mem')
|
||||
const configManager = require('./new-config-manager')
|
||||
|
|
@ -35,10 +36,13 @@ function _getRates (settings, fiatCode, cryptoCode) {
|
|||
})
|
||||
}
|
||||
|
||||
function buildTicker (fiatCode, cryptoCode, tickerName) {
|
||||
function buildTicker (fiatCode, _cryptoCode, tickerName) {
|
||||
const fiatPeggedEquivalent = _.includes(fiatCode, _.keys(PEGGED_FIAT_CURRENCIES))
|
||||
? PEGGED_FIAT_CURRENCIES[fiatCode]
|
||||
: fiatCode
|
||||
|
||||
const cryptoCode = coinUtils.getEquivalentCode(_cryptoCode)
|
||||
|
||||
if (tickerName === 'bitpay') return bitpay.ticker(fiatPeggedEquivalent, cryptoCode)
|
||||
if (tickerName === 'mock-ticker') return mockTicker.ticker(fiatPeggedEquivalent, cryptoCode)
|
||||
return ccxt.ticker(fiatPeggedEquivalent, cryptoCode, tickerName)
|
||||
|
|
|
|||
|
|
@ -38,9 +38,10 @@ function fetchWallet (settings, cryptoCode) {
|
|||
const wallet = ph.load(ph.WALLET, plugin)
|
||||
const rawAccount = settings.accounts[plugin]
|
||||
const account = _.set('seed', computeSeed(masterSeed), rawAccount)
|
||||
if (_.isFunction(wallet.run)) wallet.run(account)
|
||||
const accountWithMnemonic = _.set('mnemonic', mnemonic, account)
|
||||
if (_.isFunction(wallet.run)) wallet.run(accountWithMnemonic)
|
||||
const operatorId = computeOperatorId(masterSeed)
|
||||
return { wallet, account, operatorId }
|
||||
return { wallet, account: accountWithMnemonic, operatorId }
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue