ci: run lint tests (#1603)

* turn on lint tests

* remove tsc

* fix errors in src/utils/hashes

* fix linter errors in src/utils

* fix lint issues in test/

* resolve lint issues in src/client

* resolve dependency cycle

* resolve other linting issues in src/models

* resolve rest of linting issues

* fix tests

* fix linting errors in test/integration

* fix rest of linting issues

* fix test name
This commit is contained in:
Mayukha Vadari
2021-09-22 16:39:10 -04:00
parent 57a6586898
commit 98c9b9bc14
43 changed files with 258 additions and 558 deletions

View File

@@ -19,8 +19,10 @@ module.exports = {
// Specify global variables that are predefined
env: {
node: true, // Enable node global variables & Node.js scoping
es2020: true, // Add all ECMAScript 2020 globals and automatically set the ecmaVersion parser option to ES2020
// Enable node global variables & Node.js scoping
node: true,
// Add all ECMAScript 2020 globals and automatically set the ecmaVersion parser option to ES2020
es2020: true,
},
plugins: [],
@@ -39,9 +41,30 @@ module.exports = {
{ max: 40, skipBlankLines: true, skipComments: true },
],
'max-statements': ['warn', 25],
'id-length': ['error', { exceptions: ['_'] }], // exception for lodash
// exception for lodash
'id-length': ['error', { exceptions: ['_'] }],
// no-shadow has false-positives for enum, @typescript-eslint version fixes that
'no-shadow': 'off',
'@typescript-eslint/no-shadow': ['error'],
},
overrides: [
{
files: ['.eslintrc.js'],
rules: {
'import/no-unused-modules': 'off',
'@typescript-eslint/no-magic-numbers': 'off',
},
},
{
// TODO: remove when snippets are written
files: ['snippets/src/*.ts'],
rules: {
'max-len': 'off',
'import/unambiguous': 'off',
'import/no-unused-modules': 'off',
},
},
{
files: ['test/**/*.ts'],
rules: {
@@ -79,6 +102,9 @@ module.exports = {
// Tests are already in 2 callbacks, so max 3 is pretty restrictive
'max-nested-callbacks': 'off',
// setup/teardown client is easier to do in before/after, even if there is only one testcase
'mocha/no-hooks-for-single-case': 'off',
},
},
{

View File

@@ -10,22 +10,22 @@ on:
workflow_dispatch:
jobs:
# build-and-lint:
# runs-on: ubuntu-latest
build-and-lint:
runs-on: ubuntu-latest
# strategy:
# matrix:
# node-version: [14.x]
strategy:
matrix:
node-version: [14.x]
# steps:
# - uses: actions/checkout@v2
# - name: Use Node.js ${{ matrix.node-version }}
# uses: actions/setup-node@v1
# with:
# node-version: ${{ matrix.node-version }}
# - run: npm run install
# - run: npm run lint
# - run: npm run build
steps:
- uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- run: npm install
- run: npm run lint
- run: npm run build
unit:
runs-on: ubuntu-latest

View File

@@ -92,7 +92,7 @@ function getAgent(url: string, config: ConnectionOptions): Agent | undefined {
node/global-require, global-require, -- Necessary for the `require` */
HttpsProxyAgent = require('https-proxy-agent')
/* eslint-enable @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-require-imports,
node/global-require, global-require */
node/global-require, global-require, */
} catch (_error) {
throw new Error('"proxy" option is not supported in the browser')
}
@@ -239,9 +239,8 @@ export class Connection extends EventEmitter {
const connectionTimeoutID = setTimeout(() => {
this.onConnectionFailed(
new ConnectionError(
`Error: connect() timed out after ${this.config.connectionTimeout} ms. ` +
`If your internet connection is working, the rippled server may be blocked or inaccessible. ` +
`You can also try setting the 'connectionTimeout' option in the Client constructor.`,
`Error: connect() timed out after ${this.config.connectionTimeout} ms. If your internet connection is working, the ` +
`rippled server may be blocked or inaccessible. You can also try setting the 'connectionTimeout' option in the Client constructor.`,
),
)
}, this.config.connectionTimeout)
@@ -380,8 +379,11 @@ export class Connection extends EventEmitter {
try {
this.requestManager.handleResponse(data)
} catch (error) {
// eslint-disable-next-line max-depth -- okay here
if (error instanceof Error) {
this.emit('error', 'badMessage', error.message, message)
} else {
this.emit('error', 'badMessage', error, error)
}
}
}

View File

@@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/member-ordering -- Okay for Client so that client methods can be near the bottom */
/* eslint-disable max-lines -- This might not be necessary later, but this file needs to be big right now */
import * as assert from 'assert'
import { EventEmitter } from 'events'
@@ -171,6 +172,7 @@ class Client extends EventEmitter {
* @param server - URL of the server to connect to.
* @param options - Options for client settings.
*/
// eslint-disable-next-line max-lines-per-function -- okay because we have to set up all the connection handlers
public constructor(server: string, options: ClientOptions = {}) {
super()
if (typeof server !== 'string' || !/wss?(?:\+unix)?:\/\//u.exec(server)) {
@@ -188,6 +190,20 @@ class Client extends EventEmitter {
this.emit('error', errorCode, errorMessage, data)
})
this.connection.on('connected', () => {
this.emit('connected')
})
this.connection.on('disconnected', (code: number) => {
let finalCode = code
// 4000: Connection uses a 4000 code internally to indicate a manual disconnect/close
// Since 4000 is a normal disconnect reason, we convert this to the standard exit code 1000
if (finalCode === INTENTIONAL_DISCONNECT_CODE) {
finalCode = 1000
}
this.emit('disconnected', finalCode)
})
this.connection.on('ledgerClosed', (ledger) => {
this.emit('ledgerClosed', ledger)
})
@@ -215,20 +231,6 @@ class Client extends EventEmitter {
this.connection.on('path_find', (path) => {
this.emit('path_find', path)
})
this.connection.on('connected', () => {
this.emit('connected')
})
this.connection.on('disconnected', (code: number) => {
let finalCode = code
// 4000: Connection uses a 4000 code internally to indicate a manual disconnect/close
// Since 4000 is a normal disconnect reason, we convert this to the standard exit code 1000
if (finalCode === INTENTIONAL_DISCONNECT_CODE) {
finalCode = 1000
}
this.emit('disconnected', finalCode)
})
}
/**
@@ -379,6 +381,7 @@ class Client extends EventEmitter {
listener: (phase: ConsensusStream) => void,
): this
public on(event: 'path_find', listener: (path: PathFindStream) => void): this
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- actually needs to be any here
public on(event: 'error', listener: (...err: any[]) => void): this
/**
* Event handler for subscription streams.
@@ -387,6 +390,7 @@ class Client extends EventEmitter {
* @param listener - Function to run on event.
* @returns This, because it inherits from EventEmitter.
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- actually needs to be any here
public on(eventName: string, listener: (...args: any[]) => void): this {
return super.on(eventName, listener)
}
@@ -519,4 +523,4 @@ class Client extends EventEmitter {
public errors = errors
}
export { Client, Connection }
export { Client }

View File

@@ -1,3 +1,4 @@
/* eslint-disable import/no-unused-modules -- Used by webpack */
/* eslint-disable max-classes-per-file -- Needs to be a wrapper for ws */
import { EventEmitter } from 'events'
@@ -27,7 +28,7 @@ interface WSWrapperOptions {
* Provides `EventEmitter` interface for native browser `WebSocket`,
* same, as `ws` package provides.
*/
class WSWrapper extends EventEmitter {
export default class WSWrapper extends EventEmitter {
public static CONNECTING = 0
public static OPEN = 1
public static CLOSING = 2
@@ -96,5 +97,3 @@ class WSWrapper extends EventEmitter {
return this.ws.readyState
}
}
export default WSWrapper

View File

@@ -1,4 +1,3 @@
// eslint-disable-next-line no-shadow -- No shadow here
enum ECDSA {
ed25519 = 'ed25519',
secp256k1 = 'ecdsa-secp256k1',

View File

@@ -12,3 +12,5 @@ export * from './utils'
export * from './errors'
export { default as Wallet } from './wallet'
export * from './wallet/signer'

View File

@@ -1,6 +1,5 @@
/* eslint-disable import/max-dependencies -- Needs to export all ledger objects */
/* eslint-disable import/no-unused-modules -- Needs to export all ledger objects */
import AccountRoot from './accountRoot'
import AccountRoot, { AccountRootLedgerFlags } from './accountRoot'
import Amendments from './amendments'
import Check from './check'
import DepositPreauth from './depositPreauth'
@@ -11,14 +10,15 @@ import Ledger from './ledger'
import LedgerEntry from './ledgerEntry'
import LedgerHashes from './ledgerHashes'
import NegativeUNL from './negativeUNL'
import Offer from './offer'
import Offer, { OfferLedgerFlags } from './offer'
import PayChannel from './payChannel'
import RippleState from './rippleState'
import SignerList from './signerList'
import RippleState, { RippleStateLedgerFlags } from './rippleState'
import SignerList, { SignerListLedgerFlags } from './signerList'
import Ticket from './ticket'
export {
AccountRoot,
AccountRootLedgerFlags,
Amendments,
Check,
DepositPreauth,
@@ -30,8 +30,11 @@ export {
LedgerHashes,
NegativeUNL,
Offer,
OfferLedgerFlags,
PayChannel,
RippleState,
RippleStateLedgerFlags,
SignerList,
SignerListLedgerFlags,
Ticket,
}

View File

@@ -1,4 +1,3 @@
/* eslint-disable import/max-dependencies -- Needed for the type */
import AccountRoot from './accountRoot'
import Amendments from './amendments'
import Check from './check'

View File

@@ -19,12 +19,15 @@ export default interface RippleState extends BaseLedgerEntry {
}
export enum RippleStateLedgerFlags {
lsfLowReserve = 0x00010000, // True, if entry counts toward reserve.
// True, if entry counts toward reserve.
lsfLowReserve = 0x00010000,
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
// True, low side has set freeze flag
lsfLowFreeze = 0x00400000,
// True, high side has set freeze flag
lsfHighFreeze = 0x00800000,
}

View File

@@ -19,5 +19,6 @@ export default interface SignerList extends BaseLedgerEntry {
}
export enum SignerListLedgerFlags {
lsfOneOwnerCount = 0x00010000, // True, uses only one OwnerCount
// True, uses only one OwnerCount
lsfOneOwnerCount = 0x00010000,
}

View File

@@ -54,6 +54,7 @@ const MAX_TICK_SIZE = 15
* @param tx - An AccountSet Transaction.
* @throws When the AccountSet is Malformed.
*/
// eslint-disable-next-line max-lines-per-function -- okay for this method, only a little over
export function validateAccountSet(tx: Record<string, unknown>): void {
validateBaseTransaction(tx)

View File

@@ -101,6 +101,7 @@ export function isAmount(amount: unknown): boolean {
)
}
// eslint-disable-next-line @typescript-eslint/no-empty-interface -- no global flags right now, so this is fine
export interface GlobalFlags {}
export interface BaseTransaction {

View File

@@ -1,5 +1,3 @@
/* eslint-disable import/no-unused-modules -- Needs to export all types + validate methods */
/* eslint-disable import/max-dependencies -- Needs to export all types + validate methods */
// TODO: replace * imports with direct imports
export * from './transaction'
export {

View File

@@ -9,7 +9,6 @@ import {
isAmount,
} from './common'
// eslint-disable-next-line no-shadow -- variable declaration is unique
export enum OfferCreateTransactionFlags {
tfPassive = 0x00010000,
tfImmediateOrCancel = 0x00020000,

View File

@@ -1,4 +1,3 @@
/* eslint-disable max-statements -- Necessary for validatePayment */
/* eslint-disable complexity -- Necessary for validatePayment */
import { ValidationError } from '../../errors'
import { Amount, Path } from '../common'
@@ -95,9 +94,9 @@ function checkPartialPayment(tx: Record<string, unknown>): void {
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- Only used by JS
const flags = tx.Flags as number | PaymentFlagsInterface
const isTfPartialPayment =
typeof flags !== 'number'
? flags.tfPartialPayment ?? false
: isFlagEnabled(flags, PaymentTransactionFlags.tfPartialPayment)
typeof flags === 'number'
? isFlagEnabled(flags, PaymentTransactionFlags.tfPartialPayment)
: flags.tfPartialPayment ?? false
if (!isTfPartialPayment) {
throw new ValidationError(

View File

@@ -19,6 +19,7 @@ export interface PaymentChannelCreate extends BaseTransaction {
* @param tx - An PaymentChannelCreate Transaction.
* @throws When the PaymentChannelCreate is Malformed.
*/
// eslint-disable-next-line max-lines-per-function -- okay for this function, there's a lot of things to check
export function validatePaymentChannelCreate(
tx: Record<string, unknown>,
): void {

View File

@@ -5,7 +5,7 @@ import _ from 'lodash'
import { encode, decode } from 'ripple-binary-codec'
import { ValidationError } from '../../errors'
import { setTransactionFlagsToNumber } from '../utils'
import setTransactionFlagsToNumber from '../utils/flags'
import { AccountDelete, validateAccountDelete } from './accountDelete'
import {
@@ -86,6 +86,13 @@ export interface TransactionAndMetadata {
*/
export function validate(transaction: Record<string, unknown>): void {
const tx = { ...transaction }
if (tx.TransactionType == null) {
throw new ValidationError('Object does not have a `TransactionType`')
}
if (typeof tx.TransactionType !== 'string') {
throw new ValidationError("Object's `TransactionType` is not a string")
}
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- okay here
setTransactionFlagsToNumber(tx as unknown as Transaction)
switch (tx.TransactionType) {
case 'AccountDelete':

103
src/models/utils/flags.ts Normal file
View File

@@ -0,0 +1,103 @@
/* eslint-disable no-param-reassign -- param reassign is safe */
/* eslint-disable no-bitwise -- flags require bitwise operations */
import { ValidationError } from '../../errors'
import {
AccountSetFlagsInterface,
AccountSetTransactionFlags,
} from '../transactions/accountSet'
import { GlobalFlags } from '../transactions/common'
import {
OfferCreateFlagsInterface,
OfferCreateTransactionFlags,
} from '../transactions/offerCreate'
import {
PaymentFlagsInterface,
PaymentTransactionFlags,
} from '../transactions/payment'
import {
PaymentChannelClaimFlagsInterface,
PaymentChannelClaimTransactionFlags,
} from '../transactions/paymentChannelClaim'
import type { Transaction } from '../transactions/transaction'
import {
TrustSetFlagsInterface,
TrustSetTransactionFlags,
} from '../transactions/trustSet'
/**
* Sets a transaction's flags to its numeric representation.
*
* @param tx - A transaction to set its flags to its numeric representation.
*/
export default function setTransactionFlagsToNumber(tx: Transaction): void {
if (tx.Flags == null) {
tx.Flags = 0
return
}
if (typeof tx.Flags === 'number') {
return
}
switch (tx.TransactionType) {
case 'AccountSet':
tx.Flags = convertAccountSetFlagsToNumber(tx.Flags)
return
case 'OfferCreate':
tx.Flags = convertOfferCreateFlagsToNumber(tx.Flags)
return
case 'PaymentChannelClaim':
tx.Flags = convertPaymentChannelClaimFlagsToNumber(tx.Flags)
return
case 'Payment':
tx.Flags = convertPaymentTransactionFlagsToNumber(tx.Flags)
return
case 'TrustSet':
tx.Flags = convertTrustSetFlagsToNumber(tx.Flags)
return
default:
tx.Flags = 0
}
}
function convertAccountSetFlagsToNumber(
flags: AccountSetFlagsInterface,
): number {
return reduceFlags(flags, AccountSetTransactionFlags)
}
function convertOfferCreateFlagsToNumber(
flags: OfferCreateFlagsInterface,
): number {
return reduceFlags(flags, OfferCreateTransactionFlags)
}
function convertPaymentChannelClaimFlagsToNumber(
flags: PaymentChannelClaimFlagsInterface,
): number {
return reduceFlags(flags, PaymentChannelClaimTransactionFlags)
}
function convertPaymentTransactionFlagsToNumber(
flags: PaymentFlagsInterface,
): number {
return reduceFlags(flags, PaymentTransactionFlags)
}
function convertTrustSetFlagsToNumber(flags: TrustSetFlagsInterface): number {
return reduceFlags(flags, TrustSetTransactionFlags)
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- added ValidationError check for flagEnum
function reduceFlags(flags: GlobalFlags, flagEnum: any): number {
return Object.keys(flags).reduce((resultFlags, flag) => {
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access -- safe member access
if (flagEnum[flag] == null) {
throw new ValidationError(
`flag ${flag} doesn't exist in flagEnum: ${JSON.stringify(flagEnum)}`,
)
}
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access -- safe member access
return flags[flag] ? resultFlags | flagEnum[flag] : resultFlags
}, 0)
}

View File

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

View File

@@ -5,7 +5,7 @@ import type { Client } from '..'
import { ValidationError } from '../errors'
import { AccountInfoRequest, LedgerRequest } from '../models/methods'
import { Transaction } from '../models/transactions'
import { setTransactionFlagsToNumber } from '../models/utils'
import setTransactionFlagsToNumber from '../models/utils/flags'
import { xrpToDrops } from '../utils'
// 20 drops
@@ -138,9 +138,9 @@ async function calculateFeePerTransactionType(
signersCount = 0,
): Promise<void> {
// netFee is usually 0.00001 XRP (10 drops)
const netFeeXRP: string = await client.getFee()
const netFeeDrops: string = xrpToDrops(netFeeXRP)
let baseFee: BigNumber = new BigNumber(netFeeDrops)
const netFeeXRP = await client.getFee()
const netFeeDrops = xrpToDrops(netFeeXRP)
let baseFee = new BigNumber(netFeeDrops)
// EscrowFinish Transaction with Fulfillment
if (tx.TransactionType === 'EscrowFinish' && tx.Fulfillment != null) {

View File

@@ -30,7 +30,7 @@ interface GetBalancesOptions {
/**
* Get XRP/non-XRP balances for an account.
*
* @param client - Client.
* @param this - Client.
* @param account - Account address.
* @param options - Options to include for getting balances.
* @returns An array of XRP/non-XRP balances.

View File

@@ -10,7 +10,7 @@ const BASE_10 = 10
* Note: This is a public API that can be called directly.
* This is not used by the `prepare*` methods. See `src/transaction/utils.ts`.
*
* @param client - The Client used to connect to the ledger.
* @param this - The Client used to connect to the ledger.
* @param cushion - The fee cushion to use.
* @returns The transaction fee.
*/

View File

@@ -82,10 +82,11 @@ async function getOrderbook(
const buy: BookOffer[] = []
const sell: BookOffer[] = []
orders.forEach((order) => {
if ((order.Flags & OfferLedgerFlags.lsfSell) !== 0) {
sell.push(order)
} else {
// eslint-disable-next-line no-bitwise -- necessary for flags check
if ((order.Flags & OfferLedgerFlags.lsfSell) === 0) {
buy.push(order)
} else {
sell.push(order)
}
})
return { buy: sortOffers(buy), sell: sortOffers(sell) }

View File

@@ -7,6 +7,7 @@ import { xAddressToClassicAddress, isValidXAddress } from 'ripple-address-codec'
* @returns The account's classic address.
* @throws Error if the X-Address has an associated tag.
*/
// eslint-disable-next-line import/prefer-default-export -- okay for a utils file - there could be more exports later
export function ensureClassicAddress(account: string): string {
if (isValidXAddress(account)) {
const { classicAddress, tag } = xAddressToClassicAddress(account)

View File

@@ -154,7 +154,7 @@ export function computeStateTreeHash(entries: LedgerEntry[]): string {
return shamap.hash
}
export function computeTransactionHash(
function computeTransactionHash(
ledger: Ledger,
options: ComputeLedgerHeaderHashOptions,
): string {
@@ -184,7 +184,7 @@ export function computeTransactionHash(
return transactionHash
}
export function computeStateHash(
function computeStateHash(
ledger: Ledger,
options: ComputeLedgerHeaderHashOptions,
): string {

View File

@@ -30,11 +30,12 @@ class Leaf extends Node {
/**
* Add item to Leaf.
*
* @param tag - Index of the Node.
* @param node - Node to insert.
* @param _tag - Index of the Node.
* @param _node - Node to insert.
* @throws When called, because LeafNodes cannot addItem.
*/
public addItem(tag: string, node: Node): void {
// eslint-disable-next-line class-methods-use-this -- no `this` needed here
public addItem(_tag: string, _node: Node): void {
throw new Error('Cannot call addItem on a LeafNode')
}

View File

@@ -70,6 +70,12 @@ function removeUndefined<T extends Record<string, unknown>>(obj: T): T {
return newObj
}
/**
* Converts a string to its hex equivalent. Useful for Memos.
*
* @param string - The string to convert to Hex.
* @returns The Hex equivalent of the string.
*/
function convertStringToHex(string: string): string {
return Buffer.from(string, 'utf8').toString('hex').toUpperCase()
}

View File

@@ -12,6 +12,7 @@ import { xrpToDrops } from './xrpConversion'
* @param publicKey - Public key that signed the paymentChannelClaim.
* @returns True if the channel is valid.
*/
// eslint-disable-next-line max-params -- Needs 4 params
function verifyPaymentChannelClaim(
channel: string,
amount: string,

View File

@@ -68,19 +68,17 @@ export function xrpToDrops(xrpToConvert: BigNumber.Value): string {
if (typeof xrp === 'string') {
if (!/^-?[0-9]*\.?[0-9]*$/u.exec(xrp)) {
throw new ValidationError(
`xrpToDrops: invalid value '${xrp}',` +
` should be a number matching (^-?[0-9]*\\.?[0-9]*$).`,
`xrpToDrops: invalid value '${xrp}', should be a number matching (^-?[0-9]*\\.?[0-9]*$).`,
)
} else if (xrp === '.') {
throw new ValidationError(
`xrpToDrops: invalid value '${xrp}',` +
` should be a BigNumber or string-encoded number.`,
`xrpToDrops: invalid value '${xrp}', should be a BigNumber or string-encoded number.`,
)
}
}
// Important: specify base 10 to avoid exponential notation, e.g. '1e-7'.
xrp = new BigNumber(xrp).toString(10)
xrp = new BigNumber(xrp).toString(BASE_TEN)
// This should never happen; the value has already been
// validated above. This just ensures BigNumber did not do
// something unexpected.

View File

@@ -15,7 +15,6 @@ interface FaucetWallet {
balance: number
}
// eslint-disable-next-line no-shadow -- Not previously declared
enum FaucetNetwork {
Testnet = 'faucet.altnet.rippletest.net',
Devnet = 'faucet.devnet.rippletest.net',
@@ -69,6 +68,17 @@ async function generateFaucetWallet(
// Options to pass to https.request
const options = getOptions(client, postBody)
return returnPromise(options, client, startingBalance, fundWallet, postBody)
}
// eslint-disable-next-line max-params -- Helper function created for organizational purposes
async function returnPromise(
options: RequestOptions,
client: Client,
startingBalance: number,
fundWallet: Wallet,
postBody: Uint8Array,
): Promise<Wallet | undefined> {
return new Promise((resolve, reject) => {
const request = httpsRequest(options, (response) => {
const chunks: Uint8Array[] = []

View File

@@ -4,6 +4,7 @@ import { Client } from 'xrpl-local'
describe('client constructor', function () {
it('Client - implicit server port', function () {
// eslint-disable-next-line no-new -- Testing constructor
new Client('wss://s1.ripple.com')
})

View File

@@ -1,362 +0,0 @@
// import BigNumber from 'bignumber.js'
// import {assert} from 'chai'
// import {Client} from 'xrpl-local'
// import requests from '../fixtures/requests'
// import responses from '../fixtures/responses'
// function checkSortingOfOrders(orders) {
// let previousRate = '0'
// for (var i = 0; i < orders.length; i++) {
// const order = orders[i]
// let rate
// // We calculate the quality of output/input here as a test.
// // This won't hold in general because when output and input amounts get tiny,
// // the quality can differ significantly. However, the offer stays in the
// // order book where it was originally placed. It would be more consistent
// // to check the quality from the offer book, but for the test data set,
// // this calculation holds.
// if (order.specification.direction === 'buy') {
// rate = new BigNumber(order.specification.quantity.value)
// .dividedBy(order.specification.totalPrice.value)
// .toString()
// } else {
// rate = new BigNumber(order.specification.totalPrice.value)
// .dividedBy(order.specification.quantity.value)
// .toString()
// }
// assert(
// new BigNumber(rate).isGreaterThanOrEqualTo(previousRate),
// 'Rates must be sorted from least to greatest: ' +
// rate +
// ' should be >= ' +
// previousRate
// )
// previousRate = rate
// }
// return true
// }
describe('client.formatBidsAndAsks', function () {
// 'normal': async (client, address) => {
// const orderbookInfo = {
// base: {
// currency: 'USD',
// counterparty: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B'
// },
// counter: {
// currency: 'BTC',
// counterparty: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B'
// }
// }
// await Promise.all([
// client.request({command: 'book_offers',
// taker_gets: orderbookInfo.base,
// taker_pays: orderbookInfo.counter,
// ledger_index: 'validated',
// limit: 20,
// taker: address
// }),
// client.request({command: 'book_offers',
// taker_gets: orderbookInfo.counter,
// taker_pays: orderbookInfo.base,
// ledger_index: 'validated',
// limit: 20,
// taker: address
// })
// ]).then(([directOfferResults, reverseOfferResults]) => {
// const directOffers = (directOfferResults
// ? directOfferResults.result.offers
// : []
// ).reduce((acc, res) => acc.concat(res), [])
// const reverseOffers = (reverseOfferResults
// ? reverseOfferResults.result.offers
// : []
// ).reduce((acc, res) => acc.concat(res), [])
// const orderbook = Client.formatBidsAndAsks(orderbookInfo, [
// ...directOffers,
// ...reverseOffers
// ])
// assert.deepEqual(orderbook, responses.getOrderbook.normal)
// })
// },
// 'with XRP': async (client, address) => {
// const orderbookInfo = {
// base: {
// currency: 'USD',
// issuer: 'rp8rJYTpodf8qbSCHVTNacf8nSW8mRakFw'
// },
// counter: {
// currency: 'XRP'
// }
// }
// await Promise.all([
// client.request({command: 'book_offers',
// taker_gets: orderbookInfo.base,
// taker_pays: orderbookInfo.counter,
// ledger_index: 'validated',
// limit: 20,
// taker: address
// }),
// client.request({command: 'book_offers',
// taker_gets: orderbookInfo.counter,
// taker_pays: orderbookInfo.base,
// ledger_index: 'validated',
// limit: 20,
// taker: address
// })
// ]).then(([directOfferResults, reverseOfferResults]) => {
// const directOffers = (directOfferResults
// ? directOfferResults.result.offers
// : []
// ).reduce((acc, res) => acc.concat(res), [])
// const reverseOffers = (reverseOfferResults
// ? reverseOfferResults.result.offers
// : []
// ).reduce((acc, res) => acc.concat(res), [])
// const orderbook = Client.formatBidsAndAsks(orderbookInfo, [
// ...directOffers,
// ...reverseOffers
// ])
// assert.deepEqual(orderbook, responses.getOrderbook.withXRP)
// })
// },
// 'sample XRP/JPY book has orders sorted correctly': async (client, address) => {
// const orderbookInfo = {
// base: {
// // the first currency in pair
// currency: 'XRP'
// },
// counter: {
// currency: 'JPY',
// issuer: 'rB3gZey7VWHYRqJHLoHDEJXJ2pEPNieKiS'
// }
// }
// const myAddress = 'rE9qNjzJXpiUbVomdv7R4xhrXVeH2oVmGR'
// await Promise.all([
// client.request({command: 'book_offers',
// taker_gets: orderbookInfo.base,
// taker_pays: orderbookInfo.counter,
// ledger_index: 'validated',
// limit: 400, // must match `test/fixtures/rippled/requests/1-taker_gets-XRP-taker_pays-JPY.json`
// taker: myAddress
// }),
// client.request({command: 'book_offers',
// taker_gets: orderbookInfo.counter,
// taker_pays: orderbookInfo.base,
// ledger_index: 'validated',
// limit: 400, // must match `test/fixtures/rippled/requests/2-taker_gets-JPY-taker_pays-XRP.json`
// taker: myAddress
// })
// ]).then(([directOfferResults, reverseOfferResults]) => {
// const directOffers = (directOfferResults
// ? directOfferResults.result.offers
// : []
// ).reduce((acc, res) => acc.concat(res), [])
// const reverseOffers = (reverseOfferResults
// ? reverseOfferResults.result.offers
// : []
// ).reduce((acc, res) => acc.concat(res), [])
// const orderbook = Client.formatBidsAndAsks(orderbookInfo, [
// ...directOffers,
// ...reverseOffers
// ])
// assert.deepStrictEqual([], orderbook.bids)
// return checkSortingOfOrders(orderbook.asks)
// })
// },
// 'sample USD/XRP book has orders sorted correctly': async (client, address) => {
// const orderbookInfo = {
// counter: {currency: 'XRP'},
// base: {
// currency: 'USD',
// counterparty: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B'
// }
// }
// const myAddress = 'rE9qNjzJXpiUbVomdv7R4xhrXVeH2oVmGR'
// await Promise.all([
// client.request({command: 'book_offers',
// taker_gets: orderbookInfo.base,
// taker_pays: orderbookInfo.counter,
// ledger_index: 'validated',
// limit: 400, // must match `test/fixtures/rippled/requests/1-taker_gets-XRP-taker_pays-JPY.json`
// taker: myAddress
// }),
// client.request({command: 'book_offers',
// taker_gets: orderbookInfo.counter,
// taker_pays: orderbookInfo.base,
// ledger_index: 'validated',
// limit: 400, // must match `test/fixtures/rippled/requests/2-taker_gets-JPY-taker_pays-XRP.json`
// taker: myAddress
// })
// ]).then(([directOfferResults, reverseOfferResults]) => {
// const directOffers = (directOfferResults
// ? directOfferResults.result.offers
// : []
// ).reduce((acc, res) => acc.concat(res), [])
// const reverseOffers = (reverseOfferResults
// ? reverseOfferResults.result.offers
// : []
// ).reduce((acc, res) => acc.concat(res), [])
// const orderbook = Client.formatBidsAndAsks(orderbookInfo, [
// ...directOffers,
// ...reverseOffers
// ])
// return (
// checkSortingOfOrders(orderbook.bids) &&
// checkSortingOfOrders(orderbook.asks)
// )
// })
// },
// 'sorted so that best deals come first': async (client, address) => {
// const orderbookInfo = {
// base: {
// currency: 'USD',
// counterparty: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B'
// },
// counter: {
// currency: 'BTC',
// counterparty: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B'
// }
// }
// await Promise.all([
// client.request({command: 'book_offers',
// taker_gets: orderbookInfo.base,
// taker_pays: orderbookInfo.counter,
// ledger_index: 'validated',
// limit: 20,
// taker: address
// }),
// client.request({command: 'book_offers',
// taker_gets: orderbookInfo.counter,
// taker_pays: orderbookInfo.base,
// ledger_index: 'validated',
// limit: 20,
// taker: address
// })
// ]).then(([directOfferResults, reverseOfferResults]) => {
// const directOffers = (directOfferResults
// ? directOfferResults.result.offers
// : []
// ).reduce((acc, res) => acc.concat(res), [])
// const reverseOffers = (reverseOfferResults
// ? reverseOfferResults.result.offers
// : []
// ).reduce((acc, res) => acc.concat(res), [])
// const orderbook = Client.formatBidsAndAsks(orderbookInfo, [
// ...directOffers,
// ...reverseOffers
// ])
// const bidRates = orderbook.bids.map(
// (bid) => bid.properties.makerExchangeRate
// )
// const askRates = orderbook.asks.map(
// (ask) => ask.properties.makerExchangeRate
// )
// // makerExchangeRate = quality = takerPays.value/takerGets.value
// // so the best deal for the taker is the lowest makerExchangeRate
// // bids and asks should be sorted so that the best deals come first
// assert.deepEqual(bidRates.map((x) => Number(x)).sort(), bidRates)
// assert.deepEqual(askRates.map((x) => Number(x)).sort(), askRates)
// })
// },
// 'currency & counterparty are correct': async (client, address) => {
// const orderbookInfo = {
// base: {
// currency: 'USD',
// counterparty: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B'
// },
// counter: {
// currency: 'BTC',
// counterparty: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B'
// }
// }
// await Promise.all([
// client.request({command: 'book_offers',
// taker_gets: orderbookInfo.base,
// taker_pays: orderbookInfo.counter,
// ledger_index: 'validated',
// limit: 20,
// taker: address
// }),
// client.request({command: 'book_offers',
// taker_gets: orderbookInfo.counter,
// taker_pays: orderbookInfo.base,
// ledger_index: 'validated',
// limit: 20,
// taker: address
// })
// ]).then(([directOfferResults, reverseOfferResults]) => {
// const directOffers = (directOfferResults
// ? directOfferResults.result.offers
// : []
// ).reduce((acc, res) => acc.concat(res), [])
// const reverseOffers = (reverseOfferResults
// ? reverseOfferResults.result.offers
// : []
// ).reduce((acc, res) => acc.concat(res), [])
// const orderbook = Client.formatBidsAndAsks(orderbookInfo, [
// ...directOffers,
// ...reverseOffers
// ])
// const orders = [...orderbook.bids, ...orderbook.asks]
// orders.forEach((order) => {
// const quantity = order.specification.quantity
// const totalPrice = order.specification.totalPrice
// const {base, counter} = requests.getOrderbook.normal
// assert.strictEqual(quantity.currency, base.currency)
// assert.strictEqual(quantity.counterparty, base.counterparty)
// assert.strictEqual(totalPrice.currency, counter.currency)
// assert.strictEqual(totalPrice.counterparty, counter.counterparty)
// })
// })
// },
// 'direction is correct for bids and asks': async (client, address) => {
// const orderbookInfo = {
// base: {
// currency: 'USD',
// counterparty: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B'
// },
// counter: {
// currency: 'BTC',
// counterparty: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B'
// }
// }
// await Promise.all([
// client.request({command: 'book_offers',
// taker_gets: orderbookInfo.base,
// taker_pays: orderbookInfo.counter,
// ledger_index: 'validated',
// limit: 20,
// taker: address
// }),
// client.request({command: 'book_offers',
// taker_gets: orderbookInfo.counter,
// taker_pays: orderbookInfo.base,
// ledger_index: 'validated',
// limit: 20,
// taker: address
// })
// ]).then(([directOfferResults, reverseOfferResults]) => {
// const directOffers = (directOfferResults
// ? directOfferResults.result.offers
// : []
// ).reduce((acc, res) => acc.concat(res), [])
// const reverseOffers = (reverseOfferResults
// ? reverseOfferResults.result.offers
// : []
// ).reduce((acc, res) => acc.concat(res), [])
// const orderbook = Client.formatBidsAndAsks(orderbookInfo, [
// ...directOffers,
// ...reverseOffers
// ])
// assert(
// orderbook.bids.every((bid) => bid.specification.direction === 'buy')
// )
// assert(
// orderbook.asks.every((ask) => ask.specification.direction === 'sell')
// )
// })
// }
})

View File

@@ -2,6 +2,7 @@
// import { assert } from "chai";
import { assert } from 'chai'
import { BookOffersRequest } from '../../src'
import requests from '../fixtures/requests'
import responses from '../fixtures/responses'

View File

@@ -1,4 +1,3 @@
/* eslint-disable mocha/no-hooks-for-single-case -- Use of hooks is restricted when there is a single test case. */
import { assert } from 'chai'
import { setupClient, teardownClient } from '../setupClient'

View File

@@ -1,4 +1,3 @@
/* eslint-disable mocha/no-hooks-for-single-case -- expected for setupClient & teardownClient */
import { assert } from 'chai'
import { Transaction } from 'xrpl-local/models/transactions'

View File

@@ -3,7 +3,7 @@ import { assert } from 'chai'
import rippled from '../fixtures/rippled'
import { setupClient, teardownClient } from '../setupClient'
describe('Subscription', function () {
describe('Client subscription', function () {
beforeEach(setupClient)
afterEach(teardownClient)
@@ -74,15 +74,6 @@ describe('Subscription', function () {
this.client.connection.onMessage(JSON.stringify(rippled.streams.pathFind))
})
it('Emits peerStatusChange', async function (done) {
this.client.on('peerStatusChange', (path) => {
assert(path.type === 'peerStatusChange')
done()
})
this.client.connection.onMessage(JSON.stringify(rippled.streams.peerStatus))
})
it('Emits validationReceived', async function (done) {
this.client.on('validationReceived', (path) => {
assert(path.type === 'validationReceived')

View File

@@ -12,7 +12,7 @@ import {
XrplError,
TimeoutError,
} from 'xrpl-local'
import { Connection } from 'xrpl-local/client'
import { Connection } from 'xrpl-local/client/connection'
import rippled from './fixtures/rippled'
import { setupClient, teardownClient } from './setupClient'

View File

@@ -1,4 +1,3 @@
/* eslint-disable max-params -- helper test functions */
import { assert } from 'chai'
import _ from 'lodash'
import { decode } from 'ripple-binary-codec'

View File

@@ -2,4 +2,6 @@ import { Wallet } from 'xrpl-local'
const walletSecret = 'shK6YXzwYfnFVn3YZSaMh5zuAddKx'
export const wallet = Wallet.fromSeed(walletSecret)
const wallet = Wallet.fromSeed(walletSecret)
export default wallet

View File

@@ -12,11 +12,8 @@ import {
TrustSet,
TrustSetTransactionFlags,
} from 'xrpl-local'
import {
isFlagEnabled,
setTransactionFlagsToNumber,
} from '../../src/models/utils'
import { isFlagEnabled } from 'xrpl-local/models/utils'
import setTransactionFlagsToNumber from 'xrpl-local/models/utils/flags'
/**
* Utils Testing.

View File

@@ -1,6 +1,7 @@
import { assert } from 'chai'
import { ValidationError, XrplError } from 'xrpl-local'
import { computeLedgerHash } from '../../src/utils'
import requests from '../fixtures/requests'
import responses from '../fixtures/responses'
@@ -23,7 +24,7 @@ describe('computeLedgerHash', function () {
)
ledger.parent_close_time = ledger.close_time
let hash
let hash: string
try {
hash = computeLedgerHash(ledger, { computeTreeHashes: true })
} catch (error) {
@@ -139,6 +140,7 @@ describe('computeLedgerHash', function () {
...REQUEST_FIXTURES.header,
transaction_hash:
'325EACC5271322539EEEC2D6A5292471EF1B3E72AE7180533EFC3B8F0AD435C9',
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- okay for tests
transactions: REQUEST_FIXTURES.transactions as any,
}

View File

@@ -1,7 +1,8 @@
import { assert } from 'chai'
import ECDSA from '../../src/ecdsa'
import { UnexpectedError } from 'xrpl-local'
import ECDSA from '../../src/ecdsa'
import {
generateXAddress,
GenerateAddressOptions,