Compare commits

..

36 Commits

Author SHA1 Message Date
Omar Khan
92849e57ce release 3.1.0 (#2703)
* update HISTORYs

* update HISTORY for isomorphic

* update package.json
2024-06-03 16:33:06 -04:00
dependabot[bot]
32f0d7b121 build(deps-dev): bump ts-jest from 29.1.2 to 29.1.4 (#2702) 2024-06-03 19:19:48 +00:00
Omar Khan
b27bbb49b3 feat: add input check for OracleSet params AssetPrice and Scale (#2699)
* add input check for AssetPrice and Scale being either both present or excluded
2024-05-23 17:32:36 -04:00
Omar Khan
23adb4924b feat: add Price Oracles support (#2688) 2024-05-23 12:10:00 -04:00
Mayukha Vadari
9b3bb9c14b fix: throw error if hexToBytes or hexToString is provided a string that is not in hex (#2657)
* better error handling

* fix browser tests

* add shared variable

* re-add test case
2024-05-08 13:02:34 -04:00
dependabot[bot]
d441361999 build(deps-dev): bump @types/lodash from 4.17.0 to 4.17.1 (#2691) 2024-05-08 16:46:07 +00:00
dependabot[bot]
fb94f2a020 build(deps): bump ws from 8.16.0 to 8.17.0 (#2685) 2024-05-08 16:39:03 +00:00
dependabot[bot]
7b56a49dae build(deps-dev): bump react from 18.2.0 to 18.3.1 (#2686) 2024-05-08 16:31:33 +00:00
Mayukha Vadari
212686baae fix: better error handling for the binary codec (#2693)
* better error handling

* respond to comments

* remove --watch
2024-05-08 12:20:43 -04:00
dependabot[bot]
4d6fef597c build(deps-dev): bump webpack-bundle-analyzer from 4.10.1 to 4.10.2 (#2681)
* build(deps-dev): bump webpack-bundle-analyzer from 4.10.1 to 4.10.2

Bumps [webpack-bundle-analyzer](https://github.com/webpack-contrib/webpack-bundle-analyzer) from 4.10.1 to 4.10.2.
- [Release notes](https://github.com/webpack-contrib/webpack-bundle-analyzer/releases)
- [Changelog](https://github.com/webpack-contrib/webpack-bundle-analyzer/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack-contrib/webpack-bundle-analyzer/compare/v4.10.1...v4.10.2)

---
updated-dependencies:
- dependency-name: webpack-bundle-analyzer
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* fix: fund wallet test

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Justin Reynolds <justinr1234@gmail.com>
2024-05-08 11:00:47 -05:00
Chenna Keshava B S
8b596a6687 update Migration guide: info about the changed behavior (#2659)
* generateSeed() update: If no algorithm is specified, use ed25519 algorithm by default.

This causes breakge in downstream dependencies such as Account class. Account.familySeed makes use of generateSeed

* update Migration guide: info about the changed behavior

* updates to MIGRATION guide -- suggestions from jackson mills

---------

Co-authored-by: Mayukha Vadari <mvadari@gmail.com>
2024-04-09 09:59:35 -07:00
dependabot[bot]
8afc9ad506 build(deps-dev): bump eslint from 8.56.0 to 8.57.0 (#2655)
Bumps [eslint](https://github.com/eslint/eslint) from 8.56.0 to 8.57.0.
- [Release notes](https://github.com/eslint/eslint/releases)
- [Changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md)
- [Commits](https://github.com/eslint/eslint/compare/v8.56.0...v8.57.0)

---
updated-dependencies:
- dependency-name: eslint
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Kausty Saxena <ksaxena@ripple.com>
2024-04-08 14:26:21 -05:00
dependabot[bot]
3b08d7d379 build(deps-dev): bump karma from 6.4.2 to 6.4.3 (#2654)
Bumps [karma](https://github.com/karma-runner/karma) from 6.4.2 to 6.4.3.
- [Release notes](https://github.com/karma-runner/karma/releases)
- [Changelog](https://github.com/karma-runner/karma/blob/master/CHANGELOG.md)
- [Commits](https://github.com/karma-runner/karma/compare/v6.4.2...v6.4.3)

---
updated-dependencies:
- dependency-name: karma
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: justinr1234 <justinr1234@gmail.com>
2024-04-08 12:12:50 -05:00
Mayukha Vadari
616ad4af60 fix: add PreviousFields to DeletedNode metadata type (#2668) 2024-04-08 11:05:37 +02:00
Chenna Keshava B S
dbdb35abb5 rectify the flag name tfNoRippleDirect (#2647)
* rectify the flag name tfNoRippleDirect

* Update th History.md file

* use "Breaking Changes" heading in the API Changelog

* Update HISTORY.md

---------
2024-04-02 14:54:02 -04:00
Omar Khan
445a05e6ef fix: add missing lsfAMMNode flag (#2674)
* add missing lsfAMMNode flag

* update HISTORY

* update HISTORY
2024-03-28 19:03:30 -04:00
dependabot[bot]
ccad092fc2 build(deps-dev): bump webpack from 5.90.3 to 5.91.0 (#2673)
Bumps [webpack](https://github.com/webpack/webpack) from 5.90.3 to 5.91.0.
- [Release notes](https://github.com/webpack/webpack/releases)
- [Commits](https://github.com/webpack/webpack/compare/v5.90.3...v5.91.0)

---
updated-dependencies:
- dependency-name: webpack
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Chenna Keshava B S <21219765+ckeshava@users.noreply.github.com>
2024-03-25 14:21:51 -07:00
dependabot[bot]
faa23b430e build(deps): bump @scure/bip39 from 1.2.2 to 1.3.0 (#2672)
Bumps [@scure/bip39](https://github.com/paulmillr/scure-bip39) from 1.2.2 to 1.3.0.
- [Release notes](https://github.com/paulmillr/scure-bip39/releases)
- [Commits](https://github.com/paulmillr/scure-bip39/compare/1.2.2...1.3.0)

---
updated-dependencies:
- dependency-name: "@scure/bip39"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-25 14:10:55 -07:00
dependabot[bot]
923e5d16ac build(deps): bump @scure/bip32 from 1.3.3 to 1.4.0 (#2670)
Bumps [@scure/bip32](https://github.com/paulmillr/scure-bip32) from 1.3.3 to 1.4.0.
- [Release notes](https://github.com/paulmillr/scure-bip32/releases)
- [Commits](https://github.com/paulmillr/scure-bip32/compare/1.3.3...1.4.0)

---
updated-dependencies:
- dependency-name: "@scure/bip32"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-25 13:50:33 -07:00
dependabot[bot]
602ac481d7 build(deps-dev): bump follow-redirects from 1.15.4 to 1.15.6 (#2662)
Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.15.4 to 1.15.6.
- [Release notes](https://github.com/follow-redirects/follow-redirects/releases)
- [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.15.4...v1.15.6)

---
updated-dependencies:
- dependency-name: follow-redirects
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Omar Khan <khancodegt@gmail.com>
2024-03-21 16:22:51 -04:00
Chenna Keshava B S
b322396a99 additional amm flags are introduced (#2667)
* additional amm flags are introduced

Co-authored-by: Mayukha Vadari <mvadari@gmail.com>

---------

Co-authored-by: Mayukha Vadari <mvadari@gmail.com>
2024-03-21 10:27:30 -07:00
dependabot[bot]
b9af7bdb6c build(deps): bump @noble/curves from 1.3.0 to 1.4.0 (#2664) 2024-03-19 00:43:05 +00:00
dependabot[bot]
bdb3ad7f3e build(deps-dev): bump @types/lodash from 4.14.202 to 4.17.0 (#2663)
Bumps [@types/lodash](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/lodash) from 4.14.202 to 4.17.0.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/lodash)

---
updated-dependencies:
- dependency-name: "@types/lodash"
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-18 15:50:06 -07:00
dependabot[bot]
1fdbf85d47 build(deps): bump @noble/hashes from 1.3.3 to 1.4.0 (#2665)
Bumps [@noble/hashes](https://github.com/paulmillr/noble-hashes) from 1.3.3 to 1.4.0.
- [Release notes](https://github.com/paulmillr/noble-hashes/releases)
- [Commits](https://github.com/paulmillr/noble-hashes/compare/1.3.3...1.4.0)

---
updated-dependencies:
- dependency-name: "@noble/hashes"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-18 15:38:57 -07:00
zgrguric
3e3911464f Update APPLICATIONS.md - Add XRPLWin to the list (#2619)
Update APPLICATIONS.md

Co-authored-by: Jackson Mills <jmills@ripple.com>
Co-authored-by: Caleb Kniffen <ckniffen@ripple.com>
Co-authored-by: Chenna Keshava B S <21219765+ckeshava@users.noreply.github.com>
2024-03-14 17:14:01 -04:00
Jackson Mills
be732a4a6b Update docs for Channel, ClientOptions, and ConnectionUserOptions (#2630)
* Update docs for three objects

* Fix my updated Channel type

* Fix docs wording in client

* Export channel again

* Update HISTORY.md

* Lint
2024-02-26 17:37:58 -05:00
dependabot[bot]
e505843dc6 build(deps-dev): bump ip from 2.0.0 to 2.0.1 (#2652) 2024-02-21 17:08:35 +00:00
dependabot[bot]
ff9489ba10 build(deps-dev): bump webpack from 5.90.1 to 5.90.3 (#2651) 2024-02-20 17:29:34 +00:00
Mayukha Vadari
365763d6f7 fix: get client.requestAll to handle filters better (#2649)
* fix requestAll issue

* update changelog

* fix typo
2024-02-15 19:20:22 -05:00
dependabot[bot]
9349a6ba1a build(deps-dev): bump webpack from 5.90.0 to 5.90.1 (#2646)
Bumps [webpack](https://github.com/webpack/webpack) from 5.90.0 to 5.90.1.
- [Release notes](https://github.com/webpack/webpack/releases)
- [Commits](https://github.com/webpack/webpack/compare/v5.90.0...v5.90.1)

---
updated-dependencies:
- dependency-name: webpack
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-12 13:21:28 -05:00
dependabot[bot]
34f35a5912 build(deps): bump ws from 8.14.2 to 8.16.0 (#2638)
Bumps [ws](https://github.com/websockets/ws) from 8.14.2 to 8.16.0.
- [Release notes](https://github.com/websockets/ws/releases)
- [Commits](https://github.com/websockets/ws/compare/8.14.2...8.16.0)

---
updated-dependencies:
- dependency-name: ws
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-12 13:14:56 -05:00
dependabot[bot]
05f16068ff build(deps-dev): bump karma-webpack from 5.0.0 to 5.0.1 (#2644)
Bumps [karma-webpack](https://github.com/webpack-contrib/karma-webpack) from 5.0.0 to 5.0.1.
- [Release notes](https://github.com/webpack-contrib/karma-webpack/releases)
- [Changelog](https://github.com/codymikol/karma-webpack/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack-contrib/karma-webpack/compare/v5.0.0...v5.0.1)

---
updated-dependencies:
- dependency-name: karma-webpack
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Omar Khan <khancodegt@gmail.com>
2024-02-12 12:42:08 -05:00
Jackson Mills
ddda7f4552 Fix: Migration guide package version formatting (#2635)
Update MIGRATION.md with better formatting on package versions

Co-authored-by: Omar Khan <khancodegt@gmail.com>
2024-02-12 12:29:00 -05:00
dependabot[bot]
38406212c3 build(deps-dev): bump @types/jest from 29.5.11 to 29.5.12 (#2641)
Bumps [@types/jest](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/jest) from 29.5.11 to 29.5.12.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/jest)

---
updated-dependencies:
- dependency-name: "@types/jest"
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-12 12:17:39 -05:00
dependabot[bot]
62a0d39ac3 build(deps): bump @scure/bip39 from 1.2.1 to 1.2.2 (#2640)
Bumps [@scure/bip39](https://github.com/paulmillr/scure-bip39) from 1.2.1 to 1.2.2.
- [Release notes](https://github.com/paulmillr/scure-bip39/releases)
- [Commits](https://github.com/paulmillr/scure-bip39/compare/1.2.1...1.2.2)

---
updated-dependencies:
- dependency-name: "@scure/bip39"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-12 12:07:13 -05:00
dependabot[bot]
0e6ab4e4ee build(deps): bump @scure/bip32 from 1.3.2 to 1.3.3 (#2639)
Bumps [@scure/bip32](https://github.com/paulmillr/scure-bip32) from 1.3.2 to 1.3.3.
- [Release notes](https://github.com/paulmillr/scure-bip32/releases)
- [Commits](https://github.com/paulmillr/scure-bip32/compare/1.3.2...1.3.3)

---
updated-dependencies:
- dependency-name: "@scure/bip32"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-12 11:56:04 -05:00
54 changed files with 1629 additions and 532 deletions

View File

@@ -170,3 +170,11 @@ fixNFTokenRemint
# 2.0.0 Amendments
XChainBridge
DID
# 2.2.0-b3 Amendments
fixNFTokenReserve
fixInnerObjTemplate
fixAMMOverflowOffer
PriceOracle
fixEmptyDID
fixXChainRewardRounding
fixPreviousTxnID

View File

@@ -4,7 +4,7 @@
name: Node.js CI
env:
RIPPLED_DOCKER_IMAGE: rippleci/rippled:2.0.0-b4
RIPPLED_DOCKER_IMAGE: rippleci/rippled:2.2.0-b3
on:
push:

View File

@@ -39,7 +39,7 @@
"enable": true
},
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
"source.fixAll.eslint": "explicit"
},
"files.insertFinalNewline": true,
"files.trimFinalNewlines": true,

View File

@@ -58,6 +58,10 @@ Warning: Use at your own risk.
XRP Ledger explorer, API, metrics, and analytics using a graph database that is synchronized live with the XRPL.
- **[XRPLWin](https://xrplwin.com)**
XRP Ledger and Xahau explorer, Hooks explorer, metrics, and analytics using a XWA backend that is synchronized live with the XRPL and Xahau.
## Data monitoring
- **[zerptracker](https://zerptracker.com)**

View File

@@ -90,7 +90,7 @@ This should be run from the `xrpl.js` top level folder (one above the `packages`
```bash
npm run build
# sets up the rippled standalone Docker container - you can skip this step if you already have it set up
docker run -p 6006:6006 --interactive -t --volume $PWD/.ci-config:/opt/ripple/etc/ --platform linux/amd64 rippleci/rippled:2.0.0-b3 /opt/ripple/bin/rippled -a --conf /opt/ripple/etc/rippled.cfg
docker run -p 6006:6006 --interactive -t --volume $PWD/.ci-config:/opt/ripple/etc/ --platform linux/amd64 rippleci/rippled:2.2.0-b3 /opt/ripple/bin/rippled -a --conf /opt/ripple/etc/rippled.cfg
npm run test:browser
```

View File

@@ -2,12 +2,15 @@
In xrpl.js 3.0, we've made significant improvements that result in a 60% reduction in bundle size for browser applications. We've also eliminated the need for polyfills with minimal disruption to existing code. This was achieved by replacing node-specific dependencies with ones that are compatible with browsers.
The main change you'll notice is the update replacing `Buffer` with `Uint8Array` across the board. This was done since browsers don't support `Buffer`. Fortunately, this transition is relatively straightforward, as `Buffer` is a subclass of `Uint8Array`, meaning in many circumstances `Buffer` can be directly replaced by `Uint8Array`. The primary difference is that `Buffer` has additional helper functions. We've listed the affected client library functions below in the `Uint8Array` section for your reference.
The two main changes you'll notice are:
* A breaking change to `Wallet` object creation, to use a more performant algorithm by default. See [here](#8-wallet-functions-default-to-ed25519-instead-of-secp256k1-signing-algorithm) for details.
* Replacing `Buffer` with `Uint8Array` across the board. This was done since browsers don't support `Buffer`. Fortunately, this transition is relatively straightforward, as `Buffer` is a subclass of `Uint8Array`, meaning in many circumstances `Buffer` can be directly replaced by `Uint8Array`. The primary difference is that `Buffer` has additional helper functions. We've listed the affected client library functions below in the `Uint8Array` section for your reference.
This migration guide also applies to:
`ripple-address-codec` 4.3.1 -> 5.0.0
`ripple-binary-codec` 1.11.0 -> 2.0.0
`ripple-keypairs` 1.3.1 -> 2.0.0
- `ripple-address-codec` 4.3.1 -> 5.0.0
- `ripple-binary-codec` 1.11.0 -> 2.0.0
- `ripple-keypairs` 1.3.1 -> 2.0.0
- `xrpl-secret-numbers` 0.3.4 -> `@xrplf/secret-numbers` 1.0.0
# Why update to 3.0?
@@ -229,9 +232,27 @@ This was done to remove a hard dependency on `https-proxy-agent` when running
authorization: 'authorization'
}`
### 8. Bug fix: Setting an explicit `algorithm` when generating a wallet works now
### 8. `Wallet` functions default to `ed25519` instead of `secp256k1` signing algorithm
`Wallet.generate()` and `Wallet.fromSeed` were ignoring the `algorithm` parameter. This means that if you were manually specifying `algorithm` in any `Wallet` constructors, you may generate a different `Wallet` keypair when upgrading to 3.0. In that case to get the same generated wallets as before, dont specify the `algorithm` parameter.
In previous releases of this library, `Wallet.generate()` and `Wallet.fromSeed` were ignoring the `algorithm` parameter. Instead, the algorithm was assumed from the seed provided; if it started with `sEd`, it would use `ed25519`, and otherwise it would use `secp256k1`. However, seeds do not actually have algorithms; a seed starting with `s...` can still use the `ed25519` algorithm.
With 3.0, we updated the default signing algorithm used by the `Wallet` object to always be `ed25519` in order to default to the higher-performance algorithm. This is a breaking change to all functions used to generate a Wallet, so if you have a pre-existing XRPL account that you're using to generate a specific Wallet using older versions of xrpl.js, you may need to specify that you are using `secp256k1` as the algorithm to decode your private key / seed / etc to get the same behavior as before. See below for specifically how to update your code.
If you are creating new accounts each time (ex. via `Client.fundWallet` or `Wallet.generate`), you do not need to specify the signing algorithm.
**Before**
```
Wallet.fromSeed('s...')
Wallet.fromEntropy(entropy)
deriveKeyPair(seed="s...")
```
**After**
```
Wallet.fromSeed(seed='s...',algorithm: 'ecdsa-secp256k1')
Wallet.fromEntropy(entropy, opts={algorithm: 'ecdsa-secp256k1'})
deriveKeypair(seed='s...', opts={ algorithm: 'ecdsa-secp256k1' }) (ripple-keypairs)
```
### 9. `AssertionError` → `Error`

640
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,13 @@
# @xrplf/isomorphic Release History
## Unreleased
## 1.0.1 (2024-06-03)
### Fixed
* Throw error if `hexToBytes` or `hexToString` is provided a string that is not in hex
## 1.0.0 (2024-02-01)
Initial release providing isomorphic and tree-shakable implementations of:
@@ -14,21 +22,3 @@ Initial release providing isomorphic and tree-shakable implementations of:
* randomBytes
* stringToHex
* ws
## 1.0.0 Beta 1 (2023-11-30)
## Added
* hexToString
* stringToHex
## 1.0.0 Beta 0 (2023-10-19)
Initial release providing isomorphic and tree-shakable implementations of:
* ripemd160
* sha256
* sha512
* bytesToHash
* hashToBytes
* randomBytes
* ws_

View File

@@ -1,6 +1,6 @@
{
"name": "@xrplf/isomorphic",
"version": "1.0.0",
"version": "1.0.1",
"description": "A collection of isomorphic and tree-shakeable crypto hashes and utils for xrpl.js",
"keywords": [
"crypto",

View File

@@ -9,6 +9,7 @@ import type {
RandomBytesFn,
StringToHexFn,
} from './types'
import { HEX_REGEX } from './shared'
/* eslint-disable func-style -- Typed to ensure uniformity between node and browser implementations and docs */
export const bytesToHex: typeof BytesToHexFn = (bytes) => {
@@ -22,6 +23,9 @@ export const bytesToHex: typeof BytesToHexFn = (bytes) => {
export const hexToBytes: typeof HexToBytesFn = (hex): Uint8Array => {
const len = hex.length
const array = new Uint8Array(len / 2)
if (!HEX_REGEX.test(hex)) {
throw new Error('Invalid hex string')
}
for (let i = 0; i < array.length; i++) {
const j = i * 2
const hexByte = hex.slice(j, j + 2)

View File

@@ -1,6 +1,7 @@
import { randomBytes as cryptoRandomBytes } from 'crypto'
import type { BytesToHexFn, HexToBytesFn, RandomBytesFn } from './types'
import { HexToStringFn, StringToHexFn } from './types'
import { HEX_REGEX } from './shared'
const OriginalBuffer = Symbol('OriginalBuffer')
@@ -64,6 +65,9 @@ export const bytesToHex: typeof BytesToHexFn = (bytes) => {
}
export const hexToBytes: typeof HexToBytesFn = (hex) => {
if (!HEX_REGEX.test(hex)) {
throw new Error('Invalid hex string')
}
return toUint8Array(Buffer.from(hex, 'hex'))
}
@@ -75,6 +79,9 @@ export const hexToString: typeof HexToStringFn = (
hex: string,
encoding = 'utf8',
): string => {
if (!HEX_REGEX.test(hex)) {
throw new Error('Invalid hex string')
}
return new TextDecoder(encoding).decode(hexToBytes(hex))
}

View File

@@ -1,5 +1,7 @@
import { concatBytes } from '@noble/hashes/utils'
export const HEX_REGEX = /^[A-F0-9]*$/iu
export function concat(views: Uint8Array[]): Uint8Array {
return concatBytes(...views)
}

View File

@@ -23,10 +23,18 @@ describe('utils', function () {
expect(hexToBytes('DEADBEEF')).toEqual(new Uint8Array([222, 173, 190, 239]))
})
it('hexToBytes - DEADBEEF', () => {
expect(hexToBytes('DEADBEEF')).toEqual(new Uint8Array([222, 173, 190, 239]))
})
it('bytesToHex - DEADBEEF', () => {
expect(bytesToHex([222, 173, 190, 239])).toEqual('DEADBEEF')
})
it('bytesToHex - bad hex', () => {
expect(() => hexToBytes('hello')).toThrow(new Error('Invalid hex string'))
})
it('bytesToHex - 010203', () => {
expect(bytesToHex([1, 2, 3])).toEqual('010203')
})
@@ -43,6 +51,10 @@ describe('utils', function () {
expect(hexToString('6465616462656566D68D')).toEqual('deadbeef֍')
})
it('hexToString - bad hex', () => {
expect(() => hexToString('hello')).toThrow(new Error('Invalid hex string'))
})
it('stringToHex - deadbeef+infinity symbol (utf8)', () => {
expect(stringToHex('deadbeef֍')).toEqual('6465616462656566D68D')
})

View File

@@ -15,25 +15,6 @@
* Eliminates 4 runtime dependencies: `base-x`, `base64-js`, `buffer`, and `ieee754`.
* Execute test in a browser in addition to node
## 5.0.0 Beta 1 (2023-11-30)
### Breaking Changes
* `Buffer` has been replaced with `UInt8Array` for both params and return values. `Buffer` may continue to work with params since they extend `UInt8Arrays`.
### Changes
* Eliminates 4 runtime dependencies: `base-x`, `base64-js`, `buffer`, and `ieee754`.
## 5.0.0 Beta 0 (2023-10-19)
### Breaking Changes
* Bump typescript to 5.x
* Remove Node 14 support
* Remove `assert` dependency. If you were catching `AssertionError` you need to change to `Error`.
* Remove `create-hash` in favor of `@noble/hashes`
### Changes
* Execute test in a browser in addition to node
## 4.3.1 (2023-09-27)
### Fixed
* Fix source-maps not finding their designated source

View File

@@ -2,6 +2,14 @@
## Unreleased
## 2.1.0 (2024-06-03)
### Added
* Support for the Price Oracles amendment (XLS-47).
### Fixed
* Better error handling/error messages for serialization/deserialization errors.
## 2.0.0 (2024-02-01)
### BREAKING CHANGES
@@ -19,23 +27,6 @@
* `Comparable` is now a generic type so that it allows `compareTo` methods to take more that the type itself.
* Eliminates 4 runtime dependencies: `base-x`, `base64-js`, `buffer`, and `ieee754`.
## 2.0.0 Beta 1 (2023-11-30)
### Breaking Changes
* `Buffer` has been replaced with `UInt8Array` for both params and return values. `Buffer` may continue to work with params since they extend `UInt8Arrays`.
### Changes
* Eliminates 4 runtime dependencies: `base-x`, `base64-js`, `buffer`, and `ieee754`.
## 2.0.0 Beta 0 (2023-10-19)
### Breaking Changes
* Bump typescript to 5.x
* Remove Node 14 support
* Remove decimal.js and big-integer. Use `BigNumber` from `bignumber.js` instead of `Decimal` and the native `BigInt` instead of `bigInt`.
* Remove `assert` dependency. If you were catching `AssertionError` you need to change to `Error`.
* Remove `create-hash` in favor of `@noble/hashes`
### Changes
* Update type definitions which causing errors in tests that the code already supported
* `makeParser` to accept a `Buffer` in addition to `string`

View File

@@ -1,6 +1,6 @@
{
"name": "ripple-binary-codec",
"version": "2.0.0",
"version": "2.1.0",
"description": "XRP Ledger binary codec",
"files": [
"dist/*",
@@ -11,7 +11,7 @@
"test": "test"
},
"dependencies": {
"@xrplf/isomorphic": "^1.0.0",
"@xrplf/isomorphic": "^1.0.1",
"bignumber.js": "^9.0.0",
"ripple-address-codec": "^5.0.0"
},

View File

@@ -23,6 +23,7 @@
"UInt512": 23,
"Issue": 24,
"XChainBridge": 25,
"Currency": 26,
"Transaction": 10001,
"LedgerEntry": 10002,
"Validation": 10003,
@@ -51,6 +52,7 @@
"NFTokenOffer": 55,
"AMM": 121,
"DID": 73,
"Oracle": 128,
"Any": -3,
"Child": -2,
"Nickname": 110,
@@ -208,6 +210,16 @@
"type": "UInt8"
}
],
[
"Scale",
{
"nth": 4,
"isVLEncoded": false,
"isSerialized": true,
"isSigningField": true,
"type": "UInt8"
}
],
[
"TickSize",
{
@@ -498,6 +510,16 @@
"type": "UInt32"
}
],
[
"LastUpdateTime",
{
"nth": 15,
"isVLEncoded": false,
"isSerialized": true,
"isSigningField": true,
"type": "UInt32"
}
],
[
"HighQualityIn",
{
@@ -828,6 +850,16 @@
"type": "UInt32"
}
],
[
"OracleDocumentID",
{
"nth": 51,
"isVLEncoded": false,
"isSerialized": true,
"isSigningField": true,
"type": "UInt32"
}
],
[
"IndexNext",
{
@@ -1028,6 +1060,16 @@
"type": "UInt64"
}
],
[
"AssetPrice",
{
"nth": 23,
"isVLEncoded": false,
"isSerialized": true,
"isSigningField": true,
"type": "UInt64"
}
],
[
"EmailHash",
{
@@ -1918,6 +1960,26 @@
"type": "Blob"
}
],
[
"AssetClass",
{
"nth": 28,
"isVLEncoded": true,
"isSerialized": true,
"isSigningField": true,
"type": "Blob"
}
],
[
"Provider",
{
"nth": 29,
"isVLEncoded": true,
"isSerialized": true,
"isSigningField": true,
"type": "Blob"
}
],
[
"Account",
{
@@ -2128,6 +2190,26 @@
"type": "PathSet"
}
],
[
"BaseAsset",
{
"nth": 1,
"isVLEncoded": false,
"isSerialized": true,
"isSigningField": true,
"type": "Currency"
}
],
[
"QuoteAsset",
{
"nth": 2,
"isVLEncoded": false,
"isSerialized": true,
"isSigningField": true,
"type": "Currency"
}
],
[
"LockingChainIssue",
{
@@ -2458,6 +2540,16 @@
"type": "STObject"
}
],
[
"PriceData",
{
"nth": 32,
"isVLEncoded": false,
"isSerialized": true,
"isSigningField": true,
"type": "STObject"
}
],
[
"Signers",
{
@@ -2628,6 +2720,16 @@
"type": "STArray"
}
],
[
"PriceDataSeries",
{
"nth": 24,
"isVLEncoded": false,
"isSerialized": true,
"isSigningField": true,
"type": "STArray"
}
],
[
"AuthAccounts",
{
@@ -2656,6 +2758,7 @@
"telWRONG_NETWORK": -386,
"telREQUIRES_NETWORK_ID": -385,
"telNETWORK_ID_MAKES_TX_NON_CANONICAL": -384,
"telENV_RPC_FAILED": -383,
"temMALFORMED": -299,
"temBAD_AMOUNT": -298,
@@ -2703,6 +2806,8 @@
"temXCHAIN_BRIDGE_BAD_MIN_ACCOUNT_CREATE_AMOUNT": -256,
"temXCHAIN_BRIDGE_BAD_REWARD_AMOUNT": -255,
"temEMPTY_DID": -254,
"temARRAY_EMPTY": -253,
"temARRAY_TOO_LARGE": -252,
"tefFAILURE": -199,
"tefALREADY": -198,
@@ -2739,7 +2844,6 @@
"terQUEUED": -89,
"terPRE_TICKET": -88,
"terNO_AMM": -87,
"terSUBMITTED": -86,
"tesSUCCESS": 0,
@@ -2815,7 +2919,11 @@
"tecXCHAIN_SELF_COMMIT": 184,
"tecXCHAIN_BAD_PUBLIC_KEY_ACCOUNT_PAIR": 185,
"tecXCHAIN_CREATE_ACCOUNT_DISABLED": 186,
"tecEMPTY_DID": 187
"tecEMPTY_DID": 187,
"tecINVALID_UPDATE_TIME": 188,
"tecTOKEN_PAIR_NOT_FOUND": 189,
"tecARRAY_EMPTY": 190,
"tecARRAY_TOO_LARGE": 191
},
"TRANSACTION_TYPES": {
"Invalid": -1,
@@ -2864,6 +2972,8 @@
"XChainCreateBridge": 48,
"DIDSet": 49,
"DIDDelete": 50,
"OracleSet": 51,
"OracleDelete": 52,
"EnableAmendment": 100,
"SetFee": 101,
"UNLModify": 102

View File

@@ -144,14 +144,18 @@ class BinaryParser {
if (type === 0) {
type = this.readUInt8()
if (type === 0 || type < 16) {
throw new Error('Cannot read FieldOrdinal, type_code out of range')
throw new Error(
`Cannot read FieldOrdinal, type_code ${type} out of range`,
)
}
}
if (nth === 0) {
nth = this.readUInt8()
if (nth === 0 || nth < 16) {
throw new Error('Cannot read FieldOrdinal, field_code out of range')
throw new Error(
`Cannot read FieldOrdinal, field_code ${nth} out of range`,
)
}
}

View File

@@ -33,6 +33,9 @@ class Blob extends SerializedType {
}
if (typeof value === 'string') {
if (!/^[A-F0-9]*$/iu.test(value)) {
throw new Error('Cannot construct Blob from a non-hex string')
}
return new Blob(hexToBytes(value))
}

View File

@@ -14,7 +14,13 @@ const OBJECT_END_MARKER = Uint8Array.from([0xe1])
*/
function isObjects(args): args is Array<JsonObject> {
return (
Array.isArray(args) && (args.length === 0 || typeof args[0] === 'object')
Array.isArray(args) &&
args.every(
(arg) =>
typeof arg === 'object' &&
Object.keys(arg).length === 1 &&
typeof Object.values(arg)[0] === 'object',
)
)
}

View File

@@ -238,19 +238,19 @@ function fieldParsingTests() {
it('Field throws when type code out of range', () => {
const parser = makeParser('0101')
expect(() => parser.readField()).toThrow(
new Error('Cannot read FieldOrdinal, type_code out of range'),
new Error('Cannot read FieldOrdinal, type_code 1 out of range'),
)
})
it('Field throws when field code out of range', () => {
const parser = makeParser('1001')
expect(() => parser.readFieldOrdinal()).toThrow(
new Error('Cannot read FieldOrdinal, field_code out of range'),
new Error('Cannot read FieldOrdinal, field_code 1 out of range'),
)
})
it('Field throws when both type and field code out of range', () => {
const parser = makeParser('000101')
expect(() => parser.readFieldOrdinal()).toThrow(
new Error('Cannot read FieldOrdinal, type_code out of range'),
new Error('Cannot read FieldOrdinal, type_code 1 out of range'),
)
})
it('readUIntN', () => {

View File

@@ -4868,6 +4868,36 @@
"TxnSignature": "AACD31A04CAE14670FC483A1382F393AA96B49C84479B58067F049FBD772999325667A6AA2520A63756EE84F3657298815019DD56A1AECE796B08535C4009C08",
"URI": "6469645F6578616D706C65"
}
},
{
"binary": "1200332FFFFFFFFF2033000004D2750B6469645F6578616D706C65701C0863757272656E6379701D0870726F7669646572811401476926B590BA3245F63C829116A0A3AF7F382DF018E020301700000000000001E2041003011A0000000000000000000000000000000000000000021A0000000000000000000000005553440000000000E1F1",
"json": {
"TransactionType": "OracleSet",
"Account": "rfmDuhDyLGgx94qiwf3YF8BUV5j6KSvE8",
"OracleDocumentID": 1234,
"LastUpdateTime": 4294967295,
"PriceDataSeries": [
{
"PriceData": {
"BaseAsset": "XRP",
"QuoteAsset": "USD",
"AssetPrice": "00000000000001E2",
"Scale": 3
}
}
],
"Provider": "70726F7669646572",
"URI": "6469645F6578616D706C65",
"AssetClass": "63757272656E6379"
}
},
{
"binary": "1200342033000004D2811401476926B590BA3245F63C829116A0A3AF7F382D",
"json": {
"TransactionType": "OracleDelete",
"Account": "rfmDuhDyLGgx94qiwf3YF8BUV5j6KSvE8",
"OracleDocumentID": 1234
}
}
],
"ledgerData": [{

View File

@@ -19,29 +19,6 @@
* Remove `brorand` as a dependency and use `@xrplf/isomorphic` instead.
* Eliminates 4 runtime dependencies: `base-x`, `base64-js`, `buffer`, and `ieee754`.
## 2.0.0 Beta 1 (2023-11-30)
### Breaking Changes
* `Buffer` has been replaced with `UInt8Array` for both params and return values. `Buffer` may continue to work with params since they extend `UInt8Arrays`.
### Changes
* Eliminates 4 runtime dependencies: `base-x`, `base64-js`, `buffer`, and `ieee754`.
## 2.0.0 Beta 0 (2023-10-19)
### Breaking Changes
* Bump typescript to 5.x
* Remove Node 14 support
* Remove `assert` dependency. If you were catching `AssertionError` you need to change to `Error`.
* Fix `deriveKeypair` ignoring manual decoding algorithm. (Specifying algorithm=`ed25519` in `opts` now works on secrets like `sNa1...`)
* Remove `crypto` polyfills, `create-hash`, `elliptic`, `hash.js`, and their many dependencies in favor of `@noble/hashes` and `@nobel/curves`
* Remove `bytesToHex` and `hexToBytes`. They can now be found in `@xrplf/isomorphic/utils`
* `verifyTransaction` will throw an error if there is no signature
* Improved key algorithm detection. It will now throw Errors with helpful messages
### Changes
* Remove `brorand` as a dependency and use `@xrplf/isomorphic` instead.
## 1.3.1 (2023-09-27)
### Fixed
* Fix source-maps not finding their designated source

View File

@@ -19,24 +19,3 @@ Subscribe to [the **xrpl-announce** mailing list](https://groups.google.com/g/xr
* Unit tests run in a browser and node.
* Remove `brorand` as a dependency and use `@xrplf/isomorphic` instead.
* Eliminates 4 runtime dependencies: `base-x`, `base64-js`, `buffer`, and `ieee754`.
## 1.0.0 Beta 1 (2023-11-30)
### BREAKING CHANGES:
* Moved all methods that were on `Utils` are now individually exported.
* `Buffer` has been replaced with `UInt8Array` for both params and return values. `Buffer` may continue to work with params since they extend `UInt8Arrays`.
### Changes
* Eliminates 4 runtime dependencies: `base-x`, `base64-js`, `buffer`, and `ieee754`.
## 1.0.0 Beta 0 (2023-10-19)
* Add `xrpl-secret-numbers` by @WietseWind to the mono repo.
* `unpkg` and `jsdelivr` support was simplified.
* Unit tests run in a browser and node.
* Remove `brorand` as a dependency and use `@xrplf/isomorphic` instead.
### BREAKING CHANGES:
* `xrpl-secret-numbers` is now `@xrplf/secret-numbers`.
* The bundled file produced changed from `dist/browerified.js` to `build/xrplf-secret-numbers-latest.js`.
* Bundle variable is `xrplf_secret_numbers` instead of using browserify's loader.

View File

@@ -4,6 +4,21 @@ Subscribe to [the **xrpl-announce** mailing list](https://groups.google.com/g/xr
## Unreleased
## 3.1.0 (2024-06-03)
### BREAKING CHANGES
* Small fix in the API to use a new flag name `tfNoDirectRipple` instead of the existing flag name `tfNoRippleDirect`
### Added
* Support for the Price Oracles amendment (XLS-47).
### Fixed
* Typo in `Channel` type `source_tab` -> `source_tag`
* Fix `client.requestAll` to handle filters better
* Add the missing `AMMDeposit` flag `tfTwoAssetIfEmpty`
* Add missing `lsfAMMNode` flag to `RippleState` ledger object
* Add `PreviousFields` to `DeletedNode` metadata type
## 3.0.0 (2024-02-01)
### BREAKING CHANGES
@@ -52,80 +67,6 @@ Bundler configurations are much more simplified. See [../UNIQUE_STEPS](Unique St
* Deprecated:
* `convertHexToString` in favor of `@xrplf/isomorphic/utils`'s `hexToString`
* `convertStringToHex` in favor of `@xrplf/isomorphic/utils`'s `stringToHex`
## 3.0.0 Beta 1 (2023-11-30)
### Breaking Changes
* `Transaction` type has been redefined to include all transactions and `SubmittableTransaction` was created to define the old value. The following functions which only handle transactions to be submitted now use `SubmittableTransaction`:
* `Client.autofill`
* `Client.submit`
* `Client.submitAndWait`
* `Client.prepareTransaction`
* `getSignedTx`
* `isAccountDelete`
* `dropsToXRP` and `Client.getXrpBalance` now return a `number` instead of a `string`
* `Buffer` has been replaced with `UInt8Array` for both params and return values. `Buffer` may continue to work with params since they extend `UInt8Arrays`.
### Bundling Changes
* `Buffer` and `process` polyfills are no longer required.
### Changes
* Deprecated:
* `convertHexToString` in favor of `@xrplf/isomorphic/utils`'s `hexToString`
* `convertStringToHex` in favor of `@xrplf/isomorphic/utils`'s `stringToHex`
* Remove `lodash` as a dependency
* Remove many polyfills that were only used for testing in the browser
* Remove `util` from bundle by switching `inspect` to `JSON.stringify`
* Add type for metadata for specific transactions(`Payment`, `NFTokenMint`, `NFTokenCreateOffer`, `NFTokenAcceptOffer`, `NFTokenCancelOffer`)
### Fixed
* Fixed Wallet.generate() ignoring the `algorithm` parameter (Only a problem once binary-codec fix for `derive_keypair` is added)
* Fixed Wallet.fromSeed() ignoring the `algorithm` parameter
* Added pseudo-transaction support to hash functions and response types
## 3.0.0 Beta 0 (2023-10-19)
### Breaking Changes
* Bump typescript to 5.x
* Remove Node 14 support
* Remove `crypto` polyfills, `create-hash`, `elliptic`, `hash.js`, and their many dependencies in favor of `@noble/hashes` and `@nobel/curves`
* Remove `bip32` and `bip39` in favor of `@scure/bip32` and `@scure/bip39`
* Remove `assert` dependency. If you were catching `AssertionError` you need to change to `Error`
* Configuring a proxy:
* Instead of passing various parameters on the `ConnectionsOptions` you know specify the `agent` parameter. This object can use be created by libraries such as `https-proxy-agent` or any that implements the `http.Agent`.
* This was changed to both support the latest `https-proxy-agent` and to remove the need to include the package in bundlers. Tests will still be done using `https-proxy-agent` and only tested in a node environment which was the only way it was previously supported anyway
* Remove `BroadcastClient` which was deprecated
* Uses `@xrplf/secret-numbers` instead of `xrpl-secret-numbers`
* Improve key algorithm detection. It will now throw Errors with helpful messages
* Move `authorizeChannel` from `wallet/signer` to `wallet/authorizeChannel` to solve a circular dependency issue.
* When using a bundler you must remove the mapping of `ws` to `WSWrapper`. ex. `ws: 'xrpl/dist/npm/client/WSWrapper'`. See [../UNIQUE_STEPS](Unique Steps) for the new, much smaller, configs.
### Bundling Changes
Bundler configurations are much more simplified. See [../UNIQUE_STEPS](Unique Steps) for the new, much smaller, configs.
* removed the following polyfills:
* `assert`
* `crypto-browserify`
* `https-browserify`
* `os-browserify`
* `stream-browserify`
* `stream-http`
* `url`
* `util` - previously added automatically by `webpack`
* `events` - previously added automatically by `webpack` but manual for `vite`
* Removed mappings for:
* `ws` to `WsWrapper`
* Excluding `https-proxy-agent`
### Changed
* Remove `lodash` as a dependency
* Remove many polyfills that were only used for testing in the browser
* Remove `util` from bundle by switching `inspect` to `JSON.stringify`
* Add type for metadata for specific transactions(`Payment`, `NFTokenMint`, `NFTokenCreateOffer`, `NFTokenAcceptOffer`, `NFTokenCancelOffer`)
### Fixed
* Fixed Wallet.generate() ignoring the `algorithm` parameter (Only a problem once binary-codec fix for `derive_keypair` is added)
* Fixed Wallet.fromSeed() ignoring the `algorithm` parameter
* Added pseudo-transaction support to hash functions and response types
## 2.14.1 (2024-02-01)

View File

@@ -1,6 +1,6 @@
{
"name": "xrpl",
"version": "3.0.0",
"version": "3.1.0",
"license": "ISC",
"description": "A TypeScript/JavaScript API for interacting with the XRP Ledger in Node.js and the browser",
"files": [
@@ -24,13 +24,13 @@
"dependencies": {
"@scure/bip32": "^1.3.1",
"@scure/bip39": "^1.2.1",
"@xrplf/isomorphic": "^1.0.0",
"@xrplf/isomorphic": "^1.0.1",
"@xrplf/secret-numbers": "^1.0.0",
"bignumber.js": "^9.0.0",
"cross-fetch": "^4.0.0",
"eventemitter3": "^5.0.1",
"ripple-address-codec": "^5.0.0",
"ripple-binary-codec": "^2.0.0",
"ripple-binary-codec": "^2.1.0",
"ripple-keypairs": "^2.0.0"
},
"devDependencies": {

View File

@@ -88,8 +88,23 @@ import {
} from './partialPayment'
export interface ClientOptions extends ConnectionUserOptions {
/**
* Multiplication factor to multiply estimated fee by to provide a cushion in case the
* required fee rises during submission of a transaction. Defaults to 1.2.
*
* @category Fee
*/
feeCushion?: number
/**
* Maximum transaction cost to allow, in decimal XRP. Must be a string-encoded
* number. Defaults to '2'.
*
* @category Fee
*/
maxFeeXRP?: string
/**
* Duration to wait for a request to timeout.
*/
timeout?: number
}
@@ -441,7 +456,6 @@ class Client extends EventEmitter<EventTypes> {
const countTo: number = request.limit == null ? Infinity : request.limit
let count = 0
let marker: unknown = request.marker
let lastBatchLength: number
const results: U[] = []
do {
const countRemaining = clamp(countTo - count, MIN_LIMIT, MAX_LIMIT)
@@ -465,11 +479,8 @@ class Client extends EventEmitter<EventTypes> {
// Make sure we handle when no data (not even an empty array) is returned.
if (Array.isArray(collectedData)) {
count += collectedData.length
lastBatchLength = collectedData.length
} else {
lastBatchLength = 0
}
} while (Boolean(marker) && count < countTo && lastBatchLength !== 0)
} while (Boolean(marker) && count < countTo)
return results
}

View File

@@ -155,3 +155,36 @@ export interface XChainBridge {
IssuingChainDoor: string
IssuingChainIssue: Currency
}
/**
* A PriceData object represents the price information for a token pair.
*
*/
export interface PriceData {
PriceData: {
/**
* The primary asset in a trading pair. Any valid identifier, such as a stock symbol, bond CUSIP, or currency code is allowed.
* For example, in the BTC/USD pair, BTC is the base asset; in 912810RR9/BTC, 912810RR9 is the base asset.
*/
BaseAsset: string
/**
* The quote asset in a trading pair. The quote asset denotes the price of one unit of the base asset. For example, in the
* BTC/USD pair,BTC is the base asset; in 912810RR9/BTC, 912810RR9 is the base asset.
*/
QuoteAsset: string
/**
* The asset price after applying the Scale precision level. It's not included if the last update transaction didn't include
* the BaseAsset/QuoteAsset pair.
*/
AssetPrice?: number | string
/**
* The scaling factor to apply to an asset price. For example, if Scale is 6 and original price is 0.155, then the scaled
* price is 155000. Valid scale ranges are 0-10. It's not included if the last update transaction didn't include the
* BaseAsset/QuoteAsset pair.
*/
Scale?: number
}
}

View File

@@ -10,6 +10,7 @@ import FeeSettings from './FeeSettings'
import LedgerHashes from './LedgerHashes'
import NegativeUNL from './NegativeUNL'
import Offer from './Offer'
import Oracle from './Oracle'
import PayChannel from './PayChannel'
import RippleState from './RippleState'
import SignerList from './SignerList'
@@ -30,6 +31,7 @@ type LedgerEntry =
| LedgerHashes
| NegativeUNL
| Offer
| Oracle
| PayChannel
| RippleState
| SignerList
@@ -52,6 +54,7 @@ type LedgerEntryFilter =
| 'nft_offer'
| 'nft_page'
| 'offer'
| 'oracle'
| 'payment_channel'
| 'signer_list'
| 'state'

View File

@@ -0,0 +1,43 @@
import { PriceData } from '../common'
import { BaseLedgerEntry, HasPreviousTxnID } from './BaseLedgerEntry'
/**
* The Oracle object type describes a single Price Oracle instance.
*
* @category Ledger Entries
*/
export default interface Oracle extends BaseLedgerEntry, HasPreviousTxnID {
LedgerEntryType: 'Oracle'
/**
* The time the data was last updated, represented as a unix timestamp in seconds.
*/
LastUpdateTime: number
/**
* The XRPL account with update and delete privileges for the oracle.
*/
Owner: string
/**
* Describes the type of asset, such as "currency", "commodity", or "index".
*/
AssetClass: string
/**
* The oracle provider, such as Chainlink, Band, or DIA.
*/
Provider: string
/**
* An array of up to 10 PriceData objects.
*/
PriceDataSeries: PriceData[]
/**
* A bit-map of boolean flags. No flags are defined for the Oracle object
* type, so this value is always 0.
*/
Flags: 0
}

View File

@@ -75,4 +75,6 @@ export enum RippleStateFlags {
lsfLowFreeze = 0x00400000,
// True, high side has set freeze flag
lsfHighFreeze = 0x00800000,
// True, trust line to AMM. Used by client apps to identify payments via AMM.
lsfAMMNode = 0x01000000,
}

View File

@@ -22,6 +22,7 @@ import NegativeUNL, { NEGATIVE_UNL_ID } from './NegativeUNL'
import { NFTokenOffer } from './NFTokenOffer'
import { NFToken, NFTokenPage } from './NFTokenPage'
import Offer, { OfferFlags } from './Offer'
import Oracle from './Oracle'
import PayChannel from './PayChannel'
import RippleState, { RippleStateFlags } from './RippleState'
import SignerList, { SignerListFlags } from './SignerList'
@@ -58,6 +59,7 @@ export {
NFToken,
Offer,
OfferFlags,
Oracle,
PayChannel,
RippleState,
RippleStateFlags,

View File

@@ -1,17 +1,80 @@
import { BaseRequest, BaseResponse, LookupByLedgerRequest } from './baseMethod'
/**
* Represents a payment channel in the XRP Ledger.
*/
export interface Channel {
/** The owner of the channel, as an Address. */
account: string
/** The total amount of XRP, in drops allocated to this channel. */
amount: string
/**
* The total amount of XRP, in drops, paid out from this channel,
* as of the ledger version used. (You can calculate the amount of
* XRP left in the channel by subtracting balance from amount.)
*/
balance: string
/**
* A unique ID for this channel, as a 64-character hexadecimal string.
* This is also the ID of the channel object in the ledger's state data.
*/
channel_id: string
/**
* The destination account of the channel, as an Address.
* Only this account can receive the XRP in the channel while it is open.
*/
destination_account: string
/**
* The number of seconds the payment channel must stay open after the owner
* of the channel requests to close it.
*/
settle_delay: number
/**
* The public key for the payment channel in the XRP Ledger's base58 format.
* Signed claims against this channel must be redeemed with the matching key pair.
*/
public_key?: string
/**
* The public key for the payment channel in hexadecimal format, if one was
* specified at channel creation. Signed claims against this channel must be
* redeemed with the matching key pair.
*/
public_key_hex?: string
/**
* Time, in seconds since the Ripple Epoch, when this channel is set to expire.
* This expiration date is mutable. If this is before the close time of the most
* recent validated ledger, the channel is expired.
*/
expiration?: number
/**
* Time, in seconds since the Ripple Epoch, of this channel's immutable expiration,
* if one was specified at channel creation. If this is before the close time of the
* most recent validated ledger, the channel is expired.
*/
cancel_after?: number
source_tab?: number
/**
* A 32-bit unsigned integer to use as a source tag for payments through this payment channel,
* if one was specified at channel creation. This indicates the payment channel's originator or
* other purpose at the source account. Conventionally, if you bounce payments from this channel,
* you should specify this value in the DestinationTag of the return payment.
*/
source_tag?: number
/**
* A 32-bit unsigned integer to use as a destination tag for payments through this channel,
* if one was specified at channel creation. This indicates the payment channel's beneficiary
* or other purpose at the destination account.
*/
destination_tag?: number
}

View File

@@ -0,0 +1,119 @@
import { BaseRequest, BaseResponse } from './baseMethod'
/**
* The `get_aggregate_price` method retrieves the aggregate price of specified Oracle objects,
* returning three price statistics: mean, median, and trimmed mean.
* Returns an {@link GetAggregatePriceResponse}.
*
* @category Requests
*/
export interface GetAggregatePriceRequest extends BaseRequest {
command: 'get_aggregate_price'
/**
* The currency code of the asset to be priced.
*/
base_asset: string
/**
* The currency code of the asset to quote the price of the base asset.
*/
quote_asset: string
/**
* The oracle identifier.
*/
oracles: Array<{
/**
* The XRPL account that controls the Oracle object.
*/
account: string
/**
* A unique identifier of the price oracle for the Account
*/
oracle_document_id: string | number
}>
/**
* The percentage of outliers to trim. Valid trim range is 1-25. If included, the API returns statistics for the trimmed mean.
*/
trim?: number
/**
* Defines a time range in seconds for filtering out older price data. Default value is 0, which doesn't filter any data.
*/
trim_threshold?: number
}
/**
* Response expected from an {@link GetAggregatePriceRequest}.
*
* @category Responses
*/
export interface GetAggregatePriceResponse extends BaseResponse {
result: {
/**
* The statistics from the collected oracle prices.
*/
entire_set: {
/**
* The simple mean.
*/
mean: string
/**
* The size of the data set to calculate the mean.
*/
size: number
/**
* The standard deviation.
*/
standard_deviation: string
}
/**
* The trimmed statistics from the collected oracle prices. Only appears if the trim field was specified in the request.
*/
trimmed_set?: {
/**
* The simple mean of the trimmed data.
*/
mean: string
/**
* The size of the data to calculate the trimmed mean.
*/
size: number
/**
* The standard deviation of the trimmed data.
*/
standard_deviation: string
}
/**
* The median of the collected oracle prices.
*/
median: string
/**
* The most recent timestamp out of all LastUpdateTime values.
*/
time: number
/**
* The ledger index of the ledger version that was used to generate this
* response.
*/
ledger_current_index: number
/**
* If included and set to true, the information in this response comes from
* a validated ledger version. Otherwise, the information is subject to
* change.
*/
validated: boolean
}
}

View File

@@ -66,6 +66,10 @@ import {
GatewayBalancesRequest,
GatewayBalancesResponse,
} from './gatewayBalances'
import {
GetAggregatePriceRequest,
GetAggregatePriceResponse,
} from './getAggregatePrice'
import {
LedgerBinary,
LedgerModifiedOfferCreateTransaction,
@@ -210,6 +214,8 @@ type Request =
| NFTHistoryRequest
// AMM methods
| AMMInfoRequest
// Price Oracle methods
| GetAggregatePriceRequest
/**
* @category Responses
@@ -264,6 +270,8 @@ type Response =
| NFTHistoryResponse
// AMM methods
| AMMInfoResponse
// Price Oracle methods
| GetAggregatePriceResponse
export type RequestResponseMap<T> = T extends AccountChannelsRequest
? AccountChannelsResponse
@@ -285,6 +293,8 @@ export type RequestResponseMap<T> = T extends AccountChannelsRequest
? AMMInfoResponse
: T extends GatewayBalancesRequest
? GatewayBalancesResponse
: T extends GetAggregatePriceRequest
? GetAggregatePriceResponse
: T extends NoRippleCheckRequest
? NoRippleCheckResponse
: // NOTE: The order of these LedgerRequest types is important
@@ -473,6 +483,8 @@ export {
GatewayBalance,
GatewayBalancesRequest,
GatewayBalancesResponse,
GetAggregatePriceRequest,
GetAggregatePriceResponse,
NoRippleCheckRequest,
NoRippleCheckResponse,
// ledger methods

View File

@@ -21,6 +21,7 @@ export enum AMMDepositFlags {
tfTwoAsset = 0x00100000,
tfOneAssetLPToken = 0x00200000,
tfLimitLPToken = 0x00400000,
tfTwoAssetIfEmpty = 0x00800000,
}
export interface AMMDepositFlagsInterface extends GlobalFlags {
@@ -29,6 +30,7 @@ export interface AMMDepositFlagsInterface extends GlobalFlags {
tfTwoAsset?: boolean
tfOneAssetLPToken?: boolean
tfLimitLPToken?: boolean
tfTwoAssetIfEmpty?: boolean
}
/**

View File

@@ -58,6 +58,8 @@ export {
OfferCreateFlagsInterface,
OfferCreate,
} from './offerCreate'
export { OracleDelete } from './oracleDelete'
export { OracleSet } from './oracleSet'
export { PaymentFlags, PaymentFlagsInterface, Payment } from './payment'
export {
PaymentChannelClaimFlags,

View File

@@ -40,6 +40,7 @@ export interface DeletedNode {
DeletedNode: {
LedgerEntryType: string
LedgerIndex: string
PreviousFields?: { [field: string]: unknown }
FinalFields: { [field: string]: unknown }
}
}

View File

@@ -0,0 +1,32 @@
import {
BaseTransaction,
isNumber,
validateBaseTransaction,
validateRequiredField,
} from './common'
/**
* Delete an Oracle ledger entry.
*
* @category Transaction Models
*/
export interface OracleDelete extends BaseTransaction {
TransactionType: 'OracleDelete'
/**
* A unique identifier of the price oracle for the Account.
*/
OracleDocumentID: number
}
/**
* Verify the form and type of a OracleDelete at runtime.
*
* @param tx - A OracleDelete Transaction.
* @throws When the OracleDelete is malformed.
*/
export function validateOracleDelete(tx: Record<string, unknown>): void {
validateBaseTransaction(tx)
validateRequiredField(tx, 'OracleDocumentID', isNumber)
}

View File

@@ -0,0 +1,176 @@
import { ValidationError } from '../../errors'
import { PriceData } from '../common'
import {
BaseTransaction,
isNumber,
isString,
validateBaseTransaction,
validateOptionalField,
validateRequiredField,
} from './common'
const PRICE_DATA_SERIES_MAX_LENGTH = 10
const SCALE_MAX = 10
/**
* Creates a new Oracle ledger entry or updates the fields of an existing one, using the Oracle ID.
*
* The oracle provider must complete these steps before submitting this transaction:
* 1. Create or own the XRPL account in the Owner field and have enough XRP to meet the reserve and transaction fee requirements.
* 2. Publish the XRPL account public key, so it can be used for verification by dApps.
* 3. Publish a registry of available price oracles with their unique OracleDocumentID.
*
* @category Transaction Models
*/
export interface OracleSet extends BaseTransaction {
TransactionType: 'OracleSet'
/**
* A unique identifier of the price oracle for the Account.
*/
OracleDocumentID: number
/**
* The time the data was last updated, represented as a unix timestamp in seconds.
*/
LastUpdateTime: number
/**
* An array of up to 10 PriceData objects, each representing the price information
* for a token pair. More than five PriceData objects require two owner reserves.
*/
PriceDataSeries: PriceData[]
/**
* An arbitrary value that identifies an oracle provider, such as Chainlink, Band,
* or DIA. This field is a string, up to 256 ASCII hex encoded characters (0x20-0x7E).
* This field is required when creating a new Oracle ledger entry, but is optional for updates.
*/
Provider?: string
/**
* An optional Universal Resource Identifier to reference price data off-chain. This field is limited to 256 bytes.
*/
URI?: string
/**
* Describes the type of asset, such as "currency", "commodity", or "index". This field is a string, up to 16 ASCII
* hex encoded characters (0x20-0x7E). This field is required when creating a new Oracle ledger entry, but is optional
* for updates.
*/
AssetClass?: string
}
/**
* Verify the form and type of a OracleSet at runtime.
*
* @param tx - A OracleSet Transaction.
* @throws When the OracleSet is malformed.
*/
// eslint-disable-next-line max-lines-per-function -- necessary to validate many fields
export function validateOracleSet(tx: Record<string, unknown>): void {
validateBaseTransaction(tx)
validateRequiredField(tx, 'OracleDocumentID', isNumber)
validateRequiredField(tx, 'LastUpdateTime', isNumber)
validateOptionalField(tx, 'Provider', isString)
validateOptionalField(tx, 'URI', isString)
validateOptionalField(tx, 'AssetClass', isString)
// eslint-disable-next-line max-lines-per-function -- necessary to validate many fields
validateRequiredField(tx, 'PriceDataSeries', (value) => {
if (!Array.isArray(value)) {
throw new ValidationError('OracleSet: PriceDataSeries must be an array')
}
if (value.length > PRICE_DATA_SERIES_MAX_LENGTH) {
throw new ValidationError(
`OracleSet: PriceDataSeries must have at most ${PRICE_DATA_SERIES_MAX_LENGTH} PriceData objects`,
)
}
// TODO: add support for handling inner objects easier (similar to validateRequiredField/validateOptionalField)
for (const priceData of value) {
if (typeof priceData !== 'object') {
throw new ValidationError(
'OracleSet: PriceDataSeries must be an array of objects',
)
}
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access -- we are validating the type
if (priceData.PriceData == null) {
throw new ValidationError(
'OracleSet: PriceDataSeries must have a `PriceData` object',
)
}
// check if priceData only has PriceData
if (Object.keys(priceData).length !== 1) {
throw new ValidationError(
'OracleSet: PriceDataSeries must only have a single PriceData object',
)
}
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access -- we are validating the type
if (typeof priceData.PriceData.BaseAsset !== 'string') {
throw new ValidationError(
'OracleSet: PriceDataSeries must have a `BaseAsset` string',
)
}
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access -- we are validating the type
if (typeof priceData.PriceData.QuoteAsset !== 'string') {
throw new ValidationError(
'OracleSet: PriceDataSeries must have a `QuoteAsset` string',
)
}
// Either AssetPrice and Scale are both present or both excluded
if (
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access -- we are validating the type
(priceData.PriceData.AssetPrice == null) !==
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access -- we are validating the type
(priceData.PriceData.Scale == null)
) {
throw new ValidationError(
'OracleSet: PriceDataSeries must have both `AssetPrice` and `Scale` if any are present',
)
}
if (
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access -- we are validating the type
'AssetPrice' in priceData.PriceData &&
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access -- we are validating the type
!isNumber(priceData.PriceData.AssetPrice)
) {
throw new ValidationError('OracleSet: invalid field AssetPrice')
}
if (
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access -- we are validating the type
'Scale' in priceData.PriceData &&
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access -- we are validating the type
!isNumber(priceData.PriceData.Scale)
) {
throw new ValidationError('OracleSet: invalid field Scale')
}
if (
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access -- we are validating the type
priceData.PriceData.Scale < 0 ||
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access -- we are validating the type
priceData.PriceData.Scale > SCALE_MAX
) {
throw new ValidationError(
`OracleSet: Scale must be in range 0-${SCALE_MAX}`,
)
}
}
return true
})
}

View File

@@ -26,7 +26,7 @@ export enum PaymentFlags {
* This is intended to force the transaction to take arbitrage opportunities.
* Most clients do not need this.
*/
tfNoDirectRipple = 0x00010000,
tfNoRippleDirect = 0x00010000,
/**
* If the specified Amount cannot be sent without spending more than SendMax,
* reduce the received amount instead of failing outright. See Partial.
@@ -88,7 +88,7 @@ export interface PaymentFlagsInterface extends GlobalFlags {
* This is intended to force the transaction to take arbitrage opportunities.
* Most clients do not need this.
*/
tfNoDirectRipple?: boolean
tfNoRippleDirect?: boolean
/**
* If the specified Amount cannot be sent without spending more than SendMax,
* reduce the received amount instead of failing outright. See Partial.

View File

@@ -43,6 +43,8 @@ import {
import { NFTokenMint, validateNFTokenMint } from './NFTokenMint'
import { OfferCancel, validateOfferCancel } from './offerCancel'
import { OfferCreate, validateOfferCreate } from './offerCreate'
import { OracleDelete, validateOracleDelete } from './oracleDelete'
import { OracleSet, validateOracleSet } from './oracleSet'
import { Payment, validatePayment } from './payment'
import {
PaymentChannelClaim,
@@ -120,6 +122,8 @@ export type SubmittableTransaction =
| NFTokenMint
| OfferCancel
| OfferCreate
| OracleDelete
| OracleSet
| Payment
| PaymentChannelClaim
| PaymentChannelCreate
@@ -330,6 +334,14 @@ export function validate(transaction: Record<string, unknown>): void {
validateOfferCreate(tx)
break
case 'OracleDelete':
validateOracleDelete(tx)
break
case 'OracleSet':
validateOracleSet(tx)
break
case 'Payment':
validatePayment(tx)
break

View File

@@ -10,9 +10,18 @@ import {
const rippledResponse = function (request: Request): Record<string, unknown> {
if ('marker' in request) {
return rippled.ledger_data.last_page
return rippled.ledger_data.lastPage
}
return rippled.ledger_data.first_page
return rippled.ledger_data.firstPage
}
const rippledResponseFirstEmpty = function (
request: Request,
): Record<string, unknown> {
if ('marker' in request) {
return rippled.ledger_data.lastPage
}
return rippled.ledger_data.firstPageEmpty
}
describe('client.requestAll', function () {
@@ -34,14 +43,25 @@ describe('client.requestAll', function () {
)
})
it('rejects when there are no more pages', async function () {
it('stops when there are no more pages', async function () {
testContext.mockRippled!.addResponse(
'ledger_data',
rippled.ledger_data.last_page,
rippled.ledger_data.lastPage,
)
const allResponses = await testContext.client.requestAll({
command: 'ledger_data',
})
assert.equal(allResponses.length, 1)
})
it('handles when the first page has no results', async function () {
testContext.mockRippled!.addResponse(
'ledger_data',
rippledResponseFirstEmpty,
)
const allResponses = await testContext.client.requestAll({
command: 'ledger_data',
})
assert.equal(allResponses.length, 2)
})
})

View File

@@ -11,9 +11,9 @@ import { assertRejects } from '../testUtils'
const rippledResponse = function (request: Request): Record<string, unknown> {
if ('marker' in request) {
return rippled.ledger_data.last_page
return rippled.ledger_data.lastPage
}
return rippled.ledger_data.first_page
return rippled.ledger_data.firstPage
}
describe('client.requestNextPage', function () {

View File

@@ -6,8 +6,9 @@ import fabric from './bookOffers'
import usd_xrp from './bookOffersUsdXrp.json'
import xrp_usd from './bookOffersXrpUsd.json'
import normalLedger from './ledger.json'
import first_page from './ledgerDataFirstPage.json'
import last_page from './ledgerDataLastPage.json'
import firstPage from './ledgerDataFirstPage.json'
import firstPageEmpty from './ledgerDataFirstPageEmpty.json'
import lastPage from './ledgerDataLastPage.json'
import iouPartialPayment from './partialPaymentIOU.json'
import xrpPartialPayment from './partialPaymentXRP.json'
import normalServerInfo from './serverInfo.json'
@@ -82,8 +83,9 @@ const book_offers = {
}
const ledger_data = {
first_page,
last_page,
firstPage,
firstPageEmpty,
lastPage,
}
const server_info = {

View File

@@ -0,0 +1,14 @@
{
"id": 0,
"status": "success",
"type": "response",
"result": {
"ledger_hash":
"102A6E70FFB18C18E97BB56E3047B0E45EA1BCC90BFCCB8CBB0D07BF0E2AB449",
"ledger_index": 38202000,
"marker":
"000B714B790C3C79FEE00D17C4DEB436B375466F29679447BA64F265FD63D730",
"state": [],
"validated": true
}
}

View File

@@ -104,7 +104,7 @@ describe('fundWallet', function () {
})
assert.equal(dropsToXrp(info.result.account_data.Balance), balance)
assert.equal(balance, 10000)
assert.equal(balance, 1000)
/*
* No test for fund given wallet because the hooks v3 testnet faucet
@@ -124,10 +124,10 @@ describe('fundWallet', function () {
await api.connect()
const { wallet, balance } = await api.fundWallet(null, {
amount: '2000',
amount: '1000',
usageContext: 'integration-test',
})
assert.equal(balance, 2000)
assert.equal(balance, 1000)
assert.notStrictEqual(wallet, undefined)
assert(isValidClassicAddress(wallet.classicAddress))
assert(isValidXAddress(wallet.getXAddress()))

View File

@@ -0,0 +1,78 @@
import { stringToHex } from '@xrplf/isomorphic/utils'
import { assert } from 'chai'
import { OracleSet } from '../../../src'
import serverUrl from '../serverUrl'
import {
setupClient,
teardownClient,
type XrplIntegrationTestContext,
} from '../setup'
import { testTransaction } from '../utils'
// how long before each test case times out
const TIMEOUT = 20000
describe('get_aggregate_price', function () {
let testContext: XrplIntegrationTestContext
beforeEach(async () => {
testContext = await setupClient(serverUrl)
})
afterEach(async () => teardownClient(testContext))
it(
'base',
async () => {
const tx: OracleSet = {
TransactionType: 'OracleSet',
Account: testContext.wallet.classicAddress,
OracleDocumentID: 1234,
LastUpdateTime: Math.floor(Date.now() / 1000),
PriceDataSeries: [
{
PriceData: {
BaseAsset: 'XRP',
QuoteAsset: 'USD',
AssetPrice: 740,
Scale: 3,
},
},
],
Provider: stringToHex('chainlink'),
URI: '6469645F6578616D706C65',
AssetClass: stringToHex('currency'),
}
await testTransaction(testContext.client, tx, testContext.wallet)
// confirm that the Oracle was actually created
const getAggregatePriceResponse = await testContext.client.request({
command: 'get_aggregate_price',
account: testContext.wallet.classicAddress,
base_asset: 'XRP',
quote_asset: 'USD',
trim: 20,
oracles: [
{
account: testContext.wallet.classicAddress,
oracle_document_id: 1234,
},
],
})
assert.deepEqual(getAggregatePriceResponse.result.entire_set, {
mean: '0.74',
size: 1,
standard_deviation: '0',
})
assert.deepEqual(getAggregatePriceResponse.result.trimmed_set, {
mean: '0.74',
size: 1,
standard_deviation: '0',
})
assert.equal(getAggregatePriceResponse.result.median, '0.74')
assert.equal(getAggregatePriceResponse.result.time, tx.LastUpdateTime)
},
TIMEOUT,
)
})

View File

@@ -0,0 +1,76 @@
import { stringToHex } from '@xrplf/isomorphic/utils'
import { assert } from 'chai'
import { OracleSet, OracleDelete } from '../../../src'
import serverUrl from '../serverUrl'
import {
setupClient,
teardownClient,
type XrplIntegrationTestContext,
} from '../setup'
import { testTransaction } from '../utils'
// how long before each test case times out
const TIMEOUT = 20000
describe('OracleDelete', function () {
let testContext: XrplIntegrationTestContext
beforeEach(async () => {
testContext = await setupClient(serverUrl)
})
afterEach(async () => teardownClient(testContext))
it(
'base',
async () => {
const setTx: OracleSet = {
TransactionType: 'OracleSet',
Account: testContext.wallet.classicAddress,
OracleDocumentID: 1234,
LastUpdateTime: Math.floor(Date.now() / 1000),
PriceDataSeries: [
{
PriceData: {
BaseAsset: 'XRP',
QuoteAsset: 'USD',
AssetPrice: 740,
Scale: 3,
},
},
],
Provider: stringToHex('chainlink'),
URI: '6469645F6578616D706C65',
AssetClass: stringToHex('currency'),
}
await testTransaction(testContext.client, setTx, testContext.wallet)
const aoResult = await testContext.client.request({
command: 'account_objects',
account: testContext.wallet.classicAddress,
type: 'oracle',
})
// confirm that the Oracle was created
assert.equal(aoResult.result.account_objects.length, 1)
const deleteTx: OracleDelete = {
TransactionType: 'OracleDelete',
Account: testContext.wallet.classicAddress,
OracleDocumentID: 1234,
}
await testTransaction(testContext.client, deleteTx, testContext.wallet)
const aoResult2 = await testContext.client.request({
command: 'account_objects',
account: testContext.wallet.classicAddress,
})
// confirm that the Oracle was actually deleted
assert.equal(aoResult2.result.account_objects.length, 0)
},
TIMEOUT,
)
})

View File

@@ -0,0 +1,74 @@
import { stringToHex } from '@xrplf/isomorphic/utils'
import { assert } from 'chai'
import { OracleSet } from '../../../src'
import { Oracle } from '../../../src/models/ledger'
import serverUrl from '../serverUrl'
import {
setupClient,
teardownClient,
type XrplIntegrationTestContext,
} from '../setup'
import { testTransaction } from '../utils'
// how long before each test case times out
const TIMEOUT = 20000
describe('OracleSet', function () {
let testContext: XrplIntegrationTestContext
beforeEach(async () => {
testContext = await setupClient(serverUrl)
})
afterEach(async () => teardownClient(testContext))
it(
'base',
async () => {
const tx: OracleSet = {
TransactionType: 'OracleSet',
Account: testContext.wallet.classicAddress,
OracleDocumentID: 1234,
LastUpdateTime: Math.floor(Date.now() / 1000),
PriceDataSeries: [
{
PriceData: {
BaseAsset: 'XRP',
QuoteAsset: 'USD',
AssetPrice: 740,
Scale: 3,
},
},
],
Provider: stringToHex('chainlink'),
URI: '6469645F6578616D706C65',
AssetClass: stringToHex('currency'),
}
await testTransaction(testContext.client, tx, testContext.wallet)
const result = await testContext.client.request({
command: 'account_objects',
account: testContext.wallet.classicAddress,
type: 'oracle',
})
// confirm that the Oracle was actually created
assert.equal(result.result.account_objects.length, 1)
// confirm details of Oracle ledger entry object
const oracle = result.result.account_objects[0] as Oracle
assert.equal(oracle.LastUpdateTime, tx.LastUpdateTime)
assert.equal(oracle.Owner, testContext.wallet.classicAddress)
assert.equal(oracle.AssetClass, tx.AssetClass)
assert.equal(oracle.Provider, tx.Provider)
assert.equal(oracle.PriceDataSeries.length, 1)
assert.equal(oracle.PriceDataSeries[0].PriceData.BaseAsset, 'XRP')
assert.equal(oracle.PriceDataSeries[0].PriceData.QuoteAsset, 'USD')
assert.equal(oracle.PriceDataSeries[0].PriceData.AssetPrice, '2e4')
assert.equal(oracle.PriceDataSeries[0].PriceData.Scale, 3)
assert.equal(oracle.Flags, 0)
},
TIMEOUT,
)
})

View File

@@ -0,0 +1,40 @@
import { assert } from 'chai'
import { validate, ValidationError } from '../../src'
import { validateOracleDelete } from '../../src/models/transactions/oracleDelete'
/**
* OracleDelete Transaction Verification Testing.
*
* Providing runtime verification testing for each specific transaction type.
*/
describe('OracleDelete', function () {
let tx
beforeEach(function () {
tx = {
TransactionType: 'OracleDelete',
Account: 'rfmDuhDyLGgx94qiwf3YF8BUV5j6KSvE8',
OracleDocumentID: 1234,
} as any
})
it('verifies valid OracleDelete', function () {
assert.doesNotThrow(() => validateOracleDelete(tx))
assert.doesNotThrow(() => validate(tx))
})
it(`throws w/ missing field OracleDocumentID`, function () {
delete tx.OracleDocumentID
const errorMessage = 'OracleDelete: missing field OracleDocumentID'
assert.throws(() => validateOracleDelete(tx), ValidationError, errorMessage)
assert.throws(() => validate(tx), ValidationError, errorMessage)
})
it(`throws w/ invalid OracleDocumentID`, function () {
tx.OracleDocumentID = '1234'
const errorMessage = 'OracleDelete: invalid field OracleDocumentID'
assert.throws(() => validateOracleDelete(tx), ValidationError, errorMessage)
assert.throws(() => validate(tx), ValidationError, errorMessage)
})
})

View File

@@ -0,0 +1,196 @@
import { stringToHex } from '@xrplf/isomorphic/dist/utils'
import { assert } from 'chai'
import { validate, ValidationError } from '../../src'
import { validateOracleSet } from '../../src/models/transactions/oracleSet'
/**
* OracleSet Transaction Verification Testing.
*
* Providing runtime verification testing for each specific transaction type.
*/
describe('OracleSet', function () {
let tx
beforeEach(function () {
tx = {
TransactionType: 'OracleSet',
Account: 'rfmDuhDyLGgx94qiwf3YF8BUV5j6KSvE8',
OracleDocumentID: 1234,
LastUpdateTime: 768062172,
PriceDataSeries: [
{
PriceData: {
BaseAsset: 'XRP',
QuoteAsset: 'USD',
AssetPrice: 740,
Scale: 3,
},
},
],
Provider: stringToHex('chainlink'),
URI: '6469645F6578616D706C65',
AssetClass: stringToHex('currency'),
} as any
})
it('verifies valid OracleSet', function () {
assert.doesNotThrow(() => validateOracleSet(tx))
assert.doesNotThrow(() => validate(tx))
})
it(`throws w/ missing field OracleDocumentID`, function () {
delete tx.OracleDocumentID
const errorMessage = 'OracleSet: missing field OracleDocumentID'
assert.throws(() => validateOracleSet(tx), ValidationError, errorMessage)
assert.throws(() => validate(tx), ValidationError, errorMessage)
})
it(`throws w/ invalid OracleDocumentID`, function () {
tx.OracleDocumentID = '1234'
const errorMessage = 'OracleSet: invalid field OracleDocumentID'
assert.throws(() => validateOracleSet(tx), ValidationError, errorMessage)
assert.throws(() => validate(tx), ValidationError, errorMessage)
})
it(`throws w/ missing field LastUpdateTime`, function () {
delete tx.LastUpdateTime
const errorMessage = 'OracleSet: missing field LastUpdateTime'
assert.throws(() => validateOracleSet(tx), ValidationError, errorMessage)
assert.throws(() => validate(tx), ValidationError, errorMessage)
})
it(`throws w/ invalid LastUpdateTime`, function () {
tx.LastUpdateTime = '768062172'
const errorMessage = 'OracleSet: invalid field LastUpdateTime'
assert.throws(() => validateOracleSet(tx), ValidationError, errorMessage)
assert.throws(() => validate(tx), ValidationError, errorMessage)
})
it(`throws w/ missing invalid Provider`, function () {
tx.Provider = 1234
const errorMessage = 'OracleSet: invalid field Provider'
assert.throws(() => validateOracleSet(tx), ValidationError, errorMessage)
assert.throws(() => validate(tx), ValidationError, errorMessage)
})
it(`throws w/ missing invalid URI`, function () {
tx.URI = 1234
const errorMessage = 'OracleSet: invalid field URI'
assert.throws(() => validateOracleSet(tx), ValidationError, errorMessage)
assert.throws(() => validate(tx), ValidationError, errorMessage)
})
it(`throws w/ missing invalid AssetClass`, function () {
tx.AssetClass = 1234
const errorMessage = 'OracleSet: invalid field AssetClass'
assert.throws(() => validateOracleSet(tx), ValidationError, errorMessage)
assert.throws(() => validate(tx), ValidationError, errorMessage)
})
it(`throws w/ invalid PriceDataSeries must be an array`, function () {
tx.PriceDataSeries = 1234
const errorMessage = 'OracleSet: PriceDataSeries must be an array'
assert.throws(() => validateOracleSet(tx), ValidationError, errorMessage)
assert.throws(() => validate(tx), ValidationError, errorMessage)
})
it(`throws w/ invalid PriceDataSeries must be an array of objects`, function () {
tx.PriceDataSeries = [1234]
const errorMessage =
'OracleSet: PriceDataSeries must be an array of objects'
assert.throws(() => validateOracleSet(tx), ValidationError, errorMessage)
assert.throws(() => validate(tx), ValidationError, errorMessage)
})
it(`throws w/ PriceDataSeries must have at most 10 PriceData objects`, function () {
tx.PriceDataSeries = new Array(11).fill({
PriceData: {
BaseAsset: 'XRP',
QuoteAsset: 'USD',
AssetPrice: 740,
Scale: 3,
},
})
const errorMessage =
'OracleSet: PriceDataSeries must have at most 10 PriceData objects'
assert.throws(() => validateOracleSet(tx), ValidationError, errorMessage)
assert.throws(() => validate(tx), ValidationError, errorMessage)
})
it(`throws w/ PriceDataSeries must have a PriceData object`, function () {
delete tx.PriceDataSeries[0].PriceData
const errorMessage =
'OracleSet: PriceDataSeries must have a `PriceData` object'
assert.throws(() => validateOracleSet(tx), ValidationError, errorMessage)
assert.throws(() => validate(tx), ValidationError, errorMessage)
})
it(`throws w/ PriceDataSeries must only have a single PriceData object`, function () {
tx.PriceDataSeries[0].ExtraProp = 'extraprop'
const errorMessage =
'OracleSet: PriceDataSeries must only have a single PriceData object'
assert.throws(() => validateOracleSet(tx), ValidationError, errorMessage)
assert.throws(() => validate(tx), ValidationError, errorMessage)
})
it(`throws w/ missing BaseAsset of PriceDataSeries`, function () {
delete tx.PriceDataSeries[0].PriceData.BaseAsset
const errorMessage =
'OracleSet: PriceDataSeries must have a `BaseAsset` string'
assert.throws(() => validateOracleSet(tx), ValidationError, errorMessage)
assert.throws(() => validate(tx), ValidationError, errorMessage)
})
it(`throws w/ missing QuoteAsset of PriceDataSeries`, function () {
delete tx.PriceDataSeries[0].PriceData.QuoteAsset
const errorMessage =
'OracleSet: PriceDataSeries must have a `QuoteAsset` string'
assert.throws(() => validateOracleSet(tx), ValidationError, errorMessage)
assert.throws(() => validate(tx), ValidationError, errorMessage)
})
it(`throws w/ missing AssetPrice with Scale present of PriceDataSeries`, function () {
delete tx.PriceDataSeries[0].PriceData.AssetPrice
const errorMessage =
'OracleSet: PriceDataSeries must have both `AssetPrice` and `Scale` if any are present'
assert.throws(() => validateOracleSet(tx), ValidationError, errorMessage)
assert.throws(() => validate(tx), ValidationError, errorMessage)
})
it(`throws w/ missing Scale with AssetPrice present of PriceDataSeries`, function () {
delete tx.PriceDataSeries[0].PriceData.Scale
const errorMessage =
'OracleSet: PriceDataSeries must have both `AssetPrice` and `Scale` if any are present'
assert.throws(() => validateOracleSet(tx), ValidationError, errorMessage)
assert.throws(() => validate(tx), ValidationError, errorMessage)
})
it(`throws w/ invalid AssetPrice of PriceDataSeries`, function () {
tx.PriceDataSeries[0].PriceData.AssetPrice = '1234'
const errorMessage = 'OracleSet: invalid field AssetPrice'
assert.throws(() => validateOracleSet(tx), ValidationError, errorMessage)
assert.throws(() => validate(tx), ValidationError, errorMessage)
})
it(`throws w/ invalid Scale of PriceDataSeries`, function () {
tx.PriceDataSeries[0].PriceData.Scale = '1234'
const errorMessage = 'OracleSet: invalid field Scale'
assert.throws(() => validateOracleSet(tx), ValidationError, errorMessage)
assert.throws(() => validate(tx), ValidationError, errorMessage)
})
it(`throws w/ Scale must be in range 0-10 when above max`, function () {
tx.PriceDataSeries[0].PriceData.Scale = 11
const errorMessage = 'OracleSet: Scale must be in range 0-10'
assert.throws(() => validateOracleSet(tx), ValidationError, errorMessage)
assert.throws(() => validate(tx), ValidationError, errorMessage)
})
it(`throws w/ Scale must be in range 0-10 when below min`, function () {
tx.PriceDataSeries[0].PriceData.Scale = -1
const errorMessage = 'OracleSet: Scale must be in range 0-10'
assert.throws(() => validateOracleSet(tx), ValidationError, errorMessage)
assert.throws(() => validate(tx), ValidationError, errorMessage)
})
})

View File

@@ -100,7 +100,7 @@ describe('Models Utils', function () {
Amount: '1234',
Destination: 'rfkE1aSy9G8Upk4JssnwBxhEv5p4mn2KTy',
Flags: {
tfNoDirectRipple: false,
tfNoRippleDirect: false,
tfPartialPayment: true,
tfLimitQuality: true,
},

View File

@@ -5,12 +5,12 @@ import fixtures from '../fixtures/rippled'
describe('hasNextPage', function () {
it('returns true when response has marker', function () {
const firstPage = fixtures.ledger_data.first_page
const firstPage = fixtures.ledger_data.firstPage
assert.isTrue(hasNextPage(firstPage))
})
it('returns false when response does not have marker', function () {
const lastPage = fixtures.ledger_data.last_page
const lastPage = fixtures.ledger_data.lastPage
assert.isFalse(hasNextPage(lastPage))
})
})