implement Autofill Transaction (#1574)

Implements autofill() and setTransactionFlagsToNumber() to allow a Client to autofill fields in a Transaction.
This commit is contained in:
Omar Khan
2021-09-08 12:49:44 -04:00
committed by Mayukha Vadari
parent 949cc031ee
commit 04dd65af38
9 changed files with 677 additions and 8 deletions

View File

@@ -23,6 +23,7 @@ import {
import { constants, errors, txFlags, ensureClassicAddress } from '../common' import { constants, errors, txFlags, ensureClassicAddress } from '../common'
import { ValidationError, XrplError } from '../common/errors' import { ValidationError, XrplError } from '../common/errors'
import getFee from '../common/fee' import getFee from '../common/fee'
import autofill from '../ledger/autofill'
import getBalances from '../ledger/balances' import getBalances from '../ledger/balances'
import { getOrderbook, formatBidsAndAsks } from '../ledger/orderbook' import { getOrderbook, formatBidsAndAsks } from '../ledger/orderbook'
import getPaths from '../ledger/pathfind' import getPaths from '../ledger/pathfind'
@@ -169,6 +170,20 @@ function getCollectKeyFromCommand(command: string): string | null {
} }
} }
/**
* It returns a function that prepends params to the given func.
* A sugar function for JavaScript .bind() without the "this" (keyword) binding.
*
* @param func - A function to prepend params.
* @param params - Parameters to prepend to a function.
* @returns A function bound with params.
*/
// eslint-disable-next-line @typescript-eslint/ban-types -- expected param types
function prepend(func: Function, ...params: unknown[]): Function {
// eslint-disable-next-line @typescript-eslint/no-unsafe-return -- safe to return
return func.bind(null, ...params)
}
interface MarkerRequest extends BaseRequest { interface MarkerRequest extends BaseRequest {
limit?: number limit?: number
marker?: unknown marker?: unknown
@@ -198,6 +213,9 @@ class Client extends EventEmitter {
// number. Defaults to '2'. // number. Defaults to '2'.
public readonly maxFeeXRP: string public readonly maxFeeXRP: string
// TODO: Use partial for other instance methods as well.
public autofill = prepend(autofill, this)
/** /**
* Creates a new Client with a websocket connection to a rippled server. * Creates a new Client with a websocket connection to a rippled server.
* *

View File

@@ -16,7 +16,7 @@ const BASE_10 = 10
*/ */
export default async function getFee( export default async function getFee(
this: Client, this: Client,
cushion: number | null, cushion?: number,
): Promise<string> { ): Promise<string> {
const feeCushion = cushion ?? this.feeCushion const feeCushion = cushion ?? this.feeCushion

194
src/ledger/autofill.ts Normal file
View File

@@ -0,0 +1,194 @@
import BigNumber from 'bignumber.js'
import { xAddressToClassicAddress, isValidXAddress } from 'ripple-address-codec'
import type { Client } from '..'
import { ValidationError } from '../common/errors'
import { AccountInfoRequest, LedgerRequest } from '../models/methods'
import { Transaction } from '../models/transactions'
import { setTransactionFlagsToNumber } from '../models/utils'
import { xrpToDrops } from '../utils'
// 20 drops
const LEDGER_OFFSET = 20
// 5 XRP
const ACCOUNT_DELETE_FEE = 5000000
interface ClassicAccountAndTag {
classicAccount: string
tag: number | false | undefined
}
/**
* Autofills fields in a transaction.
*
* @param client - A client.
* @param tx - A transaction to autofill fields.
* @param signersCount - The expected number of signers for this transaction. Used for multisign.
* @returns An autofilled transaction.
*/
async function autofill(
client: Client,
tx: Transaction,
signersCount?: number,
): Promise<Transaction> {
setValidAddresses(tx)
setTransactionFlagsToNumber(tx)
const promises: Array<Promise<void>> = []
if (tx.Sequence == null) {
promises.push(setNextValidSequenceNumber(client, tx))
}
if (tx.Fee == null) {
promises.push(calculateFeePerTransactionType(client, tx, signersCount))
}
if (tx.LastLedgerSequence == null) {
promises.push(setLatestValidatedLedgerSequence(client, tx))
}
return Promise.all(promises).then(() => tx)
}
function setValidAddresses(tx: Transaction): void {
validateAccountAddress(tx, 'Account', 'SourceTag')
// eslint-disable-next-line @typescript-eslint/dot-notation -- Destination can exist on Transaction
if (tx['Destination'] != null) {
validateAccountAddress(tx, 'Destination', 'DestinationTag')
}
// DepositPreauth:
convertToClassicAddress(tx, 'Authorize')
convertToClassicAddress(tx, 'Unauthorize')
// EscrowCancel, EscrowFinish:
convertToClassicAddress(tx, 'Owner')
// SetRegularKey:
convertToClassicAddress(tx, 'RegularKey')
}
function validateAccountAddress(
tx: Transaction,
accountField: string,
tagField: string,
): void {
// if X-address is given, convert it to classic address
const { classicAccount, tag } = getClassicAccountAndTag(tx[accountField])
// eslint-disable-next-line no-param-reassign -- param reassign is safe
tx[accountField] = classicAccount
if (tag != null) {
if (tx[tagField] && tx[tagField] !== tag) {
throw new ValidationError(
`The ${tagField}, if present, must match the tag of the ${accountField} X-address`,
)
}
// eslint-disable-next-line no-param-reassign -- param reassign is safe
tx[tagField] = tag
}
}
function getClassicAccountAndTag(
Account: string,
expectedTag?: number,
): ClassicAccountAndTag {
if (isValidXAddress(Account)) {
const classic = xAddressToClassicAddress(Account)
if (expectedTag != null && classic.tag !== expectedTag) {
throw new ValidationError(
'address includes a tag that does not match the tag specified in the transaction',
)
}
return {
classicAccount: classic.classicAddress,
tag: classic.tag,
}
}
return {
classicAccount: Account,
tag: expectedTag,
}
}
function convertToClassicAddress(tx: Transaction, fieldName: string): void {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- assignment is safe
const account = tx[fieldName]
if (typeof account === 'string') {
const { classicAccount } = getClassicAccountAndTag(account)
// eslint-disable-next-line no-param-reassign -- param reassign is safe
tx[fieldName] = classicAccount
}
}
async function setNextValidSequenceNumber(
client: Client,
tx: Transaction,
): Promise<void> {
const request: AccountInfoRequest = {
command: 'account_info',
account: tx.Account,
}
const data = await client.request(request)
// eslint-disable-next-line no-param-reassign, require-atomic-updates -- param reassign is safe with no race condition
tx.Sequence = data.result.account_data.Sequence
}
async function calculateFeePerTransactionType(
client: Client,
tx: Transaction,
signersCount = 0,
): Promise<void> {
// netFee is usually 0.00001 XRP (10 drops)
const netFeeXRP: string = await client.getFee()
const netFeeDrops: string = xrpToDrops(netFeeXRP)
let baseFee: BigNumber = new BigNumber(netFeeDrops)
// EscrowFinish Transaction with Fulfillment
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 = new BigNumber(ACCOUNT_DELETE_FEE)
}
// 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(
client: Client,
tx: Transaction,
): Promise<void> {
const request: LedgerRequest = {
command: 'ledger',
ledger_index: 'validated',
}
const data = await client.request(request)
const ledgerSequence = data.result.ledger_index
// eslint-disable-next-line no-param-reassign -- param reassign is safe
tx.LastLedgerSequence = ledgerSequence + LEDGER_OFFSET
}
export default autofill

View File

@@ -95,9 +95,7 @@ export function isAmount(amount: unknown): boolean {
) )
} }
export interface GlobalFlags { export interface GlobalFlags {}
tfFullyCanonicalSig: boolean
}
export interface BaseTransaction { export interface BaseTransaction {
Account: string Account: string

View File

@@ -9,6 +9,14 @@ import {
isAmount, isAmount,
} from './common' } from './common'
// eslint-disable-next-line no-shadow -- variable declaration is unique
export enum OfferCreateFlagsEnum {
tfPassive = 0x00010000,
tfImmediateOrCancel = 0x00020000,
tfFillOrKill = 0x00040000,
tfSell = 0x00080000,
}
export interface OfferCreateFlags extends GlobalFlags { export interface OfferCreateFlags extends GlobalFlags {
tfPassive?: boolean tfPassive?: boolean
tfImmediateOrCancel?: boolean tfImmediateOrCancel?: boolean

View File

@@ -3,6 +3,12 @@ import { ValidationError } from '../../common/errors'
import { BaseTransaction, GlobalFlags, verifyBaseTransaction } from './common' import { BaseTransaction, GlobalFlags, verifyBaseTransaction } from './common'
// eslint-disable-next-line no-shadow -- variable declaration is unique
export enum PaymentChannelClaimFlagsEnum {
tfRenew = 0x00010000,
tfClose = 0x00020000,
}
export interface PaymentChannelClaimFlags extends GlobalFlags { export interface PaymentChannelClaimFlags extends GlobalFlags {
tfRenew?: boolean tfRenew?: boolean
tfClose?: boolean tfClose?: boolean

View File

@@ -1,3 +1,20 @@
/* eslint-disable no-param-reassign -- param reassign is safe */
/* eslint-disable no-bitwise -- flags require bitwise operations */
import { ValidationError } from '../../common/errors'
// eslint-disable-next-line import/no-cycle -- cycle is safe
import {
OfferCreateFlags,
OfferCreateFlagsEnum,
PaymentChannelClaimFlags,
PaymentChannelClaimFlagsEnum,
PaymentTransactionFlags,
PaymentTransactionFlagsEnum,
Transaction,
TrustSetFlags,
TrustSetFlagsEnum,
} from '../transactions'
import type { GlobalFlags } from '../transactions/common'
/** /**
* Verify that all fields of an object are in fields. * Verify that all fields of an object are in fields.
* *
@@ -20,6 +37,71 @@ export function onlyHasFields(
* @returns True if checkFlag is enabled within Flags. * @returns True if checkFlag is enabled within Flags.
*/ */
export function isFlagEnabled(Flags: number, checkFlag: number): boolean { export function isFlagEnabled(Flags: number, checkFlag: number): boolean {
// eslint-disable-next-line no-bitwise -- Flags require bitwise operations
return (checkFlag & Flags) === checkFlag return (checkFlag & Flags) === checkFlag
} }
/**
* Sets a transaction's flags to its numeric representation.
*
* @param tx - A transaction to set its flags to its numeric representation.
*/
export function setTransactionFlagsToNumber(tx: Transaction): void {
if (tx.Flags == null) {
tx.Flags = 0
return
}
if (typeof tx.Flags === 'number') {
return
}
switch (tx.TransactionType) {
case 'OfferCreate':
tx.Flags = convertOfferCreateFlagsToNumber(tx.Flags)
return
case 'PaymentChannelClaim':
tx.Flags = convertPaymentChannelClaimFlagsToNumber(tx.Flags)
return
case 'Payment':
tx.Flags = convertPaymentTransactionFlagsToNumber(tx.Flags)
return
case 'TrustSet':
tx.Flags = convertTrustSetFlagsToNumber(tx.Flags)
return
default:
tx.Flags = 0
}
}
function convertOfferCreateFlagsToNumber(flags: OfferCreateFlags): number {
return reduceFlags(flags, OfferCreateFlagsEnum)
}
function convertPaymentChannelClaimFlagsToNumber(
flags: PaymentChannelClaimFlags,
): number {
return reduceFlags(flags, PaymentChannelClaimFlagsEnum)
}
function convertPaymentTransactionFlagsToNumber(
flags: PaymentTransactionFlags,
): number {
return reduceFlags(flags, PaymentTransactionFlagsEnum)
}
function convertTrustSetFlagsToNumber(flags: TrustSetFlags): number {
return reduceFlags(flags, TrustSetFlagsEnum)
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- added ValidationError check for flagEnum
function reduceFlags(flags: GlobalFlags, flagEnum: any): number {
return Object.keys(flags).reduce((resultFlags, flag) => {
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access -- safe member access
if (flagEnum[flag] == null) {
throw new ValidationError(
`flag ${flag} doesn't exist in flagEnum: ${JSON.stringify(flagEnum)}`,
)
}
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access -- safe member access
return flags[flag] ? resultFlags | flagEnum[flag] : resultFlags
}, 0)
}

241
test/client/autofill.ts Normal file
View File

@@ -0,0 +1,241 @@
import { assert } from 'chai'
import {
AccountDelete,
EscrowFinish,
Payment,
Transaction,
} from '../../src/models/transactions'
import rippled from '../fixtures/rippled'
import { setupClient, teardownClient } from '../setupClient'
const Fee = '10'
const Sequence = 1432
const LastLedgerSequence = 2908734
describe('client.autofill', function () {
beforeEach(setupClient)
afterEach(teardownClient)
it('should not autofill if fields are present', async function () {
const tx: Transaction = {
TransactionType: 'DepositPreauth',
Account: 'rGWrZyQqhTp9Xu7G5Pkayo7bXjH4k4QYpf',
Authorize: 'rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo',
Fee,
Sequence,
LastLedgerSequence,
}
const txResult = await this.client.autofill(tx)
assert.strictEqual(txResult.Fee, Fee)
assert.strictEqual(txResult.Sequence, Sequence)
assert.strictEqual(txResult.LastLedgerSequence, LastLedgerSequence)
})
it('converts Account & Destination X-address to their classic address', async function () {
const tx: Payment = {
TransactionType: 'Payment',
Account: 'XVLhHMPHU98es4dbozjVtdWzVrDjtV18pX8yuPT7y4xaEHi',
Amount: '1234',
Destination: 'X7AcgcsBL6XDcUb289X4mJ8djcdyKaB5hJDWMArnXr61cqZ',
}
this.mockRippled.addResponse('account_info', rippled.account_info.normal)
this.mockRippled.addResponse('server_info', rippled.server_info.normal)
this.mockRippled.addResponse('ledger', rippled.ledger.normal)
const txResult = await this.client.autofill(tx)
assert.strictEqual(txResult.Account, 'rGWrZyQqhTp9Xu7G5Pkayo7bXjH4k4QYpf')
assert.strictEqual(
txResult.Destination,
'r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59',
)
})
it("should autofill Sequence when it's missing", async function () {
const tx: Transaction = {
TransactionType: 'DepositPreauth',
Account: 'rGWrZyQqhTp9Xu7G5Pkayo7bXjH4k4QYpf',
Authorize: 'rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo',
Fee,
LastLedgerSequence,
}
this.mockRippled.addResponse('account_info', {
status: 'success',
type: 'response',
result: {
account_data: {
Sequence: 23,
},
},
})
const txResult = await this.client.autofill(tx)
assert.strictEqual(txResult.Sequence, 23)
})
describe('when autofill Fee is missing', function () {
it('should autofill Fee of a Transaction', async function () {
const tx: Transaction = {
TransactionType: 'DepositPreauth',
Account: 'rGWrZyQqhTp9Xu7G5Pkayo7bXjH4k4QYpf',
Authorize: 'rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo',
Sequence,
LastLedgerSequence,
}
this.mockRippled.addResponse('server_info', {
status: 'success',
type: 'response',
result: {
info: {
validated_ledger: {
base_fee_xrp: 0.00001,
},
},
},
})
const txResult = await this.client.autofill(tx)
assert.strictEqual(txResult.Fee, '12')
})
it('should autofill Fee of an EscrowFinish transaction', async function () {
const tx: EscrowFinish = {
Account: 'rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn',
TransactionType: 'EscrowFinish',
Owner: 'rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn',
OfferSequence: 7,
Condition:
'A0258020E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855810100',
Fulfillment: 'A0028000',
}
this.mockRippled.addResponse('account_info', rippled.account_info.normal)
this.mockRippled.addResponse('ledger', rippled.ledger.normal)
this.mockRippled.addResponse('server_info', {
status: 'success',
type: 'response',
result: {
info: {
validated_ledger: {
base_fee_xrp: 0.00001,
},
},
},
})
const txResult = await this.client.autofill(tx)
assert.strictEqual(txResult.Fee, '399')
})
it('should autofill Fee of an AccountDelete transaction', async function () {
const tx: AccountDelete = {
Account: 'rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn',
TransactionType: 'AccountDelete',
Destination: 'X7AcgcsBL6XDcUb289X4mJ8djcdyKaB5hJDWMArnXr61cqZ',
}
this.mockRippled.addResponse('account_info', rippled.account_info.normal)
this.mockRippled.addResponse('ledger', rippled.ledger.normal)
this.mockRippled.addResponse('server_info', {
status: 'success',
type: 'response',
result: {
info: {
validated_ledger: {
base_fee_xrp: 0.00001,
},
},
},
})
const txResult = await this.client.autofill(tx)
assert.strictEqual(txResult.Fee, '5000000')
})
it('should autofill Fee of an EscrowFinish transaction with signersCount', async function () {
const tx: EscrowFinish = {
Account: 'rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn',
TransactionType: 'EscrowFinish',
Owner: 'rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn',
OfferSequence: 7,
Condition:
'A0258020E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855810100',
Fulfillment: 'A0028000',
}
this.mockRippled.addResponse('account_info', rippled.account_info.normal)
this.mockRippled.addResponse('ledger', rippled.ledger.normal)
this.mockRippled.addResponse('server_info', {
status: 'success',
type: 'response',
result: {
info: {
validated_ledger: {
base_fee_xrp: 0.00001,
},
},
},
})
const txResult = await this.client.autofill(tx, 4)
assert.strictEqual(txResult.Fee, '459')
})
})
it("should autofill LastLedgerSequence when it's missing", async function () {
const tx: Transaction = {
TransactionType: 'DepositPreauth',
Account: 'rGWrZyQqhTp9Xu7G5Pkayo7bXjH4k4QYpf',
Authorize: 'rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo',
Fee,
Sequence,
}
this.mockRippled.addResponse('ledger', {
status: 'success',
type: 'response',
result: {
ledger_index: 9038214,
},
})
const txResult = await this.client.autofill(tx)
assert.strictEqual(txResult.LastLedgerSequence, 9038234)
})
it('should autofill fields when all are missing', async function () {
const tx: Transaction = {
TransactionType: 'DepositPreauth',
Account: 'rGWrZyQqhTp9Xu7G5Pkayo7bXjH4k4QYpf',
Authorize: 'rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo',
}
this.mockRippled.addResponse('account_info', {
status: 'success',
type: 'response',
result: {
account_data: {
Sequence: 23,
},
},
})
this.mockRippled.addResponse('ledger', {
status: 'success',
type: 'response',
result: {
ledger_index: 9038214,
},
})
this.mockRippled.addResponse('server_info', {
status: 'success',
type: 'response',
result: {
info: {
validated_ledger: {
base_fee_xrp: 0.00001,
},
},
},
})
const txResult = await this.client.autofill(tx)
assert.strictEqual(txResult.Fee, '12')
assert.strictEqual(txResult.Sequence, 23)
assert.strictEqual(txResult.LastLedgerSequence, 9038234)
})
})

View File

@@ -1,6 +1,21 @@
/* eslint-disable no-bitwise -- flags require bitwise operations */
import { assert } from 'chai' import { assert } from 'chai'
import { isFlagEnabled } from '../../src/models/utils' import {
DepositPreauth,
OfferCreate,
OfferCreateFlagsEnum,
PaymentChannelClaim,
PaymentChannelClaimFlagsEnum,
Payment,
PaymentTransactionFlagsEnum,
TrustSet,
TrustSetFlagsEnum,
} from '../../src/models/transactions'
import {
isFlagEnabled,
setTransactionFlagsToNumber,
} from '../../src/models/utils'
/** /**
* Utils Testing. * Utils Testing.
@@ -18,13 +33,120 @@ describe('Models Utils', function () {
}) })
it('verifies a flag is enabled', function () { it('verifies a flag is enabled', function () {
flags += flag1 + flag2 flags |= flag1 | flag2
assert.isTrue(isFlagEnabled(flags, flag1)) assert.isTrue(isFlagEnabled(flags, flag1))
}) })
it('verifies a flag is not enabled', function () { it('verifies a flag is not enabled', function () {
flags += flag2 flags |= flag2
assert.isFalse(isFlagEnabled(flags, flag1)) assert.isFalse(isFlagEnabled(flags, flag1))
}) })
}) })
describe('setTransactionFlagsToNumber', function () {
it('sets OfferCreateFlags to its numeric value', function () {
const tx: OfferCreate = {
Account: 'r3rhWeE31Jt5sWmi4QiGLMZnY3ENgqw96W',
Fee: '10',
TakerGets: {
currency: 'DSH',
issuer: 'rcXY84C4g14iFp6taFXjjQGVeHqSCh9RX',
value: '43.11584856965009',
},
TakerPays: '12928290425',
TransactionType: 'OfferCreate',
TxnSignature:
'3045022100D874CDDD6BB24ED66E83B1D3574D3ECAC753A78F26DB7EBA89EAB8E7D72B95F802207C8CCD6CEA64E4AE2014E59EE9654E02CA8F03FE7FCE0539E958EAE182234D91',
Flags: {
tfPassive: true,
tfImmediateOrCancel: false,
tfFillOrKill: true,
tfSell: false,
},
}
const { tfPassive, tfFillOrKill } = OfferCreateFlagsEnum
const expected: number = tfPassive | tfFillOrKill
setTransactionFlagsToNumber(tx)
assert.strictEqual(tx.Flags, expected)
})
it('sets PaymentChannelClaimFlags to its numeric value', function () {
const tx: PaymentChannelClaim = {
Account: 'r...',
TransactionType: 'PaymentChannelClaim',
Channel:
'C1AE6DDDEEC05CF2978C0BAD6FE302948E9533691DC749DCDD3B9E5992CA6198',
Flags: {
tfRenew: true,
tfClose: false,
},
}
const { tfRenew } = PaymentChannelClaimFlagsEnum
const expected: number = tfRenew
setTransactionFlagsToNumber(tx)
assert.strictEqual(tx.Flags, expected)
})
it('sets PaymentTransactionFlags to its numeric value', function () {
const tx: Payment = {
TransactionType: 'Payment',
Account: 'rUn84CUYbNjRoTQ6mSW7BVJPSVJNLb1QLo',
Amount: '1234',
Destination: 'rfkE1aSy9G8Upk4JssnwBxhEv5p4mn2KTy',
Flags: {
tfNoDirectRipple: false,
tfPartialPayment: true,
tfLimitQuality: true,
},
}
const { tfPartialPayment, tfLimitQuality } = PaymentTransactionFlagsEnum
const expected: number = tfPartialPayment | tfLimitQuality
setTransactionFlagsToNumber(tx)
assert.strictEqual(tx.Flags, expected)
})
it('sets TrustSetFlags to its numeric value', function () {
const tx: TrustSet = {
TransactionType: 'TrustSet',
Account: 'rUn84CUYbNjRoTQ6mSW7BVJPSVJNLb1QLo',
LimitAmount: {
currency: 'XRP',
issuer: 'rcXY84C4g14iFp6taFXjjQGVeHqSCh9RX',
value: '4329.23',
},
QualityIn: 1234,
QualityOut: 4321,
Flags: {
tfSetfAuth: true,
tfSetNoRipple: false,
tfClearNoRipple: true,
tfSetFreeze: false,
tfClearFreeze: true,
},
}
const { tfSetfAuth, tfClearNoRipple, tfClearFreeze } = TrustSetFlagsEnum
const expected: number = tfSetfAuth | tfClearNoRipple | tfClearFreeze
setTransactionFlagsToNumber(tx)
assert.strictEqual(tx.Flags, expected)
})
it('sets other transaction types flags to its numeric value', function () {
const tx: DepositPreauth = {
TransactionType: 'DepositPreauth',
Account: 'rUn84CUYbNjRoTQ6mSW7BVJPSVJNLb1QLo',
Flags: {},
}
setTransactionFlagsToNumber(tx)
assert.strictEqual(tx.Flags, 0)
})
})
}) })