feat: tron cash-out

This commit is contained in:
Rafael Taranto 2023-09-11 12:13:14 +01:00
parent bb8b6ce354
commit 7f2963bc85
15 changed files with 795 additions and 9637 deletions

View file

@ -22,10 +22,8 @@ const PLUGINS = {
BTC: require('./bitcoin.js'), BTC: require('./bitcoin.js'),
BCH: require('./bitcoincash.js'), BCH: require('./bitcoincash.js'),
DASH: require('./dash.js'), DASH: require('./dash.js'),
ETH: require('./ethereum.js'),
LTC: require('./litecoin.js'), LTC: require('./litecoin.js'),
XMR: require('./monero.js'), XMR: require('./monero.js')
ZEC: require('./zcash.js')
} }
const BLOCKCHAIN_DIR = process.env.BLOCKCHAIN_DIR const BLOCKCHAIN_DIR = process.env.BLOCKCHAIN_DIR
@ -145,10 +143,6 @@ function isInstalled (crypto) {
function isDisabled (crypto) { function isDisabled (crypto) {
switch (crypto.cryptoCode) { switch (crypto.cryptoCode) {
case 'ETH':
return 'Use admin\'s Infura plugin'
case 'ZEC':
return 'Use admin\'s BitGo plugin'
case 'XMR': case 'XMR':
return isInstalled(crypto) && 'Installed' || isInstalled(_.find(it => it.code === 'zcash', cryptos)) && 'Insufficient resources. Contact support.' return isInstalled(crypto) && 'Installed' || isInstalled(_.find(it => it.code === 'zcash', cryptos)) && 'Insufficient resources. Contact support.'
default: default:
@ -158,11 +152,10 @@ function isDisabled (crypto) {
function run () { function run () {
const choices = _.flow([ const choices = _.flow([
_.filter(c => c.type !== 'erc-20'), _.filter(c => !c.hideFromInstall),
_.map(c => { _.map(c => {
const name = c.code === 'ethereum' ? 'Ethereum and/or USDT' : c.display
return { return {
name, name: c.display,
value: c.code, value: c.code,
checked: isInstalled(c), checked: isInstalled(c),
disabled: isDisabled(c) disabled: isDisabled(c)

View file

@ -51,7 +51,7 @@ const searchPathWrapper = (t, cb) => {
} }
const pgp = Pgp({ const pgp = Pgp({
// pgNative: true, pgNative: true,
schema: 'ERROR_SCHEMA', schema: 'ERROR_SCHEMA',
extend (obj, dbContext) { extend (obj, dbContext) {
obj.__taskEx = function (cb, throwOnError = true) { obj.__taskEx = function (cb, throwOnError = true) {

View file

@ -88,6 +88,7 @@ const staticConfig = ({ currentConfigVersion, deviceId, deviceName, pq, settings
'cashInFee', 'cashInFee',
'cashOutCommission', 'cashOutCommission',
'cryptoCode', 'cryptoCode',
'cryptoCodeDisplay',
'cryptoNetwork', 'cryptoNetwork',
'cryptoUnits', 'cryptoUnits',
'display', 'display',

View file

@ -2,6 +2,7 @@ const { gql } = require('apollo-server-express')
module.exports = gql` module.exports = gql`
type Coin { type Coin {
cryptoCode: String! cryptoCode: String!
cryptoCodeDisplay: String!
display: String! display: String!
minimumTx: String! minimumTx: String!
cashInFee: String! cashInFee: String!

View file

@ -34,7 +34,8 @@ const mapLanguage = lang => {
const massageCryptos = cryptos => { const massageCryptos = cryptos => {
const convert = crypto => ({ const convert = crypto => ({
code: crypto['cryptoCode'], code: crypto['cryptoCode'],
display: crypto['display'] display: crypto['display'],
codeDisplay: crypto['cryptoCodeDisplay'] ?? crypto['cryptoCode']
}) })
return _.map(convert, cryptos) return _.map(convert, cryptos)

View file

@ -9,6 +9,7 @@ const typeDef = gql`
type CryptoCurrency { type CryptoCurrency {
code: String! code: String!
display: String! display: String!
codeDisplay: String!
} }
type Query { type Query {

View file

@ -214,6 +214,7 @@ function plugins (settings, deviceId) {
return { return {
cryptoCode, cryptoCode,
cryptoCodeDisplay: cryptoRec.cryptoCodeDisplay ?? cryptoCode,
display: cryptoRec.display, display: cryptoRec.display,
isCashInOnly: Boolean(cryptoRec.isCashinOnly), isCashInOnly: Boolean(cryptoRec.isCashinOnly),
minimumTx: BN.max(minimumTx, cashInFee), minimumTx: BN.max(minimumTx, cashInFee),

View file

@ -1,11 +1,18 @@
const TronWeb = require('tronweb') const TronWeb = require('tronweb')
const coins = require('@lamassu/coins') const coins = require('@lamassu/coins')
const { default: PQueue } = require('p-queue')
const BN = require('../../../bn') const BN = require('../../../bn')
let tronWeb = null let tronWeb = null
const DEFAULT_PREFIX_PATH = "m/44'/195'/1'/0" 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) { function checkCryptoCode (cryptoCode) {
if (cryptoCode === 'TRX' || coins.utils.isTrc20Token(cryptoCode)) { if (cryptoCode === 'TRX' || coins.utils.isTrc20Token(cryptoCode)) {
@ -18,9 +25,19 @@ function defaultWallet (account) {
const mnemonic = account.mnemonic const mnemonic = account.mnemonic
if (!mnemonic) throw new Error('No mnemonic seed!') if (!mnemonic) throw new Error('No mnemonic seed!')
const key = TronWeb.fromMnemonic(mnemonic.replace(/[\r\n]/gm, ' ').trim(), `${DEFAULT_PREFIX_PATH}\/0`) return TronWeb.fromMnemonic(mnemonic.replace(/[\r\n]/gm, ' ').trim(), `${DEFAULT_PREFIX_PATH}\/0`)
}
return key 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) { function defaultAddress (account) {
@ -41,8 +58,6 @@ const _balance = async (address, cryptoCode) => {
const contract = tronWeb.contract(abi.entrys, contractAddress) const contract = tronWeb.contract(abi.entrys, contractAddress)
const balance = await contract.methods.balanceOf(address).call() const balance = await contract.methods.balanceOf(address).call()
// const decimals = await contract.methods.decimals().call()
// BN(balance.toString()).div(10 ** decimals).toString()
return BN(balance.toString()) return BN(balance.toString())
} }
@ -61,6 +76,7 @@ const sendCoins = async (account, tx) => {
try { try {
response = await tronWeb.trx.sendRawTransaction(rawTx) response = await tronWeb.trx.sendRawTransaction(rawTx)
if (!response.result) throw new Error(response.code)
} catch (err) { } catch (err) {
// for some reason err here is just a string // for some reason err here is just a string
throw new Error(err) throw new Error(err)
@ -112,6 +128,29 @@ function newFunding (account, cryptoCode) {
}) })
} }
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) { function connect(account) {
if (tronWeb != null) return if (tronWeb != null) return
const endpoint = account.endpoint const endpoint = account.endpoint
@ -133,15 +172,19 @@ function getStatus (account, tx, requested, settings, operatorId) {
}) })
} }
function getTxHashesByAddress (cryptoCode, address) {
throw new Error(`Transactions hash retrieval is not implemented for this coin!`)
}
module.exports = { module.exports = {
balance, balance,
sendCoins, sendCoins,
// newAddress, newAddress,
getStatus, getStatus,
// sweep, sweep,
defaultAddress, defaultAddress,
supportsHd: true, supportsHd: true,
newFunding, newFunding,
connect, connect,
// getTxHashesByAddress, getTxHashesByAddress,
} }

View file

@ -4,12 +4,11 @@ const base = require('../tron/base')
const NAME = 'trongrid' const NAME = 'trongrid'
function run (account) { function run (account) {
if (!account.endpoint) throw new Error('Need to configure API endpoint for Infura') if (!account.endpoint) throw new Error('Need to configure API endpoint for trongrid')
const endpoint = _.startsWith('https://')(account.endpoint) const endpoint = 'https://api.trongrid.io'
? account.endpoint : `https://${account.endpoint}`
base.connect(endpoint) base.connect({ ...account, endpoint })
} }
module.exports = _.merge(base, { NAME, run }) module.exports = _.merge(base, { NAME, run })

File diff suppressed because it is too large Load diff

View file

@ -4,7 +4,7 @@
"license": "unlicense", "license": "unlicense",
"dependencies": { "dependencies": {
"@apollo/react-hooks": "^3.1.3", "@apollo/react-hooks": "^3.1.3",
"@lamassu/coins": "v1.3.1-trx.1", "@lamassu/coins": "v1.3.1-trx.3",
"@material-ui/core": "4.11.0", "@material-ui/core": "4.11.0",
"@material-ui/icons": "4.9.1", "@material-ui/icons": "4.9.1",
"@material-ui/lab": "^4.0.0-alpha.56", "@material-ui/lab": "^4.0.0-alpha.56",

View file

@ -190,7 +190,7 @@ const Funding = () => {
{selected && !viewHistory && !selected.errorMsg && ( {selected && !viewHistory && !selected.errorMsg && (
<div className={classes.main}> <div className={classes.main}>
<div className={classes.firstSide}> <div className={classes.firstSide}>
<H3>Balance ({selected.display})</H3> <H3>Balance</H3>
<div className={classes.coinTotal}> <div className={classes.coinTotal}>
<Info1 inline noMargin> <Info1 inline noMargin>
{`${selected.confirmedBalance} ${selected.cryptoCode}`} {`${selected.confirmedBalance} ${selected.cryptoCode}`}

View file

@ -9,16 +9,10 @@ export default {
elements: [ elements: [
{ {
code: 'apiKey', code: 'apiKey',
display: 'Project ID', display: 'API Key',
component: TextInputFormik, component: TextInputFormik,
face: true, face: true,
long: true long: true
},
{
code: 'endpoint',
display: 'Endpoint',
component: TextInputFormik,
face: true
} }
], ],
getValidationSchema: account => { getValidationSchema: account => {

5187
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -9,7 +9,7 @@
"@ethereumjs/common": "^2.6.4", "@ethereumjs/common": "^2.6.4",
"@ethereumjs/tx": "^3.5.1", "@ethereumjs/tx": "^3.5.1",
"@graphql-tools/merge": "^6.2.5", "@graphql-tools/merge": "^6.2.5",
"@lamassu/coins": "v1.3.1-trx.1", "@lamassu/coins": "v1.3.1-trx.3",
"@simplewebauthn/server": "^3.0.0", "@simplewebauthn/server": "^3.0.0",
"@vonage/auth": "^1.5.0", "@vonage/auth": "^1.5.0",
"@vonage/sms": "^1.7.0", "@vonage/sms": "^1.7.0",