Skip to content
This repository has been archived by the owner on Mar 23, 2023. It is now read-only.

Commit

Permalink
feat: fetch remote fees from node api (#1822)
Browse files Browse the repository at this point in the history
  • Loading branch information
dav1app authored May 4, 2020
1 parent 7d54075 commit 104c0e3
Show file tree
Hide file tree
Showing 4 changed files with 105 additions and 86 deletions.
75 changes: 27 additions & 48 deletions __tests__/unit/components/Input/InputFee.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -275,27 +275,33 @@ describe('InputFee', () => {
})

describe('prepareFeeStatistics', () => {
describe('when the average fee of the network is more than the V1 fee', () => {
describe('when any fee of the network is more than the V1 max fee', () => {
beforeEach(() => {
mockNetwork.feeStatistics = [{
type: 0,
fees: {
avgFee: 1000 * 1e8,
maxFee: 0.03 * 1e8,
minFee: 0.0006 * 1e8
avgFee: 900 * 1e8,
maxFee: 1000 * 1e8,
minFee: 800 * 1e8
}
}]
})

it('should use the V1 fee as average always', () => {
it('should use the V1 max fee', () => {
const wrapper = mountComponent()

const maxV1fee = new BigNumber(wrapper.vm.maxV1fee * 1e-8)

expect(wrapper.vm.feeChoices.MAXIMUM).toBeInstanceOf(BigNumber)
expect(wrapper.vm.feeChoices.MAXIMUM).toEqual(maxV1fee)
expect(wrapper.vm.feeChoices.AVERAGE).toBeInstanceOf(BigNumber)
expect(wrapper.vm.feeChoices.AVERAGE.toString()).toEqual('0.1')
expect(wrapper.vm.feeChoices.AVERAGE).toEqual(maxV1fee)
expect(wrapper.vm.feeChoices.MINIMUM).toBeInstanceOf(BigNumber)
expect(wrapper.vm.feeChoices.MINIMUM).toEqual(maxV1fee)
})
})

describe('when the average fee of the network is less than the V1 fee', () => {
describe('when any fee of the network is less than the V1 max fee', () => {
beforeEach(() => {
mockNetwork.feeStatistics = [{
type: 0,
Expand All @@ -307,64 +313,37 @@ describe('InputFee', () => {
}]
})

it('should use it as average', () => {
it('should use it as the fee', () => {
const wrapper = mountComponent()

expect(wrapper.vm.feeChoices.MAXIMUM).toBeInstanceOf(BigNumber)
expect(wrapper.vm.feeChoices.MAXIMUM).toBeWithin(0.03, 0.03000001)
expect(wrapper.vm.feeChoices.AVERAGE).toBeInstanceOf(BigNumber)
expect(wrapper.vm.feeChoices.AVERAGE).toBeWithin(0.0048, 0.0048000001)
expect(wrapper.vm.feeChoices.AVERAGE).toBeWithin(0.0048, 0.00480001)
expect(wrapper.vm.feeChoices.MINIMUM).toBeInstanceOf(BigNumber)
expect(wrapper.vm.feeChoices.MINIMUM).toBeWithin(0.0006, 0.00060001)
})
})

describe('when the maximum fee of the network is more than the V1 fee', () => {
describe('when the network returns no statistics', () => {
beforeEach(() => {
mockNetwork.feeStatistics = [{
type: 0,
fees: {
avgFee: 0.0048 * 1e8,
maxFee: 1000 * 1e8,
minFee: 0.0006 * 1e8
}
}]
mockNetwork.feeStatistics = []
})

it('should use the V1 fee as maximum always', () => {
it('should use V1 max fee for maximum', () => {
const wrapper = mountComponent()

expect(wrapper.vm.feeChoices.MAXIMUM).toBeInstanceOf(BigNumber)
expect(wrapper.vm.feeChoices.MAXIMUM.toString()).toEqual('0.1')
})
})

describe('when the maximum fee of the network is less than the V1 fee', () => {
beforeEach(() => {
mockNetwork.feeStatistics = [{
type: 0,
fees: {
avgFee: 0.0048 * 1e8,
maxFee: 0.03 * 1e8,
minFee: 0.0006 * 1e8
}
}]
})

it('should use it as maximum', () => {
const wrapper = mountComponent()
const maxV1fee = (wrapper.vm.maxV1fee * 1e-8).toString()

expect(wrapper.vm.feeChoices.MAXIMUM).toBeInstanceOf(BigNumber)
expect(wrapper.vm.feeChoices.MAXIMUM).toBeWithin(0.03, 0.03000001)
})
})

describe('when the network returns no statistics', () => {
beforeEach(() => {
mockNetwork.feeStatistics = []
expect(wrapper.vm.feeChoices.MAXIMUM.toString()).toBe(maxV1fee)
})

it('should use it as maximum', () => {
it('should use the absolute minimum fee (0.00000001) for minimum', () => {
const wrapper = mountComponent()

expect(wrapper.vm.feeChoices.MAXIMUM).toBeInstanceOf(BigNumber)
expect(wrapper.vm.feeChoices.MAXIMUM.toString()).toBe('0.1')
expect(wrapper.vm.feeChoices.MINIMUM).toBeInstanceOf(BigNumber)
expect(wrapper.vm.feeChoices.MINIMUM.toString()).toBe('0.00000001')
})
})
})
Expand Down
17 changes: 10 additions & 7 deletions src/renderer/components/Input/InputFee.vue
Original file line number Diff line number Diff line change
Expand Up @@ -191,36 +191,39 @@ export default {
return {
avgFee: this.maxV1fee,
maxFee: this.maxV1fee
maxFee: this.maxV1fee,
minFee: 1
}
},
lastFee () {
return this.$store.getters['session/lastFeeByType'](this.transactionType, this.transactionGroup)
},
feeChoiceMin () {
return this.feeChoices.MINIMUM
return this.currency_subToUnit(1)
},
feeChoiceMax () {
return this.isAdvancedFee ? this.feeChoices.MAXIMUM.multipliedBy(10) : this.feeChoices.MAXIMUM
},
feeChoices () {
const { avgFee, maxFee } = this.feeStatistics
const { avgFee, maxFee, minFee } = this.feeStatistics
// Even if the network provides average or maximum fees higher than V1, they will be corrected
// If any of the fees are higher than the maximum V1 fee, than use the maximum.
const average = this.currency_subToUnit(avgFee < this.maxV1fee ? avgFee : this.maxV1fee)
const minimum = this.currency_subToUnit(minFee < this.maxV1fee ? minFee : this.maxV1fee)
const maximum = this.currency_subToUnit(maxFee < this.maxV1fee ? maxFee : this.maxV1fee)
const fees = {
MINIMUM: this.currency_subToUnit(1),
MINIMUM: minimum,
AVERAGE: average,
MAXIMUM: this.currency_subToUnit(maxFee < this.maxV1fee ? maxFee : this.maxV1fee),
MAXIMUM: maximum,
INPUT: average,
ADVANCED: average
}
return this.lastFee ? Object.assign({}, { LAST: this.currency_subToUnit(this.lastFee) }, fees) : fees
},
minimumError () {
const min = this.feeChoices.MINIMUM
const min = this.feeChoiceMin
const fee = this.currency_format(min, { currency: this.currency, currencyDisplay: 'code' })
return this.$t('INPUT_FEE.ERROR.LESS_THAN_MINIMUM', { fee })
},
Expand Down
89 changes: 59 additions & 30 deletions src/renderer/services/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import TransactionService from '@/services/transaction'
import { TransactionBuilderService } from './crypto/transaction-builder.service'
import { TransactionSigner } from './crypto/transaction-signer'
import BigNumber from '@/plugins/bignumber'
import { cammelToUpperSnake } from '@/utils'

export default class ClientService {
/**
Expand Down Expand Up @@ -73,43 +74,71 @@ export default class ClientService {
}

static async fetchFeeStatistics (server, timeout) {
let data

try {
const { body } = await ClientService.newConnection(server, timeout)
const response = await ClientService.newConnection(server, timeout)
.api('node')
.fees(7)

if (!body.data[0]) {
return Object.values(TRANSACTION_GROUPS)
.filter(group => !!body.data[group])
.reduce((accumulator, group) => {
accumulator[group] = Object.keys(body.data[group]).map(key => {
const fee = body.data[group][key]

return {
type: TRANSACTION_TYPES[`GROUP_${group}`][key.toUpperCase()],
fees: {
minFee: Number(fee.min),
maxFee: Number(fee.max),
avgFee: Number(fee.avg)
}
}
})

return accumulator
}, {})
}

return body.data.map(fee => ({
type: Number(fee.type),
fees: {
minFee: Number(fee.min),
maxFee: Number(fee.max),
avgFee: Number(fee.avg)
}
}))
data = response.body.data
} catch (error) {
return []
}

/*
The peer can send 2 types of response: an Array and and Object.
In case it sends an Object, the fees should be parsed according to the
transaction groups and types from @config.
*/

// Case it is an Object
if (!Array.isArray(data)) {
// Remove the groups that are not in the response
const groupsIds = Object.values(TRANSACTION_GROUPS).filter(groupId => !!data[groupId])

const parsedFees = groupsIds.reduce((accumulator, groupId) => {
const retrivedTypeNames = Object.keys(data[groupId])

// Parse the fees and add to accumulator
accumulator[groupId] = retrivedTypeNames.map(typeName => {
const fees = data[groupId][typeName]

/*
Notice that the types are in different format.
Response is in cammelCase. Eg: 'bussinesUpdate'
@config is in UPPER_SNAKE_CASE. Eg: 'BUSSINES_UPDATE'
*/
const groupName = `GROUP_${groupId}`
const parsedTypeName = cammelToUpperSnake(typeName)

const type = TRANSACTION_TYPES[groupName][parsedTypeName]

return {
type,
fees: {
minFee: Number(fees.min),
maxFee: Number(fees.max),
avgFee: Number(fees.avg)
}
}
})

return accumulator
}, {})

return parsedFees
}

// Case the response is an Array
return data.map(fee => ({
type: Number(fee.type),
fees: {
minFee: Number(fee.min),
maxFee: Number(fee.max),
avgFee: Number(fee.avg)
}
}))
}

constructor () {
Expand Down
10 changes: 9 additions & 1 deletion src/renderer/utils/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,19 @@ const max = nums => {
}
}

/**
* Converts cammelCaseString to UPPER_CAMMEL_CASE_STRING.
* @param {string} string The cammelCaseString
* @returns {string} The UPPER_CAMMEL_CASE.
*/
const cammelToUpperSnake = string => string.split(/(?=[A-Z])/).join('_').toUpperCase()

export {
upperFirst,
capitalize,
isNil,
min,
max,
sortByProps
sortByProps,
cammelToUpperSnake
}

0 comments on commit 104c0e3

Please sign in to comment.