mirror of
https://github.com/Xahau/xahau.js.git
synced 2026-06-29 05:26:46 +00:00
Compare commits
4 Commits
PriceOracl
...
xahau-bina
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
94751fc4bc | ||
|
|
c9a22ffa23 | ||
|
|
e351c57e88 | ||
|
|
8f1c5db1f3 |
2
.github/workflows/nodejs.yml
vendored
2
.github/workflows/nodejs.yml
vendored
@@ -4,7 +4,7 @@
|
|||||||
name: Node.js CI
|
name: Node.js CI
|
||||||
|
|
||||||
env:
|
env:
|
||||||
XAHAUD_VERSION: 2026.6.16-dev+3330
|
XAHAUD_VERSION: 2026.6.21-release+3350
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
|
|||||||
6
package-lock.json
generated
6
package-lock.json
generated
@@ -16048,7 +16048,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"packages/xahau": {
|
"packages/xahau": {
|
||||||
"version": "4.0.4",
|
"version": "4.1.0",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@scure/bip32": "^1.3.1",
|
"@scure/bip32": "^1.3.1",
|
||||||
@@ -16058,7 +16058,7 @@
|
|||||||
"bignumber.js": "^9.0.0",
|
"bignumber.js": "^9.0.0",
|
||||||
"eventemitter3": "^5.0.1",
|
"eventemitter3": "^5.0.1",
|
||||||
"xahau-address-codec": "^5.0.0",
|
"xahau-address-codec": "^5.0.0",
|
||||||
"xahau-binary-codec": "^2.1.3",
|
"xahau-binary-codec": "^2.2.0",
|
||||||
"xahau-keypairs": "^2.0.0"
|
"xahau-keypairs": "^2.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@@ -16091,7 +16091,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"packages/xahau-binary-codec": {
|
"packages/xahau-binary-codec": {
|
||||||
"version": "2.1.3",
|
"version": "2.2.0",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@xrplf/isomorphic": "^1.0.1",
|
"@xrplf/isomorphic": "^1.0.1",
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
## Unreleased
|
## Unreleased
|
||||||
|
|
||||||
## 2.1.0 (2024-06-03)
|
## 2.1.0 (2026-06-23)
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
* Support for the Price Oracles amendment (XLS-47).
|
* Support for the Price Oracles amendment (XLS-47).
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "xahau-binary-codec",
|
"name": "xahau-binary-codec",
|
||||||
"version": "2.1.3",
|
"version": "2.2.0",
|
||||||
"description": "Xahau Network binary codec",
|
"description": "Xahau Network binary codec",
|
||||||
"files": [
|
"files": [
|
||||||
"dist/*",
|
"dist/*",
|
||||||
|
|||||||
@@ -4449,6 +4449,36 @@
|
|||||||
"Flags": 0,
|
"Flags": 0,
|
||||||
"Sequence": 62
|
"Sequence": 62
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"binary": "12003C2FFFFFFFFF2033000004D2750B6469645F6578616D706C65701D0863757272656E6379701E0870726F7669646572811401476926B590BA3245F63C829116A0A3AF7F382DF018E020301700000000000001E2041003011A0000000000000000000000000000000000000000021A0000000000000000000000005553440000000000E1F1",
|
||||||
|
"json": {
|
||||||
|
"TransactionType": "OracleSet",
|
||||||
|
"Account": "rfmDuhDyLGgx94qiwf3YF8BUV5j6KSvE8",
|
||||||
|
"OracleDocumentID": 1234,
|
||||||
|
"LastUpdateTime": 4294967295,
|
||||||
|
"PriceDataSeries": [
|
||||||
|
{
|
||||||
|
"PriceData": {
|
||||||
|
"BaseAsset": "XAH",
|
||||||
|
"QuoteAsset": "USD",
|
||||||
|
"AssetPrice": "00000000000001E2",
|
||||||
|
"Scale": 3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"Provider": "70726F7669646572",
|
||||||
|
"URI": "6469645F6578616D706C65",
|
||||||
|
"AssetClass": "63757272656E6379"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"binary": "12003D2033000004D2811401476926B590BA3245F63C829116A0A3AF7F382D",
|
||||||
|
"json": {
|
||||||
|
"TransactionType": "OracleDelete",
|
||||||
|
"Account": "rfmDuhDyLGgx94qiwf3YF8BUV5j6KSvE8",
|
||||||
|
"OracleDocumentID": 1234
|
||||||
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"ledgerData": [{
|
"ledgerData": [{
|
||||||
|
|||||||
@@ -1,14 +1,15 @@
|
|||||||
# xrpl.js Release History
|
# xahau.js Release History
|
||||||
|
|
||||||
Subscribe to [the **xrpl-announce** mailing list](https://groups.google.com/g/xrpl-announce) for release announcements. We recommend that xrpl.js (ripple-lib) users stay up-to-date with the latest stable release.
|
|
||||||
|
|
||||||
## Unreleased Changes
|
## Unreleased Changes
|
||||||
|
|
||||||
|
## 4.1.0 (2026-06-23)
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
* Support for HookOnV2 Amendment
|
* Support for HookOnV2 Amendment
|
||||||
* Support for IOUClaimReward Amendment
|
* Support for IOUClaimReward Amendment
|
||||||
* Support for NamedHooks
|
* Support for NamedHooks
|
||||||
* Support for the `simulate` RPC ([XLS-69](https://github.com/XRPLF/XRPL-Standards/tree/master/XLS-0069-simulate))
|
* Support for the `simulate` RPC ([XLS-69](https://github.com/XRPLF/XRPL-Standards/tree/master/XLS-0069-simulate))
|
||||||
|
* Support for the Price Oracles amendment (XLS-47).
|
||||||
|
|
||||||
## 4.0.4 (2026-05-27)
|
## 4.0.4 (2026-05-27)
|
||||||
|
|
||||||
@@ -67,6 +68,9 @@ Subscribe to [the **xrpl-announce** mailing list](https://groups.google.com/g/xr
|
|||||||
### Added
|
### Added
|
||||||
* Support for the Price Oracles amendment (XLS-47).
|
* Support for the Price Oracles amendment (XLS-47).
|
||||||
|
|
||||||
|
### Added
|
||||||
|
* Support for the Price Oracles amendment (XLS-47).
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
* Typo in `Channel` type `source_tab` -> `source_tag`
|
* Typo in `Channel` type `source_tab` -> `source_tag`
|
||||||
* Fix `client.requestAll` to handle filters better
|
* Fix `client.requestAll` to handle filters better
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "xahau",
|
"name": "xahau",
|
||||||
"version": "4.0.4",
|
"version": "4.1.0",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"description": "A TypeScript/JavaScript API for interacting with the Xahau Network in Node.js and the browser",
|
"description": "A TypeScript/JavaScript API for interacting with the Xahau Network in Node.js and the browser",
|
||||||
"files": [
|
"files": [
|
||||||
@@ -29,7 +29,7 @@
|
|||||||
"bignumber.js": "^9.0.0",
|
"bignumber.js": "^9.0.0",
|
||||||
"eventemitter3": "^5.0.1",
|
"eventemitter3": "^5.0.1",
|
||||||
"xahau-address-codec": "^5.0.0",
|
"xahau-address-codec": "^5.0.0",
|
||||||
"xahau-binary-codec": "^2.1.3",
|
"xahau-binary-codec": "^2.2.0",
|
||||||
"xahau-keypairs": "^2.0.0"
|
"xahau-keypairs": "^2.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
@@ -660,7 +660,7 @@ class Client extends EventEmitter<EventTypes> {
|
|||||||
* @returns The autofilled transaction.
|
* @returns The autofilled transaction.
|
||||||
* @throws ValidationError If Amount and DeliverMax fields are not identical in a Payment Transaction
|
* @throws ValidationError If Amount and DeliverMax fields are not identical in a Payment Transaction
|
||||||
*/
|
*/
|
||||||
|
// eslint-disable-next-line complexity -- handling Payment transaction API v2 requires more logic
|
||||||
public async autofill<T extends SubmittableTransaction>(
|
public async autofill<T extends SubmittableTransaction>(
|
||||||
transaction: T,
|
transaction: T,
|
||||||
signersCount?: number,
|
signersCount?: number,
|
||||||
@@ -681,6 +681,34 @@ class Client extends EventEmitter<EventTypes> {
|
|||||||
promises.push(setLatestValidatedLedgerSequence(this, tx))
|
promises.push(setLatestValidatedLedgerSequence(this, tx))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment -- ignore type-assertions on the DeliverMax property
|
||||||
|
// @ts-expect-error -- DeliverMax property exists only at the RPC level, not at the protocol level
|
||||||
|
if (tx.TransactionType === 'Payment' && tx.DeliverMax != null) {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- This is a valid null check for Amount
|
||||||
|
if (tx.Amount == null) {
|
||||||
|
// If only DeliverMax is provided, use it to populate the Amount field
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment -- ignore type-assertions on the DeliverMax property
|
||||||
|
// @ts-expect-error -- DeliverMax property exists only at the RPC level, not at the protocol level
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- DeliverMax is a known RPC-level property
|
||||||
|
tx.Amount = tx.DeliverMax
|
||||||
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment -- ignore type-assertions on the DeliverMax property
|
||||||
|
// @ts-expect-error -- DeliverMax property exists only at the RPC level, not at the protocol level
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- This is a valid null check for Amount
|
||||||
|
if (tx.Amount != null && tx.Amount !== tx.DeliverMax) {
|
||||||
|
return Promise.reject(
|
||||||
|
new ValidationError(
|
||||||
|
'PaymentTransaction: Amount and DeliverMax fields must be identical when both are provided',
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment -- ignore type-assertions on the DeliverMax property
|
||||||
|
// @ts-expect-error -- DeliverMax property exists only at the RPC level, not at the protocol level
|
||||||
|
delete tx.DeliverMax
|
||||||
|
}
|
||||||
|
|
||||||
await Promise.all(promises).then(() => tx)
|
await Promise.all(promises).then(() => tx)
|
||||||
|
|
||||||
if (tx.Fee == null) {
|
if (tx.Fee == null) {
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import ImportVLSequence from './ImportVLSequence'
|
|||||||
import LedgerHashes from './LedgerHashes'
|
import LedgerHashes from './LedgerHashes'
|
||||||
import NegativeUNL from './NegativeUNL'
|
import NegativeUNL from './NegativeUNL'
|
||||||
import Offer from './Offer'
|
import Offer from './Offer'
|
||||||
|
import Oracle from './Oracle'
|
||||||
import PayChannel from './PayChannel'
|
import PayChannel from './PayChannel'
|
||||||
import RippleState from './RippleState'
|
import RippleState from './RippleState'
|
||||||
import SignerList from './SignerList'
|
import SignerList from './SignerList'
|
||||||
@@ -38,6 +39,7 @@ type LedgerEntry =
|
|||||||
| LedgerHashes
|
| LedgerHashes
|
||||||
| NegativeUNL
|
| NegativeUNL
|
||||||
| Offer
|
| Offer
|
||||||
|
| Oracle
|
||||||
| PayChannel
|
| PayChannel
|
||||||
| RippleState
|
| RippleState
|
||||||
| SignerList
|
| SignerList
|
||||||
@@ -60,6 +62,7 @@ type LedgerEntryFilter =
|
|||||||
| 'import_vl_sequence'
|
| 'import_vl_sequence'
|
||||||
| 'hashes'
|
| 'hashes'
|
||||||
| 'offer'
|
| 'offer'
|
||||||
|
| 'oracle'
|
||||||
| 'payment_channel'
|
| 'payment_channel'
|
||||||
| 'signer_list'
|
| 'signer_list'
|
||||||
| 'state'
|
| 'state'
|
||||||
|
|||||||
43
packages/xahau/src/models/ledger/Oracle.ts
Normal file
43
packages/xahau/src/models/ledger/Oracle.ts
Normal 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
|
||||||
|
}
|
||||||
@@ -23,6 +23,7 @@ import { LedgerEntry, LedgerEntryFilter } from './LedgerEntry'
|
|||||||
import LedgerHashes from './LedgerHashes'
|
import LedgerHashes from './LedgerHashes'
|
||||||
import NegativeUNL, { NEGATIVE_UNL_ID } from './NegativeUNL'
|
import NegativeUNL, { NEGATIVE_UNL_ID } from './NegativeUNL'
|
||||||
import Offer, { OfferFlags } from './Offer'
|
import Offer, { OfferFlags } from './Offer'
|
||||||
|
import Oracle from './Oracle'
|
||||||
import PayChannel from './PayChannel'
|
import PayChannel from './PayChannel'
|
||||||
import RippleState, { RippleStateFlags } from './RippleState'
|
import RippleState, { RippleStateFlags } from './RippleState'
|
||||||
import SignerList, { SignerListFlags } from './SignerList'
|
import SignerList, { SignerListFlags } from './SignerList'
|
||||||
@@ -60,6 +61,7 @@ export {
|
|||||||
NegativeUNL,
|
NegativeUNL,
|
||||||
Offer,
|
Offer,
|
||||||
OfferFlags,
|
OfferFlags,
|
||||||
|
Oracle,
|
||||||
PayChannel,
|
PayChannel,
|
||||||
RippleState,
|
RippleState,
|
||||||
RippleStateFlags,
|
RippleStateFlags,
|
||||||
|
|||||||
119
packages/xahau/src/models/methods/getAggregatePrice.ts
Normal file
119
packages/xahau/src/models/methods/getAggregatePrice.ts
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -78,6 +78,10 @@ import {
|
|||||||
GatewayBalancesRequest,
|
GatewayBalancesRequest,
|
||||||
GatewayBalancesResponse,
|
GatewayBalancesResponse,
|
||||||
} from './gatewayBalances'
|
} from './gatewayBalances'
|
||||||
|
import {
|
||||||
|
GetAggregatePriceRequest,
|
||||||
|
GetAggregatePriceResponse,
|
||||||
|
} from './getAggregatePrice'
|
||||||
import {
|
import {
|
||||||
LedgerBinary,
|
LedgerBinary,
|
||||||
LedgerModifiedOfferCreateTransaction,
|
LedgerModifiedOfferCreateTransaction,
|
||||||
@@ -221,6 +225,8 @@ type Request =
|
|||||||
// utility methods
|
// utility methods
|
||||||
| PingRequest
|
| PingRequest
|
||||||
| RandomRequest
|
| RandomRequest
|
||||||
|
// Price Oracle methods
|
||||||
|
| GetAggregatePriceRequest
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @category Responses
|
* @category Responses
|
||||||
@@ -269,6 +275,8 @@ type Response<Version extends APIVersion = typeof DEFAULT_API_VERSION> =
|
|||||||
// utility methods
|
// utility methods
|
||||||
| PingResponse
|
| PingResponse
|
||||||
| RandomResponse
|
| RandomResponse
|
||||||
|
// Price Oracle methods
|
||||||
|
| GetAggregatePriceResponse
|
||||||
|
|
||||||
export type RequestResponseMap<
|
export type RequestResponseMap<
|
||||||
T,
|
T,
|
||||||
@@ -289,6 +297,8 @@ export type RequestResponseMap<
|
|||||||
? AccountTxVersionResponseMap<Version>
|
? AccountTxVersionResponseMap<Version>
|
||||||
: T extends GatewayBalancesRequest
|
: T extends GatewayBalancesRequest
|
||||||
? GatewayBalancesResponse
|
? GatewayBalancesResponse
|
||||||
|
: T extends GetAggregatePriceRequest
|
||||||
|
? GetAggregatePriceResponse
|
||||||
: T extends NoRippleCheckRequest
|
: T extends NoRippleCheckRequest
|
||||||
? NoRippleCheckResponse
|
? NoRippleCheckResponse
|
||||||
: // NOTE: The order of these LedgerRequest types is important
|
: // NOTE: The order of these LedgerRequest types is important
|
||||||
@@ -481,6 +491,8 @@ export {
|
|||||||
GatewayBalance,
|
GatewayBalance,
|
||||||
GatewayBalancesRequest,
|
GatewayBalancesRequest,
|
||||||
GatewayBalancesResponse,
|
GatewayBalancesResponse,
|
||||||
|
GetAggregatePriceRequest,
|
||||||
|
GetAggregatePriceResponse,
|
||||||
NoRippleCheckRequest,
|
NoRippleCheckRequest,
|
||||||
NoRippleCheckResponse,
|
NoRippleCheckResponse,
|
||||||
// ledger methods
|
// ledger methods
|
||||||
|
|||||||
@@ -32,6 +32,8 @@ export {
|
|||||||
OfferCreateFlagsInterface,
|
OfferCreateFlagsInterface,
|
||||||
OfferCreate,
|
OfferCreate,
|
||||||
} from './offerCreate'
|
} from './offerCreate'
|
||||||
|
export { OracleDelete } from './oracleDelete'
|
||||||
|
export { OracleSet } from './oracleSet'
|
||||||
export { PaymentFlags, PaymentFlagsInterface, Payment } from './payment'
|
export { PaymentFlags, PaymentFlagsInterface, Payment } from './payment'
|
||||||
export {
|
export {
|
||||||
PaymentChannelClaimFlags,
|
PaymentChannelClaimFlags,
|
||||||
|
|||||||
32
packages/xahau/src/models/transactions/oracleDelete.ts
Normal file
32
packages/xahau/src/models/transactions/oracleDelete.ts
Normal 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)
|
||||||
|
}
|
||||||
198
packages/xahau/src/models/transactions/oracleSet.ts
Normal file
198
packages/xahau/src/models/transactions/oracleSet.ts
Normal file
@@ -0,0 +1,198 @@
|
|||||||
|
import { ValidationError } from '../../errors'
|
||||||
|
import { PriceData } from '../common'
|
||||||
|
import { isHex } from '../utils'
|
||||||
|
|
||||||
|
import {
|
||||||
|
BaseTransaction,
|
||||||
|
isNumber,
|
||||||
|
isString,
|
||||||
|
validateBaseTransaction,
|
||||||
|
validateOptionalField,
|
||||||
|
validateRequiredField,
|
||||||
|
} from './common'
|
||||||
|
|
||||||
|
const PRICE_DATA_SERIES_MAX_LENGTH = 10
|
||||||
|
const SCALE_MAX = 10
|
||||||
|
const MINIMUM_ASSET_PRICE_LENGTH = 1
|
||||||
|
const MAXIMUM_ASSET_PRICE_LENGTH = 16
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 max-statements, 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',
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/* eslint-disable @typescript-eslint/no-unsafe-member-access, max-depth --
|
||||||
|
we need to validate priceData.PriceData.AssetPrice value */
|
||||||
|
if ('AssetPrice' in priceData.PriceData) {
|
||||||
|
if (!isNumber(priceData.PriceData.AssetPrice)) {
|
||||||
|
if (typeof priceData.PriceData.AssetPrice !== 'string') {
|
||||||
|
throw new ValidationError(
|
||||||
|
'OracleSet: Field AssetPrice must be a string or a number',
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (!isHex(priceData.PriceData.AssetPrice)) {
|
||||||
|
throw new ValidationError(
|
||||||
|
'OracleSet: Field AssetPrice must be a valid hex string',
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
priceData.PriceData.AssetPrice.length <
|
||||||
|
MINIMUM_ASSET_PRICE_LENGTH ||
|
||||||
|
priceData.PriceData.AssetPrice.length > MAXIMUM_ASSET_PRICE_LENGTH
|
||||||
|
) {
|
||||||
|
throw new ValidationError(
|
||||||
|
`OracleSet: Length of AssetPrice field must be between ${MINIMUM_ASSET_PRICE_LENGTH} and ${MAXIMUM_ASSET_PRICE_LENGTH} characters long`,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* eslint-enable @typescript-eslint/no-unsafe-member-access, max-depth */
|
||||||
|
|
||||||
|
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
|
||||||
|
})
|
||||||
|
/* eslint-enable max-statements, max-lines-per-function */
|
||||||
|
}
|
||||||
@@ -24,6 +24,8 @@ import { Invoke, validateInvoke } from './invoke'
|
|||||||
import { TransactionMetadata } from './metadata'
|
import { TransactionMetadata } from './metadata'
|
||||||
import { OfferCancel, validateOfferCancel } from './offerCancel'
|
import { OfferCancel, validateOfferCancel } from './offerCancel'
|
||||||
import { OfferCreate, validateOfferCreate } from './offerCreate'
|
import { OfferCreate, validateOfferCreate } from './offerCreate'
|
||||||
|
import { OracleDelete, validateOracleDelete } from './oracleDelete'
|
||||||
|
import { OracleSet, validateOracleSet } from './oracleSet'
|
||||||
import { Payment, validatePayment } from './payment'
|
import { Payment, validatePayment } from './payment'
|
||||||
import {
|
import {
|
||||||
PaymentChannelClaim,
|
PaymentChannelClaim,
|
||||||
@@ -79,6 +81,8 @@ export type SubmittableTransaction =
|
|||||||
| Invoke
|
| Invoke
|
||||||
| OfferCancel
|
| OfferCancel
|
||||||
| OfferCreate
|
| OfferCreate
|
||||||
|
| OracleDelete
|
||||||
|
| OracleSet
|
||||||
| Payment
|
| Payment
|
||||||
| PaymentChannelClaim
|
| PaymentChannelClaim
|
||||||
| PaymentChannelCreate
|
| PaymentChannelCreate
|
||||||
@@ -249,6 +253,14 @@ export function validate(transaction: Record<string, unknown>): void {
|
|||||||
validateOfferCreate(tx)
|
validateOfferCreate(tx)
|
||||||
break
|
break
|
||||||
|
|
||||||
|
case 'OracleDelete':
|
||||||
|
validateOracleDelete(tx)
|
||||||
|
break
|
||||||
|
|
||||||
|
case 'OracleSet':
|
||||||
|
validateOracleSet(tx)
|
||||||
|
break
|
||||||
|
|
||||||
case 'Payment':
|
case 'Payment':
|
||||||
validatePayment(tx)
|
validatePayment(tx)
|
||||||
break
|
break
|
||||||
@@ -315,6 +327,7 @@ export function validate(transaction: Record<string, unknown>): void {
|
|||||||
|
|
||||||
default:
|
default:
|
||||||
throw new ValidationError(
|
throw new ValidationError(
|
||||||
|
// eslint-disable-next-line max-lines -- allowed here
|
||||||
`Invalid field TransactionType: ${tx.TransactionType}`,
|
`Invalid field TransactionType: ${tx.TransactionType}`,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,14 @@
|
|||||||
import { assert } from 'chai'
|
import { assert } from 'chai'
|
||||||
|
|
||||||
import { EscrowFinish, Payment, Transaction } from '../../src'
|
import { EscrowFinish, Payment, Transaction } from '../../src'
|
||||||
|
import { ValidationError } from '../../src/errors'
|
||||||
import xahaud from '../fixtures/xahaud'
|
import xahaud from '../fixtures/xahaud'
|
||||||
import {
|
import {
|
||||||
setupClient,
|
setupClient,
|
||||||
teardownClient,
|
teardownClient,
|
||||||
type XrplTestContext,
|
type XrplTestContext,
|
||||||
} from '../setupClient'
|
} from '../setupClient'
|
||||||
|
import { assertRejects } from '../testUtils'
|
||||||
|
|
||||||
const NetworkID = 1025
|
const NetworkID = 1025
|
||||||
const Fee = '10'
|
const Fee = '10'
|
||||||
@@ -15,6 +17,8 @@ const LastLedgerSequence = 2908734
|
|||||||
|
|
||||||
describe('client.autofill', function () {
|
describe('client.autofill', function () {
|
||||||
let testContext: XrplTestContext
|
let testContext: XrplTestContext
|
||||||
|
const AMOUNT = '1234'
|
||||||
|
let paymentTx: Payment
|
||||||
|
|
||||||
async function setupMockRippledVersionAndID(
|
async function setupMockRippledVersionAndID(
|
||||||
buildVersion: string,
|
buildVersion: string,
|
||||||
@@ -38,6 +42,64 @@ describe('client.autofill', function () {
|
|||||||
})
|
})
|
||||||
afterAll(async () => teardownClient(testContext))
|
afterAll(async () => teardownClient(testContext))
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
paymentTx = {
|
||||||
|
TransactionType: 'Payment',
|
||||||
|
Account: 'rUn84CUYbNjRoTQ6mSW7BVJPSVJNLb1QLo',
|
||||||
|
Amount: AMOUNT,
|
||||||
|
Destination: 'rfkE1aSy9G8Upk4JssnwBxhEv5p4mn2KTy',
|
||||||
|
DestinationTag: 1,
|
||||||
|
Fee: '12',
|
||||||
|
Flags: 2147483648,
|
||||||
|
LastLedgerSequence: 65953073,
|
||||||
|
Sequence: 65923914,
|
||||||
|
SigningPubKey:
|
||||||
|
'02F9E33F16DF9507705EC954E3F94EB5F10D1FC4A354606DBE6297DBB1096FE654',
|
||||||
|
TxnSignature:
|
||||||
|
'3045022100E3FAE0EDEC3D6A8FF6D81BC9CF8288A61B7EEDE8071E90FF9314CB4621058D10022043545CF631706D700CEE65A1DB83EFDD185413808292D9D90F14D87D3DC2D8CB',
|
||||||
|
InvoiceID:
|
||||||
|
'6F1DFD1D0FE8A32E40E1F2C05CF1C15545BAB56B617F9C6C2D63A6B704BEF59B',
|
||||||
|
Paths: [
|
||||||
|
[{ currency: 'BTC', issuer: 'r9vbV3EHvXWjSkeQ6CAcYVPGeq7TuiXY2X' }],
|
||||||
|
],
|
||||||
|
SendMax: '100000000',
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
it('Validate Payment transaction API v2: Payment Transaction: Specify Only Amount field', async function () {
|
||||||
|
const txResult = await testContext.client.autofill(paymentTx)
|
||||||
|
|
||||||
|
assert.strictEqual(txResult.Amount, AMOUNT)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('Validate Payment transaction API v2: Payment Transaction: Specify Only DeliverMax field', async function () {
|
||||||
|
// @ts-expect-error -- DeliverMax is a non-protocol, RPC level field in Payment transactions
|
||||||
|
paymentTx.DeliverMax = paymentTx.Amount
|
||||||
|
// @ts-expect-error -- DeliverMax is a non-protocol, RPC level field in Payment transactions
|
||||||
|
delete paymentTx.Amount
|
||||||
|
const txResult = await testContext.client.autofill(paymentTx)
|
||||||
|
|
||||||
|
assert.strictEqual(txResult.Amount, AMOUNT)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('Validate Payment transaction API v2: Payment Transaction: identical DeliverMax and Amount fields', async function () {
|
||||||
|
// @ts-expect-error -- DeliverMax is a non-protocol, RPC level field in Payment transactions
|
||||||
|
paymentTx.DeliverMax = paymentTx.Amount
|
||||||
|
|
||||||
|
const txResult = await testContext.client.autofill(paymentTx)
|
||||||
|
|
||||||
|
assert.strictEqual(txResult.Amount, AMOUNT)
|
||||||
|
assert.strictEqual('DeliverMax' in txResult, false)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('Validate Payment transaction API v2: Payment Transaction: differing DeliverMax and Amount fields', async function () {
|
||||||
|
// @ts-expect-error -- DeliverMax is a non-protocol, RPC level field in Payment transactions
|
||||||
|
paymentTx.DeliverMax = '6789'
|
||||||
|
paymentTx.Amount = '1234'
|
||||||
|
|
||||||
|
await assertRejects(testContext.client.autofill(paymentTx), ValidationError)
|
||||||
|
})
|
||||||
|
|
||||||
it('should not autofill if fields are present', async function () {
|
it('should not autofill if fields are present', async function () {
|
||||||
const tx: Transaction = {
|
const tx: Transaction = {
|
||||||
TransactionType: 'DepositPreauth',
|
TransactionType: 'DepositPreauth',
|
||||||
|
|||||||
@@ -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,
|
||||||
|
)
|
||||||
|
})
|
||||||
@@ -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,
|
||||||
|
)
|
||||||
|
})
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
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,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
PriceData: {
|
||||||
|
BaseAsset: 'XRP',
|
||||||
|
QuoteAsset: 'INR',
|
||||||
|
// Upper bound admissible value for AssetPrice field
|
||||||
|
// large numeric values necessarily have to use str type in Javascript
|
||||||
|
// number type uses double-precision floating point representation, hence represents a smaller range of values
|
||||||
|
AssetPrice: 'ffffffffffffffff',
|
||||||
|
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, 2)
|
||||||
|
assert.equal(oracle.PriceDataSeries[1].PriceData.BaseAsset, 'XRP')
|
||||||
|
assert.equal(oracle.PriceDataSeries[1].PriceData.QuoteAsset, 'USD')
|
||||||
|
assert.equal(oracle.PriceDataSeries[1].PriceData.AssetPrice, '2e4')
|
||||||
|
assert.equal(oracle.PriceDataSeries[1].PriceData.Scale, 3)
|
||||||
|
assert.equal(oracle.Flags, 0)
|
||||||
|
|
||||||
|
// validate the serialization of large AssetPrice values
|
||||||
|
assert.equal(
|
||||||
|
oracle.PriceDataSeries[0].PriceData.AssetPrice,
|
||||||
|
'ffffffffffffffff',
|
||||||
|
)
|
||||||
|
},
|
||||||
|
TIMEOUT,
|
||||||
|
)
|
||||||
|
})
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import { assert } from 'chai'
|
||||||
|
|
||||||
import { Payment, Wallet } from '../../../src'
|
import { Payment, Wallet } from '../../../src'
|
||||||
import serverUrl from '../serverUrl'
|
import serverUrl from '../serverUrl'
|
||||||
import {
|
import {
|
||||||
@@ -12,9 +14,22 @@ const TIMEOUT = 20000
|
|||||||
|
|
||||||
describe('Payment', function () {
|
describe('Payment', function () {
|
||||||
let testContext: XrplIntegrationTestContext
|
let testContext: XrplIntegrationTestContext
|
||||||
|
let paymentTx: Payment
|
||||||
|
const AMOUNT = '10000000'
|
||||||
// This wallet is used for DeliverMax related tests
|
// This wallet is used for DeliverMax related tests
|
||||||
let senderWallet: Wallet
|
let senderWallet: Wallet
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
// this payment transaction JSON needs to be refreshed before every test.
|
||||||
|
// Because, we tinker with Amount and DeliverMax fields in the API v2 tests
|
||||||
|
paymentTx = {
|
||||||
|
TransactionType: 'Payment',
|
||||||
|
Account: senderWallet.classicAddress,
|
||||||
|
Amount: AMOUNT,
|
||||||
|
Destination: 'rfkE1aSy9G8Upk4JssnwBxhEv5p4mn2KTy',
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
testContext = await setupClient(serverUrl)
|
testContext = await setupClient(serverUrl)
|
||||||
senderWallet = await generateFundedWallet(testContext.client)
|
senderWallet = await generateFundedWallet(testContext.client)
|
||||||
@@ -34,4 +49,57 @@ describe('Payment', function () {
|
|||||||
},
|
},
|
||||||
TIMEOUT,
|
TIMEOUT,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
it(
|
||||||
|
'Validate Payment transaction API v2: Payment Transaction: Specify Only Amount field',
|
||||||
|
async () => {
|
||||||
|
const result = await testTransaction(
|
||||||
|
testContext.client,
|
||||||
|
paymentTx,
|
||||||
|
senderWallet,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert.equal(result.result.engine_result_code, 0)
|
||||||
|
assert.equal((result.result.tx_json as Payment).Amount, AMOUNT)
|
||||||
|
},
|
||||||
|
TIMEOUT,
|
||||||
|
)
|
||||||
|
|
||||||
|
it(
|
||||||
|
'Validate Payment transaction API v2: Payment Transaction: Specify Only DeliverMax field',
|
||||||
|
async () => {
|
||||||
|
// @ts-expect-error -- DeliverMax is a non-protocol, RPC level field in Payment transactions
|
||||||
|
paymentTx.DeliverMax = paymentTx.Amount
|
||||||
|
// @ts-expect-error -- DeliverMax is a non-protocol, RPC level field in Payment transactions
|
||||||
|
delete paymentTx.Amount
|
||||||
|
|
||||||
|
const result = await testTransaction(
|
||||||
|
testContext.client,
|
||||||
|
paymentTx,
|
||||||
|
senderWallet,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert.equal(result.result.engine_result_code, 0)
|
||||||
|
assert.equal((result.result.tx_json as Payment).Amount, AMOUNT)
|
||||||
|
},
|
||||||
|
TIMEOUT,
|
||||||
|
)
|
||||||
|
|
||||||
|
it(
|
||||||
|
'Validate Payment transaction API v2: Payment Transaction: identical DeliverMax and Amount fields',
|
||||||
|
async () => {
|
||||||
|
// @ts-expect-error -- DeliverMax is a non-protocol, RPC level field in Payment transactions
|
||||||
|
paymentTx.DeliverMax = paymentTx.Amount
|
||||||
|
|
||||||
|
const result = await testTransaction(
|
||||||
|
testContext.client,
|
||||||
|
paymentTx,
|
||||||
|
senderWallet,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert.equal(result.result.engine_result_code, 0)
|
||||||
|
assert.equal((result.result.tx_json as Payment).Amount, AMOUNT)
|
||||||
|
},
|
||||||
|
TIMEOUT,
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|||||||
40
packages/xahau/test/models/oracleDelete.test.ts
Normal file
40
packages/xahau/test/models/oracleDelete.test.ts
Normal 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)
|
||||||
|
})
|
||||||
|
})
|
||||||
212
packages/xahau/test/models/oracleSet.test.ts
Normal file
212
packages/xahau/test/models/oracleSet.test.ts
Normal file
@@ -0,0 +1,212 @@
|
|||||||
|
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 () {
|
||||||
|
// value cannot be parsed as hexadecimal number
|
||||||
|
tx.PriceDataSeries[0].PriceData.AssetPrice = 'ghij'
|
||||||
|
const errorMessage =
|
||||||
|
'OracleSet: Field AssetPrice must be a valid hex string'
|
||||||
|
assert.throws(() => validateOracleSet(tx), ValidationError, errorMessage)
|
||||||
|
assert.throws(() => validate(tx), ValidationError, errorMessage)
|
||||||
|
})
|
||||||
|
|
||||||
|
it(`verifies valid AssetPrice of PriceDataSeries`, function () {
|
||||||
|
// valid string which can be parsed as hexadecimal number
|
||||||
|
tx.PriceDataSeries[0].PriceData.AssetPrice = 'ab15'
|
||||||
|
assert.doesNotThrow(() => validate(tx))
|
||||||
|
})
|
||||||
|
|
||||||
|
it(`throws w/ invalid AssetPrice type in PriceDataSeries`, function () {
|
||||||
|
tx.PriceDataSeries[0].PriceData.AssetPrice = ['sample', 'invalid', 'type']
|
||||||
|
const errorMessage =
|
||||||
|
'OracleSet: Field AssetPrice must be a string or a number'
|
||||||
|
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)
|
||||||
|
})
|
||||||
|
})
|
||||||
Reference in New Issue
Block a user