Skip to content

Commit

Permalink
Merge pull request #387 from EdgeApp/sam/maximumFeeRate
Browse files Browse the repository at this point in the history
Enable AltcoinJS fee check using currency's `maximumFeeRate` config
  • Loading branch information
samholmes authored Oct 23, 2023
2 parents 9da3d16 + d46b375 commit ca8bb99
Show file tree
Hide file tree
Showing 34 changed files with 199 additions and 127 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# edge-currency-plugins

## Unreleased

- changed: Re-enabled maximum fee rate checks from AltcoinJS.
- added: Configurable `maximumFeeRate` field for currency info's `EngineInfo` to change from default 5000 sats-per-vbyte

## v2.3.0 (2023-09-29)

- removed: BSV from BCH's split options
Expand Down
10 changes: 5 additions & 5 deletions src/common/fees/calcMinerFeePerByte.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
import { add, div, gte, lte, mul, round, sub } from 'biggystring'
import { EdgeSpendInfo } from 'edge-core-js/types'

import { SimpleFeeSettings } from '../plugin/types'
import { FeeInfo } from '../plugin/types'

type NetworkFeeOption = EdgeSpendInfo['networkFeeOption']

/**
* Calculate the sat/byte mining fee given an amount to spend and a SimpleFeeSettings object
* @param nativeAmount
* @param fees
* @param feeInfo
* @param networkFeeOption
* @param customNetworkFee
* @returns {string}
*/
export const calcMinerFeePerByte = (
nativeAmount: string,
fees: SimpleFeeSettings,
feeInfo: FeeInfo,
networkFeeOption?: NetworkFeeOption,
customNetworkFee?: string
): string => {
Expand All @@ -26,8 +26,8 @@ export const calcMinerFeePerByte = (
standardFeeHighFudgeFactor = '1',
standardFeeLowAmount,
standardFeeLowFudgeFactor = '1'
} = fees
let { highFee, lowFee, standardFeeHigh, standardFeeLow } = fees
} = feeInfo
let { highFee, lowFee, standardFeeHigh, standardFeeLow } = feeInfo

