From eb33203d26c383c7677c925ec255380dd823ce51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Oliveira?= Date: Wed, 19 May 2021 13:31:19 +0100 Subject: [PATCH 01/11] feat: apply fee discount for outgoing tx --- lib/plugins/wallet/bitcoind/bitcoind.js | 26 ++++++- lib/wallet.js | 13 +++- migrations/1620954224627-add-fee-priority.js | 20 +++++ new-lamassu-admin/src/pages/Wallet/Wallet.js | 73 +++++++++++++++++++ .../src/pages/Wallet/Wallet.styles.js | 7 ++ 5 files changed, 136 insertions(+), 3 deletions(-) create mode 100644 migrations/1620954224627-add-fee-priority.js create mode 100644 new-lamassu-admin/src/pages/Wallet/Wallet.styles.js diff --git a/lib/plugins/wallet/bitcoind/bitcoind.js b/lib/plugins/wallet/bitcoind/bitcoind.js index a64e99c3..81258e5e 100644 --- a/lib/plugins/wallet/bitcoind/bitcoind.js +++ b/lib/plugins/wallet/bitcoind/bitcoind.js @@ -51,11 +51,32 @@ function balance (account, cryptoCode, settings, operatorId) { return accountBalance(cryptoCode) } -function sendCoins (account, tx, settings, operatorId) { +function estimateFee () { + return fetch('estimatesmartfee', [6, 'unset']) + .then(result => BN(result.feerate)) + .catch(() => {}) +} + +function calculateFeeDiscount (feeDiscount) { + // 0 makes bitcoind do automatic fee selection + const AUTOMATIC_FEE = 0 + if (!feeDiscount) return AUTOMATIC_FEE + return estimateFee() + .then(estimatedFee => { + if (!estimatedFee) return AUTOMATIC_FEE + const newFee = estimatedFee.times(feeDiscount) + if (newFee.lt(0.00001) || newFee.gt(0.1)) return AUTOMATIC_FEE + return newFee + }) +} + +function sendCoins (account, tx, settings, operatorId, feeDiscount) { const { toAddress, cryptoAtoms, cryptoCode } = tx const coins = cryptoAtoms.shiftedBy(-unitScale).toFixed(8) return checkCryptoCode(cryptoCode) + .then(() => calculateFeeDiscount(feeDiscount)) + .then(newFee => fetch('settxfee', [newFee])) .then(() => fetch('sendtoaddress', [toAddress, coins])) .then((txId) => fetch('gettransaction', [txId])) .then((res) => _.pick(['fee', 'txid'], res)) @@ -148,5 +169,6 @@ module.exports = { getStatus, newFunding, cryptoNetwork, - fetchRBF + fetchRBF, + estimateFee } diff --git a/lib/wallet.js b/lib/wallet.js index f5078e12..84480499 100644 --- a/lib/wallet.js +++ b/lib/wallet.js @@ -3,6 +3,7 @@ const mem = require('mem') const hkdf = require('futoin-hkdf') const configManager = require('./new-config-manager') +const { loadLatestConfig } = require('./new-settings-loader') const pify = require('pify') const fs = pify(require('fs')) @@ -59,6 +60,17 @@ function _balance (settings, cryptoCode) { function sendCoins (settings, tx) { return fetchWallet(settings, tx.cryptoCode) .then(r => { + if (tx.cryptoCode === 'BTC') { + return loadLatestConfig() + .then(config => { + const feeDiscount = config.wallets_BTC_feeDiscount + return r.wallet.sendCoins(r.account, tx, settings, r.operatorId, feeDiscount) + }) + .then(res => { + mem.clear(module.exports.balance) + return res + }) + } return r.wallet.sendCoins(r.account, tx, settings, r.operatorId) .then(res => { mem.clear(module.exports.balance) @@ -69,7 +81,6 @@ function sendCoins (settings, tx) { if (err.name === INSUFFICIENT_FUNDS_NAME) { throw httpError(INSUFFICIENT_FUNDS_NAME, INSUFFICIENT_FUNDS_CODE) } - throw err }) } diff --git a/migrations/1620954224627-add-fee-priority.js b/migrations/1620954224627-add-fee-priority.js new file mode 100644 index 00000000..ebd630b1 --- /dev/null +++ b/migrations/1620954224627-add-fee-priority.js @@ -0,0 +1,20 @@ +const { saveConfig, loadLatest } = require('../lib/new-settings-loader') + +exports.up = function (next) { + const newConfig = { + wallets_BTC_feeDiscount: 'Default' + } + return loadLatest() + .then(config => { + return saveConfig(newConfig) + .then(() => next()) + .catch(err => { + console.log(err.message) + return next(err) + }) + }) +} + +module.exports.down = function (next) { + next() +} \ No newline at end of file diff --git a/new-lamassu-admin/src/pages/Wallet/Wallet.js b/new-lamassu-admin/src/pages/Wallet/Wallet.js index 386cc1a6..4fbe5754 100644 --- a/new-lamassu-admin/src/pages/Wallet/Wallet.js +++ b/new-lamassu-admin/src/pages/Wallet/Wallet.js @@ -1,18 +1,24 @@ import { useQuery, useMutation } from '@apollo/react-hooks' +import { DialogActions, makeStyles, Box } from '@material-ui/core' import gql from 'graphql-tag' import * as R from 'ramda' import React, { useState } from 'react' import Modal from 'src/components/Modal' +import { IconButton, Button } from 'src/components/buttons' import { NamespacedTable as EditableTable } from 'src/components/editableTable' +import { RadioGroup } from 'src/components/inputs' import TitleSection from 'src/components/layout/TitleSection' +import { P, Label1 } from 'src/components/typography' import FormRenderer from 'src/pages/Services/FormRenderer' import schemas from 'src/pages/Services/schemas' +import { ReactComponent as EditIcon } from 'src/styling/icons/action/edit/enabled.svg' import { ReactComponent as ReverseSettingsIcon } from 'src/styling/icons/circle buttons/settings/white.svg' import { ReactComponent as SettingsIcon } from 'src/styling/icons/circle buttons/settings/zodiac.svg' import { fromNamespace, toNamespace } from 'src/utils/config' import AdvancedWallet from './AdvancedWallet' +import styles from './Wallet.styles.js' import Wizard from './Wizard' import { WalletSchema, getElements } from './helper' @@ -46,9 +52,15 @@ const GET_INFO = gql` } } ` + const LOCALE = 'locale' +const useStyles = makeStyles(styles) + const Wallet = ({ name: SCREEN_KEY }) => { + const classes = useStyles() + const [editingFeeDiscount, setEditingFeeDiscount] = useState(null) + const [selectedDiscount, setSelectedDiscount] = useState(null) const [editingSchema, setEditingSchema] = useState(null) const [onChangeFunction, setOnChangeFunction] = useState(null) const [wizard, setWizard] = useState(false) @@ -104,6 +116,25 @@ const Wallet = ({ name: SCREEN_KEY }) => { return it }) + const saveFeeDiscount = rawConfig => { + const config = toNamespace(SCREEN_KEY)(rawConfig) + setEditingFeeDiscount(false) + return saveConfig({ variables: { config } }) + } + + const handleRadioButtons = evt => { + const selectedDiscount = R.path(['target', 'value'])(evt) + setSelectedDiscount(selectedDiscount) + } + + const radioButtonOptions = [ + { display: '-20%', code: '-20' }, + { display: 'Default', code: 'Default' }, + { display: '+20%', code: '+20' }, + { display: '+40%', code: '+40' }, + { display: '+60%', code: '+60' } + ] + return ( <> { /> {!advancedSettings && ( <> + + Fee discount + +

{selectedDiscount}

+ setEditingFeeDiscount(true)} + className={classes.button}> + + +
+
{ )} {advancedSettings && } + {editingFeeDiscount && ( + setEditingFeeDiscount(null)} + open={true}> +

+ Set a priority level for your outgoing BTC transactions, selecting a + percentage off of the fee estimate your wallet uses. +

+ + + + +
+ )} ) } diff --git a/new-lamassu-admin/src/pages/Wallet/Wallet.styles.js b/new-lamassu-admin/src/pages/Wallet/Wallet.styles.js new file mode 100644 index 00000000..ca6dfc95 --- /dev/null +++ b/new-lamassu-admin/src/pages/Wallet/Wallet.styles.js @@ -0,0 +1,7 @@ +export default { + tableWidth: { + display: 'flex', + alignItems: 'center', + marginRight: 90 + } +} From d83825eedf6b646f5b52873151c5596bfa492835 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Oliveira?= Date: Thu, 20 May 2021 23:11:56 +0100 Subject: [PATCH 02/11] refactor: remove console log and improve BN usage --- migrations/1620954224627-add-fee-priority.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/migrations/1620954224627-add-fee-priority.js b/migrations/1620954224627-add-fee-priority.js index ebd630b1..37cce0e9 100644 --- a/migrations/1620954224627-add-fee-priority.js +++ b/migrations/1620954224627-add-fee-priority.js @@ -7,9 +7,8 @@ exports.up = function (next) { return loadLatest() .then(config => { return saveConfig(newConfig) - .then(() => next()) + .then(next) .catch(err => { - console.log(err.message) return next(err) }) }) @@ -17,4 +16,4 @@ exports.up = function (next) { module.exports.down = function (next) { next() -} \ No newline at end of file +} From 6a35e6bdeeb55c4eb2c592834a1083b3be1fb515 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Oliveira?= Date: Fri, 21 May 2021 21:47:04 +0100 Subject: [PATCH 03/11] fix: fee percentage discounts --- new-lamassu-admin/src/pages/Wallet/Wallet.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/new-lamassu-admin/src/pages/Wallet/Wallet.js b/new-lamassu-admin/src/pages/Wallet/Wallet.js index 4fbe5754..41f26b37 100644 --- a/new-lamassu-admin/src/pages/Wallet/Wallet.js +++ b/new-lamassu-admin/src/pages/Wallet/Wallet.js @@ -128,11 +128,11 @@ const Wallet = ({ name: SCREEN_KEY }) => { } const radioButtonOptions = [ - { display: '-20%', code: '-20' }, - { display: 'Default', code: 'Default' }, - { display: '+20%', code: '+20' }, - { display: '+40%', code: '+40' }, - { display: '+60%', code: '+60' } + { display: '+20%', code: '1.2' }, + { display: 'Default', code: '1' }, + { display: '-20%', code: '0.8' }, + { display: '-40%', code: '0.6' }, + { display: '-60%', code: '0.4' } ] return ( From 6c253ec4cfe628c381474ceaff459440c90d5656 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Oliveira?= Date: Fri, 21 May 2021 21:48:57 +0100 Subject: [PATCH 04/11] refactor: improve fee calculationreadability --- migrations/1620954224627-add-fee-priority.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/migrations/1620954224627-add-fee-priority.js b/migrations/1620954224627-add-fee-priority.js index 37cce0e9..19c1aef7 100644 --- a/migrations/1620954224627-add-fee-priority.js +++ b/migrations/1620954224627-add-fee-priority.js @@ -2,7 +2,7 @@ const { saveConfig, loadLatest } = require('../lib/new-settings-loader') exports.up = function (next) { const newConfig = { - wallets_BTC_feeDiscount: 'Default' + wallets_BTC_feeDiscount: '1' } return loadLatest() .then(config => { From d6771840bdd5a3d55a3c8b8236b3ff27733fc720 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Oliveira?= Date: Wed, 26 May 2021 15:52:03 +0100 Subject: [PATCH 05/11] refactor: improve promise handling --- migrations/1620954224627-add-fee-priority.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/migrations/1620954224627-add-fee-priority.js b/migrations/1620954224627-add-fee-priority.js index 19c1aef7..332f22ea 100644 --- a/migrations/1620954224627-add-fee-priority.js +++ b/migrations/1620954224627-add-fee-priority.js @@ -7,10 +7,10 @@ exports.up = function (next) { return loadLatest() .then(config => { return saveConfig(newConfig) - .then(next) - .catch(err => { - return next(err) - }) + }) + .then(next) + .catch(err => { + return next(err) }) } From 32f79bc450be07bf180c5474542780d43d06d713 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Oliveira?= Date: Fri, 24 Sep 2021 18:48:04 +0100 Subject: [PATCH 06/11] feat: migrate fee discount config field for each installed coin --- lib/wallet.js | 17 +++++------------ migrations/1620954224627-add-fee-priority.js | 8 +++++--- new-lamassu-admin/src/pages/Wallet/Wallet.js | 2 +- 3 files changed, 11 insertions(+), 16 deletions(-) diff --git a/lib/wallet.js b/lib/wallet.js index 84480499..4b090cf6 100644 --- a/lib/wallet.js +++ b/lib/wallet.js @@ -60,18 +60,11 @@ function _balance (settings, cryptoCode) { function sendCoins (settings, tx) { return fetchWallet(settings, tx.cryptoCode) .then(r => { - if (tx.cryptoCode === 'BTC') { - return loadLatestConfig() - .then(config => { - const feeDiscount = config.wallets_BTC_feeDiscount - return r.wallet.sendCoins(r.account, tx, settings, r.operatorId, feeDiscount) - }) - .then(res => { - mem.clear(module.exports.balance) - return res - }) - } - return r.wallet.sendCoins(r.account, tx, settings, r.operatorId) + return loadLatestConfig() + .then(config => { + const feeMultiplier = config[`wallets_${tx.cryptoCode}_feeMultiplier`] + return r.wallet.sendCoins(r.account, tx, settings, r.operatorId, feeMultiplier) + }) .then(res => { mem.clear(module.exports.balance) return res diff --git a/migrations/1620954224627-add-fee-priority.js b/migrations/1620954224627-add-fee-priority.js index 332f22ea..e1a46496 100644 --- a/migrations/1620954224627-add-fee-priority.js +++ b/migrations/1620954224627-add-fee-priority.js @@ -1,11 +1,13 @@ +const _ = require('lodash/fp') const { saveConfig, loadLatest } = require('../lib/new-settings-loader') +const { getCryptosFromWalletNamespace } = require('../lib/new-config-manager') exports.up = function (next) { - const newConfig = { - wallets_BTC_feeDiscount: '1' - } + const newConfig = {} return loadLatest() .then(config => { + const coins = getCryptosFromWalletNamespace(config) + _.map(coin => { newConfig[`wallets_${coin}_feeMultiplier`] = '1' }, coins) return saveConfig(newConfig) }) .then(next) diff --git a/new-lamassu-admin/src/pages/Wallet/Wallet.js b/new-lamassu-admin/src/pages/Wallet/Wallet.js index 41f26b37..57a51644 100644 --- a/new-lamassu-admin/src/pages/Wallet/Wallet.js +++ b/new-lamassu-admin/src/pages/Wallet/Wallet.js @@ -227,7 +227,7 @@ const Wallet = ({ name: SCREEN_KEY }) => { From 8c948d08c720e4b79e50283ee312492423315483 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Oliveira?= Date: Fri, 24 Sep 2021 19:32:42 +0100 Subject: [PATCH 07/11] fix: rename argument for fee discount calculation --- lib/plugins/wallet/bitcoind/bitcoind.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/plugins/wallet/bitcoind/bitcoind.js b/lib/plugins/wallet/bitcoind/bitcoind.js index 81258e5e..7bd866e6 100644 --- a/lib/plugins/wallet/bitcoind/bitcoind.js +++ b/lib/plugins/wallet/bitcoind/bitcoind.js @@ -57,25 +57,25 @@ function estimateFee () { .catch(() => {}) } -function calculateFeeDiscount (feeDiscount) { +function calculateFeeDiscount (feeMultiplier) { // 0 makes bitcoind do automatic fee selection const AUTOMATIC_FEE = 0 - if (!feeDiscount) return AUTOMATIC_FEE + if (!feeMultiplier) return AUTOMATIC_FEE return estimateFee() .then(estimatedFee => { if (!estimatedFee) return AUTOMATIC_FEE - const newFee = estimatedFee.times(feeDiscount) + const newFee = estimatedFee.times(feeMultiplier) if (newFee.lt(0.00001) || newFee.gt(0.1)) return AUTOMATIC_FEE return newFee }) } -function sendCoins (account, tx, settings, operatorId, feeDiscount) { +function sendCoins (account, tx, settings, operatorId, feeMultiplier) { const { toAddress, cryptoAtoms, cryptoCode } = tx const coins = cryptoAtoms.shiftedBy(-unitScale).toFixed(8) return checkCryptoCode(cryptoCode) - .then(() => calculateFeeDiscount(feeDiscount)) + .then(() => calculateFeeDiscount(feeMultiplier)) .then(newFee => fetch('settxfee', [newFee])) .then(() => fetch('sendtoaddress', [toAddress, coins])) .then((txId) => fetch('gettransaction', [txId])) From c200b0824ab8110999e4adee83549ed158d77995 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Oliveira?= Date: Thu, 28 Oct 2021 21:12:52 +0100 Subject: [PATCH 08/11] fix: remove unnecessary config load and fee calculation --- lib/plugins/wallet/bitcoind/bitcoind.js | 2 +- lib/wallet.js | 7 ++----- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/lib/plugins/wallet/bitcoind/bitcoind.js b/lib/plugins/wallet/bitcoind/bitcoind.js index 7bd866e6..7166f769 100644 --- a/lib/plugins/wallet/bitcoind/bitcoind.js +++ b/lib/plugins/wallet/bitcoind/bitcoind.js @@ -60,7 +60,7 @@ function estimateFee () { function calculateFeeDiscount (feeMultiplier) { // 0 makes bitcoind do automatic fee selection const AUTOMATIC_FEE = 0 - if (!feeMultiplier) return AUTOMATIC_FEE + if (!feeMultiplier || feeMultiplier.eq(1)) return AUTOMATIC_FEE return estimateFee() .then(estimatedFee => { if (!estimatedFee) return AUTOMATIC_FEE diff --git a/lib/wallet.js b/lib/wallet.js index 4b090cf6..9391addb 100644 --- a/lib/wallet.js +++ b/lib/wallet.js @@ -60,11 +60,8 @@ function _balance (settings, cryptoCode) { function sendCoins (settings, tx) { return fetchWallet(settings, tx.cryptoCode) .then(r => { - return loadLatestConfig() - .then(config => { - const feeMultiplier = config[`wallets_${tx.cryptoCode}_feeMultiplier`] - return r.wallet.sendCoins(r.account, tx, settings, r.operatorId, feeMultiplier) - }) + const feeMultiplier = settings[`wallets_${tx.cryptoCode}_feeMultiplier`] + return r.wallet.sendCoins(r.account, tx, settings, r.operatorId, feeMultiplier) .then(res => { mem.clear(module.exports.balance) return res From 82490145cd458c2bcc9e56cebee554f8cab0bc3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Oliveira?= Date: Tue, 16 Nov 2021 18:49:00 +0000 Subject: [PATCH 09/11] fix: fee discount selector alignment --- new-lamassu-admin/src/pages/Wallet/Wallet.js | 53 +++++++++---------- .../src/pages/Wallet/Wallet.styles.js | 13 ++++- 2 files changed, 37 insertions(+), 29 deletions(-) diff --git a/new-lamassu-admin/src/pages/Wallet/Wallet.js b/new-lamassu-admin/src/pages/Wallet/Wallet.js index 57a51644..b80ea805 100644 --- a/new-lamassu-admin/src/pages/Wallet/Wallet.js +++ b/new-lamassu-admin/src/pages/Wallet/Wallet.js @@ -137,32 +137,32 @@ const Wallet = ({ name: SCREEN_KEY }) => { return ( <> - +
+ + + Fee discount + +

{selectedDiscount}

+ setEditingFeeDiscount(true)}> + + +
+
+
{!advancedSettings && ( <> - - Fee discount - -

{selectedDiscount}

- setEditingFeeDiscount(true)} - className={classes.button}> - - -
-
{ width={478} handleClose={() => setEditingFeeDiscount(null)} open={true}> -

+

Set a priority level for your outgoing BTC transactions, selecting a percentage off of the fee estimate your wallet uses.

@@ -222,9 +222,8 @@ const Wallet = ({ name: SCREEN_KEY }) => { value={selectedDiscount} options={radioButtonOptions} onChange={handleRadioButtons} - className={classes.radioButtons} /> - +