mirror of
https://github.com/Xahau/xahau.js.git
synced 2025-11-04 21:15:47 +00:00
fix: remove the sugar/parse directory (#1622)
* fix: remove the sugar/parse directory * feat: adds lsf flags
This commit is contained in:
committed by
Mayukha Vadari
parent
fd78d1edcd
commit
3cbdbac4f9
@@ -18,3 +18,15 @@ export default interface AccountRoot extends BaseLedgerEntry {
|
||||
TickSize?: number
|
||||
TransferRate?: number
|
||||
}
|
||||
|
||||
export enum AccountRootLedgerFlags {
|
||||
lsfPasswordSpent = 0x00010000,
|
||||
lsfRequireDestTag = 0x00020000,
|
||||
lsfRequireAuth = 0x00040000,
|
||||
lsfDisallowXRP = 0x00080000,
|
||||
lsfDisableMaster = 0x00100000,
|
||||
lsfNoFreeze = 0x00200000,
|
||||
lsfGlobalFreeze = 0x00400000,
|
||||
lsfDefaultRipple = 0x00800000,
|
||||
lsfDepositAuth = 0x01000000,
|
||||
}
|
||||
|
||||
@@ -16,3 +16,8 @@ export default interface Offer extends BaseLedgerEntry {
|
||||
PreviousTxnLgrSeq: number
|
||||
Expiration?: number
|
||||
}
|
||||
|
||||
export enum OfferLedgerFlags {
|
||||
lsfPassive = 0x00010000,
|
||||
lsfSell = 0x00020000,
|
||||
}
|
||||
|
||||
@@ -17,3 +17,14 @@ export default interface RippleState extends BaseLedgerEntry {
|
||||
HighQualityIn?: number
|
||||
HighQualityOut?: number
|
||||
}
|
||||
|
||||
export enum RippleStateLedgerFlags {
|
||||
lsfLowReserve = 0x00010000, // True, if entry counts toward reserve.
|
||||
lsfHighReserve = 0x00020000,
|
||||
lsfLowAuth = 0x00040000,
|
||||
lsfHighAuth = 0x00080000,
|
||||
lsfLowNoRipple = 0x00100000,
|
||||
lsfHighNoRipple = 0x00200000,
|
||||
lsfLowFreeze = 0x00400000, // True, low side has set freeze flag
|
||||
lsfHighFreeze = 0x00800000, // True, high side has set freeze flag
|
||||
}
|
||||
|
||||
@@ -17,3 +17,7 @@ export default interface SignerList extends BaseLedgerEntry {
|
||||
SignerListID: number
|
||||
SignerQuorum: number
|
||||
}
|
||||
|
||||
export enum SignerListLedgerFlags {
|
||||
lsfOneOwnerCount = 0x00010000, // True, uses only one OwnerCount
|
||||
}
|
||||
|
||||
@@ -3,14 +3,13 @@ import _ from 'lodash'
|
||||
|
||||
import type { Client } from '../client'
|
||||
import { LedgerIndex } from '../models/common'
|
||||
import { OfferLedgerFlags } from '../models/ledger/offer'
|
||||
import {
|
||||
BookOffer,
|
||||
BookOffersRequest,
|
||||
TakerAmount,
|
||||
} from '../models/methods/bookOffers'
|
||||
|
||||
import { orderFlags } from './parse/flags'
|
||||
|
||||
function sortOffers(offers: BookOffer[]): BookOffer[] {
|
||||
return offers.sort((offerA, offerB) => {
|
||||
const qualityA = offerA.quality ?? 0
|
||||
@@ -64,6 +63,7 @@ async function getOrderbook(
|
||||
request.taker_gets = takerPays
|
||||
request.taker_pays = takerGets
|
||||
const reverseOfferResults = await client.requestAll(request)
|
||||
|
||||
// 3. Return Formatted Response
|
||||
const directOffers = _.flatMap(
|
||||
directOfferResults,
|
||||
@@ -73,6 +73,7 @@ async function getOrderbook(
|
||||
reverseOfferResults,
|
||||
(reverseOfferResult) => reverseOfferResult.result.offers,
|
||||
)
|
||||
|
||||
// Sort the orders
|
||||
// for both buys and sells, lowest quality is closest to mid-market
|
||||
// we sort the orders so that earlier orders are closer to mid-market
|
||||
@@ -82,7 +83,7 @@ async function getOrderbook(
|
||||
const buy: BookOffer[] = []
|
||||
const sell: BookOffer[] = []
|
||||
orders.forEach((order) => {
|
||||
if (order.Flags === orderFlags.Sell) {
|
||||
if ((order.Flags & OfferLedgerFlags.lsfSell) !== 0) {
|
||||
sell.push(order)
|
||||
} else {
|
||||
buy.push(order)
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
import * as assert from 'assert'
|
||||
|
||||
import { classicAddressToXAddress } from 'ripple-address-codec'
|
||||
|
||||
import { removeUndefined } from '../../utils'
|
||||
|
||||
import { parseMemos } from './utils'
|
||||
|
||||
export interface FormattedAccountDelete {
|
||||
// account (address) of an account to receive any leftover XRP after deleting the sending account.
|
||||
// Must be a funded account in the ledger, and must not be the sending account.
|
||||
destination: string
|
||||
|
||||
// (Optional) Arbitrary destination tag that identifies a hosted recipient or other information
|
||||
// for the recipient of the deleted account's leftover XRP. NB: Ensure that the hosted recipient is
|
||||
// able to account for AccountDelete transactions; if not, your balance may not be properly credited.
|
||||
destinationTag?: number
|
||||
|
||||
// X-address of an account to receive any leftover XRP after deleting the sending account.
|
||||
// Must be a funded account in the ledger, and must not be the sending account.
|
||||
destinationXAddress: string
|
||||
}
|
||||
|
||||
function parseAccountDelete(tx: any): FormattedAccountDelete {
|
||||
assert.ok(tx.TransactionType === 'AccountDelete')
|
||||
|
||||
return removeUndefined({
|
||||
memos: parseMemos(tx),
|
||||
destination: tx.Destination,
|
||||
destinationTag: tx.DestinationTag,
|
||||
destinationXAddress: classicAddressToXAddress(
|
||||
tx.Destination,
|
||||
tx.DestinationTag == null ? false : tx.DestinationTag,
|
||||
false,
|
||||
),
|
||||
})
|
||||
}
|
||||
|
||||
export default parseAccountDelete
|
||||
@@ -1,62 +0,0 @@
|
||||
import BigNumber from 'bignumber.js'
|
||||
|
||||
import { FormattedOrderSpecification } from '../../common/types/objects'
|
||||
import { removeUndefined } from '../../utils'
|
||||
|
||||
import parseAmount from './amount'
|
||||
import { orderFlags } from './flags'
|
||||
import { parseTimestamp, adjustQualityForXRP } from './utils'
|
||||
|
||||
export interface FormattedAccountOrder {
|
||||
specification: FormattedOrderSpecification
|
||||
properties: {
|
||||
maker: string
|
||||
sequence: number
|
||||
makerExchangeRate: string
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: remove this function once rippled provides quality directly
|
||||
function computeQuality(takerGets, takerPays) {
|
||||
const quotient = new BigNumber(takerPays.value).dividedBy(takerGets.value)
|
||||
return quotient.precision(16, BigNumber.ROUND_HALF_UP).toString()
|
||||
}
|
||||
|
||||
// rippled 'account_offers' returns a different format for orders than 'tx'
|
||||
// the flags are also different
|
||||
export function parseAccountOrder(
|
||||
address: string,
|
||||
order: any,
|
||||
): FormattedAccountOrder {
|
||||
const direction = (order.flags & orderFlags.Sell) === 0 ? 'buy' : 'sell'
|
||||
const takerGetsAmount = parseAmount(order.taker_gets)
|
||||
const takerPaysAmount = parseAmount(order.taker_pays)
|
||||
const quantity = direction === 'buy' ? takerPaysAmount : takerGetsAmount
|
||||
const totalPrice = direction === 'buy' ? takerGetsAmount : takerPaysAmount
|
||||
|
||||
// note: immediateOrCancel and fillOrKill orders cannot enter the order book
|
||||
// so we can omit those flags here
|
||||
const specification = removeUndefined({
|
||||
direction,
|
||||
quantity,
|
||||
totalPrice,
|
||||
passive: (order.flags & orderFlags.Passive) !== 0 || undefined,
|
||||
// rippled currently does not provide "expiration" in account_offers
|
||||
expirationTime: parseTimestamp(order.expiration),
|
||||
})
|
||||
|
||||
const makerExchangeRate = order.quality
|
||||
? adjustQualityForXRP(
|
||||
order.quality.toString(),
|
||||
takerGetsAmount.currency,
|
||||
takerPaysAmount.currency,
|
||||
)
|
||||
: computeQuality(takerGetsAmount, takerPaysAmount)
|
||||
const properties = {
|
||||
maker: address,
|
||||
sequence: order.seq,
|
||||
makerExchangeRate,
|
||||
}
|
||||
|
||||
return { specification, properties }
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
import {
|
||||
Trustline,
|
||||
FormattedTrustline,
|
||||
} from '../../common/types/objects/trustlines'
|
||||
import { removeUndefined } from '../../utils'
|
||||
|
||||
import { parseQuality } from './utils'
|
||||
|
||||
// rippled 'account_lines' returns a different format for
|
||||
// trustlines than 'tx'
|
||||
function parseAccountTrustline(trustline: Trustline): FormattedTrustline {
|
||||
const specification = removeUndefined({
|
||||
limit: trustline.limit,
|
||||
currency: trustline.currency,
|
||||
counterparty: trustline.account,
|
||||
qualityIn: parseQuality(trustline.quality_in) || undefined,
|
||||
qualityOut: parseQuality(trustline.quality_out) || undefined,
|
||||
ripplingDisabled: trustline.no_ripple,
|
||||
frozen: trustline.freeze,
|
||||
authorized: trustline.authorized,
|
||||
})
|
||||
// rippled doesn't provide the counterparty's qualities
|
||||
const counterparty = removeUndefined({
|
||||
limit: trustline.limit_peer,
|
||||
ripplingDisabled: trustline.no_ripple_peer,
|
||||
frozen: trustline.freeze_peer,
|
||||
authorized: trustline.peer_authorized,
|
||||
})
|
||||
const state = {
|
||||
balance: trustline.balance,
|
||||
}
|
||||
return { specification, counterparty, state }
|
||||
}
|
||||
|
||||
export default parseAccountTrustline
|
||||
@@ -1,7 +0,0 @@
|
||||
function parseAmendment(tx: any) {
|
||||
return {
|
||||
amendment: tx.Amendment,
|
||||
}
|
||||
}
|
||||
|
||||
export default parseAmendment
|
||||
@@ -1,18 +0,0 @@
|
||||
import { Amount, RippledAmount } from '../../common/types/objects'
|
||||
import { dropsToXrp } from '../../utils'
|
||||
|
||||
function parseAmount(amount: RippledAmount): Amount {
|
||||
if (typeof amount === 'string') {
|
||||
return {
|
||||
currency: 'XRP',
|
||||
value: dropsToXrp(amount),
|
||||
}
|
||||
}
|
||||
return {
|
||||
currency: amount.currency,
|
||||
value: amount.value,
|
||||
counterparty: amount.issuer,
|
||||
}
|
||||
}
|
||||
|
||||
export default parseAmount
|
||||
@@ -1,13 +0,0 @@
|
||||
import * as assert from 'assert'
|
||||
|
||||
import { parseMemos } from './utils'
|
||||
|
||||
function parseOrderCancellation(tx: any): object {
|
||||
assert.ok(tx.TransactionType === 'OfferCancel')
|
||||
return {
|
||||
memos: parseMemos(tx),
|
||||
orderSequence: tx.OfferSequence,
|
||||
}
|
||||
}
|
||||
|
||||
export default parseOrderCancellation
|
||||
@@ -1,21 +0,0 @@
|
||||
import * as assert from 'assert'
|
||||
|
||||
import { removeUndefined } from '../../utils'
|
||||
|
||||
import { parseMemos } from './utils'
|
||||
|
||||
export interface FormattedCheckCancel {
|
||||
// ID of the Check ledger object to cancel.
|
||||
checkID: string
|
||||
}
|
||||
|
||||
function parseCheckCancel(tx: any): FormattedCheckCancel {
|
||||
assert.ok(tx.TransactionType === 'CheckCancel')
|
||||
|
||||
return removeUndefined({
|
||||
memos: parseMemos(tx),
|
||||
checkID: tx.CheckID,
|
||||
})
|
||||
}
|
||||
|
||||
export default parseCheckCancel
|
||||
@@ -1,38 +0,0 @@
|
||||
import * as assert from 'assert'
|
||||
|
||||
import { Amount } from '../../common/types/objects'
|
||||
import { removeUndefined } from '../../utils'
|
||||
|
||||
import parseAmount from './amount'
|
||||
import { parseMemos } from './utils'
|
||||
|
||||
export interface FormattedCheckCash {
|
||||
// ID of the Check ledger object to cash.
|
||||
checkID: string
|
||||
|
||||
// (Optional) redeem the Check for exactly this amount, if possible.
|
||||
// The currency must match that of the `SendMax` of the corresponding
|
||||
// `CheckCreate` transaction.
|
||||
amount: Amount
|
||||
|
||||
// (Optional) redeem the Check for at least this amount and
|
||||
// for as much as possible.
|
||||
// The currency must match that of the `SendMax` of the corresponding
|
||||
// `CheckCreate` transaction.
|
||||
deliverMin: Amount
|
||||
|
||||
// *must* include either Amount or DeliverMin, but not both.
|
||||
}
|
||||
|
||||
function parseCheckCash(tx: any): FormattedCheckCash {
|
||||
assert.ok(tx.TransactionType === 'CheckCash')
|
||||
|
||||
return removeUndefined({
|
||||
memos: parseMemos(tx),
|
||||
checkID: tx.CheckID,
|
||||
amount: tx.Amount && parseAmount(tx.Amount),
|
||||
deliverMin: tx.DeliverMin && parseAmount(tx.DeliverMin),
|
||||
})
|
||||
}
|
||||
|
||||
export default parseCheckCash
|
||||
@@ -1,40 +0,0 @@
|
||||
import * as assert from 'assert'
|
||||
|
||||
import { Amount } from '../../common/types/objects'
|
||||
import { removeUndefined } from '../../utils'
|
||||
|
||||
import parseAmount from './amount'
|
||||
import { parseTimestamp, parseMemos } from './utils'
|
||||
|
||||
export interface FormattedCheckCreate {
|
||||
// account that can cash the check.
|
||||
destination: string
|
||||
|
||||
// amount the check is allowed to debit the sender,
|
||||
// including transfer fees on non-XRP currencies.
|
||||
sendMax: Amount
|
||||
|
||||
// (Optional) identifies the reason for the check, or a hosted recipient.
|
||||
destinationTag?: string
|
||||
|
||||
// (Optional) time in seconds since the Ripple Epoch.
|
||||
expiration?: string
|
||||
|
||||
// (Optional) 256-bit hash representing a specific reason or identifier.
|
||||
invoiceID?: string
|
||||
}
|
||||
|
||||
function parseCheckCreate(tx: any): FormattedCheckCreate {
|
||||
assert.ok(tx.TransactionType === 'CheckCreate')
|
||||
|
||||
return removeUndefined({
|
||||
memos: parseMemos(tx),
|
||||
destination: tx.Destination,
|
||||
sendMax: parseAmount(tx.SendMax),
|
||||
destinationTag: tx.DestinationTag,
|
||||
expiration: tx.Expiration && parseTimestamp(tx.Expiration),
|
||||
invoiceID: tx.InvoiceID,
|
||||
})
|
||||
}
|
||||
|
||||
export default parseCheckCreate
|
||||
@@ -1,25 +0,0 @@
|
||||
import * as assert from 'assert'
|
||||
|
||||
import { removeUndefined } from '../../utils'
|
||||
|
||||
import { parseMemos } from './utils'
|
||||
|
||||
export interface FormattedDepositPreauth {
|
||||
// account (address) of the sender to preauthorize
|
||||
authorize: string
|
||||
|
||||
// account (address) of the sender whose preauthorization should be revoked
|
||||
unauthorize: string
|
||||
}
|
||||
|
||||
function parseDepositPreauth(tx: any): FormattedDepositPreauth {
|
||||
assert.ok(tx.TransactionType === 'DepositPreauth')
|
||||
|
||||
return removeUndefined({
|
||||
memos: parseMemos(tx),
|
||||
authorize: tx.Authorize,
|
||||
unauthorize: tx.Unauthorize,
|
||||
})
|
||||
}
|
||||
|
||||
export default parseDepositPreauth
|
||||
@@ -1,17 +0,0 @@
|
||||
import * as assert from 'assert'
|
||||
|
||||
import { removeUndefined } from '../../utils'
|
||||
|
||||
import { parseMemos } from './utils'
|
||||
|
||||
function parseEscrowCancellation(tx: any): object {
|
||||
assert.ok(tx.TransactionType === 'EscrowCancel')
|
||||
|
||||
return removeUndefined({
|
||||
memos: parseMemos(tx),
|
||||
owner: tx.Owner,
|
||||
escrowSequence: tx.OfferSequence,
|
||||
})
|
||||
}
|
||||
|
||||
export default parseEscrowCancellation
|
||||
@@ -1,23 +0,0 @@
|
||||
import * as assert from 'assert'
|
||||
|
||||
import { removeUndefined } from '../../utils'
|
||||
|
||||
import parseAmount from './amount'
|
||||
import { parseTimestamp, parseMemos } from './utils'
|
||||
|
||||
function parseEscrowCreation(tx: any): object {
|
||||
assert.ok(tx.TransactionType === 'EscrowCreate')
|
||||
|
||||
return removeUndefined({
|
||||
amount: parseAmount(tx.Amount).value,
|
||||
destination: tx.Destination,
|
||||
memos: parseMemos(tx),
|
||||
condition: tx.Condition,
|
||||
allowCancelAfter: parseTimestamp(tx.CancelAfter),
|
||||
allowExecuteAfter: parseTimestamp(tx.FinishAfter),
|
||||
sourceTag: tx.SourceTag,
|
||||
destinationTag: tx.DestinationTag,
|
||||
})
|
||||
}
|
||||
|
||||
export default parseEscrowCreation
|
||||
@@ -1,19 +0,0 @@
|
||||
import * as assert from 'assert'
|
||||
|
||||
import { removeUndefined } from '../../utils'
|
||||
|
||||
import { parseMemos } from './utils'
|
||||
|
||||
function parseEscrowExecution(tx: any): object {
|
||||
assert.ok(tx.TransactionType === 'EscrowFinish')
|
||||
|
||||
return removeUndefined({
|
||||
memos: parseMemos(tx),
|
||||
owner: tx.Owner,
|
||||
escrowSequence: tx.OfferSequence,
|
||||
condition: tx.Condition,
|
||||
fulfillment: tx.Fulfillment,
|
||||
})
|
||||
}
|
||||
|
||||
export default parseEscrowExecution
|
||||
@@ -1,18 +0,0 @@
|
||||
import BigNumber from 'bignumber.js'
|
||||
|
||||
import { dropsToXrp } from '../../utils'
|
||||
|
||||
import { parseMemos } from './utils'
|
||||
|
||||
function parseFeeUpdate(tx: any) {
|
||||
const baseFeeDrops = new BigNumber(tx.BaseFee, 16).toString()
|
||||
return {
|
||||
memos: parseMemos(tx),
|
||||
baseFeeXRP: dropsToXrp(baseFeeDrops),
|
||||
referenceFeeUnits: tx.ReferenceFeeUnits,
|
||||
reserveBaseXRP: dropsToXrp(tx.ReserveBase),
|
||||
reserveIncrementXRP: dropsToXrp(tx.ReserveIncrement),
|
||||
}
|
||||
}
|
||||
|
||||
export default parseFeeUpdate
|
||||
@@ -1,54 +0,0 @@
|
||||
import BigNumber from 'bignumber.js'
|
||||
import _ from 'lodash'
|
||||
|
||||
import { constants } from '..'
|
||||
|
||||
const AccountFields = constants.AccountFields
|
||||
|
||||
function parseField(info, value) {
|
||||
if (info.encoding === 'hex' && !info.length) {
|
||||
// e.g. "domain"
|
||||
return Buffer.from(value, 'hex').toString('ascii')
|
||||
}
|
||||
if (info.shift) {
|
||||
return new BigNumber(value).shiftedBy(-info.shift).toNumber()
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
function parseFields(data: any): object {
|
||||
const settings: any = {}
|
||||
for (const fieldName in AccountFields) {
|
||||
const fieldValue = data[fieldName]
|
||||
if (fieldValue != null) {
|
||||
const info = AccountFields[fieldName]
|
||||
settings[info.name] = parseField(info, fieldValue)
|
||||
}
|
||||
}
|
||||
|
||||
if (data.RegularKey) {
|
||||
settings.regularKey = data.RegularKey
|
||||
}
|
||||
|
||||
// Since an account can own at most one SignerList,
|
||||
// this array must have exactly one member if it is present.
|
||||
if (data.signer_lists && data.signer_lists.length === 1) {
|
||||
settings.signers = {}
|
||||
if (data.signer_lists[0].SignerQuorum) {
|
||||
settings.signers.threshold = data.signer_lists[0].SignerQuorum
|
||||
}
|
||||
if (data.signer_lists[0].SignerEntries) {
|
||||
settings.signers.weights = data.signer_lists[0].SignerEntries.map(
|
||||
(entry: any) => {
|
||||
return {
|
||||
address: entry.SignerEntry.Account,
|
||||
weight: entry.SignerEntry.SignerWeight,
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
return settings
|
||||
}
|
||||
|
||||
export default parseFields
|
||||
@@ -1,17 +0,0 @@
|
||||
const orderFlags = {
|
||||
Passive: 0x00010000,
|
||||
Sell: 0x00020000, // offer was placed as a sell
|
||||
}
|
||||
|
||||
const trustlineFlags = {
|
||||
LowReserve: 0x00010000, // entry counts toward reserve
|
||||
HighReserve: 0x00020000,
|
||||
LowAuth: 0x00040000,
|
||||
HighAuth: 0x00080000,
|
||||
LowNoRipple: 0x00100000,
|
||||
HighNoRipple: 0x00200000,
|
||||
LowFreeze: 0x00400000,
|
||||
HighFreeze: 0x00800000,
|
||||
}
|
||||
|
||||
export { orderFlags, trustlineFlags }
|
||||
@@ -1,92 +0,0 @@
|
||||
import _ from 'lodash'
|
||||
|
||||
import { TransactionAndMetadata } from '../../models/transactions'
|
||||
import { removeUndefined, rippleTimeToISOTime } from '../../utils'
|
||||
|
||||
import parseTransaction from './transaction'
|
||||
|
||||
export interface FormattedLedger {
|
||||
// TODO: properties in type don't match response object. Fix!
|
||||
// closed: boolean,
|
||||
stateHash: string
|
||||
closeTime: string
|
||||
closeTimeResolution: number
|
||||
closeFlags: number
|
||||
ledgerHash: string
|
||||
ledgerVersion: number
|
||||
parentLedgerHash: string
|
||||
parentCloseTime: string
|
||||
totalDrops: string
|
||||
transactionHash: string
|
||||
transactions?: object[]
|
||||
transactionHashes?: string[]
|
||||
rawState?: string
|
||||
stateHashes?: string[]
|
||||
}
|
||||
|
||||
function parseTransactionWrapper(
|
||||
ledgerVersion: number,
|
||||
tx: TransactionAndMetadata,
|
||||
) {
|
||||
// renames metaData to meta and adds ledger_index
|
||||
const transaction = {
|
||||
..._.omit(tx, 'metadata'),
|
||||
meta: tx.metadata,
|
||||
ledger_index: ledgerVersion,
|
||||
}
|
||||
const result = parseTransaction(transaction, true)
|
||||
if (!result.outcome.ledgerVersion) {
|
||||
result.outcome.ledgerVersion = ledgerVersion
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
function parseTransactions(
|
||||
transactions: string[] | TransactionAndMetadata[],
|
||||
ledgerVersion: number,
|
||||
) {
|
||||
if (_.isEmpty(transactions)) {
|
||||
return {}
|
||||
}
|
||||
if (typeof transactions[0] === 'string') {
|
||||
return { transactionHashes: transactions as unknown as string[] }
|
||||
}
|
||||
return {
|
||||
transactions: (transactions as unknown as TransactionAndMetadata[]).map(
|
||||
_.partial(parseTransactionWrapper, ledgerVersion),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
function parseState(state) {
|
||||
if (_.isEmpty(state)) {
|
||||
return {}
|
||||
}
|
||||
if (typeof state[0] === 'string') {
|
||||
return { stateHashes: state }
|
||||
}
|
||||
return { rawState: JSON.stringify(state) }
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ledger - Must be a *closed* ledger with valid `close_time` and `parent_close_time`.
|
||||
* @returns Formatted ledger.
|
||||
* @throws RangeError: Invalid time value (rippleTimeToISOTime).
|
||||
*/
|
||||
export function parseLedger(ledger): FormattedLedger {
|
||||
const ledgerVersion = parseInt(ledger.ledger_index, 10)
|
||||
return removeUndefined({
|
||||
stateHash: ledger.account_hash,
|
||||
closeTime: rippleTimeToISOTime(ledger.close_time),
|
||||
closeTimeResolution: ledger.close_time_resolution,
|
||||
closeFlags: ledger.close_flags,
|
||||
ledgerHash: ledger.ledger_hash,
|
||||
ledgerVersion,
|
||||
parentLedgerHash: ledger.parent_hash,
|
||||
parentCloseTime: rippleTimeToISOTime(ledger.parent_close_time),
|
||||
totalDrops: ledger.total_coins,
|
||||
transactionHash: ledger.transaction_hash,
|
||||
...parseTransactions(ledger.transactions, ledgerVersion),
|
||||
...parseState(ledger.accountState),
|
||||
})
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
import * as assert from 'assert'
|
||||
|
||||
import { txFlags } from '..'
|
||||
import {
|
||||
FormattedOrderSpecification,
|
||||
OfferCreateTransaction,
|
||||
} from '../../common/types/objects/index'
|
||||
import { removeUndefined } from '../../utils'
|
||||
|
||||
import parseAmount from './amount'
|
||||
import { parseTimestamp, parseMemos } from './utils'
|
||||
|
||||
const flags = txFlags.OfferCreate
|
||||
|
||||
function parseOrder(tx: OfferCreateTransaction): FormattedOrderSpecification {
|
||||
assert.ok(tx.TransactionType === 'OfferCreate')
|
||||
|
||||
const direction = (tx.Flags & flags.Sell) === 0 ? 'buy' : 'sell'
|
||||
const takerGetsAmount = parseAmount(tx.TakerGets)
|
||||
const takerPaysAmount = parseAmount(tx.TakerPays)
|
||||
const quantity = direction === 'buy' ? takerPaysAmount : takerGetsAmount
|
||||
const totalPrice = direction === 'buy' ? takerGetsAmount : takerPaysAmount
|
||||
|
||||
return removeUndefined({
|
||||
memos: parseMemos(tx),
|
||||
direction,
|
||||
quantity,
|
||||
totalPrice,
|
||||
passive: (tx.Flags & flags.Passive) !== 0 || undefined,
|
||||
immediateOrCancel: (tx.Flags & flags.ImmediateOrCancel) !== 0 || undefined,
|
||||
fillOrKill: (tx.Flags & flags.FillOrKill) !== 0 || undefined,
|
||||
expirationTime: parseTimestamp(tx.Expiration),
|
||||
})
|
||||
}
|
||||
|
||||
export default parseOrder
|
||||
@@ -1,74 +0,0 @@
|
||||
import _ from 'lodash'
|
||||
|
||||
import { BookOffer } from '../../common/types/commands'
|
||||
import { Amount, FormattedOrderSpecification } from '../../common/types/objects'
|
||||
import { removeUndefined } from '../../utils'
|
||||
|
||||
import parseAmount from './amount'
|
||||
import { orderFlags } from './flags'
|
||||
import { parseTimestamp, adjustQualityForXRP } from './utils'
|
||||
|
||||
export interface FormattedOrderbookOrder {
|
||||
specification: FormattedOrderSpecification
|
||||
properties: {
|
||||
maker: string
|
||||
sequence: number
|
||||
makerExchangeRate: string
|
||||
}
|
||||
state?: {
|
||||
fundedAmount: Amount
|
||||
priceOfFundedAmount: Amount
|
||||
}
|
||||
data: BookOffer
|
||||
}
|
||||
|
||||
export function parseOrderbookOrder(data: BookOffer): FormattedOrderbookOrder {
|
||||
const direction = (data.Flags & orderFlags.Sell) === 0 ? 'buy' : 'sell'
|
||||
const takerGetsAmount = parseAmount(data.TakerGets)
|
||||
const takerPaysAmount = parseAmount(data.TakerPays)
|
||||
const quantity = direction === 'buy' ? takerPaysAmount : takerGetsAmount
|
||||
const totalPrice = direction === 'buy' ? takerGetsAmount : takerPaysAmount
|
||||
|
||||
// note: immediateOrCancel and fillOrKill orders cannot enter the order book
|
||||
// so we can omit those flags here
|
||||
const specification: FormattedOrderSpecification = removeUndefined({
|
||||
direction,
|
||||
quantity,
|
||||
totalPrice,
|
||||
passive: (data.Flags & orderFlags.Passive) !== 0 || undefined,
|
||||
expirationTime: parseTimestamp(data.Expiration),
|
||||
})
|
||||
|
||||
if (data.quality == null) {
|
||||
throw new Error('parseOrderBookOrder: Could not find quality')
|
||||
}
|
||||
|
||||
const properties = {
|
||||
maker: data.Account,
|
||||
sequence: data.Sequence,
|
||||
makerExchangeRate: adjustQualityForXRP(
|
||||
data.quality,
|
||||
takerGetsAmount.currency,
|
||||
takerPaysAmount.currency,
|
||||
),
|
||||
}
|
||||
|
||||
const takerGetsFunded = data.taker_gets_funded
|
||||
? parseAmount(data.taker_gets_funded)
|
||||
: undefined
|
||||
const takerPaysFunded = data.taker_pays_funded
|
||||
? parseAmount(data.taker_pays_funded)
|
||||
: undefined
|
||||
const available = removeUndefined({
|
||||
fundedAmount: takerGetsFunded,
|
||||
priceOfFundedAmount: takerPaysFunded,
|
||||
})
|
||||
const state = _.isEmpty(available) ? undefined : available
|
||||
|
||||
return removeUndefined({
|
||||
specification,
|
||||
properties,
|
||||
state,
|
||||
data,
|
||||
}) as FormattedOrderbookOrder
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
import _ from 'lodash'
|
||||
|
||||
import { Amount, RippledAmount } from '../../common/types/objects'
|
||||
import { Path, GetPaths, RippledPathsResponse } from '../pathfind-types'
|
||||
|
||||
import parseAmount from './amount'
|
||||
|
||||
function parsePaths(paths) {
|
||||
return paths.map((steps) =>
|
||||
steps.map((step) => _.omit(step, ['type', 'type_hex'])),
|
||||
)
|
||||
}
|
||||
|
||||
function removeAnyCounterpartyEncoding(address: string, amount: Amount) {
|
||||
return amount.counterparty === address
|
||||
? _.omit(amount, 'counterparty')
|
||||
: amount
|
||||
}
|
||||
|
||||
function createAdjustment(
|
||||
address: string,
|
||||
adjustmentWithoutAddress: object,
|
||||
): any {
|
||||
const amountKey = Object.keys(adjustmentWithoutAddress)[0]
|
||||
const amount = adjustmentWithoutAddress[amountKey]
|
||||
return _.set(
|
||||
{ address },
|
||||
amountKey,
|
||||
removeAnyCounterpartyEncoding(address, amount),
|
||||
)
|
||||
}
|
||||
|
||||
function parseAlternative(
|
||||
sourceAddress: string,
|
||||
destinationAddress: string,
|
||||
destinationAmount: RippledAmount,
|
||||
alternative: any,
|
||||
): Path {
|
||||
// we use "maxAmount"/"minAmount" here so that the result can be passed
|
||||
// directly to preparePayment
|
||||
const amounts =
|
||||
alternative.destination_amount != null
|
||||
? {
|
||||
source: { amount: parseAmount(alternative.source_amount) },
|
||||
destination: {
|
||||
minAmount: parseAmount(alternative.destination_amount),
|
||||
},
|
||||
}
|
||||
: {
|
||||
source: { maxAmount: parseAmount(alternative.source_amount) },
|
||||
destination: { amount: parseAmount(destinationAmount) },
|
||||
}
|
||||
|
||||
return {
|
||||
source: createAdjustment(sourceAddress, amounts.source),
|
||||
destination: createAdjustment(destinationAddress, amounts.destination),
|
||||
paths: JSON.stringify(parsePaths(alternative.paths_computed)),
|
||||
}
|
||||
}
|
||||
|
||||
function parsePathfind(pathfindResult: RippledPathsResponse): GetPaths {
|
||||
const sourceAddress = pathfindResult.source_account
|
||||
const destinationAddress = pathfindResult.destination_account
|
||||
const destinationAmount = pathfindResult.destination_amount
|
||||
return pathfindResult.alternatives.map((alt) =>
|
||||
parseAlternative(sourceAddress, destinationAddress, destinationAmount, alt),
|
||||
)
|
||||
}
|
||||
|
||||
export default parsePathfind
|
||||
@@ -1,26 +0,0 @@
|
||||
import * as assert from 'assert'
|
||||
|
||||
import { txFlags } from '..'
|
||||
import { removeUndefined } from '../../utils'
|
||||
|
||||
import parseAmount from './amount'
|
||||
import { parseMemos } from './utils'
|
||||
|
||||
const claimFlags = txFlags.PaymentChannelClaim
|
||||
|
||||
function parsePaymentChannelClaim(tx: any): object {
|
||||
assert.ok(tx.TransactionType === 'PaymentChannelClaim')
|
||||
|
||||
return removeUndefined({
|
||||
memos: parseMemos(tx),
|
||||
channel: tx.Channel,
|
||||
balance: tx.Balance && parseAmount(tx.Balance).value,
|
||||
amount: tx.Amount && parseAmount(tx.Amount).value,
|
||||
signature: tx.Signature,
|
||||
publicKey: tx.PublicKey,
|
||||
renew: Boolean(tx.Flags & claimFlags.Renew) || undefined,
|
||||
close: Boolean(tx.Flags & claimFlags.Close) || undefined,
|
||||
})
|
||||
}
|
||||
|
||||
export default parsePaymentChannelClaim
|
||||
@@ -1,23 +0,0 @@
|
||||
import * as assert from 'assert'
|
||||
|
||||
import { removeUndefined } from '../../utils'
|
||||
|
||||
import parseAmount from './amount'
|
||||
import { parseTimestamp, parseMemos } from './utils'
|
||||
|
||||
function parsePaymentChannelCreate(tx: any): object {
|
||||
assert.ok(tx.TransactionType === 'PaymentChannelCreate')
|
||||
|
||||
return removeUndefined({
|
||||
memos: parseMemos(tx),
|
||||
amount: parseAmount(tx.Amount).value,
|
||||
destination: tx.Destination,
|
||||
settleDelay: tx.SettleDelay,
|
||||
publicKey: tx.PublicKey,
|
||||
cancelAfter: tx.CancelAfter && parseTimestamp(tx.CancelAfter),
|
||||
sourceTag: tx.SourceTag,
|
||||
destinationTag: tx.DestinationTag,
|
||||
})
|
||||
}
|
||||
|
||||
export default parsePaymentChannelCreate
|
||||
@@ -1,19 +0,0 @@
|
||||
import * as assert from 'assert'
|
||||
|
||||
import { removeUndefined } from '../../utils'
|
||||
|
||||
import parseAmount from './amount'
|
||||
import { parseTimestamp, parseMemos } from './utils'
|
||||
|
||||
function parsePaymentChannelFund(tx: any): object {
|
||||
assert.ok(tx.TransactionType === 'PaymentChannelFund')
|
||||
|
||||
return removeUndefined({
|
||||
memos: parseMemos(tx),
|
||||
channel: tx.Channel,
|
||||
amount: parseAmount(tx.Amount).value,
|
||||
expiration: tx.Expiration && parseTimestamp(tx.Expiration),
|
||||
})
|
||||
}
|
||||
|
||||
export default parsePaymentChannelFund
|
||||
@@ -1,37 +0,0 @@
|
||||
import { PayChannel } from '../../models/ledger'
|
||||
import { removeUndefined, dropsToXrp } from '../../utils'
|
||||
|
||||
import { parseTimestamp, parseMemos } from './utils'
|
||||
|
||||
export interface FormattedPaymentChannel {
|
||||
account: string
|
||||
amount: string
|
||||
balance: string
|
||||
publicKey: string
|
||||
destination: string
|
||||
settleDelay: number
|
||||
expiration?: string
|
||||
cancelAfter?: string
|
||||
sourceTag?: number
|
||||
destinationTag?: number
|
||||
previousAffectingTransactionID: string
|
||||
previousAffectingTransactionLedgerVersion: number
|
||||
}
|
||||
|
||||
export function parsePaymentChannel(data: PayChannel): FormattedPaymentChannel {
|
||||
return removeUndefined({
|
||||
memos: parseMemos(data),
|
||||
account: data.Account,
|
||||
amount: dropsToXrp(data.Amount),
|
||||
balance: dropsToXrp(data.Balance),
|
||||
destination: data.Destination,
|
||||
publicKey: data.PublicKey,
|
||||
settleDelay: data.SettleDelay,
|
||||
expiration: parseTimestamp(data.Expiration),
|
||||
cancelAfter: parseTimestamp(data.CancelAfter),
|
||||
sourceTag: data.SourceTag,
|
||||
destinationTag: data.DestinationTag,
|
||||
previousAffectingTransactionID: data.PreviousTxnID,
|
||||
previousAffectingTransactionLedgerVersion: data.PreviousTxnLgrSeq,
|
||||
})
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
import * as assert from 'assert'
|
||||
|
||||
import _ from 'lodash'
|
||||
|
||||
import { txFlags } from '..'
|
||||
import { removeUndefined } from '../../utils'
|
||||
|
||||
import parseAmount from './amount'
|
||||
import * as utils from './utils'
|
||||
|
||||
function isNoDirectRipple(tx) {
|
||||
return (tx.Flags & txFlags.Payment.NoRippleDirect) !== 0
|
||||
}
|
||||
|
||||
function isQualityLimited(tx) {
|
||||
return (tx.Flags & txFlags.Payment.LimitQuality) !== 0
|
||||
}
|
||||
|
||||
function removeGenericCounterparty(amount, address) {
|
||||
return amount.counterparty === address
|
||||
? _.omit(amount, 'counterparty')
|
||||
: amount
|
||||
}
|
||||
|
||||
// Payment specification
|
||||
function parsePayment(tx: any): object {
|
||||
assert.ok(tx.TransactionType === 'Payment')
|
||||
|
||||
const source = {
|
||||
address: tx.Account,
|
||||
maxAmount: removeGenericCounterparty(
|
||||
parseAmount(tx.SendMax || tx.Amount),
|
||||
tx.Account,
|
||||
),
|
||||
tag: tx.SourceTag,
|
||||
}
|
||||
|
||||
const destination: {
|
||||
address: string
|
||||
tag: number | undefined
|
||||
} = {
|
||||
address: tx.Destination,
|
||||
tag: tx.DestinationTag,
|
||||
// Notice that `amount` is omitted to prevent misinterpretation
|
||||
}
|
||||
|
||||
return removeUndefined({
|
||||
source: removeUndefined(source),
|
||||
destination: removeUndefined(destination),
|
||||
memos: utils.parseMemos(tx),
|
||||
invoiceID: tx.InvoiceID,
|
||||
paths: tx.Paths ? JSON.stringify(tx.Paths) : undefined,
|
||||
allowPartialPayment: utils.isPartialPayment(tx) || undefined,
|
||||
noDirectRipple: isNoDirectRipple(tx) || undefined,
|
||||
limitQuality: isQualityLimited(tx) || undefined,
|
||||
})
|
||||
}
|
||||
|
||||
export default parsePayment
|
||||
@@ -1,69 +0,0 @@
|
||||
import * as assert from 'assert'
|
||||
|
||||
import _ from 'lodash'
|
||||
|
||||
import { constants } from '..'
|
||||
|
||||
import parseFields from './fields'
|
||||
|
||||
const AccountFlags = constants.AccountFlags
|
||||
|
||||
function getAccountRootModifiedNode(tx: any) {
|
||||
const modifiedNodes = tx.meta.AffectedNodes.filter(
|
||||
(node) => node.ModifiedNode.LedgerEntryType === 'AccountRoot',
|
||||
)
|
||||
assert.ok(modifiedNodes.length === 1)
|
||||
return modifiedNodes[0].ModifiedNode
|
||||
}
|
||||
|
||||
function parseFlags(tx: any): any {
|
||||
const settings: any = {}
|
||||
if (tx.TransactionType !== 'AccountSet') {
|
||||
return settings
|
||||
}
|
||||
|
||||
const node = getAccountRootModifiedNode(tx)
|
||||
const oldFlags = _.get(node.PreviousFields, 'Flags')
|
||||
const newFlags = _.get(node.FinalFields, 'Flags')
|
||||
|
||||
if (oldFlags != null && newFlags != null) {
|
||||
const changedFlags = oldFlags ^ newFlags
|
||||
const setFlags = newFlags & changedFlags
|
||||
const clearedFlags = oldFlags & changedFlags
|
||||
Object.entries(AccountFlags).forEach((entry) => {
|
||||
const [flagName, flagValue] = entry
|
||||
if (setFlags & flagValue) {
|
||||
settings[flagName] = true
|
||||
} else if (clearedFlags & flagValue) {
|
||||
settings[flagName] = false
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// enableTransactionIDTracking requires a special case because it
|
||||
// does not affect the Flags field; instead it adds/removes a field called
|
||||
// "AccountTxnID" to/from the account root.
|
||||
|
||||
const oldField = _.get(node.PreviousFields, 'AccountTxnID')
|
||||
const newField = _.get(node.FinalFields, 'AccountTxnID')
|
||||
if (newField && !oldField) {
|
||||
settings.enableTransactionIDTracking = true
|
||||
} else if (oldField && !newField) {
|
||||
settings.enableTransactionIDTracking = false
|
||||
}
|
||||
|
||||
return settings
|
||||
}
|
||||
|
||||
function parseSettings(tx: any) {
|
||||
const txType = tx.TransactionType
|
||||
assert.ok(
|
||||
txType === 'AccountSet' ||
|
||||
txType === 'SetRegularKey' ||
|
||||
txType === 'SignerListSet',
|
||||
)
|
||||
|
||||
return { ...parseFlags(tx), ...parseFields(tx) }
|
||||
}
|
||||
|
||||
export default parseSettings
|
||||
@@ -1,15 +0,0 @@
|
||||
import * as assert from 'assert'
|
||||
|
||||
import { removeUndefined } from '../../utils'
|
||||
|
||||
import { parseMemos } from './utils'
|
||||
|
||||
function parseTicketCreate(tx: any): object {
|
||||
assert.ok(tx.TransactionType === 'TicketCreate')
|
||||
return removeUndefined({
|
||||
memos: parseMemos(tx),
|
||||
ticketCount: tx.TicketCount,
|
||||
})
|
||||
}
|
||||
|
||||
export default parseTicketCreate
|
||||
@@ -1,103 +0,0 @@
|
||||
import { removeUndefined } from '../../utils'
|
||||
|
||||
import parseAccountDelete from './account-delete'
|
||||
import parseAmendment from './amendment' // pseudo-transaction
|
||||
import parseOrderCancellation from './cancellation'
|
||||
import parseCheckCancel from './check-cancel'
|
||||
import parseCheckCash from './check-cash'
|
||||
import parseCheckCreate from './check-create'
|
||||
import parseDepositPreauth from './deposit-preauth'
|
||||
import parseEscrowCancellation from './escrow-cancellation'
|
||||
import parseEscrowCreation from './escrow-creation'
|
||||
import parseEscrowExecution from './escrow-execution'
|
||||
import parseFeeUpdate from './fee-update' // pseudo-transaction
|
||||
import parseOrder from './order'
|
||||
import parsePayment from './payment'
|
||||
import parsePaymentChannelClaim from './payment-channel-claim'
|
||||
import parsePaymentChannelCreate from './payment-channel-create'
|
||||
import parsePaymentChannelFund from './payment-channel-fund'
|
||||
import parseSettings from './settings'
|
||||
import parseTicketCreate from './ticket-create'
|
||||
import parseTrustline from './trustline'
|
||||
import { parseOutcome } from './utils'
|
||||
|
||||
function parseTransactionType(type) {
|
||||
// Ordering matches https://developers.ripple.com/transaction-types.html
|
||||
const mapping = {
|
||||
AccountSet: 'settings',
|
||||
AccountDelete: 'accountDelete',
|
||||
CheckCancel: 'checkCancel',
|
||||
CheckCash: 'checkCash',
|
||||
CheckCreate: 'checkCreate',
|
||||
DepositPreauth: 'depositPreauth',
|
||||
EscrowCancel: 'escrowCancellation',
|
||||
EscrowCreate: 'escrowCreation',
|
||||
EscrowFinish: 'escrowExecution',
|
||||
OfferCancel: 'orderCancellation',
|
||||
OfferCreate: 'order',
|
||||
Payment: 'payment',
|
||||
PaymentChannelClaim: 'paymentChannelClaim',
|
||||
PaymentChannelCreate: 'paymentChannelCreate',
|
||||
PaymentChannelFund: 'paymentChannelFund',
|
||||
SetRegularKey: 'settings',
|
||||
SignerListSet: 'settings',
|
||||
TicketCreate: 'ticketCreate',
|
||||
TrustSet: 'trustline',
|
||||
|
||||
EnableAmendment: 'amendment', // pseudo-transaction
|
||||
SetFee: 'feeUpdate', // pseudo-transaction
|
||||
}
|
||||
return mapping[type] || null
|
||||
}
|
||||
|
||||
// includeRawTransaction: undefined by default (getTransaction)
|
||||
function parseTransaction(tx: any, includeRawTransaction: boolean): any {
|
||||
const type = parseTransactionType(tx.TransactionType)
|
||||
const mapping = {
|
||||
settings: parseSettings,
|
||||
accountDelete: parseAccountDelete,
|
||||
checkCancel: parseCheckCancel,
|
||||
checkCash: parseCheckCash,
|
||||
checkCreate: parseCheckCreate,
|
||||
depositPreauth: parseDepositPreauth,
|
||||
escrowCancellation: parseEscrowCancellation,
|
||||
escrowCreation: parseEscrowCreation,
|
||||
escrowExecution: parseEscrowExecution,
|
||||
orderCancellation: parseOrderCancellation,
|
||||
order: parseOrder,
|
||||
payment: parsePayment,
|
||||
paymentChannelClaim: parsePaymentChannelClaim,
|
||||
paymentChannelCreate: parsePaymentChannelCreate,
|
||||
paymentChannelFund: parsePaymentChannelFund,
|
||||
ticketCreate: parseTicketCreate,
|
||||
trustline: parseTrustline,
|
||||
|
||||
amendment: parseAmendment, // pseudo-transaction
|
||||
feeUpdate: parseFeeUpdate, // pseudo-transaction
|
||||
}
|
||||
const parser: Function = mapping[type]
|
||||
|
||||
const specification = parser
|
||||
? parser(tx)
|
||||
: {
|
||||
UNAVAILABLE: 'Unrecognized transaction type.',
|
||||
SEE_RAW_TRANSACTION:
|
||||
'Since this type is unrecognized, `rawTransaction` is included in this response.',
|
||||
}
|
||||
if (!parser) {
|
||||
includeRawTransaction = true
|
||||
}
|
||||
|
||||
const outcome = parseOutcome(tx)
|
||||
return removeUndefined({
|
||||
type,
|
||||
address: tx.Account,
|
||||
sequence: tx.Sequence,
|
||||
id: tx.hash,
|
||||
specification: removeUndefined(specification),
|
||||
outcome: outcome ? removeUndefined(outcome) : undefined,
|
||||
rawTransaction: includeRawTransaction ? JSON.stringify(tx) : undefined,
|
||||
})
|
||||
}
|
||||
|
||||
export default parseTransaction
|
||||
@@ -1,40 +0,0 @@
|
||||
import * as assert from 'assert'
|
||||
|
||||
import { txFlags } from '..'
|
||||
import { removeUndefined } from '../../utils'
|
||||
|
||||
import { parseQuality, parseMemos } from './utils'
|
||||
|
||||
const flags = txFlags.TrustSet
|
||||
|
||||
function parseFlag(flagsValue, trueValue, falseValue) {
|
||||
if (flagsValue & trueValue) {
|
||||
return true
|
||||
}
|
||||
if (flagsValue & falseValue) {
|
||||
return false
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
|
||||
function parseTrustline(tx: any): object {
|
||||
assert.ok(tx.TransactionType === 'TrustSet')
|
||||
|
||||
return removeUndefined({
|
||||
limit: tx.LimitAmount.value,
|
||||
currency: tx.LimitAmount.currency,
|
||||
counterparty: tx.LimitAmount.issuer,
|
||||
memos: parseMemos(tx),
|
||||
qualityIn: parseQuality(tx.QualityIn),
|
||||
qualityOut: parseQuality(tx.QualityOut),
|
||||
ripplingDisabled: parseFlag(
|
||||
tx.Flags,
|
||||
flags.SetNoRipple,
|
||||
flags.ClearNoRipple,
|
||||
),
|
||||
frozen: parseFlag(tx.Flags, flags.SetFreeze, flags.ClearFreeze),
|
||||
authorized: parseFlag(tx.Flags, flags.SetAuth, 0),
|
||||
})
|
||||
}
|
||||
|
||||
export default parseTrustline
|
||||
@@ -1,178 +0,0 @@
|
||||
import BigNumber from 'bignumber.js'
|
||||
import transactionParser from 'ripple-lib-transactionparser'
|
||||
|
||||
import { txFlags } from '..'
|
||||
import { Amount, Memo } from '../../common/types/objects'
|
||||
import { removeUndefined, dropsToXrp, rippleTimeToISOTime } from '../../utils'
|
||||
|
||||
import parseAmount from './amount'
|
||||
|
||||
interface OfferDescription {
|
||||
direction: string
|
||||
quantity: any
|
||||
totalPrice: any
|
||||
sequence: number
|
||||
status: string
|
||||
makerExchangeRate: string
|
||||
}
|
||||
|
||||
interface Orderbook {
|
||||
[key: string]: OfferDescription[]
|
||||
}
|
||||
|
||||
interface BalanceSheetItem {
|
||||
counterparty: string
|
||||
currency: string
|
||||
value: string
|
||||
}
|
||||
|
||||
interface BalanceSheet {
|
||||
[key: string]: BalanceSheetItem[]
|
||||
}
|
||||
|
||||
function adjustQualityForXRP(
|
||||
quality: string,
|
||||
takerGetsCurrency: string,
|
||||
takerPaysCurrency: string,
|
||||
) {
|
||||
// quality = takerPays.value/takerGets.value
|
||||
// using drops (1e-6 XRP) for XRP values
|
||||
const numeratorShift = takerPaysCurrency === 'XRP' ? -6 : 0
|
||||
const denominatorShift = takerGetsCurrency === 'XRP' ? -6 : 0
|
||||
const shift = numeratorShift - denominatorShift
|
||||
return shift === 0
|
||||
? quality
|
||||
: new BigNumber(quality).shiftedBy(shift).toString()
|
||||
}
|
||||
|
||||
function parseQuality(quality?: number | null): number | undefined {
|
||||
if (typeof quality !== 'number') {
|
||||
return undefined
|
||||
}
|
||||
return new BigNumber(quality).shiftedBy(-9).toNumber()
|
||||
}
|
||||
|
||||
function parseTimestamp(rippleTime?: number | null): string | undefined {
|
||||
if (typeof rippleTime !== 'number') {
|
||||
return undefined
|
||||
}
|
||||
return rippleTimeToISOTime(rippleTime)
|
||||
}
|
||||
|
||||
function removeEmptyCounterparty(amount) {
|
||||
if (amount.counterparty === '') {
|
||||
delete amount.counterparty
|
||||
}
|
||||
}
|
||||
|
||||
function removeEmptyCounterpartyInBalanceChanges(balanceChanges: BalanceSheet) {
|
||||
Object.entries(balanceChanges).forEach(([_, changes]) => {
|
||||
changes.forEach(removeEmptyCounterparty)
|
||||
})
|
||||
}
|
||||
|
||||
function removeEmptyCounterpartyInOrderbookChanges(
|
||||
orderbookChanges: Orderbook,
|
||||
) {
|
||||
Object.entries(orderbookChanges).forEach(([_, changes]) => {
|
||||
changes.forEach((change) => {
|
||||
Object.entries(change).forEach(removeEmptyCounterparty)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
function isPartialPayment(tx: any) {
|
||||
return (tx.Flags & txFlags.Payment.PartialPayment) !== 0
|
||||
}
|
||||
|
||||
function parseDeliveredAmount(tx: any): Amount | void {
|
||||
if (
|
||||
tx.TransactionType !== 'Payment' ||
|
||||
tx.meta.TransactionResult !== 'tesSUCCESS'
|
||||
) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
if (tx.meta.delivered_amount && tx.meta.delivered_amount === 'unavailable') {
|
||||
return undefined
|
||||
}
|
||||
|
||||
// parsable delivered_amount
|
||||
if (tx.meta.delivered_amount) {
|
||||
return parseAmount(tx.meta.delivered_amount)
|
||||
}
|
||||
|
||||
// DeliveredAmount only present on partial payments
|
||||
if (tx.meta.DeliveredAmount) {
|
||||
return parseAmount(tx.meta.DeliveredAmount)
|
||||
}
|
||||
|
||||
// no partial payment flag, use tx.Amount
|
||||
if (tx.Amount && !isPartialPayment(tx)) {
|
||||
return parseAmount(tx.Amount)
|
||||
}
|
||||
|
||||
// DeliveredAmount field was introduced at
|
||||
// ledger 4594095 - after that point its absence
|
||||
// on a tx flagged as partial payment indicates
|
||||
// the full amount was transferred. The amount
|
||||
// transferred with a partial payment before
|
||||
// that date must be derived from metadata.
|
||||
if (tx.Amount && tx.ledger_index > 4594094) {
|
||||
return parseAmount(tx.Amount)
|
||||
}
|
||||
|
||||
return undefined
|
||||
}
|
||||
|
||||
function parseOutcome(tx: any): any | undefined {
|
||||
const metadata = tx.meta || tx.metaData
|
||||
if (!metadata) {
|
||||
return undefined
|
||||
}
|
||||
const balanceChanges = transactionParser.parseBalanceChanges(metadata)
|
||||
const orderbookChanges = transactionParser.parseOrderbookChanges(metadata)
|
||||
const channelChanges = transactionParser.parseChannelChanges(metadata)
|
||||
|
||||
removeEmptyCounterpartyInBalanceChanges(balanceChanges)
|
||||
removeEmptyCounterpartyInOrderbookChanges(orderbookChanges)
|
||||
|
||||
return removeUndefined({
|
||||
result: tx.meta.TransactionResult,
|
||||
timestamp: parseTimestamp(tx.date),
|
||||
fee: dropsToXrp(tx.Fee),
|
||||
balanceChanges,
|
||||
orderbookChanges,
|
||||
channelChanges,
|
||||
ledgerVersion: tx.ledger_index,
|
||||
indexInLedger: tx.meta.TransactionIndex,
|
||||
deliveredAmount: parseDeliveredAmount(tx),
|
||||
})
|
||||
}
|
||||
|
||||
function hexToString(hex: string): string | undefined {
|
||||
return hex ? Buffer.from(hex, 'hex').toString('utf-8') : undefined
|
||||
}
|
||||
|
||||
function parseMemos(tx: any): Memo[] | undefined {
|
||||
if (!Array.isArray(tx.Memos) || tx.Memos.length === 0) {
|
||||
return undefined
|
||||
}
|
||||
return tx.Memos.map((m) => {
|
||||
return removeUndefined({
|
||||
type: m.Memo.parsed_memo_type || hexToString(m.Memo.MemoType),
|
||||
format: m.Memo.parsed_memo_format || hexToString(m.Memo.MemoFormat),
|
||||
data: m.Memo.parsed_memo_data || hexToString(m.Memo.MemoData),
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
export {
|
||||
parseQuality,
|
||||
parseOutcome,
|
||||
parseMemos,
|
||||
hexToString,
|
||||
parseTimestamp,
|
||||
adjustQualityForXRP,
|
||||
isPartialPayment,
|
||||
}
|
||||
@@ -1,12 +1,13 @@
|
||||
// import BigNumber from "bignumber.js";
|
||||
// import { assert } from "chai";
|
||||
|
||||
import { assert } from 'chai'
|
||||
import { BookOffersRequest } from '../../src'
|
||||
import requests from '../fixtures/requests'
|
||||
import responses from '../fixtures/responses'
|
||||
import rippled from '../fixtures/rippled'
|
||||
import { setupClient, teardownClient } from '../setupClient'
|
||||
import { addressTests, assertResultMatch } from '../testUtils'
|
||||
import { addressTests } from '../testUtils'
|
||||
|
||||
// function checkSortingOfOrders(orders) {
|
||||
// let previousRate = "0";
|
||||
@@ -94,11 +95,7 @@ describe('client.getOrderbook', function () {
|
||||
limit: 1,
|
||||
},
|
||||
)
|
||||
assertResultMatch(
|
||||
response,
|
||||
responses.getOrderbook.normal,
|
||||
'getOrderbook',
|
||||
)
|
||||
assert.deepEqual(response, responses.getOrderbook.normal)
|
||||
})
|
||||
|
||||
// it("invalid options", async function () {
|
||||
|
||||
Reference in New Issue
Block a user