highFee = round(mul(highFee, highFeeFudgeFactor), 0)
lowFee = round(mul(lowFee, lowFeeFudgeFactor), 0)
Expand Down
51 changes: 22 additions & 29 deletions src/common/fees/makeFees.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as bs from 'biggystring'
import { asMaybe } from 'cleaners'
import { asMaybe, Cleaner } from 'cleaners'
import { Disklet } from 'disklet'
import {
EdgeIo,
Expand All @@ -11,13 +11,8 @@ import { makeMemlet, Memlet } from 'memlet'

import { removeUndefined } from '../../util/filterUndefined'
import { FEES_PATH, INFO_SERVER_URI } from '../constants'
import {
asSimpleFeeSettings,
PluginInfo,
SimpleFeeSettings
} from '../plugin/types'
import { asFeeInfo, FeeInfo, PluginInfo } from '../plugin/types'
import { calcMinerFeePerByte } from './calcMinerFeePerByte'
import { processInfoServerFees } from './processInfoServerFees'
import { processMempoolSpaceFees } from './processMempoolSpaceFees'

interface MakeFeesConfig extends Common {
Expand All @@ -35,17 +30,17 @@ export interface Fees {
stop: () => void
clearCache: () => Promise<void>
getRate: (edgeSpendInfo: EdgeSpendInfo) => Promise<string>
fees: SimpleFeeSettings
feeInfo: FeeInfo
}

export const makeFees = async (config: MakeFeesConfig): Promise<Fees> => {
const { disklet, pluginInfo, ...common } = config
const { currencyInfo, engineInfo } = pluginInfo

const memlet = makeMemlet(disklet)
const fees: SimpleFeeSettings = await fetchCachedFees(
const feeInfo: FeeInfo = await fetchCachedFees(
memlet,
engineInfo.simpleFeeSettings
engineInfo.defaultFeeInfo
)
// The last time the fees were updated
let timestamp = 0
Expand All @@ -59,20 +54,20 @@ export const makeFees = async (config: MakeFeesConfig): Promise<Fees> => {
mempoolSpaceFeeInfoServer: engineInfo.mempoolSpaceFeeInfoServer
})
const cleanedVendorFees = removeUndefined(vendorFees ?? {})
Object.assign(fees, cleanedVendorFees)
Object.assign(feeInfo, cleanedVendorFees)
timestamp = Date.now()

await cacheFees(memlet, fees)
await cacheFees(memlet, feeInfo)
}

return {
async start(): Promise<void> {
const edgeFees = await fetchFees({
...common,
uri: `${INFO_SERVER_URI}/v1/networkFees/${currencyInfo.pluginId}`,
processor: processInfoServerFees
cleaner: asMaybe(asFeeInfo, null)
})
Object.assign(fees, edgeFees)
Object.assign(feeInfo, edgeFees)
await updateVendorFees()
vendorIntervalId = setInterval(
// eslint-disable-next-line @typescript-eslint/no-misused-promises
Expand Down Expand Up @@ -109,32 +104,30 @@ export const makeFees = async (config: MakeFeesConfig): Promise<Fees> => {

const rate = calcMinerFeePerByte(
sumSpendTargets(spendTargets),
fees,
feeInfo,
networkFeeOption,
customNetworkFee[customFeeTemplate.key]
)
return rate
},

get fees(): SimpleFeeSettings {
return fees
get feeInfo(): FeeInfo {
return feeInfo
}
}
}

const fetchCachedFees = async (
memlet: Memlet,
fallback: SimpleFeeSettings
): Promise<SimpleFeeSettings> => {
fallback: FeeInfo
): Promise<FeeInfo> => {
const data = await memlet.getJson(FEES_PATH).catch(() => undefined)
const feeSettings = asMaybe(asSimpleFeeSettings, fallback)(data)
const feeSettings = asMaybe(asFeeInfo, fallback)(data)
return feeSettings
}

const cacheFees = async (
memlet: Memlet,
fees: SimpleFeeSettings
): Promise<void> => await memlet.setJson(FEES_PATH, fees)
const cacheFees = async (memlet: Memlet, feeInfo: FeeInfo): Promise<void> =>
await memlet.setJson(FEES_PATH, feeInfo)

const sumSpendTargets = (spendTargets: EdgeSpendTarget[]): string =>
spendTargets.reduce((amount, { nativeAmount }) => {
Expand All @@ -145,17 +138,17 @@ const sumSpendTargets = (spendTargets: EdgeSpendTarget[]): string =>

interface FetchFeesArgs<T> extends Common {
uri: string
processor: (fees: unknown) => T
cleaner: Cleaner<T>
}
const fetchFees = async <T>(args: FetchFeesArgs<T>): Promise<T | null> => {
const { uri, processor, io, log } = args
const { uri, cleaner, io, log } = args

try {
const response = await io.fetch(uri)
if (!response.ok) throw new Error(`Error fetching fees from ${uri}`)

const fees = await response.json()
return processor(fees)
return cleaner(fees)
} catch (err) {
log(err.message)
return null
Expand All @@ -168,12 +161,12 @@ interface FetchFeesFromVendorArgs extends Common {

const fetchFeesFromVendor = async (
args: FetchFeesFromVendorArgs
): Promise<Partial<SimpleFeeSettings>> => {
): Promise<Partial<FeeInfo>> => {
if (args.mempoolSpaceFeeInfoServer != null) {
const mempoolFees = await fetchFees({
...args,
uri: args.mempoolSpaceFeeInfoServer,
processor: processMempoolSpaceFees
cleaner: processMempoolSpaceFees
})
if (mempoolFees != null) {
return mempoolFees
Expand Down
16 changes: 0 additions & 16 deletions src/common/fees/processInfoServerFees.ts

This file was deleted.

21 changes: 17 additions & 4 deletions src/common/plugin/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
asMaybe,
asNumber,
asObject,
asOptional,
asString,
asValue,
Cleaner
Expand Down Expand Up @@ -61,7 +62,7 @@ export interface EngineInfo {
defaultFee: number
feeUpdateInterval: number
mempoolSpaceFeeInfoServer?: string
simpleFeeSettings: SimpleFeeSettings
defaultFeeInfo: FeeInfo
scriptTemplates?: ScriptTemplates
// Codec Cleaners
asBlockbookAddress?: Cleaner<string>
Expand Down Expand Up @@ -151,17 +152,29 @@ export const asFeeRates = asObject({
highFee: asString
})

export type SimpleFeeSettings = ReturnType<typeof asSimpleFeeSettings>
export const asSimpleFeeSettings = asObject({
export type FeeInfo = FeeRates & {
lowFeeFudgeFactor?: string
standardFeeLowFudgeFactor?: string
standardFeeHighFudgeFactor?: string
highFeeFudgeFactor?: string
standardFeeLowAmount: string
standardFeeHighAmount: string
maximumFeeRate?: string
}
export const asFeeInfo = asObject<FeeInfo>({
...asFeeRates.shape,

lowFeeFudgeFactor: asMaybe(asString),
standardFeeLowFudgeFactor: asMaybe(asString),
standardFeeHighFudgeFactor: asMaybe(asString),
highFeeFudgeFactor: asMaybe(asString),

// The amount of satoshis which will be charged the standardFeeLow
standardFeeLowAmount: asString,
standardFeeHighAmount: asString
// The amount of satoshis which will be charged the standardFeeHigh
standardFeeHighAmount: asString,
// A safe-guard for any potential software bugs:
maximumFeeRate: asOptional(asString)
})

export interface EngineConfig {
Expand Down
23 changes: 23 additions & 0 deletions src/common/plugin/util/maximumFeeRateCalculator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { div, mul, round } from 'biggystring'
import { EdgeCurrencyInfo } from 'edge-core-js/types'

/**
* Calculates a maximum fee rate of 1 USD per vByte given a USD exchange rate
* (usdPrice).
*/
export const maximumFeeRateCalculator = (
currencyInfo: EdgeCurrencyInfo,
usdPrice: number
): string | undefined => {
const { currencyCode, denominations } = currencyInfo
const primaryDenomination = denominations.find(
denom => denom.name === currencyCode
)
if (primaryDenomination == null)
throw new Error(`Missing primary currency denomination for ${currencyCode}`)

return round(
mul(div('1', String(usdPrice), 16), primaryDenomination.multiplier),
0
)
}
5 changes: 3 additions & 2 deletions src/common/utxobased/engine/makeUtxoEngine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -625,9 +625,10 @@ export async function makeUtxoEngine(
}
})()
const signedTx = await signTx({
psbtBase64: psbt.base64,
coin: coinInfo.name,
privateKeyEncodings: privateKeyEncodings
feeInfo: fees.feeInfo,
privateKeyEncodings: privateKeyEncodings,
psbtBase64: psbt.base64
})
transaction.txid = signedTx.id
transaction.signedTx = signedTx.hex
Expand Down
2 changes: 1 addition & 1 deletion src/common/utxobased/info/badcoin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ const engineInfo: EngineInfo = {
gapLimit: 10,
defaultFee: 500000,
feeUpdateInterval: 60000,
simpleFeeSettings: {
defaultFeeInfo: {
lowFeeFudgeFactor: undefined,
standardFeeLowFudgeFactor: undefined,
standardFeeHighFudgeFactor: undefined,
Expand Down
6 changes: 4 additions & 2 deletions src/common/utxobased/info/bitcoin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { EdgeCurrencyInfo } from 'edge-core-js/types'

import { IMAGE_SERVER_URL } from '../../constants'
import { CoinInfo, EngineInfo, PluginInfo } from '../../plugin/types'
import { maximumFeeRateCalculator } from '../../plugin/util/maximumFeeRateCalculator'
import { memoInfo } from './commonInfo'

const currencyInfo: EdgeCurrencyInfo = {
Expand Down Expand Up @@ -59,7 +60,7 @@ const engineInfo: EngineInfo = {
defaultFee: 1000,
feeUpdateInterval: 60000,
mempoolSpaceFeeInfoServer: 'https://mempool.space/api/v1/fees/recommended',
simpleFeeSettings: {
defaultFeeInfo: {
lowFeeFudgeFactor: undefined,
standardFeeLowFudgeFactor: undefined,
standardFeeHighFudgeFactor: undefined,
Expand All @@ -70,7 +71,8 @@ const engineInfo: EngineInfo = {
standardFeeLow: '50',
standardFeeHigh: '100',
standardFeeLowAmount: '173200',
standardFeeHighAmount: '8670000'
standardFeeHighAmount: '8670000',
maximumFeeRate: maximumFeeRateCalculator(currencyInfo, 27460.65)
}
}

Expand Down
6 changes: 4 additions & 2 deletions src/common/utxobased/info/bitcoincash.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { EdgeCurrencyInfo } from 'edge-core-js/types'

import { IMAGE_SERVER_URL } from '../../constants'
import { CoinInfo, EngineInfo, PluginInfo } from '../../plugin/types'
import { maximumFeeRateCalculator } from '../../plugin/util/maximumFeeRateCalculator'
import { memoInfo } from './commonInfo'
import { scriptTemplates } from './scriptTemplates/bitcoincashScriptTemplates'

Expand Down Expand Up @@ -60,7 +61,7 @@ const engineInfo: EngineInfo = {
gapLimit: 10,
defaultFee: 10000,
feeUpdateInterval: 60000,
simpleFeeSettings: {
defaultFeeInfo: {
lowFeeFudgeFactor: undefined,
standardFeeLowFudgeFactor: undefined,
standardFeeHighFudgeFactor: undefined,
Expand All @@ -71,7 +72,8 @@ const engineInfo: EngineInfo = {
standardFeeLow: '5',
standardFeeHigh: '10',
standardFeeLowAmount: '1000000',
standardFeeHighAmount: '65000000'
standardFeeHighAmount: '65000000',
maximumFeeRate: maximumFeeRateCalculator(currencyInfo, 212.86)
},
scriptTemplates,
asBlockbookAddress: asCodec(
Expand Down
2 changes: 1 addition & 1 deletion src/common/utxobased/info/bitcoincashtestnet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ const engineInfo: EngineInfo = {
gapLimit: 10,
defaultFee: 10000,
feeUpdateInterval: 60000,
simpleFeeSettings: {
defaultFeeInfo: {
lowFeeFudgeFactor: undefined,
standardFeeLowFudgeFactor: undefined,
standardFeeHighFudgeFactor: undefined,
Expand Down
6 changes: 4 additions & 2 deletions src/common/utxobased/info/bitcoingold.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Psbt } from 'altcoin-js'
import { EdgeCurrencyInfo } from 'edge-core-js/types'

import { CoinInfo, EngineInfo, PluginInfo } from '../../plugin/types'
import { maximumFeeRateCalculator } from '../../plugin/util/maximumFeeRateCalculator'
import { memoInfo } from './commonInfo'

const currencyInfo: EdgeCurrencyInfo = {
Expand Down Expand Up @@ -51,7 +52,7 @@ const engineInfo: EngineInfo = {
gapLimit: 10,
defaultFee: 1000,
feeUpdateInterval: 60000,
simpleFeeSettings: {
defaultFeeInfo: {
lowFeeFudgeFactor: undefined,
standardFeeLowFudgeFactor: undefined,
standardFeeHighFudgeFactor: undefined,
Expand All @@ -62,7 +63,8 @@ const engineInfo: EngineInfo = {
standardFeeLow: '15',
standardFeeHigh: '140',
standardFeeLowAmount: '17320',
standardFeeHighAmount: '86700000'
standardFeeHighAmount: '86700000',
maximumFeeRate: maximumFeeRateCalculator(currencyInfo, 12.62)
}
}

Expand Down
Loading

0 comments on commit ca8bb99

Please sign in to comment.