diff --git a/packages/controller-utils/CHANGELOG.md b/packages/controller-utils/CHANGELOG.md index c1b196e62af..daa01a7c5e4 100644 --- a/packages/controller-utils/CHANGELOG.md +++ b/packages/controller-utils/CHANGELOG.md @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- Update `onDegraded` in `createServicePolicy` so that its event payload now includes an `error` property which can be used to access the error produced by the last request when the maximum number of retries is exceeded ([#6188](https://github.com/MetaMask/core/pull/6188)) + - This `error` property will be `undefined` if the degraded event merely represents a slow request + ## [11.11.0] ### Added diff --git a/packages/controller-utils/src/create-service-policy.ts b/packages/controller-utils/src/create-service-policy.ts index e2028b42dce..fbce1ed88d8 100644 --- a/packages/controller-utils/src/create-service-policy.ts +++ b/packages/controller-utils/src/create-service-policy.ts @@ -14,6 +14,7 @@ import { import type { CircuitBreakerPolicy, Event as CockatielEvent, + FailureReason, IBackoffFactory, IPolicy, Policy, @@ -95,7 +96,7 @@ export type ServicePolicy = IPolicy & { * never succeeds before the retry policy gives up and before the maximum * number of consecutive failures has been reached. */ - onDegraded: CockatielEvent; + onDegraded: CockatielEvent | void>; /** * A function which will be called by the retry policy each time the service * fails and the policy kicks off a timer to re-run the service. This is @@ -229,10 +230,11 @@ export function createServicePolicy( }); const onBreak = circuitBreakerPolicy.onBreak.bind(circuitBreakerPolicy); - const onDegradedEventEmitter = new CockatielEventEmitter(); - retryPolicy.onGiveUp(() => { + const onDegradedEventEmitter = + new CockatielEventEmitter | void>(); + retryPolicy.onGiveUp((data) => { if (circuitBreakerPolicy.state === CircuitState.Closed) { - onDegradedEventEmitter.emit(); + onDegradedEventEmitter.emit(data); } }); retryPolicy.onSuccess(({ duration }) => { diff --git a/packages/network-controller/CHANGELOG.md b/packages/network-controller/CHANGELOG.md index 4ec2e5b1b45..a07f31d1b9b 100644 --- a/packages/network-controller/CHANGELOG.md +++ b/packages/network-controller/CHANGELOG.md @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- The object in the `NetworkController:rpcEndpointDegraded` event payload now includes an `error` property, which can be used to access the error produced by the last request when the maximum number of retries is exceeded ([#6188](https://github.com/MetaMask/core/pull/6188)) + - This `error` property will be `undefined` if the degraded event merely represents a slow request + ### Changed - Bump `@metamask/base-controller` from `^8.0.1` to `^8.1.0` ([#6284](https://github.com/MetaMask/core/pull/6284)) diff --git a/packages/network-controller/src/NetworkController.ts b/packages/network-controller/src/NetworkController.ts index d23f6d55aae..ceaef81f810 100644 --- a/packages/network-controller/src/NetworkController.ts +++ b/packages/network-controller/src/NetworkController.ts @@ -470,6 +470,7 @@ export type NetworkControllerRpcEndpointDegradedEvent = { { chainId: Hex; endpointUrl: string; + error: unknown; }, ]; }; diff --git a/packages/network-controller/src/create-network-client.ts b/packages/network-controller/src/create-network-client.ts index c6388ae3c18..7ac7f920dc9 100644 --- a/packages/network-controller/src/create-network-client.ts +++ b/packages/network-controller/src/create-network-client.ts @@ -111,10 +111,18 @@ export function createNetworkClient({ error, }); }); - rpcServiceChain.onDegraded(({ endpointUrl }) => { + rpcServiceChain.onDegraded(({ endpointUrl, ...rest }) => { + let error: unknown; + if ('error' in rest) { + error = rest.error; + } else if ('value' in rest) { + error = rest.value; + } + messenger.publish('NetworkController:rpcEndpointDegraded', { chainId: configuration.chainId, endpointUrl, + error, }); }); rpcServiceChain.onRetry(({ endpointUrl, attempt }) => { diff --git a/packages/network-controller/src/rpc-service/rpc-service.ts b/packages/network-controller/src/rpc-service/rpc-service.ts index 383b98d3605..037429364ac 100644 --- a/packages/network-controller/src/rpc-service/rpc-service.ts +++ b/packages/network-controller/src/rpc-service/rpc-service.ts @@ -361,8 +361,8 @@ export class RpcService implements AbstractRpcService { { endpointUrl: string } >, ) { - return this.#policy.onDegraded(() => { - listener({ endpointUrl: this.endpointUrl.toString() }); + return this.#policy.onDegraded((data) => { + listener({ ...(data ?? {}), endpointUrl: this.endpointUrl.toString() }); }); } diff --git a/packages/network-controller/src/rpc-service/shared.ts b/packages/network-controller/src/rpc-service/shared.ts index 68e4c78b250..e33ae6129ad 100644 --- a/packages/network-controller/src/rpc-service/shared.ts +++ b/packages/network-controller/src/rpc-service/shared.ts @@ -9,8 +9,5 @@ export type FetchOptions = RequestInit; */ export type AddToCockatielEventData = EventListener extends (data: infer Data) => void - ? // Prevent Data from being split if it's a type union - [Data] extends [void] - ? (data: AdditionalData) => void - : (data: Data & AdditionalData) => void + ? (data: Data extends void ? AdditionalData : Data & AdditionalData) => void : never;