Compare commits

...

4 Commits

Author SHA1 Message Date
Denis Angell
b6c11d1df8 Publish
- @transia/xrpl@2.7.3-alpha.22
2023-12-17 08:40:26 +01:00
Denis Angell
526d1e7686 update autofill fee 2023-12-17 08:40:10 +01:00
Denis Angell
3c668f41b8 Publish
- @transia/xrpl@2.7.3-alpha.21
2023-12-15 12:09:52 +01:00
Denis Angell
aeb29a20a1 add fee estimation to autofill 2023-12-15 12:09:25 +01:00
3 changed files with 20 additions and 62 deletions

View File

@@ -1,6 +1,6 @@
{ {
"name": "@transia/xrpl", "name": "@transia/xrpl",
"version": "2.7.3-alpha.20", "version": "2.7.3-alpha.22",
"license": "ISC", "license": "ISC",
"description": "A TypeScript/JavaScript API for interacting with the XRP Ledger in Node.js and the browser", "description": "A TypeScript/JavaScript API for interacting with the XRP Ledger in Node.js and the browser",
"files": [ "files": [

View File

@@ -2,16 +2,15 @@ import {
xAddressToClassicAddress, xAddressToClassicAddress,
isValidXAddress, isValidXAddress,
} from '@transia/ripple-address-codec' } from '@transia/ripple-address-codec'
import BigNumber from 'bignumber.js' import { encode } from '@transia/ripple-binary-codec'
import type { Client } from '..' import type { Client } from '..'
import { ValidationError, XrplError } from '../errors' import { ValidationError, XrplError } from '../errors'
import { AccountInfoRequest, AccountObjectsRequest } from '../models/methods' import { AccountInfoRequest, AccountObjectsRequest } from '../models/methods'
import { Transaction } from '../models/transactions' import { Transaction } from '../models/transactions'
import { setTransactionFlagsToNumber } from '../models/utils/flags' import { setTransactionFlagsToNumber } from '../models/utils/flags'
import { xrpToDrops } from '../utils'
import { getFeeXrp } from './getFeeXrp' import { getFeeEstimateXrp } from './getFeeXrp'
// Expire unconfirmed transactions after 20 ledger versions, approximately 1 minute, by default // Expire unconfirmed transactions after 20 ledger versions, approximately 1 minute, by default
const LEDGER_OFFSET = 20 const LEDGER_OFFSET = 20
@@ -50,17 +49,18 @@ async function autofill<T extends Transaction>(
if (tx.Sequence == null) { if (tx.Sequence == null) {
promises.push(setNextValidSequenceNumber(this, tx)) promises.push(setNextValidSequenceNumber(this, tx))
} }
if (tx.Fee == null) {
promises.push(calculateFeePerTransactionType(this, tx, signersCount))
}
if (tx.LastLedgerSequence == null) { if (tx.LastLedgerSequence == null) {
promises.push(setLatestValidatedLedgerSequence(this, tx)) promises.push(setLatestValidatedLedgerSequence(this, tx))
} }
if (tx.TransactionType === 'AccountDelete') { if (tx.TransactionType === 'AccountDelete') {
promises.push(checkAccountDeleteBlockers(this, tx)) promises.push(checkAccountDeleteBlockers(this, tx))
} }
await Promise.all(promises).then(() => tx)
return Promise.all(promises).then(() => tx) if (tx.Fee == null) {
await calculateFeePerTransactionType(this, tx, signersCount)
}
return tx
} }
function setValidAddresses(tx: Transaction): void { function setValidAddresses(tx: Transaction): void {
@@ -146,64 +146,17 @@ async function setNextValidSequenceNumber(
tx.Sequence = data.result.account_data.Sequence tx.Sequence = data.result.account_data.Sequence
} }
async function fetchAccountDeleteFee(client: Client): Promise<BigNumber> {
const response = await client.request({ command: 'server_state' })
const fee = response.result.state.validated_ledger?.reserve_inc
if (fee == null) {
return Promise.reject(new Error('Could not fetch Owner Reserve.'))
}
return new BigNumber(fee)
}
async function calculateFeePerTransactionType( async function calculateFeePerTransactionType(
client: Client, client: Client,
tx: Transaction, tx: Transaction,
signersCount = 0, signersCount = 0,
): Promise<void> { ): Promise<void> {
// netFee is usually 0.00001 XRP (10 drops) const copyTx = { ...tx }
const netFeeXRP = await getFeeXrp(client) copyTx.SigningPubKey = ``
const netFeeDrops = xrpToDrops(netFeeXRP) copyTx.Fee = `0`
let baseFee = new BigNumber(netFeeDrops) const tx_blob = encode(copyTx)
// eslint-disable-next-line require-atomic-updates, no-param-reassign -- ignore
// EscrowFinish Transaction with Fulfillment tx.Fee = await getFeeEstimateXrp(client, tx_blob, signersCount)
if (tx.TransactionType === 'EscrowFinish' && tx.Fulfillment != null) {
const fulfillmentBytesSize: number = Math.ceil(tx.Fulfillment.length / 2)
// 10 drops × (33 + (Fulfillment size in bytes / 16))
const product = new BigNumber(
// eslint-disable-next-line @typescript-eslint/no-magic-numbers -- expected use of magic numbers
scaleValue(netFeeDrops, 33 + fulfillmentBytesSize / 16),
)
baseFee = product.dp(0, BigNumber.ROUND_CEIL)
}
// AccountDelete Transaction
if (tx.TransactionType === 'AccountDelete') {
baseFee = await fetchAccountDeleteFee(client)
}
/*
* Multi-signed Transaction
* 10 drops × (1 + Number of Signatures Provided)
*/
if (signersCount > 0) {
baseFee = BigNumber.sum(baseFee, scaleValue(netFeeDrops, 1 + signersCount))
}
const maxFeeDrops = xrpToDrops(client.maxFeeXRP)
const totalFee =
tx.TransactionType === 'AccountDelete'
? baseFee
: BigNumber.min(baseFee, maxFeeDrops)
// Round up baseFee and return it as a string
// eslint-disable-next-line no-param-reassign, @typescript-eslint/no-magic-numbers -- param reassign is safe, base 10 magic num
tx.Fee = totalFee.dp(0, BigNumber.ROUND_CEIL).toString(10)
}
function scaleValue(value, multiplier): string {
return new BigNumber(value).times(multiplier).toString()
} }
async function setLatestValidatedLedgerSequence( async function setLatestValidatedLedgerSequence(

View File

@@ -50,15 +50,20 @@ export async function getFeeXrp(
* *
* @param client - The Client used to connect to the ledger. * @param client - The Client used to connect to the ledger.
* @param txBlob - The encoded transaction to estimate the fee for. * @param txBlob - The encoded transaction to estimate the fee for.
* @param signersCount - The number of multisigners.
* @returns The transaction fee. * @returns The transaction fee.
*/ */
export async function getFeeEstimateXrp( export async function getFeeEstimateXrp(
client: Client, client: Client,
txBlob: string, txBlob: string,
signersCount = 0,
): Promise<string> { ): Promise<string> {
const response = await client.request({ const response = await client.request({
command: 'fee', command: 'fee',
tx_blob: txBlob, tx_blob: txBlob,
}) })
return response.result.drops.open_ledger_fee const openLedgerFee = response.result.drops.open_ledger_fee
const baseFee = new BigNumber(response.result.drops.base_fee)
const totalFee = BigNumber.sum(openLedgerFee, Number(baseFee) * signersCount)
return new BigNumber(totalFee.toFixed(NUM_DECIMAL_PLACES)).toString(BASE_10)
} }