mirror of
https://github.com/Xahau/xahau.js.git
synced 2026-06-19 00:26:43 +00:00
Compare commits
9 Commits
PriceOracl
...
workflow-p
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b43f5a4d8e | ||
|
|
8e371d5fb7 | ||
|
|
d1670c5f3b | ||
|
|
e4569e492a | ||
|
|
6eb09d8606 | ||
|
|
bd0e5e6537 | ||
|
|
355bb3fcce | ||
|
|
40c07a68b9 | ||
|
|
b2046efe85 |
@@ -101,105 +101,69 @@ r.ripple.com 51235
|
||||
# If you need the version of rippled to be more up to date, you may need to make a comment on this repo: https://github.com/WietseWind/docker-rippled
|
||||
|
||||
[features]
|
||||
fixGuardDepth32
|
||||
NamedHooks
|
||||
IOURewardClaim
|
||||
fixIOULockedBalanceInvariant
|
||||
fixImportIssuer
|
||||
HookAPISerializedType240
|
||||
# PermissionedDomains Supported::no
|
||||
# DynamicNFT Supported::no
|
||||
# Credentials Supported::no
|
||||
AMMClawback
|
||||
# MPTokensV1 Supported::no
|
||||
# InvariantsV1_1 Supported::no
|
||||
fixNFTokenPageLinks
|
||||
fixEnforceNFTokenTrustline
|
||||
fixReducedOffersV2
|
||||
# NFTokenMintOffer Supported::no
|
||||
fixPreviousTxnID
|
||||
PriceOracle
|
||||
fixInnerObjTemplate
|
||||
fixNFTokenReserve
|
||||
fixFillOrKill
|
||||
# DID Supported::no
|
||||
fixDisallowIncomingV1
|
||||
# XChainBridge Supported::no
|
||||
AMM
|
||||
fixReducedOffersV1
|
||||
HooksUpdate2
|
||||
HookOnV2
|
||||
fixHookAPI20251128
|
||||
fixCronStacking
|
||||
ExtendedHookState
|
||||
fixInvalidTxFlags
|
||||
Cron
|
||||
IOUIssuerWeakTSH
|
||||
DeepFreeze
|
||||
fixProvisionalDoubleThreading
|
||||
Clawback
|
||||
fixRewardClaimFlags
|
||||
HookCanEmit
|
||||
fix20250131
|
||||
fixXahauV3
|
||||
fixReduceImport
|
||||
Touch
|
||||
Remarks
|
||||
fixFloatDivide
|
||||
fix240911
|
||||
fixPageCap
|
||||
fix240819
|
||||
fixNSDelete
|
||||
ZeroB2M
|
||||
Remit
|
||||
fixXahauV2
|
||||
fixXahauV1
|
||||
HooksUpdate1
|
||||
XahauGenesis
|
||||
Import
|
||||
URIToken
|
||||
PaychanAndEscrowForTokens
|
||||
BalanceRewards
|
||||
Hooks
|
||||
fixNFTokenRemint
|
||||
fixNonFungibleTokensV1_2
|
||||
fixUniversalNumber
|
||||
XRPFees
|
||||
DisallowIncoming
|
||||
ImmediateOfferKilled
|
||||
# Amendments
|
||||
NegativeUNL
|
||||
fixRemoveNFTokenAutoTrustLine
|
||||
fixTrustLinesToSelf
|
||||
NonFungibleTokensV1_1
|
||||
ExpandedSignerList
|
||||
NonFungibleTokensV1
|
||||
CheckCashMakesTrustLine
|
||||
fixRmSmallIncreasedQOffers
|
||||
fixSTAmountCanonicalize
|
||||
FlowSortStrands
|
||||
TicketBatch
|
||||
NegativeUNL
|
||||
fixAmendmentMajorityCalc
|
||||
HardenedValidations
|
||||
fix1781
|
||||
RequireFullyCanonicalSig
|
||||
fixQualityUpperBound
|
||||
DeletableAccounts
|
||||
fixPayChanRecipientOwnerDir
|
||||
fixCheckThreading
|
||||
fixMasterKeyAsRegularKey
|
||||
fixTakerDryOfferRemoval
|
||||
MultiSignReserve
|
||||
fix1578
|
||||
fix1515
|
||||
DepositPreauth
|
||||
fix1623
|
||||
fix1543
|
||||
fix1571
|
||||
Checks
|
||||
DepositAuth
|
||||
fix1513
|
||||
FlowCross
|
||||
HardenedValidations
|
||||
DepositPreauth
|
||||
MultiSignReserve
|
||||
fix1623
|
||||
fix1513
|
||||
RequireFullyCanonicalSig
|
||||
fix1543
|
||||
fix1781
|
||||
fixCheckThreading
|
||||
fix1515
|
||||
CryptoConditionsSuite
|
||||
fixPayChanRecipientOwnerDir
|
||||
fix1578
|
||||
fix1571
|
||||
fixAmendmentMajorityCalc
|
||||
fixTakerDryOfferRemoval
|
||||
fixMasterKeyAsRegularKey
|
||||
Flow
|
||||
# OwnerPaysFee Supported::no
|
||||
DeletableAccounts
|
||||
DepositAuth
|
||||
Checks
|
||||
NonFungibleTokensV1_1
|
||||
DisallowIncoming
|
||||
fixNonFungibleTokensV1_2
|
||||
fixUniversalNumber
|
||||
ImmediateOfferKilled
|
||||
XRPFees
|
||||
ExpandedSignerList
|
||||
fixNFTokenRemint
|
||||
# Additional Amendments
|
||||
BalanceRewards
|
||||
Hooks
|
||||
HooksUpdate1
|
||||
Import
|
||||
Remit
|
||||
URIToken
|
||||
XahauGenesis
|
||||
ZeroB2M
|
||||
fix240819
|
||||
fix240911
|
||||
fixFloatDivide
|
||||
fixNFTokenDirV1
|
||||
fixNFTokenNegOffer
|
||||
fixNSDelete
|
||||
fixPageCap
|
||||
fixReduceImport
|
||||
fixXahauV1
|
||||
fixXahauV2
|
||||
fixXahauV3
|
||||
PaychanAndEscrowForTokens
|
||||
DeepFreeze
|
||||
Clawback
|
||||
|
||||
[network_id]
|
||||
21337
|
||||
|
||||
2
.github/workflows/nodejs.yml
vendored
2
.github/workflows/nodejs.yml
vendored
@@ -4,7 +4,7 @@
|
||||
name: Node.js CI
|
||||
|
||||
env:
|
||||
XAHAUD_VERSION: 2026.6.16-dev+3330
|
||||
XAHAUD_VERSION: 2025.12.1-release+2609
|
||||
|
||||
on:
|
||||
push:
|
||||
|
||||
13
.github/workflows/npm-publish.yml
vendored
13
.github/workflows/npm-publish.yml
vendored
@@ -43,7 +43,7 @@ jobs:
|
||||
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: "24"
|
||||
node-version: "20"
|
||||
registry-url: https://registry.npmjs.org
|
||||
package-manager-cache: false
|
||||
|
||||
@@ -51,7 +51,7 @@ jobs:
|
||||
run: npm ci
|
||||
|
||||
- name: Build package
|
||||
run: npm run build
|
||||
run: npm run build "${{ steps.release.outputs.package-path }}"
|
||||
|
||||
- id: package
|
||||
name: Read package metadata
|
||||
@@ -61,18 +61,11 @@ jobs:
|
||||
name="$(jq -r .name "$package_json")"
|
||||
version="$(jq -r .version "$package_json")"
|
||||
tag="$name@$version"
|
||||
if [[ "$version" == *"-"* ]]; then
|
||||
release_tag="${version#*-}"
|
||||
release_tag="${release_tag%%.*}"
|
||||
else
|
||||
release_tag="latest"
|
||||
fi
|
||||
|
||||
{
|
||||
echo "name=$name"
|
||||
echo "version=$version"
|
||||
echo "tag=$tag"
|
||||
echo "release_tag=$release_tag"
|
||||
} >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Check release tag matches package version
|
||||
@@ -106,4 +99,4 @@ jobs:
|
||||
exit 1
|
||||
|
||||
- name: Publish to npm
|
||||
run: npm publish --workspace "${{ steps.release.outputs.package-path }}" --registry https://registry.npmjs.org --tag "${{ steps.package.outputs.release_tag }}"
|
||||
run: npm publish --workspace "${{ steps.release.outputs.package-path }}" --registry https://registry.npmjs.org
|
||||
|
||||
14
package-lock.json
generated
14
package-lock.json
generated
@@ -2724,6 +2724,9 @@
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -2738,6 +2741,9 @@
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -2752,6 +2758,9 @@
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -2766,6 +2775,9 @@
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -16048,7 +16060,7 @@
|
||||
}
|
||||
},
|
||||
"packages/xahau": {
|
||||
"version": "4.0.4",
|
||||
"version": "4.0.3",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@scure/bip32": "^1.3.1",
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
## Unreleased
|
||||
|
||||
## 2.1.0 (2024-06-03)
|
||||
|
||||
### Added
|
||||
* Support for the Price Oracles amendment (XLS-47).
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -4449,36 +4449,6 @@
|
||||
"Flags": 0,
|
||||
"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": [{
|
||||
|
||||
@@ -73,9 +73,7 @@ describe('Signing data', function () {
|
||||
const customPaymentDefinitions = JSON.parse(
|
||||
JSON.stringify(normalDefinitions),
|
||||
)
|
||||
|
||||
// custom number would need to updated in case it has been used by an existing transaction type
|
||||
customPaymentDefinitions.TRANSACTION_TYPES.Payment = 200
|
||||
customPaymentDefinitions.TRANSACTION_TYPES.Payment = 31
|
||||
|
||||
const newDefs = new XrplDefinitions(customPaymentDefinitions)
|
||||
const actual = encodeForSigning(tx_json, newDefs)
|
||||
@@ -84,7 +82,7 @@ describe('Signing data', function () {
|
||||
'53545800', // signingPrefix
|
||||
// TransactionType
|
||||
'12',
|
||||
'00C8',
|
||||
'001F',
|
||||
// Flags
|
||||
'22',
|
||||
'80000000',
|
||||
@@ -178,9 +176,7 @@ describe('Signing data', function () {
|
||||
const customPaymentDefinitions = JSON.parse(
|
||||
JSON.stringify(normalDefinitions),
|
||||
)
|
||||
|
||||
// custom number would need to updated in case it has been used by an existing transaction type
|
||||
customPaymentDefinitions.TRANSACTION_TYPES.Payment = 200
|
||||
customPaymentDefinitions.TRANSACTION_TYPES.Payment = 31
|
||||
|
||||
const newDefs = new XrplDefinitions(customPaymentDefinitions)
|
||||
const signingAccount = 'rJZdUusLDtY9NEsGea7ijqhVrXv98rYBYN'
|
||||
@@ -191,7 +187,7 @@ describe('Signing data', function () {
|
||||
'534D5400', // signingPrefix
|
||||
// TransactionType
|
||||
'12',
|
||||
'00C8',
|
||||
'001F',
|
||||
// Flags
|
||||
'22',
|
||||
'80000000',
|
||||
|
||||
@@ -4,23 +4,6 @@ Subscribe to [the **xrpl-announce** mailing list](https://groups.google.com/g/xr
|
||||
|
||||
## Unreleased Changes
|
||||
|
||||
### Added
|
||||
* Support for HookOnV2 Amendment
|
||||
* Support for IOUClaimReward Amendment
|
||||
* Support for NamedHooks
|
||||
* 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)
|
||||
|
||||
### Added
|
||||
* Improve HookStateScale validation
|
||||
|
||||
### Fixed
|
||||
* Add lsfTshCollect flag in AccountRoot
|
||||
* Refactor amount assignment in partialPayment.ts
|
||||
* Fix setTransactionFlagsToNumber for Xahau transactions
|
||||
|
||||
## 4.0.3 (2025-11-18)
|
||||
|
||||
### Added
|
||||
@@ -68,9 +51,6 @@ Subscribe to [the **xrpl-announce** mailing list](https://groups.google.com/g/xr
|
||||
### Added
|
||||
* Support for the Price Oracles amendment (XLS-47).
|
||||
|
||||
### 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "xahau",
|
||||
"version": "4.0.4",
|
||||
"version": "4.0.3",
|
||||
"license": "ISC",
|
||||
"description": "A TypeScript/JavaScript API for interacting with the Xahau Network in Node.js and the browser",
|
||||
"files": [
|
||||
|
||||
@@ -40,13 +40,8 @@ import type {
|
||||
MarkerRequest,
|
||||
MarkerResponse,
|
||||
SubmitResponse,
|
||||
SimulateRequest,
|
||||
} from '../models/methods'
|
||||
import type { BookOffer, BookOfferCurrency } from '../models/methods/bookOffers'
|
||||
import {
|
||||
SimulateBinaryResponse,
|
||||
SimulateJsonResponse,
|
||||
} from '../models/methods/simulate'
|
||||
import type {
|
||||
EventTypes,
|
||||
OnEventToListenerMap,
|
||||
@@ -739,41 +734,6 @@ class Client extends EventEmitter<EventTypes> {
|
||||
return submitRequest(this, signedTx, opts?.failHard)
|
||||
}
|
||||
|
||||
/**
|
||||
* Simulates an unsigned transaction.
|
||||
* Steps performed on a transaction:
|
||||
* 1. Autofill.
|
||||
* 2. Sign & Encode.
|
||||
* 3. Submit.
|
||||
*
|
||||
* @category Core
|
||||
*
|
||||
* @param transaction - A transaction to autofill, sign & encode, and submit.
|
||||
* @param opts - (Optional) Options used to sign and submit a transaction.
|
||||
* @param opts.binary - If true, return the metadata in a binary encoding.
|
||||
*
|
||||
* @returns A promise that contains SimulateResponse.
|
||||
* @throws RippledError if the simulate request fails.
|
||||
*/
|
||||
|
||||
public async simulate<Binary extends boolean = false>(
|
||||
transaction: SubmittableTransaction | string,
|
||||
opts?: {
|
||||
// If true, return the binary-encoded representation of the results.
|
||||
binary?: Binary
|
||||
},
|
||||
): Promise<
|
||||
Binary extends true ? SimulateBinaryResponse : SimulateJsonResponse
|
||||
> {
|
||||
// send request
|
||||
const binary = opts?.binary ?? false
|
||||
const request: SimulateRequest =
|
||||
typeof transaction === 'string'
|
||||
? { command: 'simulate', tx_blob: transaction, binary }
|
||||
: { command: 'simulate', tx_json: transaction, binary }
|
||||
return this.request(request)
|
||||
}
|
||||
|
||||
/**
|
||||
* Asynchronously submits a transaction and verifies that it has been included in a
|
||||
* validated ledger (or has errored/will not be included for some reason).
|
||||
|
||||
@@ -86,14 +86,6 @@ export interface Hook {
|
||||
* The transactions that triggers the hook. Represented as a 256Hash
|
||||
*/
|
||||
HookOn?: string
|
||||
/**
|
||||
* The incoming transactions that triggers to the hook. Represented as a 256Hash
|
||||
*/
|
||||
HookOnIncoming?: string
|
||||
/**
|
||||
* The outgoing transactions that triggers from the hook. Represented as a 256Hash
|
||||
*/
|
||||
HookOnOutgoing?: string
|
||||
/**
|
||||
* The transactions that can emit from the hook. Represented as a 256Hash
|
||||
*/
|
||||
@@ -114,10 +106,6 @@ export interface Hook {
|
||||
* The grants of the hook.
|
||||
*/
|
||||
HookGrants?: HookGrant[]
|
||||
/**
|
||||
* The name of the hook.
|
||||
*/
|
||||
HookName?: string
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -27,16 +27,6 @@ export default interface HookDefintion
|
||||
*/
|
||||
HookOn?: string
|
||||
|
||||
/**
|
||||
* The incoming transactions that triggers to the hook. Represented as a 256Hash
|
||||
*/
|
||||
HookOnIncoming?: string
|
||||
|
||||
/**
|
||||
* The outgoing transactions that triggers from the hook. Represented as a 256Hash
|
||||
*/
|
||||
HookOnOutgoing?: string
|
||||
|
||||
/**
|
||||
* The namespace of the hook.
|
||||
*/
|
||||
|
||||
@@ -14,7 +14,6 @@ import ImportVLSequence from './ImportVLSequence'
|
||||
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'
|
||||
@@ -39,7 +38,6 @@ type LedgerEntry =
|
||||
| LedgerHashes
|
||||
| NegativeUNL
|
||||
| Offer
|
||||
| Oracle
|
||||
| PayChannel
|
||||
| RippleState
|
||||
| SignerList
|
||||
@@ -62,7 +60,6 @@ type LedgerEntryFilter =
|
||||
| 'import_vl_sequence'
|
||||
| 'hashes'
|
||||
| 'offer'
|
||||
| 'oracle'
|
||||
| 'payment_channel'
|
||||
| 'signer_list'
|
||||
| 'state'
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
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
|
||||
}
|
||||
@@ -2,13 +2,6 @@ import { IssuedCurrencyAmount } from '../common'
|
||||
|
||||
import { BaseLedgerEntry, HasPreviousTxnID } from './BaseLedgerEntry'
|
||||
|
||||
export interface RippleStateReward {
|
||||
RewardLgrFirst: number
|
||||
RewardLgrLast: number
|
||||
RewardTime: number
|
||||
TrustLineRewardAccumulator: IssuedCurrencyAmount
|
||||
}
|
||||
|
||||
/**
|
||||
* The RippleState object type connects two accounts in a single currency.
|
||||
*
|
||||
@@ -68,8 +61,6 @@ export default interface RippleState extends BaseLedgerEntry, HasPreviousTxnID {
|
||||
* equivalent to 1 billion, or face value.
|
||||
*/
|
||||
HighQualityOut?: number
|
||||
HighReward?: RippleStateReward
|
||||
LowReward?: RippleStateReward
|
||||
}
|
||||
|
||||
export enum RippleStateFlags {
|
||||
|
||||
@@ -23,7 +23,6 @@ import { LedgerEntry, LedgerEntryFilter } from './LedgerEntry'
|
||||
import LedgerHashes from './LedgerHashes'
|
||||
import NegativeUNL, { NEGATIVE_UNL_ID } from './NegativeUNL'
|
||||
import Offer, { OfferFlags } from './Offer'
|
||||
import Oracle from './Oracle'
|
||||
import PayChannel from './PayChannel'
|
||||
import RippleState, { RippleStateFlags } from './RippleState'
|
||||
import SignerList, { SignerListFlags } from './SignerList'
|
||||
@@ -61,7 +60,6 @@ export {
|
||||
NegativeUNL,
|
||||
Offer,
|
||||
OfferFlags,
|
||||
Oracle,
|
||||
PayChannel,
|
||||
RippleState,
|
||||
RippleStateFlags,
|
||||
|
||||
@@ -1,119 +0,0 @@
|
||||
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,10 +78,6 @@ import {
|
||||
GatewayBalancesRequest,
|
||||
GatewayBalancesResponse,
|
||||
} from './gatewayBalances'
|
||||
import {
|
||||
GetAggregatePriceRequest,
|
||||
GetAggregatePriceResponse,
|
||||
} from './getAggregatePrice'
|
||||
import {
|
||||
LedgerBinary,
|
||||
LedgerModifiedOfferCreateTransaction,
|
||||
@@ -137,14 +133,6 @@ import {
|
||||
StateAccountingFinal,
|
||||
} from './serverInfo'
|
||||
import { ServerStateRequest, ServerStateResponse } from './serverState'
|
||||
import {
|
||||
SimulateBinaryRequest,
|
||||
SimulateBinaryResponse,
|
||||
SimulateJsonRequest,
|
||||
SimulateJsonResponse,
|
||||
SimulateRequest,
|
||||
SimulateResponse,
|
||||
} from './simulate'
|
||||
import { SubmitRequest, SubmitResponse } from './submit'
|
||||
import {
|
||||
SubmitMultisignedRequest,
|
||||
@@ -200,7 +188,6 @@ type Request =
|
||||
| LedgerDataRequest
|
||||
| LedgerEntryRequest
|
||||
// transaction methods
|
||||
| SimulateRequest
|
||||
| SubmitRequest
|
||||
| SubmitMultisignedRequest
|
||||
| TransactionEntryRequest
|
||||
@@ -225,8 +212,6 @@ type Request =
|
||||
// utility methods
|
||||
| PingRequest
|
||||
| RandomRequest
|
||||
// Price Oracle methods
|
||||
| GetAggregatePriceRequest
|
||||
|
||||
/**
|
||||
* @category Responses
|
||||
@@ -250,7 +235,6 @@ type Response<Version extends APIVersion = typeof DEFAULT_API_VERSION> =
|
||||
| LedgerDataResponse
|
||||
| LedgerEntryResponse
|
||||
// transaction methods
|
||||
| SimulateResponse
|
||||
| SubmitResponse
|
||||
| SubmitMultisignedVersionResponseMap<Version>
|
||||
| TransactionEntryResponse
|
||||
@@ -275,8 +259,6 @@ type Response<Version extends APIVersion = typeof DEFAULT_API_VERSION> =
|
||||
// utility methods
|
||||
| PingResponse
|
||||
| RandomResponse
|
||||
// Price Oracle methods
|
||||
| GetAggregatePriceResponse
|
||||
|
||||
export type RequestResponseMap<
|
||||
T,
|
||||
@@ -297,8 +279,6 @@ export type RequestResponseMap<
|
||||
? AccountTxVersionResponseMap<Version>
|
||||
: T extends GatewayBalancesRequest
|
||||
? GatewayBalancesResponse
|
||||
: T extends GetAggregatePriceRequest
|
||||
? GetAggregatePriceResponse
|
||||
: T extends NoRippleCheckRequest
|
||||
? NoRippleCheckResponse
|
||||
: // NOTE: The order of these LedgerRequest types is important
|
||||
@@ -375,12 +355,6 @@ export type RequestResponseMap<
|
||||
? LedgerDataResponse
|
||||
: T extends LedgerEntryRequest
|
||||
? LedgerEntryResponse
|
||||
: T extends SimulateBinaryRequest
|
||||
? SimulateBinaryResponse
|
||||
: T extends SimulateJsonRequest
|
||||
? SimulateJsonResponse
|
||||
: T extends SimulateRequest
|
||||
? SimulateJsonResponse
|
||||
: T extends SubmitRequest
|
||||
? SubmitResponse
|
||||
: T extends SubmitMultisignedRequest
|
||||
@@ -491,8 +465,6 @@ export {
|
||||
GatewayBalance,
|
||||
GatewayBalancesRequest,
|
||||
GatewayBalancesResponse,
|
||||
GetAggregatePriceRequest,
|
||||
GetAggregatePriceResponse,
|
||||
NoRippleCheckRequest,
|
||||
NoRippleCheckResponse,
|
||||
// ledger methods
|
||||
@@ -514,8 +486,6 @@ export {
|
||||
LedgerEntryRequest,
|
||||
LedgerEntryResponse,
|
||||
// transaction methods with types
|
||||
SimulateRequest,
|
||||
SimulateResponse,
|
||||
SubmitRequest,
|
||||
SubmitResponse,
|
||||
SubmitMultisignedRequest,
|
||||
|
||||
@@ -203,13 +203,13 @@ export interface LedgerQueueData {
|
||||
}
|
||||
|
||||
export interface LedgerBinary
|
||||
extends Omit<Ledger, 'transactions' | 'accountState'> {
|
||||
extends Omit<Omit<Ledger, 'transactions'>, 'accountState'> {
|
||||
accountState?: string[]
|
||||
transactions?: string[]
|
||||
}
|
||||
|
||||
export interface LedgerBinaryV1
|
||||
extends Omit<LedgerV1, 'transactions' | 'accountState'> {
|
||||
extends Omit<Omit<LedgerV1, 'transactions'>, 'accountState'> {
|
||||
accountState?: string[]
|
||||
transactions?: string[]
|
||||
}
|
||||
|
||||
@@ -1,88 +0,0 @@
|
||||
import {
|
||||
BaseTransaction,
|
||||
Transaction,
|
||||
TransactionMetadata,
|
||||
} from '../transactions'
|
||||
|
||||
import { BaseRequest, BaseResponse } from './baseMethod'
|
||||
|
||||
/**
|
||||
* The `simulate` method simulates a transaction without submitting it to the network.
|
||||
* Returns a {@link SimulateResponse}.
|
||||
*
|
||||
* @category Requests
|
||||
*/
|
||||
export type SimulateRequest = BaseRequest & {
|
||||
command: 'simulate'
|
||||
|
||||
binary?: boolean
|
||||
} & (
|
||||
| {
|
||||
tx_blob: string
|
||||
tx_json?: never
|
||||
}
|
||||
| {
|
||||
tx_json: Transaction
|
||||
tx_blob?: never
|
||||
}
|
||||
)
|
||||
|
||||
export type SimulateBinaryRequest = SimulateRequest & {
|
||||
binary: true
|
||||
}
|
||||
|
||||
export type SimulateJsonRequest = SimulateRequest & {
|
||||
binary?: false
|
||||
}
|
||||
|
||||
/**
|
||||
* Response expected from an {@link SimulateRequest}.
|
||||
*
|
||||
* @category Responses
|
||||
*/
|
||||
export type SimulateResponse = SimulateJsonResponse | SimulateBinaryResponse
|
||||
|
||||
export interface SimulateBinaryResponse extends BaseResponse {
|
||||
result: {
|
||||
applied: false
|
||||
|
||||
engine_result: string
|
||||
|
||||
engine_result_code: number
|
||||
|
||||
engine_result_message: string
|
||||
|
||||
tx_blob: string
|
||||
|
||||
meta_blob: string
|
||||
|
||||
/**
|
||||
* The ledger index of the ledger version that was used to generate this
|
||||
* response.
|
||||
*/
|
||||
ledger_index: number
|
||||
}
|
||||
}
|
||||
|
||||
export interface SimulateJsonResponse<T extends BaseTransaction = Transaction>
|
||||
extends BaseResponse {
|
||||
result: {
|
||||
applied: false
|
||||
|
||||
engine_result: string
|
||||
|
||||
engine_result_code: number
|
||||
|
||||
engine_result_message: string
|
||||
|
||||
/**
|
||||
* The ledger index of the ledger version that was used to generate this
|
||||
* response.
|
||||
*/
|
||||
ledger_index: number
|
||||
|
||||
tx_json: T
|
||||
|
||||
meta?: TransactionMetadata<T>
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
import { ValidationError } from '../../errors'
|
||||
import { Currency } from '../common'
|
||||
|
||||
import { BaseTransaction, GlobalFlags, validateBaseTransaction } from './common'
|
||||
/**
|
||||
@@ -57,7 +56,6 @@ export interface ClaimReward extends BaseTransaction {
|
||||
Flags?: number | ClaimRewardFlagsInterface
|
||||
/** The unique address of the issuer where the reward.c hook is installed. */
|
||||
Issuer?: string
|
||||
ClaimCurrency?: Currency
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -275,10 +275,6 @@ export interface BaseTransaction {
|
||||
* The hook parameters of the transaction.
|
||||
*/
|
||||
HookParameters?: HookParameter[]
|
||||
/**
|
||||
* The name of the hooks triggered by the transaction.
|
||||
*/
|
||||
HookName?: string
|
||||
/**
|
||||
* The hook parameters of the transaction.
|
||||
*/
|
||||
|
||||
@@ -32,8 +32,6 @@ export {
|
||||
OfferCreateFlagsInterface,
|
||||
OfferCreate,
|
||||
} from './offerCreate'
|
||||
export { OracleDelete } from './oracleDelete'
|
||||
export { OracleSet } from './oracleSet'
|
||||
export { PaymentFlags, PaymentFlagsInterface, Payment } from './payment'
|
||||
export {
|
||||
PaymentChannelClaimFlags,
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
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)
|
||||
}
|
||||
@@ -1,198 +0,0 @@
|
||||
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 */
|
||||
}
|
||||
@@ -18,10 +18,6 @@ export interface SetHook extends BaseTransaction {
|
||||
|
||||
const MAX_HOOKS = 10
|
||||
const HEX_REGEX = /^[0-9A-Fa-f]{64}$/u
|
||||
/**
|
||||
* 4-16 bytes in hex
|
||||
*/
|
||||
const HOOKNAME_REGEX = /^[0-9A-Fa-f]{8,32}$/u
|
||||
|
||||
/**
|
||||
* Verify the form and type of an SetHook at runtime.
|
||||
@@ -29,7 +25,6 @@ const HOOKNAME_REGEX = /^[0-9A-Fa-f]{8,32}$/u
|
||||
* @param tx - An SetHook Transaction.
|
||||
* @throws When the SetHook is Malformed.
|
||||
*/
|
||||
// eslint-disable-next-line max-lines-per-function -- okay for this method
|
||||
export function validateSetHook(tx: Record<string, unknown>): void {
|
||||
validateBaseTransaction(tx)
|
||||
|
||||
@@ -46,29 +41,12 @@ export function validateSetHook(tx: Record<string, unknown>): void {
|
||||
for (const hook of tx.Hooks) {
|
||||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- Should be a Hook
|
||||
const hookObject = hook as Hook
|
||||
const {
|
||||
HookOn,
|
||||
HookOnIncoming,
|
||||
HookOnOutgoing,
|
||||
HookCanEmit,
|
||||
HookNamespace,
|
||||
HookName,
|
||||
} = hookObject.Hook
|
||||
const { HookOn, HookCanEmit, HookNamespace } = hookObject.Hook
|
||||
if (HookOn !== undefined && !HEX_REGEX.test(HookOn)) {
|
||||
throw new ValidationError(
|
||||
`SetHook: HookOn in Hook must be a 256-bit (32-byte) hexadecimal value`,
|
||||
)
|
||||
}
|
||||
if (HookOnIncoming !== undefined && !HEX_REGEX.test(HookOnIncoming)) {
|
||||
throw new ValidationError(
|
||||
`SetHook: HookOnIncoming in Hook must be a 256-bit (32-byte) hexadecimal value`,
|
||||
)
|
||||
}
|
||||
if (HookOnOutgoing !== undefined && !HEX_REGEX.test(HookOnOutgoing)) {
|
||||
throw new ValidationError(
|
||||
`SetHook: HookOnOutgoing in Hook must be a 256-bit (32-byte) hexadecimal value`,
|
||||
)
|
||||
}
|
||||
if (HookCanEmit !== undefined && !HEX_REGEX.test(HookCanEmit)) {
|
||||
throw new ValidationError(
|
||||
`SetHook: HookCanEmit in Hook must be a 256-bit (32-byte) hexadecimal value`,
|
||||
@@ -79,10 +57,5 @@ export function validateSetHook(tx: Record<string, unknown>): void {
|
||||
`SetHook: HookNamespace in Hook must be a 256-bit (32-byte) hexadecimal value`,
|
||||
)
|
||||
}
|
||||
if (HookName !== undefined && !HOOKNAME_REGEX.test(HookName)) {
|
||||
throw new ValidationError(
|
||||
`SetHook: HookName in Hook must be a hex string of 8-32 hex characters`,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,8 +24,6 @@ import { Invoke, validateInvoke } from './invoke'
|
||||
import { TransactionMetadata } from './metadata'
|
||||
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,
|
||||
@@ -81,8 +79,6 @@ export type SubmittableTransaction =
|
||||
| Invoke
|
||||
| OfferCancel
|
||||
| OfferCreate
|
||||
| OracleDelete
|
||||
| OracleSet
|
||||
| Payment
|
||||
| PaymentChannelClaim
|
||||
| PaymentChannelCreate
|
||||
@@ -253,14 +249,6 @@ 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
|
||||
@@ -327,7 +315,6 @@ export function validate(transaction: Record<string, unknown>): void {
|
||||
|
||||
default:
|
||||
throw new ValidationError(
|
||||
// eslint-disable-next-line max-lines -- allowed here
|
||||
`Invalid field TransactionType: ${tx.TransactionType}`,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { decode, encode } from 'xahau-binary-codec'
|
||||
|
||||
import type {
|
||||
Client,
|
||||
SubmitRequest,
|
||||
@@ -10,7 +12,6 @@ import { ValidationError, XahlError } from '../errors'
|
||||
import { Signer } from '../models/common'
|
||||
import { TxResponse } from '../models/methods'
|
||||
import { BaseTransaction } from '../models/transactions/common'
|
||||
import { decode, encode } from '../utils'
|
||||
|
||||
/** Approximate time for a ledger to close, in milliseconds */
|
||||
const LEDGER_CLOSE_TIME = 1000
|
||||
@@ -51,7 +52,7 @@ export async function submitRequest(
|
||||
failHard = false,
|
||||
): Promise<SubmitResponse> {
|
||||
if (!isSigned(signedTransaction)) {
|
||||
throw new ValidationError('Transaction must be signed.')
|
||||
throw new ValidationError('Transaction must be signed')
|
||||
}
|
||||
|
||||
const signedTxEncoded =
|
||||
|
||||
@@ -1,78 +0,0 @@
|
||||
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,
|
||||
)
|
||||
})
|
||||
@@ -128,7 +128,6 @@ describe('server_info (xahaud)', function () {
|
||||
'node_size',
|
||||
'initial_sync_duration_us',
|
||||
'ports',
|
||||
'git',
|
||||
]
|
||||
assert.deepEqual(
|
||||
omit(response.result.info, removeKeys),
|
||||
|
||||
@@ -118,7 +118,6 @@ describe('server_state', function () {
|
||||
'node_size',
|
||||
'initial_sync_duration_us',
|
||||
'ports',
|
||||
'git',
|
||||
]
|
||||
assert.deepEqual(
|
||||
omit(response.result.state, removeKeys),
|
||||
|
||||
@@ -1,85 +0,0 @@
|
||||
import { assert } from 'chai'
|
||||
|
||||
import { AccountSet, SimulateRequest } from '../../../src'
|
||||
import { SimulateBinaryRequest } from '../../../src/models/methods/simulate'
|
||||
import serverUrl from '../serverUrl'
|
||||
import {
|
||||
setupClient,
|
||||
teardownClient,
|
||||
type XrplIntegrationTestContext,
|
||||
} from '../setup'
|
||||
|
||||
// how long before each test case times out
|
||||
const TIMEOUT = 20000
|
||||
|
||||
describe('simulate', function () {
|
||||
let testContext: XrplIntegrationTestContext
|
||||
|
||||
beforeEach(async () => {
|
||||
testContext = await setupClient(serverUrl)
|
||||
})
|
||||
afterEach(async () => teardownClient(testContext))
|
||||
|
||||
it(
|
||||
'json',
|
||||
async () => {
|
||||
const simulateRequest: SimulateRequest = {
|
||||
command: 'simulate',
|
||||
tx_json: {
|
||||
TransactionType: 'AccountSet',
|
||||
Account: testContext.wallet.address,
|
||||
NFTokenMinter: testContext.wallet.address,
|
||||
},
|
||||
}
|
||||
const simulateResponse = await testContext.client.request(simulateRequest)
|
||||
|
||||
assert.equal(simulateResponse.type, 'response')
|
||||
assert.typeOf(simulateResponse.result.meta, 'object')
|
||||
assert.typeOf(simulateResponse.result.tx_json, 'object')
|
||||
assert.equal(simulateResponse.result.engine_result, 'tesSUCCESS')
|
||||
assert.isFalse(simulateResponse.result.applied)
|
||||
},
|
||||
TIMEOUT,
|
||||
)
|
||||
|
||||
it(
|
||||
'binary',
|
||||
async () => {
|
||||
const simulateRequest: SimulateBinaryRequest = {
|
||||
command: 'simulate',
|
||||
tx_json: {
|
||||
TransactionType: 'AccountSet',
|
||||
Account: testContext.wallet.address,
|
||||
},
|
||||
binary: true,
|
||||
}
|
||||
const simulateResponse = await testContext.client.request(simulateRequest)
|
||||
|
||||
assert.equal(simulateResponse.type, 'response')
|
||||
assert.typeOf(simulateResponse.result.meta_blob, 'string')
|
||||
assert.typeOf(simulateResponse.result.tx_blob, 'string')
|
||||
assert.equal(simulateResponse.result.engine_result, 'tesSUCCESS')
|
||||
assert.isFalse(simulateResponse.result.applied)
|
||||
},
|
||||
TIMEOUT,
|
||||
)
|
||||
|
||||
it(
|
||||
'sugar',
|
||||
async () => {
|
||||
const tx: AccountSet = {
|
||||
TransactionType: 'AccountSet',
|
||||
Account: testContext.wallet.address,
|
||||
NFTokenMinter: testContext.wallet.address,
|
||||
}
|
||||
const simulateResponse = await testContext.client.simulate(tx)
|
||||
|
||||
assert.equal(simulateResponse.type, 'response')
|
||||
assert.typeOf(simulateResponse.result.meta, 'object')
|
||||
assert.typeOf(simulateResponse.result.tx_json, 'object')
|
||||
assert.equal(simulateResponse.result.engine_result, 'tesSUCCESS')
|
||||
assert.isFalse(simulateResponse.result.applied)
|
||||
},
|
||||
TIMEOUT,
|
||||
)
|
||||
})
|
||||
@@ -1,66 +1,24 @@
|
||||
import { assert } from 'chai'
|
||||
|
||||
import {
|
||||
ClaimReward,
|
||||
ClaimRewardFlags,
|
||||
SetHook,
|
||||
TrustSet,
|
||||
Wallet,
|
||||
} from '../../../src'
|
||||
import { RippleState } from '../../../src/models/ledger'
|
||||
import { ClaimReward, ClaimRewardFlags } from '../../../src'
|
||||
import serverUrl from '../serverUrl'
|
||||
import {
|
||||
setupClient,
|
||||
teardownClient,
|
||||
type XrplIntegrationTestContext,
|
||||
} from '../setup'
|
||||
import { generateFundedWallet, testTransaction } from '../utils'
|
||||
import { testTransaction } from '../utils'
|
||||
|
||||
// how long before each test case times out
|
||||
const TIMEOUT = 20000
|
||||
|
||||
const acceptHook =
|
||||
'0061736D0100000001130360027F7F017F60037F7F7E017E60017F017E02170203656E76025F67000003656E760661636365707400010302010205030100020621057F01418088040B7F004180080B7F004180080B7F00418088040B7F004180080B07080104686F6F6B00020A9D80000199800000410141011080808080001A4100410042001081808080000B'
|
||||
|
||||
describe('XAH ClaimReward', function () {
|
||||
describe('ClaimReward', function () {
|
||||
let testContext: XrplIntegrationTestContext
|
||||
let genesisWallet: Wallet
|
||||
|
||||
beforeAll(async () => {
|
||||
genesisWallet = new Wallet(
|
||||
'0330E7FC9D56BB25D6893BA3F317AE5BCF33B3291BD63DB32654A313222F7FD020',
|
||||
'001ACAAEDECE405B2A958212629E16F2EB46B153EEE94CDD350FDEFF52795525B7',
|
||||
)
|
||||
beforeEach(async () => {
|
||||
testContext = await setupClient(serverUrl)
|
||||
|
||||
const setHookTx: SetHook = {
|
||||
TransactionType: 'SetHook',
|
||||
Account: genesisWallet.classicAddress,
|
||||
Hooks: [
|
||||
{
|
||||
Hook: {
|
||||
CreateCode: acceptHook,
|
||||
HookApiVersion: 0,
|
||||
HookOn: '00'.repeat(32),
|
||||
HookNamespace: '00'.repeat(32),
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
await testTransaction(testContext.client, setHookTx, genesisWallet)
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
// reset Hook
|
||||
const setHookTx: SetHook = {
|
||||
TransactionType: 'SetHook',
|
||||
Account: genesisWallet.classicAddress,
|
||||
Hooks: [{ Hook: { CreateCode: '', Flags: { hsfOverride: true } } }],
|
||||
}
|
||||
await testTransaction(testContext.client, setHookTx, genesisWallet)
|
||||
|
||||
await teardownClient(testContext)
|
||||
})
|
||||
afterEach(async () => teardownClient(testContext))
|
||||
|
||||
it(
|
||||
'opt in',
|
||||
@@ -68,7 +26,7 @@ describe('XAH ClaimReward', function () {
|
||||
const tx: ClaimReward = {
|
||||
TransactionType: 'ClaimReward',
|
||||
Account: testContext.wallet.classicAddress,
|
||||
Issuer: genesisWallet.address,
|
||||
Issuer: 'rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh',
|
||||
}
|
||||
|
||||
await testTransaction(testContext.client, tx, testContext.wallet)
|
||||
@@ -84,7 +42,6 @@ describe('XAH ClaimReward', function () {
|
||||
},
|
||||
TIMEOUT,
|
||||
)
|
||||
|
||||
it(
|
||||
'opt out',
|
||||
async () => {
|
||||
@@ -110,135 +67,3 @@ describe('XAH ClaimReward', function () {
|
||||
TIMEOUT,
|
||||
)
|
||||
})
|
||||
|
||||
describe('IOU ClaimReward', function () {
|
||||
let testContext: XrplIntegrationTestContext
|
||||
let issuerWallet: Wallet
|
||||
let hookWallet: Wallet
|
||||
|
||||
beforeAll(async () => {
|
||||
testContext = await setupClient(serverUrl)
|
||||
|
||||
issuerWallet = await generateFundedWallet(testContext.client)
|
||||
hookWallet = await generateFundedWallet(testContext.client)
|
||||
|
||||
const setHookTx: SetHook = {
|
||||
TransactionType: 'SetHook',
|
||||
Account: hookWallet.classicAddress,
|
||||
Hooks: [
|
||||
{
|
||||
Hook: {
|
||||
CreateCode: acceptHook,
|
||||
HookApiVersion: 0,
|
||||
HookOn: '00'.repeat(32),
|
||||
HookNamespace: '00'.repeat(32),
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
await testTransaction(testContext.client, setHookTx, hookWallet)
|
||||
|
||||
const trustSetTx: TrustSet = {
|
||||
TransactionType: 'TrustSet',
|
||||
Account: testContext.wallet.classicAddress,
|
||||
LimitAmount: {
|
||||
currency: 'USD',
|
||||
issuer: issuerWallet.address,
|
||||
value: '10000000',
|
||||
},
|
||||
}
|
||||
await testTransaction(testContext.client, trustSetTx, testContext.wallet)
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
// reset Hook
|
||||
const setHookTx: SetHook = {
|
||||
TransactionType: 'SetHook',
|
||||
Account: hookWallet.classicAddress,
|
||||
Hooks: [{ Hook: { CreateCode: '', Flags: { hsfOverride: true } } }],
|
||||
}
|
||||
await testTransaction(testContext.client, setHookTx, hookWallet)
|
||||
|
||||
await teardownClient(testContext)
|
||||
})
|
||||
|
||||
it(
|
||||
'opt in',
|
||||
async () => {
|
||||
const tx: ClaimReward = {
|
||||
TransactionType: 'ClaimReward',
|
||||
Account: testContext.wallet.classicAddress,
|
||||
Issuer: hookWallet.classicAddress,
|
||||
ClaimCurrency: {
|
||||
currency: 'USD',
|
||||
issuer: issuerWallet.address,
|
||||
},
|
||||
}
|
||||
|
||||
await testTransaction(testContext.client, tx, testContext.wallet)
|
||||
|
||||
const rippleStateResponse = await testContext.client.request({
|
||||
command: 'ledger_entry',
|
||||
ripple_state: {
|
||||
currency: 'USD',
|
||||
accounts: [
|
||||
testContext.wallet.classicAddress,
|
||||
issuerWallet.classicAddress,
|
||||
],
|
||||
},
|
||||
})
|
||||
const node = rippleStateResponse.result.node as RippleState
|
||||
assert.exists(node)
|
||||
|
||||
// Either LowReward or HighReward must exist
|
||||
expect(Boolean(node.LowReward) || Boolean(node.HighReward))
|
||||
|
||||
if (node.LowReward) {
|
||||
assert.exists(node.LowReward.TrustLineRewardAccumulator)
|
||||
assert.exists(node.LowReward.RewardLgrFirst)
|
||||
assert.exists(node.LowReward.RewardLgrLast)
|
||||
assert.exists(node.LowReward.RewardTime)
|
||||
}
|
||||
if (node.HighReward) {
|
||||
assert.exists(node.HighReward.TrustLineRewardAccumulator)
|
||||
assert.exists(node.HighReward.RewardLgrFirst)
|
||||
assert.exists(node.HighReward.RewardLgrLast)
|
||||
assert.exists(node.HighReward.RewardTime)
|
||||
}
|
||||
},
|
||||
TIMEOUT,
|
||||
)
|
||||
|
||||
it(
|
||||
'opt out',
|
||||
async () => {
|
||||
const tx: ClaimReward = {
|
||||
TransactionType: 'ClaimReward',
|
||||
Account: testContext.wallet.classicAddress,
|
||||
Flags: ClaimRewardFlags.tfOptOut,
|
||||
ClaimCurrency: {
|
||||
currency: 'USD',
|
||||
issuer: issuerWallet.address,
|
||||
},
|
||||
}
|
||||
|
||||
await testTransaction(testContext.client, tx, testContext.wallet)
|
||||
|
||||
const rippleStateResponse = await testContext.client.request({
|
||||
command: 'ledger_entry',
|
||||
ripple_state: {
|
||||
currency: 'USD',
|
||||
accounts: [
|
||||
testContext.wallet.classicAddress,
|
||||
issuerWallet.classicAddress,
|
||||
],
|
||||
},
|
||||
})
|
||||
const node = rippleStateResponse.result.node as RippleState
|
||||
assert.exists(node)
|
||||
assert.notExists(node.LowReward)
|
||||
assert.notExists(node.HighReward)
|
||||
},
|
||||
TIMEOUT,
|
||||
)
|
||||
})
|
||||
|
||||
@@ -1,76 +0,0 @@
|
||||
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,
|
||||
)
|
||||
})
|
||||
@@ -1,91 +0,0 @@
|
||||
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,138 +0,0 @@
|
||||
import { SetHook, Wallet } from '../../../src'
|
||||
import { Hook, HookDefinition } from '../../../src/models/ledger'
|
||||
import serverUrl from '../serverUrl'
|
||||
import {
|
||||
setupClient,
|
||||
teardownClient,
|
||||
type XrplIntegrationTestContext,
|
||||
} from '../setup'
|
||||
import { generateFundedWallet, testTransaction } from '../utils'
|
||||
|
||||
// how long before each test case times out
|
||||
const TIMEOUT = 20000
|
||||
const acceptHook =
|
||||
'0061736D0100000001130360027F7F017F60037F7F7E017E60017F017E02170203656E76025F67000003656E760661636365707400010302010205030100020621057F01418088040B7F004180080B7F004180080B7F00418088040B7F004180080B07080104686F6F6B00020A9D80000199800000410141011080808080001A4100410042001081808080000B'
|
||||
|
||||
describe('SetHook', function () {
|
||||
let testContext: XrplIntegrationTestContext
|
||||
let wallet: Wallet
|
||||
|
||||
beforeEach(async () => {
|
||||
testContext = await setupClient(serverUrl)
|
||||
wallet = await generateFundedWallet(testContext.client)
|
||||
})
|
||||
afterEach(async () => {
|
||||
// reset Hook
|
||||
const setHookTx: SetHook = {
|
||||
TransactionType: 'SetHook',
|
||||
Account: wallet.classicAddress,
|
||||
Hooks: [{ Hook: { CreateCode: '', Flags: { hsfOverride: true } } }],
|
||||
}
|
||||
await testTransaction(testContext.client, setHookTx, wallet)
|
||||
await teardownClient(testContext)
|
||||
})
|
||||
|
||||
it(
|
||||
'base',
|
||||
async () => {
|
||||
const setHookTx: SetHook = {
|
||||
TransactionType: 'SetHook',
|
||||
Account: wallet.classicAddress,
|
||||
Hooks: [
|
||||
{
|
||||
Hook: {
|
||||
CreateCode: acceptHook,
|
||||
HookApiVersion: 0,
|
||||
HookOn: '00'.repeat(32),
|
||||
HookCanEmit: '00'.repeat(32),
|
||||
HookName: '484F4F4B',
|
||||
HookParameters: [
|
||||
{
|
||||
HookParameter: {
|
||||
HookParameterName: 'DEADBEEF',
|
||||
HookParameterValue: 'DEADBEEF',
|
||||
},
|
||||
},
|
||||
],
|
||||
HookNamespace: '00'.repeat(32),
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
await testTransaction(testContext.client, setHookTx, wallet)
|
||||
|
||||
const ledgerEntryResponse = await testContext.client.request({
|
||||
command: 'ledger_entry',
|
||||
hook: { account: wallet.classicAddress },
|
||||
})
|
||||
const node = ledgerEntryResponse.result.node as Hook
|
||||
expect(node.Hooks.length).toEqual(1)
|
||||
const hook = node.Hooks[0].Hook
|
||||
expect(Object.keys(hook).length).toEqual(2)
|
||||
expect(hook.HookHash).toBeDefined()
|
||||
expect(hook.HookName).toBeDefined()
|
||||
const hookHash = hook.HookHash!
|
||||
|
||||
const hookDefinitionResponse = await testContext.client.request({
|
||||
command: 'ledger_entry',
|
||||
hook_definition: hookHash,
|
||||
})
|
||||
const hookDefinitionNode = hookDefinitionResponse.result
|
||||
.node as HookDefinition
|
||||
expect(hookDefinitionNode.HookHash).toEqual(hookHash)
|
||||
expect(hookDefinitionNode.CreateCode).toEqual(acceptHook)
|
||||
expect(hookDefinitionNode.HookApiVersion).toEqual(0)
|
||||
expect(hookDefinitionNode.HookOn).toEqual('00'.repeat(32))
|
||||
expect(hookDefinitionNode.HookNamespace).toEqual('00'.repeat(32))
|
||||
// @ts-expect-error - HookName is not defined in HookDefinition
|
||||
expect(hookDefinitionNode.HookName).toBeUndefined()
|
||||
expect(hookDefinitionNode.HookParameters?.length).toEqual(1)
|
||||
const parameter = hookDefinitionNode.HookParameters![0].HookParameter
|
||||
expect(parameter.HookParameterName).toEqual('DEADBEEF')
|
||||
expect(parameter.HookParameterValue).toEqual('DEADBEEF')
|
||||
},
|
||||
TIMEOUT,
|
||||
)
|
||||
|
||||
it(
|
||||
'hook on incoming/outgoing',
|
||||
async () => {
|
||||
const setHookTx: SetHook = {
|
||||
TransactionType: 'SetHook',
|
||||
Account: wallet.classicAddress,
|
||||
Hooks: [
|
||||
{
|
||||
Hook: {
|
||||
CreateCode: acceptHook,
|
||||
HookApiVersion: 0,
|
||||
HookOnIncoming: '00'.repeat(32),
|
||||
// eslint-disable-next-line no-inline-comments -- for readability
|
||||
HookOnOutgoing: `01${'00'.repeat(31)}`, // should be different from HookOnIncoming
|
||||
HookNamespace: '00'.repeat(32),
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
await testTransaction(testContext.client, setHookTx, wallet)
|
||||
|
||||
const ledgerEntryResponse = await testContext.client.request({
|
||||
command: 'ledger_entry',
|
||||
hook: { account: wallet.classicAddress },
|
||||
})
|
||||
const node = ledgerEntryResponse.result.node as Hook
|
||||
const hook = node.Hooks[0].Hook
|
||||
const hookHash = hook.HookHash!
|
||||
|
||||
const hookDefinitionResponse = await testContext.client.request({
|
||||
command: 'ledger_entry',
|
||||
hook_definition: hookHash,
|
||||
})
|
||||
const hookDefinitionNode = hookDefinitionResponse.result
|
||||
.node as HookDefinition
|
||||
expect(hookDefinitionNode.HookOn).toBeUndefined()
|
||||
expect(hookDefinitionNode.HookOnIncoming).toBeDefined()
|
||||
expect(hookDefinitionNode.HookOnOutgoing).toBeDefined()
|
||||
},
|
||||
TIMEOUT,
|
||||
)
|
||||
})
|
||||
@@ -13,8 +13,6 @@ import {
|
||||
ECDSA,
|
||||
AccountLinesRequest,
|
||||
IssuedCurrency,
|
||||
XAHAUD_API_V2,
|
||||
TxResponse,
|
||||
} from '../../src'
|
||||
import {
|
||||
Payment,
|
||||
@@ -186,22 +184,20 @@ export async function verifySubmittedTransaction(
|
||||
hashTx?: string,
|
||||
): Promise<void> {
|
||||
const hash = hashTx ?? hashSignedTx(tx)
|
||||
const data: TxResponse = await client.request({
|
||||
const data = await client.request({
|
||||
command: 'tx',
|
||||
transaction: hash,
|
||||
// The current default version is v1, but we'll be using v2 for this test.
|
||||
api_version: XAHAUD_API_V2,
|
||||
})
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- TODO: handle this API change for 2.0.0
|
||||
const decodedTx: any = typeof tx === 'string' ? decode(tx) : tx
|
||||
if (decodedTx.TransactionType === 'Payment') {
|
||||
if (decodedTx.TransactionType === 'Payment' && client.apiVersion !== 1) {
|
||||
decodedTx.DeliverMax = decodedTx.Amount
|
||||
delete decodedTx.Amount
|
||||
}
|
||||
|
||||
assert(data.result)
|
||||
assert.deepEqual(
|
||||
omit(data.result.tx_json, [
|
||||
omit(data.result, [
|
||||
'ctid',
|
||||
'date',
|
||||
'hash',
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
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)
|
||||
})
|
||||
})
|
||||
@@ -1,212 +0,0 @@
|
||||
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)
|
||||
})
|
||||
})
|
||||
@@ -32,7 +32,6 @@ describe('SetHook', function () {
|
||||
HookApiVersion: 0,
|
||||
HookNamespace:
|
||||
'4FF9961269BF7630D32E15276569C94470174A5DA79FA567C0F62251AA9A36B9',
|
||||
HookName: 'DEADBEEF',
|
||||
},
|
||||
},
|
||||
],
|
||||
@@ -67,10 +66,6 @@ describe('SetHook', function () {
|
||||
'0061736D01000000011C0460057F7F7F7F7F017E60037F7F7E017E60027F7F017F60017F017E02230303656E76057472616365000003656E7606616363657074000103656E76025F670002030201030503010002062B077F0141B088040B7F004180080B7F0041A6080B7F004180080B7F0041B088040B7F0041000B7F0041010B07080104686F6F6B00030AC4800001C0800001017F230041106B220124002001200036020C41920841134180084112410010001A410022002000420010011A41012200200010021A200141106A240042000B0B2C01004180080B254163636570742E633A2043616C6C65642E00224163636570742E633A2043616C6C65642E22',
|
||||
HookOn:
|
||||
'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBFFFF7',
|
||||
HookOnIncoming:
|
||||
'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBFFFF7',
|
||||
HookOnOutgoing:
|
||||
'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBFFFF7',
|
||||
Flags: 1,
|
||||
HookApiVersion: 0,
|
||||
HookNamespace:
|
||||
@@ -98,31 +93,30 @@ describe('SetHook', function () {
|
||||
assert.throws(() => validate(setHookTx), ValidationError, errorMessage)
|
||||
})
|
||||
|
||||
it.each(['HookOn', 'HookOnIncoming', 'HookOnOutgoing'])(
|
||||
`throws w/ invalid %s in Hooks`,
|
||||
function (field: string) {
|
||||
setHookTx.Hooks = [
|
||||
{
|
||||
Hook: {
|
||||
CreateCode:
|
||||
'0061736D01000000011C0460057F7F7F7F7F017E60037F7F7E017E60027F7F017F60017F017E02230303656E76057472616365000003656E7606616363657074000103656E76025F670002030201030503010002062B077F0141B088040B7F004180080B7F0041A6080B7F004180080B7F0041B088040B7F0041000B7F0041010B07080104686F6F6B00030AC4800001C0800001017F230041106B220124002001200036020C41920841134180084112410010001A410022002000420010011A41012200200010021A200141106A240042000B0B2C01004180080B254163636570742E633A2043616C6C65642E00224163636570742E633A2043616C6C65642E22',
|
||||
[field]: '',
|
||||
Flags: 1,
|
||||
HookApiVersion: 0,
|
||||
HookNamespace:
|
||||
'4FF9961269BF7630D32E15276569C94470174A5DA79FA567C0F62251AA9A36B9',
|
||||
},
|
||||
it(`throws w/ invalid HookOn in Hooks`, function () {
|
||||
setHookTx.SignerQuorum = 2
|
||||
setHookTx.Hooks = [
|
||||
{
|
||||
Hook: {
|
||||
CreateCode:
|
||||
'0061736D01000000011C0460057F7F7F7F7F017E60037F7F7E017E60027F7F017F60017F017E02230303656E76057472616365000003656E7606616363657074000103656E76025F670002030201030503010002062B077F0141B088040B7F004180080B7F0041A6080B7F004180080B7F0041B088040B7F0041000B7F0041010B07080104686F6F6B00030AC4800001C0800001017F230041106B220124002001200036020C41920841134180084112410010001A410022002000420010011A41012200200010021A200141106A240042000B0B2C01004180080B254163636570742E633A2043616C6C65642E00224163636570742E633A2043616C6C65642E22',
|
||||
HookOn: '',
|
||||
Flags: 1,
|
||||
HookApiVersion: 0,
|
||||
HookNamespace:
|
||||
'4FF9961269BF7630D32E15276569C94470174A5DA79FA567C0F62251AA9A36B9',
|
||||
},
|
||||
]
|
||||
const errorMessage = `SetHook: ${field} in Hook must be a 256-bit (32-byte) hexadecimal value`
|
||||
assert.throws(
|
||||
() => validateSetHook(setHookTx),
|
||||
ValidationError,
|
||||
errorMessage,
|
||||
)
|
||||
assert.throws(() => validate(setHookTx), ValidationError, errorMessage)
|
||||
},
|
||||
)
|
||||
},
|
||||
]
|
||||
const errorMessage =
|
||||
'SetHook: HookOn in Hook must be a 256-bit (32-byte) hexadecimal value'
|
||||
assert.throws(
|
||||
() => validateSetHook(setHookTx),
|
||||
ValidationError,
|
||||
errorMessage,
|
||||
)
|
||||
assert.throws(() => validate(setHookTx), ValidationError, errorMessage)
|
||||
})
|
||||
|
||||
it(`throws w/ invalid HookCanEmit in Hooks`, function () {
|
||||
setHookTx.Hooks = [
|
||||
@@ -174,25 +168,4 @@ describe('SetHook', function () {
|
||||
)
|
||||
assert.throws(() => validate(setHookTx), ValidationError, errorMessage)
|
||||
})
|
||||
|
||||
it.each(['', '0'.repeat(7), '0'.repeat(33), 'ZZZZZZZZ'])(
|
||||
`throws w/ invalid HookName in Hooks: %s`,
|
||||
function (value: string) {
|
||||
setHookTx.Hooks = [
|
||||
{
|
||||
Hook: {
|
||||
HookName: value,
|
||||
},
|
||||
},
|
||||
]
|
||||
const errorMessage =
|
||||
'SetHook: HookName in Hook must be a hex string of 8-32 hex characters'
|
||||
assert.throws(
|
||||
() => validateSetHook(setHookTx),
|
||||
ValidationError,
|
||||
errorMessage,
|
||||
)
|
||||
assert.throws(() => validate(setHookTx), ValidationError, errorMessage)
|
||||
},
|
||||
)
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user