From 00f1a6bcdd6db99d250f2e33d120af9b8c6cfbd3 Mon Sep 17 00:00:00 2001 From: Mayukha Vadari Date: Wed, 10 Jul 2024 13:18:16 -0400 Subject: [PATCH] feat: add `feature` RPC (#2719) * add feature RPC * export, add tests * update history * fix test * update models * update feature models to correctly handle both cases * fix models/tests * fix test, improve type * undo type change * fix test --- packages/xrpl/HISTORY.md | 1 + packages/xrpl/src/models/methods/feature.ts | 68 +++++++++++++++++++ packages/xrpl/src/models/methods/index.ts | 16 +++++ .../test/integration/requests/feature.test.ts | 68 +++++++++++++++++++ 4 files changed, 153 insertions(+) create mode 100644 packages/xrpl/src/models/methods/feature.ts create mode 100644 packages/xrpl/test/integration/requests/feature.test.ts diff --git a/packages/xrpl/HISTORY.md b/packages/xrpl/HISTORY.md index 6164a12789..0207cfedb1 100644 --- a/packages/xrpl/HISTORY.md +++ b/packages/xrpl/HISTORY.md @@ -11,6 +11,7 @@ Subscribe to [the **xrpl-announce** mailing list](https://groups.google.com/g/xr ### Added * Add `nfts_by_issuer` clio-only API definition * Support for the `fixPreviousTxnID` amendment. +* Support for the user version of the `feature` RPC. ## 3.1.0 (2024-06-03) diff --git a/packages/xrpl/src/models/methods/feature.ts b/packages/xrpl/src/models/methods/feature.ts new file mode 100644 index 0000000000..955be97451 --- /dev/null +++ b/packages/xrpl/src/models/methods/feature.ts @@ -0,0 +1,68 @@ +import { BaseRequest, BaseResponse } from './baseMethod' + +export interface FeatureAllRequest extends BaseRequest { + command: 'feature' + + feature?: never +} + +export interface FeatureOneRequest extends BaseRequest { + command: 'feature' + + feature: string +} + +/** + * The `feature` command returns information about amendments this server knows about, including whether they are enabled. + * Returns an {@link FeatureResponse}. + * + * @category Requests + */ +export type FeatureRequest = FeatureAllRequest | FeatureOneRequest + +export interface FeatureAllResponse extends BaseResponse { + result: { + features: Record< + string, + { + /* + * Whether this amendment is currently enabled in the latest ledger. + */ + enabled: boolean + + /* + * The human-readable name for this amendment, if known. + */ + name: string + + supported: boolean + } + > + } +} + +export interface FeatureOneResponse extends BaseResponse { + result: Record< + string, + { + /* + * Whether this amendment is currently enabled in the latest ledger. + */ + enabled: boolean + + /* + * The human-readable name for this amendment, if known. + */ + name: string + + supported: boolean + } + > +} + +/** + * Response expected from an {@link FeatureRequest}. + * + * @category Responses + */ +export type FeatureResponse = FeatureAllResponse | FeatureOneResponse diff --git a/packages/xrpl/src/models/methods/index.ts b/packages/xrpl/src/models/methods/index.ts index 15d95dce0f..f06c8b4eb9 100644 --- a/packages/xrpl/src/models/methods/index.ts +++ b/packages/xrpl/src/models/methods/index.ts @@ -66,6 +66,14 @@ import { DepositAuthorizedRequest, DepositAuthorizedResponse, } from './depositAuthorized' +import { + FeatureAllRequest, + FeatureAllResponse, + FeatureOneRequest, + FeatureOneResponse, + FeatureRequest, + FeatureResponse, +} from './feature' import { FeeRequest, FeeResponse } from './fee' import { GatewayBalance, @@ -214,6 +222,7 @@ type Request = | ServerDefinitionsRequest | ServerInfoRequest | ServerStateRequest + | FeatureRequest // utility methods | PingRequest | RandomRequest @@ -271,6 +280,7 @@ type Response = | ServerDefinitionsResponse | ServerInfoResponse | ServerStateResponse + | FeatureResponse // utility methods | PingResponse | RandomResponse @@ -419,6 +429,10 @@ export type RequestResponseMap< ? ServerStateResponse : T extends ServerDefinitionsRequest ? ServerDefinitionsResponse + : T extends FeatureAllRequest + ? FeatureAllResponse + : T extends FeatureOneRequest + ? FeatureOneResponse : T extends PingRequest ? PingResponse : T extends RandomRequest @@ -591,6 +605,8 @@ export { ServerState, StateAccountingFinal, StateAccounting, + FeatureRequest, + FeatureResponse, // utility methods PingRequest, PingResponse, diff --git a/packages/xrpl/test/integration/requests/feature.test.ts b/packages/xrpl/test/integration/requests/feature.test.ts new file mode 100644 index 0000000000..fc948036ad --- /dev/null +++ b/packages/xrpl/test/integration/requests/feature.test.ts @@ -0,0 +1,68 @@ +import { assert } from 'chai' + +import { FeatureRequest } from '../../../src' +import serverUrl from '../serverUrl' +import { + setupClient, + teardownClient, + type XrplIntegrationTestContext, +} from '../setup' + +// how long before each test case times out +const TIMEOUT = 20000 +const AMENDMENT = + '8CC0774A3BF66D1D22E76BBDA8E8A232E6B6313834301B3B23E8601196AE6455' + +describe('feature', function () { + let testContext: XrplIntegrationTestContext + + beforeEach(async () => { + testContext = await setupClient(serverUrl) + }) + afterEach(async () => teardownClient(testContext)) + + it( + 'all', + async () => { + const featureRequest: FeatureRequest = { + command: 'feature', + } + const featureResponse = await testContext.client.request(featureRequest) + + assert.equal(featureResponse.type, 'response') + assert.typeOf(featureResponse.result.features, 'object') + assert.isTrue(AMENDMENT in featureResponse.result.features) + + const amendmentData = featureResponse.result.features[AMENDMENT] + assert.equal(amendmentData.name, 'AMM') + // TODO: rippled says "false" for standalone nodes for some reason + assert.typeOf(amendmentData.enabled, 'boolean') + assert.equal(amendmentData.supported, true) + }, + TIMEOUT, + ) + + it( + 'one', + async () => { + const featureRequest: FeatureRequest = { + command: 'feature', + feature: AMENDMENT, + } + const featureResponse = await testContext.client.request(featureRequest) + + assert.equal(featureResponse.type, 'response') + assert.typeOf(featureResponse.result, 'object') + assert.isTrue(AMENDMENT in featureResponse.result) + assert.lengthOf(Object.keys(featureResponse.result), 1) + + const amendmentData = featureResponse.result[AMENDMENT] + + assert.equal(amendmentData.name, 'AMM') + // TODO: rippled says "false" for standalone nodes for some reason + assert.typeOf(amendmentData.enabled, 'boolean') + assert.equal(amendmentData.supported, true) + }, + TIMEOUT, + ) +})