From 187154a2b08b2c24307d2a8503d5010f236f3fa6 Mon Sep 17 00:00:00 2001 From: "Fred K. Schott" Date: Wed, 14 Mar 2018 16:08:57 -0700 Subject: [PATCH] Types cleanup + more API methods onto new request method (#857) * major types cleanup, more formatted api methods onto new request method - getPaymentChannel() now uses this.request() - getSettings() now uses this.request() - getLedger() now uses this.request() - transaction types cleaned up a bit, but still some work left to do --- src/api.ts | 8 +- src/common/types/commands/account_info.ts | 27 +--- src/common/types/commands/book_offers.ts | 10 +- src/common/types/commands/gateway_balances.ts | 1 - src/common/types/commands/index.ts | 2 + src/common/types/commands/ledger.ts | 20 +++ src/common/types/commands/ledger_entry.ts | 31 ++++ src/common/types/objects/accounts.ts | 17 --- src/common/types/objects/index.ts | 6 +- src/common/types/objects/ledger.ts | 26 ++++ src/common/types/objects/ledger_entries.ts | 56 +++++++ src/common/types/objects/orders.ts | 17 +++ src/common/types/objects/queue_data.ts | 16 ++ src/common/types/objects/settings.ts | 30 ++++ src/common/types/objects/signers.ts | 10 -- src/ledger/ledger.ts | 25 +-- src/ledger/orderbook.ts | 66 ++++---- src/ledger/orders.ts | 11 +- src/ledger/parse/account-order.ts | 17 ++- src/ledger/parse/ledger.ts | 27 +++- src/ledger/parse/order.ts | 7 +- src/ledger/parse/orderbook-order.ts | 23 ++- src/ledger/parse/payment-channel.ts | 37 +---- src/ledger/payment-channel.ts | 34 +++-- src/ledger/settings.ts | 41 ++--- src/ledger/transaction-types.ts | 144 ------------------ src/ledger/transaction.ts | 22 ++- src/ledger/transactions.ts | 17 ++- src/ledger/types.ts | 44 ------ src/ledger/utils.ts | 5 +- src/transaction/order.ts | 14 +- src/transaction/settings.ts | 36 +---- src/transaction/types.ts | 107 +++++++++++++ test/api-test.js | 8 +- 34 files changed, 520 insertions(+), 442 deletions(-) create mode 100644 src/common/types/commands/ledger.ts create mode 100644 src/common/types/commands/ledger_entry.ts delete mode 100644 src/common/types/objects/accounts.ts create mode 100644 src/common/types/objects/ledger.ts create mode 100644 src/common/types/objects/ledger_entries.ts create mode 100644 src/common/types/objects/orders.ts create mode 100644 src/common/types/objects/queue_data.ts create mode 100644 src/common/types/objects/settings.ts delete mode 100644 src/ledger/types.ts diff --git a/src/api.ts b/src/api.ts index 82be4f69..e42929be 100644 --- a/src/api.ts +++ b/src/api.ts @@ -49,7 +49,9 @@ import { AccountInfoRequest, AccountInfoResponse, AccountLinesRequest, AccountLinesResponse, BookOffersRequest, BookOffersResponse, - GatewayBalancesRequest, GatewayBalancesResponse + GatewayBalancesRequest, GatewayBalancesResponse, + LedgerRequest, LedgerResponse, + LedgerEntryRequest, LedgerEntryResponse } from './common/types/commands' @@ -154,6 +156,10 @@ class RippleAPI extends EventEmitter { Promise async _request(command: 'gateway_balances', params: GatewayBalancesRequest): Promise + async _request(command: 'ledger', params: LedgerRequest): + Promise + async _request(command: 'ledger_entry', params: LedgerEntryRequest): + Promise async _request(command: string, params: any = {}) { return this.connection.request({ ...params, diff --git a/src/common/types/commands/account_info.ts b/src/common/types/commands/account_info.ts index cd4bf0e7..78bf6a69 100644 --- a/src/common/types/commands/account_info.ts +++ b/src/common/types/commands/account_info.ts @@ -1,4 +1,8 @@ -import {AccountRoot, SignerList} from '../objects' +import { + AccountRootLedgerEntry, + SignerListLedgerEntry, + QueueData +} from '../objects' export interface AccountInfoRequest { account: string, @@ -10,27 +14,10 @@ export interface AccountInfoRequest { } export interface AccountInfoResponse { - account_data: AccountRoot, - signer_lists?: SignerList[], + account_data: AccountRootLedgerEntry, + signer_lists?: SignerListLedgerEntry[], ledger_current_index?: number, ledger_index?: number, queue_data?: QueueData, validated?: boolean } - -export interface QueueData { - txn_count: number, - auth_change_queued?: boolean, - lowest_sequence?: number, - highest_sequence?: number, - max_spend_drops_total?: string, - transactions?: TransactionData[] -} - -export interface TransactionData { - auth_change?: boolean, - fee?: string, - fee_level?: string, - max_spend_drops?: string, - seq?: number -} diff --git a/src/common/types/commands/book_offers.ts b/src/common/types/commands/book_offers.ts index 30e1c9e2..175b9a4c 100644 --- a/src/common/types/commands/book_offers.ts +++ b/src/common/types/commands/book_offers.ts @@ -1,7 +1,7 @@ import { TakerRequestAmount, - OfferCreateTransaction, - RippledAmount + RippledAmount, + OfferCreateTransaction } from '../objects' export interface BookOffersRequest { @@ -15,15 +15,15 @@ export interface BookOffersRequest { } export interface BookOffersResponse { - offers: OrderBookOffer[], + offers: BookOffer[], ledger_hash?: string, ledger_current_index?: number, ledger_index?: number, marker?: any } -export interface OrderBookOffer extends OfferCreateTransaction { - quality?: number +export interface BookOffer extends OfferCreateTransaction { + quality?: string owner_funds?: string, taker_gets_funded?: RippledAmount, taker_pays_funded?: RippledAmount diff --git a/src/common/types/commands/gateway_balances.ts b/src/common/types/commands/gateway_balances.ts index 27812982..09171d6f 100644 --- a/src/common/types/commands/gateway_balances.ts +++ b/src/common/types/commands/gateway_balances.ts @@ -1,6 +1,5 @@ import {Amount} from '../objects' - export interface GatewayBalancesRequest { account: string, strict?: boolean, diff --git a/src/common/types/commands/index.ts b/src/common/types/commands/index.ts index 7316272e..bd6aa6ff 100644 --- a/src/common/types/commands/index.ts +++ b/src/common/types/commands/index.ts @@ -3,3 +3,5 @@ export * from './account_lines' export * from './account_offers' export * from './book_offers' export * from './gateway_balances' +export * from './ledger' +export * from './ledger_entry' diff --git a/src/common/types/commands/ledger.ts b/src/common/types/commands/ledger.ts new file mode 100644 index 00000000..05599113 --- /dev/null +++ b/src/common/types/commands/ledger.ts @@ -0,0 +1,20 @@ +import {Ledger, QueueData} from '../objects' + +export interface LedgerRequest { + ledger_hash?: string + ledger_index?: number | ('validated' | 'closed' | 'current') + full?: boolean + accounts?: boolean + transactions?: boolean + expand?: boolean + owner_funds?: boolean + binary?: boolean + queue?: boolean +} + +export interface LedgerResponse { + ledger_index: number + ledger_hash: string + ledger: Ledger + queue_data?: QueueData +} diff --git a/src/common/types/commands/ledger_entry.ts b/src/common/types/commands/ledger_entry.ts new file mode 100644 index 00000000..088fb0aa --- /dev/null +++ b/src/common/types/commands/ledger_entry.ts @@ -0,0 +1,31 @@ +import {LedgerEntry} from '../objects' + +export interface LedgerEntryRequest { + ledger_hash?: string + ledger_index?: number | ('validated' | 'closed' | 'current') + index?: string, + account_root?: string, + directory?: string | { + sub_index?: number, + dir_root: string + } | { + sub_index?: number, + owner: string + }, + offer?: string | { + account: string, + seq: number + }, + ripple_state?: { + accounts: [string, string], + currency: string + }, + binary?: boolean +} + +export interface LedgerEntryResponse { + index: string, + ledger_index: number, + node_binary?: string, + node?: LedgerEntry, +} diff --git a/src/common/types/objects/accounts.ts b/src/common/types/objects/accounts.ts deleted file mode 100644 index 6d79951a..00000000 --- a/src/common/types/objects/accounts.ts +++ /dev/null @@ -1,17 +0,0 @@ -export interface AccountRoot { - LedgerEntryType: string, - Account: string, - Flags: number, - Sequence: number, - Balance: string, - OwnerCount: number, - PreviousTxnID: string, - PreviousTxnLgrSeq: number, - AccountTxnID?: string, - RegularKey?: string, - EmailHash?: string, - MessageKey?: string - TickSize?: number, - TransferRate?: number, - Domain?: string -} diff --git a/src/common/types/objects/index.ts b/src/common/types/objects/index.ts index b11a227b..6bd5c2f6 100644 --- a/src/common/types/objects/index.ts +++ b/src/common/types/objects/index.ts @@ -1,7 +1,11 @@ -export * from './accounts' export * from './adjustments' export * from './amounts' +export * from './ledger' +export * from './ledger_entries' export * from './memos' +export * from './orders' +export * from './queue_data' +export * from './settings' export * from './signers' export * from './transactions' export * from './trustlines' diff --git a/src/common/types/objects/ledger.ts b/src/common/types/objects/ledger.ts new file mode 100644 index 00000000..5a6de839 --- /dev/null +++ b/src/common/types/objects/ledger.ts @@ -0,0 +1,26 @@ +export interface Ledger { + account_hash: string, + accounts?: any[], + close_time: number, + close_time_human: string, + close_time_resolution: number, + closed: boolean, + ledger_hash: string, + ledger_index: string, + parent_hash: string, + total_coins: string, + transaction_hash: string, + transactions: string[] | object[], + // @deprecated + seqNum?: string, + // @deprecated + totalCoins?: string, + // @deprecated + hash?: string, + // TODO: undocumented + close_flags?: number, + // TODO: undocumented + parent_close_time?: number, + // TODO: undocumented + accountState?: any +} diff --git a/src/common/types/objects/ledger_entries.ts b/src/common/types/objects/ledger_entries.ts new file mode 100644 index 00000000..a24b35ed --- /dev/null +++ b/src/common/types/objects/ledger_entries.ts @@ -0,0 +1,56 @@ +import {SignerEntry} from './index' + +export interface PayChannelLedgerEntry { + LedgerEntryType: 'PayChannel', + Sequence: number, + Account: string, + Amount: string, + Balance: string, + PublicKey: string, + Destination: string, + SettleDelay: number, + Expiration?: number, + CancelAfter?: number, + SourceTag?: number, + DestinationTag?: number, + OwnerNode: string, + PreviousTxnID: string, + PreviousTxnLgrSeq: number, + index: string +} + +export interface AccountRootLedgerEntry { + LedgerEntryType: 'AccountRoot', + Account: string, + Flags: number, + Sequence: number, + Balance: string, + OwnerCount: number, + PreviousTxnID: string, + PreviousTxnLgrSeq: number, + AccountTxnID?: string, + RegularKey?: string, + EmailHash?: string, + MessageKey?: string + TickSize?: number, + TransferRate?: number, + Domain?: string +} + +export interface SignerListLedgerEntry { + LedgerEntryType: 'SignerList', + OwnerNode: string, + SignerQuorum: number, + SignerEntries: SignerEntry[], + SignerListID: number, + PreviousTxnID: string, + PreviousTxnLgrSeq: number +} + +// TODO: Add the other ledger entry types, then remove the `any` fallback +// see https://ripple.com/build/ledger-format/#ledger-object-types +export type LedgerEntry = + PayChannelLedgerEntry | + AccountRootLedgerEntry | + SignerListLedgerEntry | + any diff --git a/src/common/types/objects/orders.ts b/src/common/types/objects/orders.ts new file mode 100644 index 00000000..11985d79 --- /dev/null +++ b/src/common/types/objects/orders.ts @@ -0,0 +1,17 @@ +import {Amount} from './amounts' +import {Memo} from './memos' + +export type FormattedOrderSpecification = { + direction: string, + quantity: Amount, + totalPrice: Amount, + immediateOrCancel?: boolean, + fillOrKill?: boolean, + expirationTime?: string, + orderToReplace?: number, + memos?: Memo[], + // If enabled, the offer will not consume offers that exactly match it, and + // instead becomes an Offer node in the ledger. It will still consume offers + // that cross it. + passive?: boolean +} diff --git a/src/common/types/objects/queue_data.ts b/src/common/types/objects/queue_data.ts new file mode 100644 index 00000000..6aeccb7c --- /dev/null +++ b/src/common/types/objects/queue_data.ts @@ -0,0 +1,16 @@ +export interface QueueTransaction { + auth_change: boolean, + fee: string, + fee_level: string, + max_spend_drops: string, + seq: number +} + +export interface QueueData { + txn_count: number, + auth_change_queued?: boolean, + lowest_sequence?: number, + highest_sequence?: number, + max_spend_drops_total?: string, + transactions?: QueueTransaction[] +} diff --git a/src/common/types/objects/settings.ts b/src/common/types/objects/settings.ts new file mode 100644 index 00000000..94ed35f0 --- /dev/null +++ b/src/common/types/objects/settings.ts @@ -0,0 +1,30 @@ +import {Memo} from './memos' + +export type WeightedSigner = { + address: string, + weight: number +} + +export type Signers = { + threshold?: number, + weights: WeightedSigner[] +} + +export type FormattedSettings = { + passwordSpent?: boolean, + requireDestinationTag?: boolean, + requireAuthorization?: boolean, + disallowIncomingXRP?: boolean, + disableMasterKey?: boolean, + enableTransactionIDTracking?: boolean, + noFreeze?: boolean, + globalFreeze?: boolean, + defaultRipple?: boolean, + emailHash?: string|null, + messageKey?: string, + domain?: string, + transferRate?: number|null, + regularKey?: string, + signers?: Signers, + memos?: Memo[] +} diff --git a/src/common/types/objects/signers.ts b/src/common/types/objects/signers.ts index a90f737e..9df63fd8 100644 --- a/src/common/types/objects/signers.ts +++ b/src/common/types/objects/signers.ts @@ -1,13 +1,3 @@ -export interface SignerList { - LedgerEntryType: string, - OwnerNode: string, - SignerQuorum: number, - SignerEntries: SignerEntry[], - SignerListID: number, - PreviousTxnID: string, - PreviousTxnLgrSeq: number -} - export interface SignerEntry { Account: string, SignerWeight: number diff --git a/src/ledger/ledger.ts b/src/ledger/ledger.ts index 9fc1322b..b7847c5f 100644 --- a/src/ledger/ledger.ts +++ b/src/ledger/ledger.ts @@ -1,28 +1,29 @@ import {validate} from '../common' -import parseLedger from './parse/ledger' -import {GetLedger} from './types' +import {FormattedLedger, parseLedger} from './parse/ledger' +import {RippleAPI} from '../api' -export type LedgerOptions = { +export type GetLedgerOptions = { ledgerVersion?: number, includeAllData?: boolean, includeTransactions?: boolean, includeState?: boolean } - -function getLedger(options: LedgerOptions = {}): Promise { +async function getLedger( + this: RippleAPI, options: GetLedgerOptions = {} +): Promise { + // 1. Validate validate.getLedger({options}) - - const request = { - command: 'ledger', + // 2. Make Request + const response = await this._request('ledger', { ledger_index: options.ledgerVersion || 'validated', expand: options.includeAllData, transactions: options.includeTransactions, accounts: options.includeState - } - - return this.connection.request(request).then(response => - parseLedger(response.ledger)) + }) + // 3. Return Formatted Response + return parseLedger(response.ledger) } + export default getLedger diff --git a/src/ledger/orderbook.ts b/src/ledger/orderbook.ts index c14d3095..d5136b35 100644 --- a/src/ledger/orderbook.ts +++ b/src/ledger/orderbook.ts @@ -1,51 +1,28 @@ import * as _ from 'lodash' import * as utils from './utils' -import parseOrderbookOrder from './parse/orderbook-order' +import { + parseOrderbookOrder, + FormattedOrderbookOrder +} from './parse/orderbook-order' import {validate} from '../common' -import {OrderSpecification} from './types' import {Amount, Issue} from '../common/types/objects' +import {BookOffer} from '../common/types/commands' import {RippleAPI} from '../api' -import {OfferCreateTransaction} from '../common/types/objects' -export type OrdersOptions = { - limit?: number, - ledgerVersion?: number -} - -export type Orderbook = { - base: Issue, - counter: Issue -} - -export type OrderbookItem = { - specification: OrderSpecification, - properties: { - maker: string, - sequence: number, - makerExchangeRate: string - }, - state?: { - fundedAmount: Amount, - priceOfFundedAmount: Amount - } -} - -export type OrderbookOrders = Array - -export type GetOrderbook = { - bids: OrderbookOrders, - asks: OrderbookOrders +export type FormattedOrderbook = { + bids: FormattedOrderbookOrder[], + asks: FormattedOrderbookOrder[] } function isSameIssue(a: Amount, b: Amount) { return a.currency === b.currency && a.counterparty === b.counterparty } -function directionFilter(direction: string, order: OrderbookItem) { +function directionFilter(direction: string, order: FormattedOrderbookOrder) { return order.specification.direction === direction } -function flipOrder(order: OrderbookItem) { +function flipOrder(order: FormattedOrderbookOrder) { const specification = order.specification const flippedSpecification = { quantity: specification.totalPrice, @@ -56,13 +33,13 @@ function flipOrder(order: OrderbookItem) { return _.merge({}, order, {specification: newSpecification}) } -function alignOrder(base: Amount, order: OrderbookItem) { +function alignOrder(base: Amount, order: FormattedOrderbookOrder) { const quantity = order.specification.quantity return isSameIssue(quantity, base) ? order : flipOrder(order) } function formatBidsAndAsks( - orderbook: Orderbook, offers: OfferCreateTransaction[]) { + orderbook: OrderbookInfo, offers: BookOffer[]) { // the "base" currency is the currency that you are buying or selling // the "counter" is the currency that the "base" is priced in // a "bid"/"ask" is an order to buy/sell the base, respectively @@ -83,7 +60,7 @@ function formatBidsAndAsks( // account is to specify a "perspective", which affects which unfunded offers // are returned async function makeRequest( - api: RippleAPI, taker: string, options: OrdersOptions, + api: RippleAPI, taker: string, options: GetOrderbookOptions, takerGets: Issue, takerPays: Issue ) { const orderData = utils.renameCounterpartyToIssuerInOrder({ @@ -99,12 +76,23 @@ async function makeRequest( }) } + +export type GetOrderbookOptions = { + limit?: number, + ledgerVersion?: number +} + +export type OrderbookInfo = { + base: Issue, + counter: Issue +} + export default async function getOrderbook( this: RippleAPI, address: string, - orderbook: Orderbook, - options: OrdersOptions = {} -): Promise { + orderbook: OrderbookInfo, + options: GetOrderbookOptions = {} +): Promise { // 1. Validate validate.getOrderbook({address, orderbook, options}) // 2. Make Request diff --git a/src/ledger/orders.ts b/src/ledger/orders.ts index 838c78b4..50f6b5ef 100644 --- a/src/ledger/orders.ts +++ b/src/ledger/orders.ts @@ -1,9 +1,8 @@ import * as _ from 'lodash' import {validate} from '../common' -import parseAccountOrder from './parse/account-order' -import {Order} from './types' +import {FormattedAccountOrder, parseAccountOrder} from './parse/account-order' import {RippleAPI} from '../api' -import {AccountOffersResponse} from '../common/types/commands/account_offers' +import {AccountOffersResponse} from '../common/types/commands' export type GetOrdersOptions = { limit?: number, @@ -12,8 +11,8 @@ export type GetOrdersOptions = { function formatResponse( address: string, responses: AccountOffersResponse[] -): Order[] { - let orders: Order[] = [] +): FormattedAccountOrder[] { + let orders: FormattedAccountOrder[] = [] for (const response of responses) { const offers = response.offers.map(offer => { return parseAccountOrder(address, offer) @@ -25,7 +24,7 @@ function formatResponse( export default async function getOrders( this: RippleAPI, address: string, options: GetOrdersOptions = {} -): Promise { +): Promise { // 1. Validate validate.getOrders({address, options}) // 2. Make Request diff --git a/src/ledger/parse/account-order.ts b/src/ledger/parse/account-order.ts index 85d3dbe0..997a7c5f 100644 --- a/src/ledger/parse/account-order.ts +++ b/src/ledger/parse/account-order.ts @@ -3,7 +3,16 @@ import parseAmount from './amount' import {parseTimestamp, adjustQualityForXRP} from './utils' import {removeUndefined} from '../../common' import {orderFlags} from './flags' -import {Order} from '../types' +import {FormattedOrderSpecification} from '../../common/types/objects' + +export type FormattedAccountOrder = { + specification: FormattedOrderSpecification, + properties: { + maker: string, + sequence: number, + makerExchangeRate: string + } +} // TODO: remove this function once rippled provides quality directly function computeQuality(takerGets, takerPays) { @@ -13,7 +22,9 @@ function computeQuality(takerGets, takerPays) { // rippled 'account_offers' returns a different format for orders than 'tx' // the flags are also different -function parseAccountOrder(address: string, order: any): Order { +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) @@ -43,5 +54,3 @@ function parseAccountOrder(address: string, order: any): Order { return {specification, properties} } - -export default parseAccountOrder diff --git a/src/ledger/parse/ledger.ts b/src/ledger/parse/ledger.ts index 5a01e640..1d35b2c0 100644 --- a/src/ledger/parse/ledger.ts +++ b/src/ledger/parse/ledger.ts @@ -1,7 +1,28 @@ import * as _ from 'lodash' import {removeUndefined, rippleTimeToISO8601} from '../../common' import parseTransaction from './transaction' -import {GetLedger} from '../types' +import {Ledger} from '../../common/types/objects' + +export type FormattedLedger = { + // TODO: properties in type don't match response object. Fix! + // accepted: boolean, + // closed: boolean, + stateHash: string, + closeTime: string, + closeTimeResolution: number, + closeFlags: number, + ledgerHash: string, + ledgerVersion: number, + parentLedgerHash: string, + parentCloseTime: string, + totalDrops: string, + transactionHash: string, + transactions?: Array, + rawTransactions?: string, + transactionHashes?: Array, + rawState?: string, + stateHashes?: Array +} function parseTransactionWrapper(ledgerVersion, tx) { const transaction = _.assign({}, _.omit(tx, 'metaData'), { @@ -39,7 +60,7 @@ function parseState(state) { return {rawState: JSON.stringify(state)} } -function parseLedger(ledger: any): GetLedger { +export function parseLedger(ledger: Ledger): FormattedLedger { const ledgerVersion = parseInt(ledger.ledger_index || ledger.seqNum, 10) return removeUndefined(Object.assign({ stateHash: ledger.account_hash, @@ -57,5 +78,3 @@ function parseLedger(ledger: any): GetLedger { parseState(ledger.accountState) )) } - -export default parseLedger diff --git a/src/ledger/parse/order.ts b/src/ledger/parse/order.ts index de54a1ae..3a086505 100644 --- a/src/ledger/parse/order.ts +++ b/src/ledger/parse/order.ts @@ -2,9 +2,14 @@ import * as assert from 'assert' import {parseTimestamp} from './utils' import parseAmount from './amount' import {removeUndefined, txFlags} from '../../common' +import { + FormattedOrderSpecification, + OfferCreateTransaction +} from '../../common/types/objects/index' + const flags = txFlags.OfferCreate -function parseOrder(tx: any): Object { +function parseOrder(tx: OfferCreateTransaction): FormattedOrderSpecification { assert(tx.TransactionType === 'OfferCreate') const direction = (tx.Flags & flags.Sell) === 0 ? 'buy' : 'sell' diff --git a/src/ledger/parse/orderbook-order.ts b/src/ledger/parse/orderbook-order.ts index c744027b..e1a6d586 100644 --- a/src/ledger/parse/orderbook-order.ts +++ b/src/ledger/parse/orderbook-order.ts @@ -4,8 +4,25 @@ import {removeUndefined} from '../../common' import {orderFlags} from './flags' import parseAmount from './amount' +import {BookOffer} from '../../common/types/commands' +import {Amount, FormattedOrderSpecification} from '../../common/types/objects' -function parseOrderbookOrder(order: any): Object { +export type FormattedOrderbookOrder = { + specification: FormattedOrderSpecification, + properties: { + maker: string, + sequence: number, + makerExchangeRate: string + }, + state?: { + fundedAmount: Amount, + priceOfFundedAmount: Amount + } +} + +export function parseOrderbookOrder( + order: BookOffer +): FormattedOrderbookOrder { const direction = (order.Flags & orderFlags.Sell) === 0 ? 'buy' : 'sell' const takerGetsAmount = parseAmount(order.TakerGets) const takerPaysAmount = parseAmount(order.TakerPays) @@ -14,7 +31,7 @@ function parseOrderbookOrder(order: any): Object { // note: immediateOrCancel and fillOrKill orders cannot enter the order book // so we can omit those flags here - const specification = removeUndefined({ + const specification: FormattedOrderSpecification = removeUndefined({ direction: direction, quantity: quantity, totalPrice: totalPrice, @@ -40,5 +57,3 @@ function parseOrderbookOrder(order: any): Object { const state = _.isEmpty(available) ? undefined : available return removeUndefined({specification, properties, state}) } - -export default parseOrderbookOrder diff --git a/src/ledger/parse/payment-channel.ts b/src/ledger/parse/payment-channel.ts index 59641ded..1e8c05c7 100644 --- a/src/ledger/parse/payment-channel.ts +++ b/src/ledger/parse/payment-channel.ts @@ -1,35 +1,8 @@ import {parseTimestamp} from './utils' import {removeUndefined, dropsToXrp} from '../../common' +import {PayChannelLedgerEntry} from '../../common/types/objects' - -export type PaymentChannel = { - Sequence: number, - Account: string, - Amount: string, - Balance: string, - PublicKey: string, - Destination: string, - SettleDelay: number, - Expiration?: number, - CancelAfter?: number, - SourceTag?: number, - DestinationTag?: number, - OwnerNode: string, - LedgerEntryType: string, - PreviousTxnID: string, - PreviousTxnLgrSeq: number, - index: string -} - -export type LedgerEntryResponse = { - node: PaymentChannel, - ledger_current_index?: number, - ledger_hash?: string, - ledger_index: number, - validated: boolean -} - -export type PaymentChannelResponse = { +export type FormattedPaymentChannel = { account: string, balance: string, publicKey: string, @@ -43,7 +16,9 @@ export type PaymentChannelResponse = { previousAffectingTransactionLedgerVersion: number } -function parsePaymentChannel(data: PaymentChannel): PaymentChannelResponse { +export function parsePaymentChannel( + data: PayChannelLedgerEntry +): FormattedPaymentChannel { return removeUndefined({ account: data.Account, amount: dropsToXrp(data.Amount), @@ -59,5 +34,3 @@ function parsePaymentChannel(data: PaymentChannel): PaymentChannelResponse { previousAffectingTransactionLedgerVersion: data.PreviousTxnLgrSeq }) } - -export default parsePaymentChannel diff --git a/src/ledger/payment-channel.ts b/src/ledger/payment-channel.ts index a97dd904..17859537 100644 --- a/src/ledger/payment-channel.ts +++ b/src/ledger/payment-channel.ts @@ -1,29 +1,35 @@ -import parsePaymentChannel, { - LedgerEntryResponse, PaymentChannel +import { + parsePaymentChannel, + FormattedPaymentChannel } from './parse/payment-channel' import {validate, errors} from '../common' +import {RippleAPI} from '../api' +import {LedgerEntryResponse} from '../common/types/commands' const NotFoundError = errors.NotFoundError -function formatResponse(response: LedgerEntryResponse) { - if (response.node !== undefined && - response.node.LedgerEntryType === 'PayChannel') { - return parsePaymentChannel(response.node) - } else { +function formatResponse( + response: LedgerEntryResponse +): FormattedPaymentChannel { + if (response.node === undefined || + response.node.LedgerEntryType !== 'PayChannel') { throw new NotFoundError('Payment channel ledger entry not found') } + return parsePaymentChannel(response.node) } -function getPaymentChannel(id: string): Promise { +async function getPaymentChannel( + this: RippleAPI, id: string +): Promise { + // 1. Validate validate.getPaymentChannel({id}) - - const request = { - command: 'ledger_entry', + // 2. Make Request + const response = await this._request('ledger_entry', { index: id, binary: false, ledger_index: 'validated' - } - - return this.connection.request(request).then(formatResponse) + }) + // 3. Return Formatted Response + return formatResponse(response) } export default getPaymentChannel diff --git a/src/ledger/settings.ts b/src/ledger/settings.ts index ce2362e4..4494cc62 100644 --- a/src/ledger/settings.ts +++ b/src/ledger/settings.ts @@ -1,31 +1,15 @@ import * as _ from 'lodash' import parseFields from './parse/fields' import {validate, constants} from '../common' +import {FormattedSettings} from '../common/types/objects' +import {AccountInfoResponse} from '../common/types/commands' +import {RippleAPI} from '../api' const AccountFlags = constants.AccountFlags export type SettingsOptions = { ledgerVersion?: number } -export type GetSettings = { - passwordSpent?: boolean, - requireDestinationTag?: boolean, - requireAuthorization?: boolean, - depositAuthorization?: boolean, - disallowIncomingXRP?: boolean, - disableMasterKey?: boolean, - enableTransactionIDTracking?: boolean, - noFreeze?: boolean, - globalFreeze?: boolean, - defaultRipple?: boolean, - emailHash?: string|null, - messageKey?: string, - domain?: string, - transferRate?: number|null, - regularKey?: string -} - - function parseFlags(value) { const settings = {} for (const flagName in AccountFlags) { @@ -36,25 +20,26 @@ function parseFlags(value) { return settings } -function formatSettings(response) { +function formatSettings(response: AccountInfoResponse) { const data = response.account_data const parsedFlags = parseFlags(data.Flags) const parsedFields = parseFields(data) return _.assign({}, parsedFlags, parsedFields) } -function getSettings(address: string, options: SettingsOptions = {} -): Promise { +async function getSettings( + this: RippleAPI, address: string, options: SettingsOptions = {} +): Promise { + // 1. Validate validate.getSettings({address, options}) - - const request = { - command: 'account_info', + // 2. Make Request + const response = await this._request('account_info', { account: address, ledger_index: options.ledgerVersion || 'validated', signer_lists: true - } - - return this.connection.request(request).then(formatSettings) + }) + // 3. Return Formatted Response + return formatSettings(response) } export default getSettings diff --git a/src/ledger/transaction-types.ts b/src/ledger/transaction-types.ts index 10caba57..e69de29b 100644 --- a/src/ledger/transaction-types.ts +++ b/src/ledger/transaction-types.ts @@ -1,144 +0,0 @@ - -import {Amount, Memo} from '../common/types/objects' - -export type Outcome = { - result: string, - ledgerVersion: number, - indexInLedger: number, - fee: string, - balanceChanges: { - [key: string]: [{ - currency: string, - counterparty?: string, - value: string - }] - }, - orderbookChanges: Object, - timestamp?: string -} - -export type Adjustment = { - address: string, - amount: { - currency: string, - counterparty?: string, - value: string - }, - tag?: number -} - -export type Trustline = { - currency: string, - counterparty: string, - limit: string, - qualityIn?: number, - qualityOut?: number, - ripplingDisabled?: boolean, - authorized?: boolean, - frozen?: boolean -} - -export type Settings = { - passwordSpent?: boolean, - requireDestinationTag?: boolean, - requireAuthorization?: boolean, - depositAuthorization?: boolean, - disallowIncomingXRP?: boolean, - disableMasterKey?: boolean, - enableTransactionIDTracking?: boolean, - noFreeze?: boolean, - globalFreeze?: boolean, - defaultRipple?: boolean, - emailHash?: string, - messageKey?: string, - domain?: string, - transferRate?: number, - regularKey?: string -} - -export type OrderCancellation = { - orderSequence: number -} - -export type Payment = { - source: Adjustment, - destination: Adjustment, - paths?: string, - memos?: Array, - invoiceID?: string, - allowPartialPayment?: boolean, - noDirectRipple?: boolean, - limitQuality?: boolean -} - -export type PaymentTransaction = { - type: string, - specification: Payment, - outcome: Outcome, - id: string, - address: string, - sequence: number -} - -export type Order = { - direction: string, - quantity: Amount, - totalPrice: Amount, - immediateOrCancel?: boolean, - fillOrKill?: boolean, - passive?: boolean, - expirationTime?: string, - orderToReplace?: number, - memos?: Memo[] -} - -export type OrderTransaction = { - type: string, - specification: Order, - outcome: Outcome, - id: string, - address: string, - sequence: number -} - -export type OrderCancellationTransaction = { - type: string, - specification: OrderCancellation, - outcome: Outcome, - id: string, - address: string, - sequence: number -} - -export type TrustlineTransaction = { - type: string, - specification: Trustline, - outcome: Outcome, - id: string, - address: string, - sequence: number -} - -export type SettingsTransaction = { - type: string, - specification: Settings, - outcome: Outcome, - id: string, - address: string, - sequence: number -} - -export type TransactionOptions = { - minLedgerVersion?: number, - maxLedgerVersion?: number -} - -export type TransactionType = PaymentTransaction | OrderTransaction | - OrderCancellationTransaction | TrustlineTransaction | SettingsTransaction - -export type TransactionResponse = TransactionType & { - hash: string, - ledger_index: number, - meta: any, - validated?: boolean -} diff --git a/src/ledger/transaction.ts b/src/ledger/transaction.ts index 51fccdc1..90b06de5 100644 --- a/src/ledger/transaction.ts +++ b/src/ledger/transaction.ts @@ -3,12 +3,22 @@ import * as utils from './utils' import parseTransaction from './parse/transaction' import {validate, errors} from '../common' import {Connection} from '../common' -import { - TransactionType, TransactionResponse, TransactionOptions -} from './transaction-types' +import {FormattedTransactionType} from '../transaction/types' + +export type TransactionOptions = { + minLedgerVersion?: number, + maxLedgerVersion?: number +} +type TransactionResponse = FormattedTransactionType & { + hash: string, + ledger_index: number, + meta: any, + validated?: boolean +} + function attachTransactionDate(connection: Connection, tx: any -): Promise { +): Promise { if (tx.date) { return Promise.resolve(tx) } @@ -71,7 +81,7 @@ function convertError(connection: Connection, options: TransactionOptions, } function formatResponse(options: TransactionOptions, tx: TransactionResponse -): TransactionType { +): FormattedTransactionType { if (tx.validated !== true || !isTransactionInRange(tx, options)) { throw new errors.NotFoundError('Transaction not found') } @@ -79,7 +89,7 @@ function formatResponse(options: TransactionOptions, tx: TransactionResponse } function getTransaction(id: string, options: TransactionOptions = {} -): Promise { +): Promise { validate.getTransaction({id, options}) const request = { diff --git a/src/ledger/transactions.ts b/src/ledger/transactions.ts index 9c7e7930..9c7af7c7 100644 --- a/src/ledger/transactions.ts +++ b/src/ledger/transactions.ts @@ -4,9 +4,8 @@ const {computeTransactionHash} = require('ripple-hashes') import * as utils from './utils' import parseTransaction from './parse/transaction' import getTransaction from './transaction' -import {validate, errors} from '../common' -import {Connection} from '../common' -import {TransactionType} from './transaction-types' +import {validate, errors, Connection} from '../common' +import {FormattedTransactionType} from '../transaction/types' export type TransactionsOptions = { @@ -20,10 +19,10 @@ export type TransactionsOptions = { counterparty?: string, types?: Array, binary?: boolean, - startTx?: TransactionType + startTx?: FormattedTransactionType } -export type GetTransactionsResponse = Array +export type GetTransactionsResponse = Array function parseBinaryTransaction(transaction) { const tx = binary.decode(transaction.tx_blob) @@ -43,7 +42,7 @@ function parseAccountTxTransaction(tx) { {meta: _tx.meta, validated: _tx.validated})) } -function counterpartyFilter(filters, tx: TransactionType) { +function counterpartyFilter(filters, tx: FormattedTransactionType) { if (tx.address === filters.counterparty) { return true } @@ -57,7 +56,7 @@ function counterpartyFilter(filters, tx: TransactionType) { } function transactionFilter(address: string, filters: TransactionsOptions, - tx: TransactionType + tx: FormattedTransactionType ) { if (filters.excludeFailures && tx.outcome.result !== 'tesSUCCESS') { return false @@ -77,7 +76,9 @@ function transactionFilter(address: string, filters: TransactionsOptions, return true } -function orderFilter(options: TransactionsOptions, tx: TransactionType) { +function orderFilter( + options: TransactionsOptions, tx: FormattedTransactionType +) { return !options.startTx || (options.earliestFirst ? utils.compareTransactions(tx, options.startTx) > 0 : utils.compareTransactions(tx, options.startTx) < 0) diff --git a/src/ledger/types.ts b/src/ledger/types.ts deleted file mode 100644 index 44d2c4ee..00000000 --- a/src/ledger/types.ts +++ /dev/null @@ -1,44 +0,0 @@ - -import {Amount} from '../common/types/objects' - -export type OrderSpecification = { - direction: string, - quantity: Amount, - totalPrice: Amount, - immediateOrCancel?: boolean, - fillOrKill?: boolean, - // If enabled, the offer will not consume offers that exactly match it, and - // instead becomes an Offer node in the ledger. It will still consume offers - // that cross it. - passive?: boolean -} - -export type Order = { - specification: OrderSpecification, - properties: { - maker: string, - sequence: number, - makerExchangeRate: string - } -} - -export type GetLedger = { - // TODO: properties in type don't match response object. Fix! - // accepted: boolean, - // closed: boolean, - stateHash: string, - closeTime: string, - closeTimeResolution: number, - closeFlags: number, - ledgerHash: string, - ledgerVersion: number, - parentLedgerHash: string, - parentCloseTime: string, - totalDrops: string, - transactionHash: string, - transactions?: Array, - rawTransactions?: string, - transactionHashes?: Array, - rawState?: string, - stateHashes?: Array -} diff --git a/src/ledger/utils.ts b/src/ledger/utils.ts index 85f26614..f9d78419 100644 --- a/src/ledger/utils.ts +++ b/src/ledger/utils.ts @@ -2,7 +2,7 @@ import * as _ from 'lodash' import * as assert from 'assert' import * as common from '../common' import {Connection} from '../common' -import {TransactionType} from './transaction-types' +import {FormattedTransactionType} from '../transaction/types' import {Issue} from '../common/types/objects' export type RecursiveData = { @@ -78,7 +78,8 @@ function signum(num) { * them based on TransactionIndex * See: https://ripple.com/build/transactions/ */ -function compareTransactions(first: TransactionType, second: TransactionType +function compareTransactions( + first: FormattedTransactionType, second: FormattedTransactionType ): number { if (!first.outcome || !second.outcome) { return 0 diff --git a/src/transaction/order.ts b/src/transaction/order.ts index b85bdf29..98afaa82 100644 --- a/src/transaction/order.ts +++ b/src/transaction/order.ts @@ -2,16 +2,18 @@ import * as _ from 'lodash' import * as utils from './utils' const offerFlags = utils.common.txFlags.OfferCreate import {validate, iso8601ToRippleTime} from '../common' -import {Instructions, Prepare} from './types' -import {Order} from '../ledger/transaction-types' +import {Instructions, Prepare, OfferCreateTransaction} from './types' +import {FormattedOrderSpecification} from '../common/types/objects/index' -function createOrderTransaction(account: string, order: Order): Object { +function createOrderTransaction( + account: string, order: FormattedOrderSpecification +): OfferCreateTransaction { const takerPays = utils.common.toRippledAmount(order.direction === 'buy' ? order.quantity : order.totalPrice) const takerGets = utils.common.toRippledAmount(order.direction === 'buy' ? order.totalPrice : order.quantity) - const txJSON: any = { + const txJSON: Partial = { TransactionType: 'OfferCreate', Account: account, TakerGets: takerGets, @@ -39,10 +41,10 @@ function createOrderTransaction(account: string, order: Order): Object { if (order.memos !== undefined) { txJSON.Memos = _.map(order.memos, utils.convertMemo) } - return txJSON + return txJSON as OfferCreateTransaction } -function prepareOrder(address: string, order: Order, +function prepareOrder(address: string, order: FormattedOrderSpecification, instructions: Instructions = {} ): Promise { validate.prepareOrder({address, order, instructions}) diff --git a/src/transaction/settings.ts b/src/transaction/settings.ts index f5a9c890..9c60f2ae 100644 --- a/src/transaction/settings.ts +++ b/src/transaction/settings.ts @@ -6,36 +6,12 @@ const validate = utils.common.validate const AccountFlagIndices = utils.common.constants.AccountFlagIndices const AccountFields = utils.common.constants.AccountFields import {Instructions, Prepare} from './types' -import {Memo} from '../common/types/objects' - -export type WeightedSigner = {address: string, weight: number} -export type SettingsSigners = { - threshold?: number, - weights: WeightedSigner[] -} -export type Settings = { - passwordSpent?: boolean, - requireDestinationTag?: boolean, - requireAuthorization?: boolean, - disallowIncomingXRP?: boolean, - disableMasterKey?: boolean, - enableTransactionIDTracking?: boolean, - noFreeze?: boolean, - globalFreeze?: boolean, - defaultRipple?: boolean, - emailHash?: string, - messageKey?: string, - domain?: string, - transferRate?: number, - regularKey?: string, - signers?: SettingsSigners, - memos?: Memo[] -} +import {FormattedSettings, WeightedSigner} from '../common/types/objects' // Emptry string passed to setting will clear it const CLEAR_SETTING = null -function setTransactionFlags(txJSON: any, values: Settings) { +function setTransactionFlags(txJSON: any, values: FormattedSettings) { const keys = Object.keys(values) assert(keys.length === 1, 'ERROR: can only set one setting per transaction') const flagName = keys[0] @@ -50,7 +26,7 @@ function setTransactionFlags(txJSON: any, values: Settings) { } } -function setTransactionFields(txJSON: Object, input: Settings) { +function setTransactionFields(txJSON: Object, input: FormattedSettings) { const fieldSchema = AccountFields for (const fieldName in fieldSchema) { const field = fieldSchema[fieldName] @@ -101,7 +77,7 @@ function formatSignerEntry(signer: WeightedSigner): Object { } function createSettingsTransactionWithoutMemos( - account: string, settings: Settings + account: string, settings: FormattedSettings ): any { if (settings.regularKey !== undefined) { const removeRegularKey = { @@ -137,7 +113,7 @@ function createSettingsTransactionWithoutMemos( return txJSON } -function createSettingsTransaction(account: string, settings: Settings +function createSettingsTransaction(account: string, settings: FormattedSettings ): Object { const txJSON = createSettingsTransactionWithoutMemos(account, settings) if (settings.memos !== undefined) { @@ -146,7 +122,7 @@ function createSettingsTransaction(account: string, settings: Settings return txJSON } -function prepareSettings(address: string, settings: Settings, +function prepareSettings(address: string, settings: FormattedSettings, instructions: Instructions = {} ): Promise { validate.prepareSettings({address, settings, instructions}) diff --git a/src/transaction/types.ts b/src/transaction/types.ts index f53028e6..470d1f2f 100644 --- a/src/transaction/types.ts +++ b/src/transaction/types.ts @@ -1,4 +1,14 @@ +import { + FormattedOrderSpecification, + FormattedTrustline, + Adjustment, + RippledAmount, + Memo, + FormattedSettings +} from '../common/types/objects' +import {ApiMemo} from './utils' + export type Instructions = { sequence?: number, fee?: string, @@ -25,3 +35,100 @@ export type Submit = { txBlob?: string, txJson?: Object } + +export interface OfferCreateTransaction { + TransactionType: 'OfferCreate', + Account: string, + Fee: string, + Flags: number, + LastLedgerSequence: number, + Sequence: number, + TakerGets: RippledAmount, + TakerPays: RippledAmount, + Expiration?: number, + OfferSequence?: number, + Memos: {Memo: ApiMemo}[] +} + +export type Outcome = { + result: string, + ledgerVersion: number, + indexInLedger: number, + fee: string, + balanceChanges: { + [key: string]: [{ + currency: string, + counterparty?: string, + value: string + }] + }, + orderbookChanges: Object, + timestamp?: string +} + +export type FormattedOrderCancellation = { + orderSequence: number +} + +export type FormattedPayment = { + source: Adjustment, + destination: Adjustment, + paths?: string, + memos?: Array, + invoiceID?: string, + allowPartialPayment?: boolean, + noDirectRipple?: boolean, + limitQuality?: boolean +} + +export type FormattedPaymentTransaction = { + type: string, + specification: FormattedPayment, + outcome: Outcome, + id: string, + address: string, + sequence: number +} + +export type FormattedOrderTransaction = { + type: string, + specification: FormattedOrderSpecification, + outcome: Outcome, + id: string, + address: string, + sequence: number +} + +export type FormattedOrderCancellationTransaction = { + type: string, + specification: FormattedOrderCancellation, + outcome: Outcome, + id: string, + address: string, + sequence: number +} + +export type FormattedTrustlineTransaction = { + type: string, + specification: FormattedTrustline, + outcome: Outcome, + id: string, + address: string, + sequence: number +} + +export type FormattedSettingsTransaction = { + type: string, + specification: FormattedSettings, + outcome: Outcome, + id: string, + address: string, + sequence: number +} + +export type FormattedTransactionType = + FormattedPaymentTransaction | + FormattedOrderTransaction | + FormattedOrderCancellationTransaction | + FormattedTrustlineTransaction | + FormattedSettingsTransaction diff --git a/test/api-test.js b/test/api-test.js index 968c3423..aeceeb3b 100644 --- a/test/api-test.js +++ b/test/api-test.js @@ -1124,9 +1124,11 @@ describe('RippleAPI', function () { }); it('getSettings - invalid options', function () { - assert.throws(() => { - this.api.getSettings(address, { invalid: 'options' }); - }, this.api.errors.ValidationError); + return this.api.getSettings(address, { invalid: 'options' }).then(() => { + assert(false, 'Should throw ValidationError'); + }).catch(error => { + assert(error instanceof this.api.errors.ValidationError); + }); }); it('getAccountInfo', function () {