fix #2911: Update OracleSet transaction model, unit, integ tests (#2913)

* fix #2911: Update OracleSet transaction model, unit, integ tests

---------

Co-authored-by: Omar Khan <khancodegt@gmail.com>
This commit is contained in:
Chenna Keshava B S
2025-03-19 13:49:10 -07:00
committed by tequ
parent bc8ae32ad0
commit d89ee1abb3
3 changed files with 66 additions and 11 deletions

View File

@@ -1,5 +1,6 @@
import { ValidationError } from '../../errors'
import { PriceData } from '../common'
import { isHex } from '../utils'
import {
BaseTransaction,
@@ -12,6 +13,8 @@ import {
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.
@@ -82,7 +85,7 @@ export function validateOracleSet(tx: Record<string, unknown>): void {
validateOptionalField(tx, 'AssetClass', isString)
// eslint-disable-next-line max-lines-per-function -- necessary to validate many fields
/* 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')
@@ -142,14 +145,32 @@ export function validateOracleSet(tx: Record<string, unknown>): void {
)
}
if (
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access -- we are validating the type
'AssetPrice' in priceData.PriceData &&
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access -- we are validating the type
!isNumber(priceData.PriceData.AssetPrice)
) {
throw new ValidationError('OracleSet: invalid field AssetPrice')
/* 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
@@ -173,4 +194,5 @@ export function validateOracleSet(tx: Record<string, unknown>): void {
}
return true
})
/* eslint-enable max-statements, max-lines-per-function */
}

View File

@@ -39,6 +39,17 @@ describe('OracleSet', function () {
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',
@@ -62,12 +73,18 @@ describe('OracleSet', function () {
assert.equal(oracle.Owner, testContext.wallet.classicAddress)
assert.equal(oracle.AssetClass, tx.AssetClass)
assert.equal(oracle.Provider, tx.Provider)
assert.equal(oracle.PriceDataSeries.length, 1)
assert.equal(oracle.PriceDataSeries.length, 2)
assert.equal(oracle.PriceDataSeries[0].PriceData.BaseAsset, 'XRP')
assert.equal(oracle.PriceDataSeries[0].PriceData.QuoteAsset, 'USD')
assert.equal(oracle.PriceDataSeries[0].PriceData.AssetPrice, '2e4')
assert.equal(oracle.PriceDataSeries[0].PriceData.Scale, 3)
assert.equal(oracle.Flags, 0)
// validate the serialization of large AssetPrice values
assert.equal(
oracle.PriceDataSeries[1].PriceData.AssetPrice,
'ffffffffffffffff',
)
},
TIMEOUT,
)

View File

@@ -167,8 +167,24 @@ describe('OracleSet', function () {
})
it(`throws w/ invalid AssetPrice of PriceDataSeries`, function () {
tx.PriceDataSeries[0].PriceData.AssetPrice = '1234'
const errorMessage = 'OracleSet: invalid field AssetPrice'
// 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)
})