@metamask-previews/earn-controller 0.0.0-preview-43593c4e

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,10 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [Unreleased]
9
+
10
+ [Unreleased]: https://github.com/MetaMask/core/
package/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 MetaMask
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
package/README.md ADDED
@@ -0,0 +1,15 @@
1
+ # `@metamask/earn-controller`
2
+
3
+ Manages state for earning features and coordinates interactions between staking services, SDK integrations, and other controllers to enable users to participate in various earning opportunities.
4
+
5
+ ## Installation
6
+
7
+ `yarn add @metamask/earn-controller`
8
+
9
+ or
10
+
11
+ `npm install @metamask/earn-controller`
12
+
13
+ ## Contributing
14
+
15
+ This package is part of a monorepo. Instructions for contributing can be found in the [monorepo README](https://github.com/MetaMask/core#readme).
@@ -0,0 +1,230 @@
1
+ "use strict";
2
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
3
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
4
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
5
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
6
+ };
7
+ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
8
+ if (kind === "m") throw new TypeError("Private method is not writable");
9
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
10
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
11
+ return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
12
+ };
13
+ var _EarnController_instances, _EarnController_stakeSDK, _EarnController_selectedNetworkClientId, _EarnController_stakingApiService, _EarnController_initializeSDK, _EarnController_getCurrentAccount, _EarnController_getCurrentChainId;
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.EarnController = exports.getDefaultEarnControllerState = exports.controllerName = void 0;
16
+ const providers_1 = require("@ethersproject/providers");
17
+ const base_controller_1 = require("@metamask/base-controller");
18
+ const controller_utils_1 = require("@metamask/controller-utils");
19
+ const stake_sdk_1 = require("@metamask/stake-sdk");
20
+ exports.controllerName = 'EarnController';
21
+ /**
22
+ * Metadata for the EarnController.
23
+ */
24
+ const earnControllerMetadata = {
25
+ pooled_staking: {
26
+ persist: true,
27
+ anonymous: false,
28
+ },
29
+ stablecoin_lending: {
30
+ persist: true,
31
+ anonymous: false,
32
+ },
33
+ lastUpdated: {
34
+ persist: false,
35
+ anonymous: true,
36
+ },
37
+ };
38
+ // === Default State ===
39
+ const DEFAULT_STABLECOIN_VAULT = {
40
+ symbol: '',
41
+ name: '',
42
+ chainId: 0,
43
+ tokenAddress: '',
44
+ vaultAddress: '',
45
+ currentAPY: '0',
46
+ supply: '0',
47
+ liquidity: '0',
48
+ };
49
+ /**
50
+ * Gets the default state for the EarnController.
51
+ *
52
+ * @returns The default EarnController state.
53
+ */
54
+ function getDefaultEarnControllerState() {
55
+ return {
56
+ pooled_staking: {
57
+ pooledStakes: {
58
+ account: '',
59
+ lifetimeRewards: '0',
60
+ assets: '0',
61
+ exitRequests: [],
62
+ },
63
+ exchangeRate: '1',
64
+ vaultData: {
65
+ apy: '0',
66
+ capacity: '0',
67
+ feePercent: 0,
68
+ totalAssets: '0',
69
+ vaultAddress: '0x0000000000000000000000000000000000000000',
70
+ },
71
+ isEligible: false,
72
+ },
73
+ stablecoin_lending: {
74
+ vaults: [DEFAULT_STABLECOIN_VAULT],
75
+ },
76
+ lastUpdated: 0,
77
+ };
78
+ }
79
+ exports.getDefaultEarnControllerState = getDefaultEarnControllerState;
80
+ // === CONTROLLER DEFINITION ===
81
+ /**
82
+ * EarnController manages DeFi earning opportunities across different protocols and chains.
83
+ */
84
+ class EarnController extends base_controller_1.BaseController {
85
+ constructor({ messenger, state = {}, }) {
86
+ super({
87
+ name: exports.controllerName,
88
+ metadata: earnControllerMetadata,
89
+ messenger,
90
+ state: {
91
+ ...getDefaultEarnControllerState(),
92
+ ...state,
93
+ },
94
+ });
95
+ _EarnController_instances.add(this);
96
+ _EarnController_stakeSDK.set(this, null);
97
+ _EarnController_selectedNetworkClientId.set(this, void 0);
98
+ _EarnController_stakingApiService.set(this, new stake_sdk_1.StakingApiService());
99
+ __classPrivateFieldGet(this, _EarnController_instances, "m", _EarnController_initializeSDK).call(this);
100
+ this.refreshPooledStakingData().catch(console.error);
101
+ const { selectedNetworkClientId } = this.messagingSystem.call('NetworkController:getState');
102
+ __classPrivateFieldSet(this, _EarnController_selectedNetworkClientId, selectedNetworkClientId, "f");
103
+ this.messagingSystem.subscribe('NetworkController:stateChange', (networkControllerState) => {
104
+ if (networkControllerState.selectedNetworkClientId !==
105
+ __classPrivateFieldGet(this, _EarnController_selectedNetworkClientId, "f")) {
106
+ __classPrivateFieldGet(this, _EarnController_instances, "m", _EarnController_initializeSDK).call(this, networkControllerState.selectedNetworkClientId);
107
+ this.refreshPooledStakingData().catch(console.error);
108
+ }
109
+ __classPrivateFieldSet(this, _EarnController_selectedNetworkClientId, networkControllerState.selectedNetworkClientId, "f");
110
+ });
111
+ // Listen for account changes
112
+ this.messagingSystem.subscribe('AccountsController:selectedAccountChange', () => {
113
+ this.refreshPooledStakingData().catch(console.error);
114
+ });
115
+ }
116
+ /**
117
+ * Refreshes the pooled stakes data for the current account.
118
+ * Fetches updated stake information including lifetime rewards, assets, and exit requests
119
+ * from the staking API service and updates the state.
120
+ *
121
+ * @returns A promise that resolves when the stakes data has been updated
122
+ */
123
+ async refreshPooledStakes() {
124
+ const currentAccount = __classPrivateFieldGet(this, _EarnController_instances, "m", _EarnController_getCurrentAccount).call(this);
125
+ if (!currentAccount?.address) {
126
+ return;
127
+ }
128
+ const chainId = __classPrivateFieldGet(this, _EarnController_instances, "m", _EarnController_getCurrentChainId).call(this);
129
+ const { accounts, exchangeRate } = await __classPrivateFieldGet(this, _EarnController_stakingApiService, "f").getPooledStakes([currentAccount.address], chainId);
130
+ this.update((state) => {
131
+ state.pooled_staking.pooledStakes = accounts[0];
132
+ state.pooled_staking.exchangeRate = exchangeRate;
133
+ });
134
+ }
135
+ /**
136
+ * Refreshes the staking eligibility status for the current account.
137
+ * Updates the eligibility status in the controller state based on the location and address blocklist for compliance.
138
+ *
139
+ * @returns A promise that resolves when the eligibility status has been updated
140
+ */
141
+ async refreshStakingEligibility() {
142
+ const currentAccount = __classPrivateFieldGet(this, _EarnController_instances, "m", _EarnController_getCurrentAccount).call(this);
143
+ if (!currentAccount?.address) {
144
+ return;
145
+ }
146
+ const { eligible: isEligible } = await __classPrivateFieldGet(this, _EarnController_stakingApiService, "f").getPooledStakingEligibility([
147
+ currentAccount.address,
148
+ ]);
149
+ this.update((state) => {
150
+ state.pooled_staking.isEligible = isEligible;
151
+ });
152
+ }
153
+ /**
154
+ * Refreshes vault data for the current chain.
155
+ * Updates the vault data in the controller state including APY, capacity,
156
+ * fee percentage, total assets, and vault address.
157
+ *
158
+ * @returns A promise that resolves when the vault data has been updated
159
+ */
160
+ async refreshVaultData() {
161
+ const chainId = __classPrivateFieldGet(this, _EarnController_instances, "m", _EarnController_getCurrentChainId).call(this);
162
+ const vaultData = await __classPrivateFieldGet(this, _EarnController_stakingApiService, "f").getVaultData(chainId);
163
+ this.update((state) => {
164
+ state.pooled_staking.vaultData = vaultData;
165
+ });
166
+ }
167
+ /**
168
+ * Refreshes all pooled staking related data including stakes, eligibility, and vault data.
169
+ * This method allows partial success, meaning some data may update while other requests fail.
170
+ * All errors are collected and thrown as a single error message.
171
+ *
172
+ * @returns A promise that resolves when all possible data has been updated
173
+ * @throws {Error} If any of the refresh operations fail, with concatenated error messages
174
+ */
175
+ async refreshPooledStakingData() {
176
+ const errors = [];
177
+ await Promise.all([
178
+ this.refreshPooledStakes().catch((error) => {
179
+ errors.push(error);
180
+ }),
181
+ this.refreshStakingEligibility().catch((error) => {
182
+ errors.push(error);
183
+ }),
184
+ this.refreshVaultData().catch((error) => {
185
+ errors.push(error);
186
+ }),
187
+ ]);
188
+ if (errors.length > 0) {
189
+ throw new Error(`Failed to refresh some staking data: ${errors
190
+ .map((e) => e.message)
191
+ .join(', ')}`);
192
+ }
193
+ }
194
+ }
195
+ exports.EarnController = EarnController;
196
+ _EarnController_stakeSDK = new WeakMap(), _EarnController_selectedNetworkClientId = new WeakMap(), _EarnController_stakingApiService = new WeakMap(), _EarnController_instances = new WeakSet(), _EarnController_initializeSDK = function _EarnController_initializeSDK(networkClientId) {
197
+ const { selectedNetworkClientId } = networkClientId
198
+ ? { selectedNetworkClientId: networkClientId }
199
+ : this.messagingSystem.call('NetworkController:getState');
200
+ const networkClient = this.messagingSystem.call('NetworkController:getNetworkClientById', selectedNetworkClientId);
201
+ if (!networkClient?.provider) {
202
+ __classPrivateFieldSet(this, _EarnController_stakeSDK, null, "f");
203
+ return;
204
+ }
205
+ const provider = new providers_1.Web3Provider(networkClient.provider);
206
+ const { chainId } = networkClient.configuration;
207
+ // Initialize appropriate contracts based on chainId
208
+ const config = {
209
+ chainId: (0, controller_utils_1.convertHexToDecimal)(chainId),
210
+ };
211
+ try {
212
+ __classPrivateFieldSet(this, _EarnController_stakeSDK, stake_sdk_1.StakeSdk.create(config), "f");
213
+ __classPrivateFieldGet(this, _EarnController_stakeSDK, "f").pooledStakingContract.connectSignerOrProvider(provider);
214
+ }
215
+ catch (error) {
216
+ __classPrivateFieldSet(this, _EarnController_stakeSDK, null, "f");
217
+ // Only log unexpected errors, not unsupported chain errors
218
+ if (!(error instanceof Error &&
219
+ error.message.includes('Unsupported chainId'))) {
220
+ console.error('Stake SDK initialization failed:', error);
221
+ }
222
+ }
223
+ }, _EarnController_getCurrentAccount = function _EarnController_getCurrentAccount() {
224
+ return this.messagingSystem.call('AccountsController:getSelectedAccount');
225
+ }, _EarnController_getCurrentChainId = function _EarnController_getCurrentChainId() {
226
+ const { selectedNetworkClientId } = this.messagingSystem.call('NetworkController:getState');
227
+ const { configuration: { chainId }, } = this.messagingSystem.call('NetworkController:getNetworkClientById', selectedNetworkClientId);
228
+ return (0, controller_utils_1.convertHexToDecimal)(chainId);
229
+ };
230
+ //# sourceMappingURL=EarnController.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EarnController.cjs","sourceRoot":"","sources":["../src/EarnController.ts"],"names":[],"mappings":"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","sourcesContent":["import { Web3Provider } from '@ethersproject/providers';\nimport type {\n AccountsControllerGetSelectedAccountAction,\n AccountsControllerSelectedAccountChangeEvent,\n} from '@metamask/accounts-controller';\nimport type {\n ControllerGetStateAction,\n ControllerStateChangeEvent,\n RestrictedControllerMessenger,\n StateMetadata,\n} from '@metamask/base-controller';\nimport { BaseController } from '@metamask/base-controller';\nimport { convertHexToDecimal } from '@metamask/controller-utils';\nimport type { NetworkControllerStateChangeEvent } from '@metamask/network-controller';\nimport type {\n NetworkControllerGetNetworkClientByIdAction,\n NetworkControllerGetStateAction,\n} from '@metamask/network-controller';\nimport {\n StakeSdk,\n StakingApiService,\n type PooledStake,\n type StakeSdkConfig,\n type VaultData,\n} from '@metamask/stake-sdk';\n\nexport const controllerName = 'EarnController';\n\nexport type PooledStakingState = {\n pooledStakes: PooledStake;\n exchangeRate: string;\n vaultData: VaultData;\n isEligible: boolean;\n};\n\nexport type StablecoinLendingState = {\n vaults: StablecoinVault[];\n};\n\nexport type StablecoinVault = {\n symbol: string;\n name: string;\n chainId: number;\n tokenAddress: string;\n vaultAddress: string;\n currentAPY: string;\n supply: string;\n liquidity: string;\n};\n\n/**\n * Metadata for the EarnController.\n */\nconst earnControllerMetadata: StateMetadata<EarnControllerState> = {\n pooled_staking: {\n persist: true,\n anonymous: false,\n },\n stablecoin_lending: {\n persist: true,\n anonymous: false,\n },\n lastUpdated: {\n persist: false,\n anonymous: true,\n },\n};\n\n// === State Types ===\nexport type EarnControllerState = {\n pooled_staking: PooledStakingState;\n stablecoin_lending?: StablecoinLendingState;\n lastUpdated: number;\n};\n\n// === Default State ===\nconst DEFAULT_STABLECOIN_VAULT: StablecoinVault = {\n symbol: '',\n name: '',\n chainId: 0,\n tokenAddress: '',\n vaultAddress: '',\n currentAPY: '0',\n supply: '0',\n liquidity: '0',\n};\n\n/**\n * Gets the default state for the EarnController.\n *\n * @returns The default EarnController state.\n */\nexport function getDefaultEarnControllerState(): EarnControllerState {\n return {\n pooled_staking: {\n pooledStakes: {\n account: '',\n lifetimeRewards: '0',\n assets: '0',\n exitRequests: [],\n },\n exchangeRate: '1',\n vaultData: {\n apy: '0',\n capacity: '0',\n feePercent: 0,\n totalAssets: '0',\n vaultAddress: '0x0000000000000000000000000000000000000000',\n },\n isEligible: false,\n },\n stablecoin_lending: {\n vaults: [DEFAULT_STABLECOIN_VAULT],\n },\n lastUpdated: 0,\n };\n}\n\n// === MESSENGER ===\n\n/**\n * The action which can be used to retrieve the state of the EarnController.\n */\nexport type EarnControllerGetStateAction = ControllerGetStateAction<\n typeof controllerName,\n EarnControllerState\n>;\n\n/**\n * All actions that EarnController registers, to be called externally.\n */\nexport type EarnControllerActions = EarnControllerGetStateAction;\n\n/**\n * All actions that EarnController calls internally.\n */\nexport type AllowedActions =\n | NetworkControllerGetNetworkClientByIdAction\n | NetworkControllerGetStateAction\n | AccountsControllerGetSelectedAccountAction;\n\n/**\n * The event that EarnController publishes when updating state.\n */\nexport type EarnControllerStateChangeEvent = ControllerStateChangeEvent<\n typeof controllerName,\n EarnControllerState\n>;\n\n/**\n * All events that EarnController publishes, to be subscribed to externally.\n */\nexport type EarnControllerEvents = EarnControllerStateChangeEvent;\n\n/**\n * All events that EarnController subscribes to internally.\n */\nexport type AllowedEvents =\n | AccountsControllerSelectedAccountChangeEvent\n | NetworkControllerStateChangeEvent;\n\n/**\n * The messenger which is restricted to actions and events accessed by\n * EarnController.\n */\nexport type EarnControllerMessenger = RestrictedControllerMessenger<\n typeof controllerName,\n EarnControllerActions | AllowedActions,\n EarnControllerEvents | AllowedEvents,\n AllowedActions['type'],\n AllowedEvents['type']\n>;\n\n// === CONTROLLER DEFINITION ===\n\n/**\n * EarnController manages DeFi earning opportunities across different protocols and chains.\n */\nexport class EarnController extends BaseController<\n typeof controllerName,\n EarnControllerState,\n EarnControllerMessenger\n> {\n #stakeSDK: StakeSdk | null = null;\n\n #selectedNetworkClientId?: string;\n\n readonly #stakingApiService: StakingApiService = new StakingApiService();\n\n constructor({\n messenger,\n state = {},\n }: {\n messenger: EarnControllerMessenger;\n state?: Partial<EarnControllerState>;\n }) {\n super({\n name: controllerName,\n metadata: earnControllerMetadata,\n messenger,\n state: {\n ...getDefaultEarnControllerState(),\n ...state,\n },\n });\n\n this.#initializeSDK();\n this.refreshPooledStakingData().catch(console.error);\n\n const { selectedNetworkClientId } = this.messagingSystem.call(\n 'NetworkController:getState',\n );\n this.#selectedNetworkClientId = selectedNetworkClientId;\n\n this.messagingSystem.subscribe(\n 'NetworkController:stateChange',\n (networkControllerState) => {\n if (\n networkControllerState.selectedNetworkClientId !==\n this.#selectedNetworkClientId\n ) {\n this.#initializeSDK(networkControllerState.selectedNetworkClientId);\n this.refreshPooledStakingData().catch(console.error);\n }\n this.#selectedNetworkClientId =\n networkControllerState.selectedNetworkClientId;\n },\n );\n\n // Listen for account changes\n this.messagingSystem.subscribe(\n 'AccountsController:selectedAccountChange',\n () => {\n this.refreshPooledStakingData().catch(console.error);\n },\n );\n }\n\n #initializeSDK(networkClientId?: string) {\n const { selectedNetworkClientId } = networkClientId\n ? { selectedNetworkClientId: networkClientId }\n : this.messagingSystem.call('NetworkController:getState');\n\n const networkClient = this.messagingSystem.call(\n 'NetworkController:getNetworkClientById',\n selectedNetworkClientId,\n );\n\n if (!networkClient?.provider) {\n this.#stakeSDK = null;\n return;\n }\n\n const provider = new Web3Provider(networkClient.provider);\n const { chainId } = networkClient.configuration;\n\n // Initialize appropriate contracts based on chainId\n const config: StakeSdkConfig = {\n chainId: convertHexToDecimal(chainId),\n };\n\n try {\n this.#stakeSDK = StakeSdk.create(config);\n this.#stakeSDK.pooledStakingContract.connectSignerOrProvider(provider);\n } catch (error) {\n this.#stakeSDK = null;\n // Only log unexpected errors, not unsupported chain errors\n if (\n !(\n error instanceof Error &&\n error.message.includes('Unsupported chainId')\n )\n ) {\n console.error('Stake SDK initialization failed:', error);\n }\n }\n }\n\n #getCurrentAccount() {\n return this.messagingSystem.call('AccountsController:getSelectedAccount');\n }\n\n #getCurrentChainId(): number {\n const { selectedNetworkClientId } = this.messagingSystem.call(\n 'NetworkController:getState',\n );\n const {\n configuration: { chainId },\n } = this.messagingSystem.call(\n 'NetworkController:getNetworkClientById',\n selectedNetworkClientId,\n );\n return convertHexToDecimal(chainId);\n }\n\n /**\n * Refreshes the pooled stakes data for the current account.\n * Fetches updated stake information including lifetime rewards, assets, and exit requests\n * from the staking API service and updates the state.\n *\n * @returns A promise that resolves when the stakes data has been updated\n */\n async refreshPooledStakes(): Promise<void> {\n const currentAccount = this.#getCurrentAccount();\n if (!currentAccount?.address) {\n return;\n }\n\n const chainId = this.#getCurrentChainId();\n\n const { accounts, exchangeRate } =\n await this.#stakingApiService.getPooledStakes(\n [currentAccount.address],\n chainId,\n );\n\n this.update((state) => {\n state.pooled_staking.pooledStakes = accounts[0];\n state.pooled_staking.exchangeRate = exchangeRate;\n });\n }\n\n /**\n * Refreshes the staking eligibility status for the current account.\n * Updates the eligibility status in the controller state based on the location and address blocklist for compliance.\n *\n * @returns A promise that resolves when the eligibility status has been updated\n */\n async refreshStakingEligibility(): Promise<void> {\n const currentAccount = this.#getCurrentAccount();\n if (!currentAccount?.address) {\n return;\n }\n\n const { eligible: isEligible } =\n await this.#stakingApiService.getPooledStakingEligibility([\n currentAccount.address,\n ]);\n\n this.update((state) => {\n state.pooled_staking.isEligible = isEligible;\n });\n }\n\n /**\n * Refreshes vault data for the current chain.\n * Updates the vault data in the controller state including APY, capacity,\n * fee percentage, total assets, and vault address.\n *\n * @returns A promise that resolves when the vault data has been updated\n */\n async refreshVaultData(): Promise<void> {\n const chainId = this.#getCurrentChainId();\n const vaultData = await this.#stakingApiService.getVaultData(chainId);\n\n this.update((state) => {\n state.pooled_staking.vaultData = vaultData;\n });\n }\n\n /**\n * Refreshes all pooled staking related data including stakes, eligibility, and vault data.\n * This method allows partial success, meaning some data may update while other requests fail.\n * All errors are collected and thrown as a single error message.\n *\n * @returns A promise that resolves when all possible data has been updated\n * @throws {Error} If any of the refresh operations fail, with concatenated error messages\n */\n async refreshPooledStakingData(): Promise<void> {\n const errors: Error[] = [];\n\n await Promise.all([\n this.refreshPooledStakes().catch((error) => {\n errors.push(error);\n }),\n this.refreshStakingEligibility().catch((error) => {\n errors.push(error);\n }),\n this.refreshVaultData().catch((error) => {\n errors.push(error);\n }),\n ]);\n\n if (errors.length > 0) {\n throw new Error(\n `Failed to refresh some staking data: ${errors\n .map((e) => e.message)\n .join(', ')}`,\n );\n }\n }\n}\n"]}
@@ -0,0 +1,109 @@
1
+ import type { AccountsControllerGetSelectedAccountAction, AccountsControllerSelectedAccountChangeEvent } from "@metamask/accounts-controller";
2
+ import type { ControllerGetStateAction, ControllerStateChangeEvent, RestrictedControllerMessenger } from "@metamask/base-controller";
3
+ import { BaseController } from "@metamask/base-controller";
4
+ import type { NetworkControllerStateChangeEvent } from "@metamask/network-controller";
5
+ import type { NetworkControllerGetNetworkClientByIdAction, NetworkControllerGetStateAction } from "@metamask/network-controller";
6
+ import { type PooledStake, type VaultData } from "@metamask/stake-sdk";
7
+ export declare const controllerName = "EarnController";
8
+ export type PooledStakingState = {
9
+ pooledStakes: PooledStake;
10
+ exchangeRate: string;
11
+ vaultData: VaultData;
12
+ isEligible: boolean;
13
+ };
14
+ export type StablecoinLendingState = {
15
+ vaults: StablecoinVault[];
16
+ };
17
+ export type StablecoinVault = {
18
+ symbol: string;
19
+ name: string;
20
+ chainId: number;
21
+ tokenAddress: string;
22
+ vaultAddress: string;
23
+ currentAPY: string;
24
+ supply: string;
25
+ liquidity: string;
26
+ };
27
+ export type EarnControllerState = {
28
+ pooled_staking: PooledStakingState;
29
+ stablecoin_lending?: StablecoinLendingState;
30
+ lastUpdated: number;
31
+ };
32
+ /**
33
+ * Gets the default state for the EarnController.
34
+ *
35
+ * @returns The default EarnController state.
36
+ */
37
+ export declare function getDefaultEarnControllerState(): EarnControllerState;
38
+ /**
39
+ * The action which can be used to retrieve the state of the EarnController.
40
+ */
41
+ export type EarnControllerGetStateAction = ControllerGetStateAction<typeof controllerName, EarnControllerState>;
42
+ /**
43
+ * All actions that EarnController registers, to be called externally.
44
+ */
45
+ export type EarnControllerActions = EarnControllerGetStateAction;
46
+ /**
47
+ * All actions that EarnController calls internally.
48
+ */
49
+ export type AllowedActions = NetworkControllerGetNetworkClientByIdAction | NetworkControllerGetStateAction | AccountsControllerGetSelectedAccountAction;
50
+ /**
51
+ * The event that EarnController publishes when updating state.
52
+ */
53
+ export type EarnControllerStateChangeEvent = ControllerStateChangeEvent<typeof controllerName, EarnControllerState>;
54
+ /**
55
+ * All events that EarnController publishes, to be subscribed to externally.
56
+ */
57
+ export type EarnControllerEvents = EarnControllerStateChangeEvent;
58
+ /**
59
+ * All events that EarnController subscribes to internally.
60
+ */
61
+ export type AllowedEvents = AccountsControllerSelectedAccountChangeEvent | NetworkControllerStateChangeEvent;
62
+ /**
63
+ * The messenger which is restricted to actions and events accessed by
64
+ * EarnController.
65
+ */
66
+ export type EarnControllerMessenger = RestrictedControllerMessenger<typeof controllerName, EarnControllerActions | AllowedActions, EarnControllerEvents | AllowedEvents, AllowedActions['type'], AllowedEvents['type']>;
67
+ /**
68
+ * EarnController manages DeFi earning opportunities across different protocols and chains.
69
+ */
70
+ export declare class EarnController extends BaseController<typeof controllerName, EarnControllerState, EarnControllerMessenger> {
71
+ #private;
72
+ constructor({ messenger, state, }: {
73
+ messenger: EarnControllerMessenger;
74
+ state?: Partial<EarnControllerState>;
75
+ });
76
+ /**
77
+ * Refreshes the pooled stakes data for the current account.
78
+ * Fetches updated stake information including lifetime rewards, assets, and exit requests
79
+ * from the staking API service and updates the state.
80
+ *
81
+ * @returns A promise that resolves when the stakes data has been updated
82
+ */
83
+ refreshPooledStakes(): Promise<void>;
84
+ /**
85
+ * Refreshes the staking eligibility status for the current account.
86
+ * Updates the eligibility status in the controller state based on the location and address blocklist for compliance.
87
+ *
88
+ * @returns A promise that resolves when the eligibility status has been updated
89
+ */
90
+ refreshStakingEligibility(): Promise<void>;
91
+ /**
92
+ * Refreshes vault data for the current chain.
93
+ * Updates the vault data in the controller state including APY, capacity,
94
+ * fee percentage, total assets, and vault address.
95
+ *
96
+ * @returns A promise that resolves when the vault data has been updated
97
+ */
98
+ refreshVaultData(): Promise<void>;
99
+ /**
100
+ * Refreshes all pooled staking related data including stakes, eligibility, and vault data.
101
+ * This method allows partial success, meaning some data may update while other requests fail.
102
+ * All errors are collected and thrown as a single error message.
103
+ *
104
+ * @returns A promise that resolves when all possible data has been updated
105
+ * @throws {Error} If any of the refresh operations fail, with concatenated error messages
106
+ */
107
+ refreshPooledStakingData(): Promise<void>;
108
+ }
109
+ //# sourceMappingURL=EarnController.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EarnController.d.cts","sourceRoot":"","sources":["../src/EarnController.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,0CAA0C,EAC1C,4CAA4C,EAC7C,sCAAsC;AACvC,OAAO,KAAK,EACV,wBAAwB,EACxB,0BAA0B,EAC1B,6BAA6B,EAE9B,kCAAkC;AACnC,OAAO,EAAE,cAAc,EAAE,kCAAkC;AAE3D,OAAO,KAAK,EAAE,iCAAiC,EAAE,qCAAqC;AACtF,OAAO,KAAK,EACV,2CAA2C,EAC3C,+BAA+B,EAChC,qCAAqC;AACtC,OAAO,EAGL,KAAK,WAAW,EAEhB,KAAK,SAAS,EACf,4BAA4B;AAE7B,eAAO,MAAM,cAAc,mBAAmB,CAAC;AAE/C,MAAM,MAAM,kBAAkB,GAAG;IAC/B,YAAY,EAAE,WAAW,CAAC;IAC1B,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,SAAS,CAAC;IACrB,UAAU,EAAE,OAAO,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,sBAAsB,GAAG;IACnC,MAAM,EAAE,eAAe,EAAE,CAAC;CAC3B,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAqBF,MAAM,MAAM,mBAAmB,GAAG;IAChC,cAAc,EAAE,kBAAkB,CAAC;IACnC,kBAAkB,CAAC,EAAE,sBAAsB,CAAC;IAC5C,WAAW,EAAE,MAAM,CAAC;CACrB,CAAC;AAcF;;;;GAIG;AACH,wBAAgB,6BAA6B,IAAI,mBAAmB,CAwBnE;AAID;;GAEG;AACH,MAAM,MAAM,4BAA4B,GAAG,wBAAwB,CACjE,OAAO,cAAc,EACrB,mBAAmB,CACpB,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,qBAAqB,GAAG,4BAA4B,CAAC;AAEjE;;GAEG;AACH,MAAM,MAAM,cAAc,GACtB,2CAA2C,GAC3C,+BAA+B,GAC/B,0CAA0C,CAAC;AAE/C;;GAEG;AACH,MAAM,MAAM,8BAA8B,GAAG,0BAA0B,CACrE,OAAO,cAAc,EACrB,mBAAmB,CACpB,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,oBAAoB,GAAG,8BAA8B,CAAC;AAElE;;GAEG;AACH,MAAM,MAAM,aAAa,GACrB,4CAA4C,GAC5C,iCAAiC,CAAC;AAEtC;;;GAGG;AACH,MAAM,MAAM,uBAAuB,GAAG,6BAA6B,CACjE,OAAO,cAAc,EACrB,qBAAqB,GAAG,cAAc,EACtC,oBAAoB,GAAG,aAAa,EACpC,cAAc,CAAC,MAAM,CAAC,EACtB,aAAa,CAAC,MAAM,CAAC,CACtB,CAAC;AAIF;;GAEG;AACH,qBAAa,cAAe,SAAQ,cAAc,CAChD,OAAO,cAAc,EACrB,mBAAmB,EACnB,uBAAuB,CACxB;;gBAOa,EACV,SAAS,EACT,KAAU,GACX,EAAE;QACD,SAAS,EAAE,uBAAuB,CAAC;QACnC,KAAK,CAAC,EAAE,OAAO,CAAC,mBAAmB,CAAC,CAAC;KACtC;IAoGD;;;;;;OAMG;IACG,mBAAmB,IAAI,OAAO,CAAC,IAAI,CAAC;IAoB1C;;;;;OAKG;IACG,yBAAyB,IAAI,OAAO,CAAC,IAAI,CAAC;IAgBhD;;;;;;OAMG;IACG,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC;IASvC;;;;;;;OAOG;IACG,wBAAwB,IAAI,OAAO,CAAC,IAAI,CAAC;CAuBhD"}
@@ -0,0 +1,109 @@
1
+ import type { AccountsControllerGetSelectedAccountAction, AccountsControllerSelectedAccountChangeEvent } from "@metamask/accounts-controller";
2
+ import type { ControllerGetStateAction, ControllerStateChangeEvent, RestrictedControllerMessenger } from "@metamask/base-controller";
3
+ import { BaseController } from "@metamask/base-controller";
4
+ import type { NetworkControllerStateChangeEvent } from "@metamask/network-controller";
5
+ import type { NetworkControllerGetNetworkClientByIdAction, NetworkControllerGetStateAction } from "@metamask/network-controller";
6
+ import { type PooledStake, type VaultData } from "@metamask/stake-sdk";
7
+ export declare const controllerName = "EarnController";
8
+ export type PooledStakingState = {
9
+ pooledStakes: PooledStake;
10
+ exchangeRate: string;
11
+ vaultData: VaultData;
12
+ isEligible: boolean;
13
+ };
14
+ export type StablecoinLendingState = {
15
+ vaults: StablecoinVault[];
16
+ };
17
+ export type StablecoinVault = {
18
+ symbol: string;
19
+ name: string;
20
+ chainId: number;
21
+ tokenAddress: string;
22
+ vaultAddress: string;
23
+ currentAPY: string;
24
+ supply: string;
25
+ liquidity: string;
26
+ };
27
+ export type EarnControllerState = {
28
+ pooled_staking: PooledStakingState;
29
+ stablecoin_lending?: StablecoinLendingState;
30
+ lastUpdated: number;
31
+ };
32
+ /**
33
+ * Gets the default state for the EarnController.
34
+ *
35
+ * @returns The default EarnController state.
36
+ */
37
+ export declare function getDefaultEarnControllerState(): EarnControllerState;
38
+ /**
39
+ * The action which can be used to retrieve the state of the EarnController.
40
+ */
41
+ export type EarnControllerGetStateAction = ControllerGetStateAction<typeof controllerName, EarnControllerState>;
42
+ /**
43
+ * All actions that EarnController registers, to be called externally.
44
+ */
45
+ export type EarnControllerActions = EarnControllerGetStateAction;
46
+ /**
47
+ * All actions that EarnController calls internally.
48
+ */
49
+ export type AllowedActions = NetworkControllerGetNetworkClientByIdAction | NetworkControllerGetStateAction | AccountsControllerGetSelectedAccountAction;
50
+ /**
51
+ * The event that EarnController publishes when updating state.
52
+ */
53
+ export type EarnControllerStateChangeEvent = ControllerStateChangeEvent<typeof controllerName, EarnControllerState>;
54
+ /**
55
+ * All events that EarnController publishes, to be subscribed to externally.
56
+ */
57
+ export type EarnControllerEvents = EarnControllerStateChangeEvent;
58
+ /**
59
+ * All events that EarnController subscribes to internally.
60
+ */
61
+ export type AllowedEvents = AccountsControllerSelectedAccountChangeEvent | NetworkControllerStateChangeEvent;
62
+ /**
63
+ * The messenger which is restricted to actions and events accessed by
64
+ * EarnController.
65
+ */
66
+ export type EarnControllerMessenger = RestrictedControllerMessenger<typeof controllerName, EarnControllerActions | AllowedActions, EarnControllerEvents | AllowedEvents, AllowedActions['type'], AllowedEvents['type']>;
67
+ /**
68
+ * EarnController manages DeFi earning opportunities across different protocols and chains.
69
+ */
70
+ export declare class EarnController extends BaseController<typeof controllerName, EarnControllerState, EarnControllerMessenger> {
71
+ #private;
72
+ constructor({ messenger, state, }: {
73
+ messenger: EarnControllerMessenger;
74
+ state?: Partial<EarnControllerState>;
75
+ });
76
+ /**
77
+ * Refreshes the pooled stakes data for the current account.
78
+ * Fetches updated stake information including lifetime rewards, assets, and exit requests
79
+ * from the staking API service and updates the state.
80
+ *
81
+ * @returns A promise that resolves when the stakes data has been updated
82
+ */
83
+ refreshPooledStakes(): Promise<void>;
84
+ /**
85
+ * Refreshes the staking eligibility status for the current account.
86
+ * Updates the eligibility status in the controller state based on the location and address blocklist for compliance.
87
+ *
88
+ * @returns A promise that resolves when the eligibility status has been updated
89
+ */
90
+ refreshStakingEligibility(): Promise<void>;
91
+ /**
92
+ * Refreshes vault data for the current chain.
93
+ * Updates the vault data in the controller state including APY, capacity,
94
+ * fee percentage, total assets, and vault address.
95
+ *
96
+ * @returns A promise that resolves when the vault data has been updated
97
+ */
98
+ refreshVaultData(): Promise<void>;
99
+ /**
100
+ * Refreshes all pooled staking related data including stakes, eligibility, and vault data.
101
+ * This method allows partial success, meaning some data may update while other requests fail.
102
+ * All errors are collected and thrown as a single error message.
103
+ *
104
+ * @returns A promise that resolves when all possible data has been updated
105
+ * @throws {Error} If any of the refresh operations fail, with concatenated error messages
106
+ */
107
+ refreshPooledStakingData(): Promise<void>;
108
+ }
109
+ //# sourceMappingURL=EarnController.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EarnController.d.mts","sourceRoot":"","sources":["../src/EarnController.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,0CAA0C,EAC1C,4CAA4C,EAC7C,sCAAsC;AACvC,OAAO,KAAK,EACV,wBAAwB,EACxB,0BAA0B,EAC1B,6BAA6B,EAE9B,kCAAkC;AACnC,OAAO,EAAE,cAAc,EAAE,kCAAkC;AAE3D,OAAO,KAAK,EAAE,iCAAiC,EAAE,qCAAqC;AACtF,OAAO,KAAK,EACV,2CAA2C,EAC3C,+BAA+B,EAChC,qCAAqC;AACtC,OAAO,EAGL,KAAK,WAAW,EAEhB,KAAK,SAAS,EACf,4BAA4B;AAE7B,eAAO,MAAM,cAAc,mBAAmB,CAAC;AAE/C,MAAM,MAAM,kBAAkB,GAAG;IAC/B,YAAY,EAAE,WAAW,CAAC;IAC1B,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,SAAS,CAAC;IACrB,UAAU,EAAE,OAAO,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,sBAAsB,GAAG;IACnC,MAAM,EAAE,eAAe,EAAE,CAAC;CAC3B,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAqBF,MAAM,MAAM,mBAAmB,GAAG;IAChC,cAAc,EAAE,kBAAkB,CAAC;IACnC,kBAAkB,CAAC,EAAE,sBAAsB,CAAC;IAC5C,WAAW,EAAE,MAAM,CAAC;CACrB,CAAC;AAcF;;;;GAIG;AACH,wBAAgB,6BAA6B,IAAI,mBAAmB,CAwBnE;AAID;;GAEG;AACH,MAAM,MAAM,4BAA4B,GAAG,wBAAwB,CACjE,OAAO,cAAc,EACrB,mBAAmB,CACpB,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,qBAAqB,GAAG,4BAA4B,CAAC;AAEjE;;GAEG;AACH,MAAM,MAAM,cAAc,GACtB,2CAA2C,GAC3C,+BAA+B,GAC/B,0CAA0C,CAAC;AAE/C;;GAEG;AACH,MAAM,MAAM,8BAA8B,GAAG,0BAA0B,CACrE,OAAO,cAAc,EACrB,mBAAmB,CACpB,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,oBAAoB,GAAG,8BAA8B,CAAC;AAElE;;GAEG;AACH,MAAM,MAAM,aAAa,GACrB,4CAA4C,GAC5C,iCAAiC,CAAC;AAEtC;;;GAGG;AACH,MAAM,MAAM,uBAAuB,GAAG,6BAA6B,CACjE,OAAO,cAAc,EACrB,qBAAqB,GAAG,cAAc,EACtC,oBAAoB,GAAG,aAAa,EACpC,cAAc,CAAC,MAAM,CAAC,EACtB,aAAa,CAAC,MAAM,CAAC,CACtB,CAAC;AAIF;;GAEG;AACH,qBAAa,cAAe,SAAQ,cAAc,CAChD,OAAO,cAAc,EACrB,mBAAmB,EACnB,uBAAuB,CACxB;;gBAOa,EACV,SAAS,EACT,KAAU,GACX,EAAE;QACD,SAAS,EAAE,uBAAuB,CAAC;QACnC,KAAK,CAAC,EAAE,OAAO,CAAC,mBAAmB,CAAC,CAAC;KACtC;IAoGD;;;;;;OAMG;IACG,mBAAmB,IAAI,OAAO,CAAC,IAAI,CAAC;IAoB1C;;;;;OAKG;IACG,yBAAyB,IAAI,OAAO,CAAC,IAAI,CAAC;IAgBhD;;;;;;OAMG;IACG,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC;IASvC;;;;;;;OAOG;IACG,wBAAwB,IAAI,OAAO,CAAC,IAAI,CAAC;CAuBhD"}
@@ -0,0 +1,225 @@
1
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
2
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
3
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
4
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
5
+ };
6
+ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
7
+ if (kind === "m") throw new TypeError("Private method is not writable");
8
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
9
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
10
+ return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
11
+ };
12
+ var _EarnController_instances, _EarnController_stakeSDK, _EarnController_selectedNetworkClientId, _EarnController_stakingApiService, _EarnController_initializeSDK, _EarnController_getCurrentAccount, _EarnController_getCurrentChainId;
13
+ import { Web3Provider } from "@ethersproject/providers";
14
+ import { BaseController } from "@metamask/base-controller";
15
+ import { convertHexToDecimal } from "@metamask/controller-utils";
16
+ import { StakeSdk, StakingApiService } from "@metamask/stake-sdk";
17
+ export const controllerName = 'EarnController';
18
+ /**
19
+ * Metadata for the EarnController.
20
+ */
21
+ const earnControllerMetadata = {
22
+ pooled_staking: {
23
+ persist: true,
24
+ anonymous: false,
25
+ },
26
+ stablecoin_lending: {
27
+ persist: true,
28
+ anonymous: false,
29
+ },
30
+ lastUpdated: {
31
+ persist: false,
32
+ anonymous: true,
33
+ },
34
+ };
35
+ // === Default State ===
36
+ const DEFAULT_STABLECOIN_VAULT = {
37
+ symbol: '',
38
+ name: '',
39
+ chainId: 0,
40
+ tokenAddress: '',
41
+ vaultAddress: '',
42
+ currentAPY: '0',
43
+ supply: '0',
44
+ liquidity: '0',
45
+ };
46
+ /**
47
+ * Gets the default state for the EarnController.
48
+ *
49
+ * @returns The default EarnController state.
50
+ */
51
+ export function getDefaultEarnControllerState() {
52
+ return {
53
+ pooled_staking: {
54
+ pooledStakes: {
55
+ account: '',
56
+ lifetimeRewards: '0',
57
+ assets: '0',
58
+ exitRequests: [],
59
+ },
60
+ exchangeRate: '1',
61
+ vaultData: {
62
+ apy: '0',
63
+ capacity: '0',
64
+ feePercent: 0,
65
+ totalAssets: '0',
66
+ vaultAddress: '0x0000000000000000000000000000000000000000',
67
+ },
68
+ isEligible: false,
69
+ },
70
+ stablecoin_lending: {
71
+ vaults: [DEFAULT_STABLECOIN_VAULT],
72
+ },
73
+ lastUpdated: 0,
74
+ };
75
+ }
76
+ // === CONTROLLER DEFINITION ===
77
+ /**
78
+ * EarnController manages DeFi earning opportunities across different protocols and chains.
79
+ */
80
+ export class EarnController extends BaseController {
81
+ constructor({ messenger, state = {}, }) {
82
+ super({
83
+ name: controllerName,
84
+ metadata: earnControllerMetadata,
85
+ messenger,
86
+ state: {
87
+ ...getDefaultEarnControllerState(),
88
+ ...state,
89
+ },
90
+ });
91
+ _EarnController_instances.add(this);
92
+ _EarnController_stakeSDK.set(this, null);
93
+ _EarnController_selectedNetworkClientId.set(this, void 0);
94
+ _EarnController_stakingApiService.set(this, new StakingApiService());
95
+ __classPrivateFieldGet(this, _EarnController_instances, "m", _EarnController_initializeSDK).call(this);
96
+ this.refreshPooledStakingData().catch(console.error);
97
+ const { selectedNetworkClientId } = this.messagingSystem.call('NetworkController:getState');
98
+ __classPrivateFieldSet(this, _EarnController_selectedNetworkClientId, selectedNetworkClientId, "f");
99
+ this.messagingSystem.subscribe('NetworkController:stateChange', (networkControllerState) => {
100
+ if (networkControllerState.selectedNetworkClientId !==
101
+ __classPrivateFieldGet(this, _EarnController_selectedNetworkClientId, "f")) {
102
+ __classPrivateFieldGet(this, _EarnController_instances, "m", _EarnController_initializeSDK).call(this, networkControllerState.selectedNetworkClientId);
103
+ this.refreshPooledStakingData().catch(console.error);
104
+ }
105
+ __classPrivateFieldSet(this, _EarnController_selectedNetworkClientId, networkControllerState.selectedNetworkClientId, "f");
106
+ });
107
+ // Listen for account changes
108
+ this.messagingSystem.subscribe('AccountsController:selectedAccountChange', () => {
109
+ this.refreshPooledStakingData().catch(console.error);
110
+ });
111
+ }
112
+ /**
113
+ * Refreshes the pooled stakes data for the current account.
114
+ * Fetches updated stake information including lifetime rewards, assets, and exit requests
115
+ * from the staking API service and updates the state.
116
+ *
117
+ * @returns A promise that resolves when the stakes data has been updated
118
+ */
119
+ async refreshPooledStakes() {
120
+ const currentAccount = __classPrivateFieldGet(this, _EarnController_instances, "m", _EarnController_getCurrentAccount).call(this);
121
+ if (!currentAccount?.address) {
122
+ return;
123
+ }
124
+ const chainId = __classPrivateFieldGet(this, _EarnController_instances, "m", _EarnController_getCurrentChainId).call(this);
125
+ const { accounts, exchangeRate } = await __classPrivateFieldGet(this, _EarnController_stakingApiService, "f").getPooledStakes([currentAccount.address], chainId);
126
+ this.update((state) => {
127
+ state.pooled_staking.pooledStakes = accounts[0];
128
+ state.pooled_staking.exchangeRate = exchangeRate;
129
+ });
130
+ }
131
+ /**
132
+ * Refreshes the staking eligibility status for the current account.
133
+ * Updates the eligibility status in the controller state based on the location and address blocklist for compliance.
134
+ *
135
+ * @returns A promise that resolves when the eligibility status has been updated
136
+ */
137
+ async refreshStakingEligibility() {
138
+ const currentAccount = __classPrivateFieldGet(this, _EarnController_instances, "m", _EarnController_getCurrentAccount).call(this);
139
+ if (!currentAccount?.address) {
140
+ return;
141
+ }
142
+ const { eligible: isEligible } = await __classPrivateFieldGet(this, _EarnController_stakingApiService, "f").getPooledStakingEligibility([
143
+ currentAccount.address,
144
+ ]);
145
+ this.update((state) => {
146
+ state.pooled_staking.isEligible = isEligible;
147
+ });
148
+ }
149
+ /**
150
+ * Refreshes vault data for the current chain.
151
+ * Updates the vault data in the controller state including APY, capacity,
152
+ * fee percentage, total assets, and vault address.
153
+ *
154
+ * @returns A promise that resolves when the vault data has been updated
155
+ */
156
+ async refreshVaultData() {
157
+ const chainId = __classPrivateFieldGet(this, _EarnController_instances, "m", _EarnController_getCurrentChainId).call(this);
158
+ const vaultData = await __classPrivateFieldGet(this, _EarnController_stakingApiService, "f").getVaultData(chainId);
159
+ this.update((state) => {
160
+ state.pooled_staking.vaultData = vaultData;
161
+ });
162
+ }
163
+ /**
164
+ * Refreshes all pooled staking related data including stakes, eligibility, and vault data.
165
+ * This method allows partial success, meaning some data may update while other requests fail.
166
+ * All errors are collected and thrown as a single error message.
167
+ *
168
+ * @returns A promise that resolves when all possible data has been updated
169
+ * @throws {Error} If any of the refresh operations fail, with concatenated error messages
170
+ */
171
+ async refreshPooledStakingData() {
172
+ const errors = [];
173
+ await Promise.all([
174
+ this.refreshPooledStakes().catch((error) => {
175
+ errors.push(error);
176
+ }),
177
+ this.refreshStakingEligibility().catch((error) => {
178
+ errors.push(error);
179
+ }),
180
+ this.refreshVaultData().catch((error) => {
181
+ errors.push(error);
182
+ }),
183
+ ]);
184
+ if (errors.length > 0) {
185
+ throw new Error(`Failed to refresh some staking data: ${errors
186
+ .map((e) => e.message)
187
+ .join(', ')}`);
188
+ }
189
+ }
190
+ }
191
+ _EarnController_stakeSDK = new WeakMap(), _EarnController_selectedNetworkClientId = new WeakMap(), _EarnController_stakingApiService = new WeakMap(), _EarnController_instances = new WeakSet(), _EarnController_initializeSDK = function _EarnController_initializeSDK(networkClientId) {
192
+ const { selectedNetworkClientId } = networkClientId
193
+ ? { selectedNetworkClientId: networkClientId }
194
+ : this.messagingSystem.call('NetworkController:getState');
195
+ const networkClient = this.messagingSystem.call('NetworkController:getNetworkClientById', selectedNetworkClientId);
196
+ if (!networkClient?.provider) {
197
+ __classPrivateFieldSet(this, _EarnController_stakeSDK, null, "f");
198
+ return;
199
+ }
200
+ const provider = new Web3Provider(networkClient.provider);
201
+ const { chainId } = networkClient.configuration;
202
+ // Initialize appropriate contracts based on chainId
203
+ const config = {
204
+ chainId: convertHexToDecimal(chainId),
205
+ };
206
+ try {
207
+ __classPrivateFieldSet(this, _EarnController_stakeSDK, StakeSdk.create(config), "f");
208
+ __classPrivateFieldGet(this, _EarnController_stakeSDK, "f").pooledStakingContract.connectSignerOrProvider(provider);
209
+ }
210
+ catch (error) {
211
+ __classPrivateFieldSet(this, _EarnController_stakeSDK, null, "f");
212
+ // Only log unexpected errors, not unsupported chain errors
213
+ if (!(error instanceof Error &&
214
+ error.message.includes('Unsupported chainId'))) {
215
+ console.error('Stake SDK initialization failed:', error);
216
+ }
217
+ }
218
+ }, _EarnController_getCurrentAccount = function _EarnController_getCurrentAccount() {
219
+ return this.messagingSystem.call('AccountsController:getSelectedAccount');
220
+ }, _EarnController_getCurrentChainId = function _EarnController_getCurrentChainId() {
221
+ const { selectedNetworkClientId } = this.messagingSystem.call('NetworkController:getState');
222
+ const { configuration: { chainId }, } = this.messagingSystem.call('NetworkController:getNetworkClientById', selectedNetworkClientId);
223
+ return convertHexToDecimal(chainId);
224
+ };
225
+ //# sourceMappingURL=EarnController.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EarnController.mjs","sourceRoot":"","sources":["../src/EarnController.ts"],"names":[],"mappings":"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","sourcesContent":["import { Web3Provider } from '@ethersproject/providers';\nimport type {\n AccountsControllerGetSelectedAccountAction,\n AccountsControllerSelectedAccountChangeEvent,\n} from '@metamask/accounts-controller';\nimport type {\n ControllerGetStateAction,\n ControllerStateChangeEvent,\n RestrictedControllerMessenger,\n StateMetadata,\n} from '@metamask/base-controller';\nimport { BaseController } from '@metamask/base-controller';\nimport { convertHexToDecimal } from '@metamask/controller-utils';\nimport type { NetworkControllerStateChangeEvent } from '@metamask/network-controller';\nimport type {\n NetworkControllerGetNetworkClientByIdAction,\n NetworkControllerGetStateAction,\n} from '@metamask/network-controller';\nimport {\n StakeSdk,\n StakingApiService,\n type PooledStake,\n type StakeSdkConfig,\n type VaultData,\n} from '@metamask/stake-sdk';\n\nexport const controllerName = 'EarnController';\n\nexport type PooledStakingState = {\n pooledStakes: PooledStake;\n exchangeRate: string;\n vaultData: VaultData;\n isEligible: boolean;\n};\n\nexport type StablecoinLendingState = {\n vaults: StablecoinVault[];\n};\n\nexport type StablecoinVault = {\n symbol: string;\n name: string;\n chainId: number;\n tokenAddress: string;\n vaultAddress: string;\n currentAPY: string;\n supply: string;\n liquidity: string;\n};\n\n/**\n * Metadata for the EarnController.\n */\nconst earnControllerMetadata: StateMetadata<EarnControllerState> = {\n pooled_staking: {\n persist: true,\n anonymous: false,\n },\n stablecoin_lending: {\n persist: true,\n anonymous: false,\n },\n lastUpdated: {\n persist: false,\n anonymous: true,\n },\n};\n\n// === State Types ===\nexport type EarnControllerState = {\n pooled_staking: PooledStakingState;\n stablecoin_lending?: StablecoinLendingState;\n lastUpdated: number;\n};\n\n// === Default State ===\nconst DEFAULT_STABLECOIN_VAULT: StablecoinVault = {\n symbol: '',\n name: '',\n chainId: 0,\n tokenAddress: '',\n vaultAddress: '',\n currentAPY: '0',\n supply: '0',\n liquidity: '0',\n};\n\n/**\n * Gets the default state for the EarnController.\n *\n * @returns The default EarnController state.\n */\nexport function getDefaultEarnControllerState(): EarnControllerState {\n return {\n pooled_staking: {\n pooledStakes: {\n account: '',\n lifetimeRewards: '0',\n assets: '0',\n exitRequests: [],\n },\n exchangeRate: '1',\n vaultData: {\n apy: '0',\n capacity: '0',\n feePercent: 0,\n totalAssets: '0',\n vaultAddress: '0x0000000000000000000000000000000000000000',\n },\n isEligible: false,\n },\n stablecoin_lending: {\n vaults: [DEFAULT_STABLECOIN_VAULT],\n },\n lastUpdated: 0,\n };\n}\n\n// === MESSENGER ===\n\n/**\n * The action which can be used to retrieve the state of the EarnController.\n */\nexport type EarnControllerGetStateAction = ControllerGetStateAction<\n typeof controllerName,\n EarnControllerState\n>;\n\n/**\n * All actions that EarnController registers, to be called externally.\n */\nexport type EarnControllerActions = EarnControllerGetStateAction;\n\n/**\n * All actions that EarnController calls internally.\n */\nexport type AllowedActions =\n | NetworkControllerGetNetworkClientByIdAction\n | NetworkControllerGetStateAction\n | AccountsControllerGetSelectedAccountAction;\n\n/**\n * The event that EarnController publishes when updating state.\n */\nexport type EarnControllerStateChangeEvent = ControllerStateChangeEvent<\n typeof controllerName,\n EarnControllerState\n>;\n\n/**\n * All events that EarnController publishes, to be subscribed to externally.\n */\nexport type EarnControllerEvents = EarnControllerStateChangeEvent;\n\n/**\n * All events that EarnController subscribes to internally.\n */\nexport type AllowedEvents =\n | AccountsControllerSelectedAccountChangeEvent\n | NetworkControllerStateChangeEvent;\n\n/**\n * The messenger which is restricted to actions and events accessed by\n * EarnController.\n */\nexport type EarnControllerMessenger = RestrictedControllerMessenger<\n typeof controllerName,\n EarnControllerActions | AllowedActions,\n EarnControllerEvents | AllowedEvents,\n AllowedActions['type'],\n AllowedEvents['type']\n>;\n\n// === CONTROLLER DEFINITION ===\n\n/**\n * EarnController manages DeFi earning opportunities across different protocols and chains.\n */\nexport class EarnController extends BaseController<\n typeof controllerName,\n EarnControllerState,\n EarnControllerMessenger\n> {\n #stakeSDK: StakeSdk | null = null;\n\n #selectedNetworkClientId?: string;\n\n readonly #stakingApiService: StakingApiService = new StakingApiService();\n\n constructor({\n messenger,\n state = {},\n }: {\n messenger: EarnControllerMessenger;\n state?: Partial<EarnControllerState>;\n }) {\n super({\n name: controllerName,\n metadata: earnControllerMetadata,\n messenger,\n state: {\n ...getDefaultEarnControllerState(),\n ...state,\n },\n });\n\n this.#initializeSDK();\n this.refreshPooledStakingData().catch(console.error);\n\n const { selectedNetworkClientId } = this.messagingSystem.call(\n 'NetworkController:getState',\n );\n this.#selectedNetworkClientId = selectedNetworkClientId;\n\n this.messagingSystem.subscribe(\n 'NetworkController:stateChange',\n (networkControllerState) => {\n if (\n networkControllerState.selectedNetworkClientId !==\n this.#selectedNetworkClientId\n ) {\n this.#initializeSDK(networkControllerState.selectedNetworkClientId);\n this.refreshPooledStakingData().catch(console.error);\n }\n this.#selectedNetworkClientId =\n networkControllerState.selectedNetworkClientId;\n },\n );\n\n // Listen for account changes\n this.messagingSystem.subscribe(\n 'AccountsController:selectedAccountChange',\n () => {\n this.refreshPooledStakingData().catch(console.error);\n },\n );\n }\n\n #initializeSDK(networkClientId?: string) {\n const { selectedNetworkClientId } = networkClientId\n ? { selectedNetworkClientId: networkClientId }\n : this.messagingSystem.call('NetworkController:getState');\n\n const networkClient = this.messagingSystem.call(\n 'NetworkController:getNetworkClientById',\n selectedNetworkClientId,\n );\n\n if (!networkClient?.provider) {\n this.#stakeSDK = null;\n return;\n }\n\n const provider = new Web3Provider(networkClient.provider);\n const { chainId } = networkClient.configuration;\n\n // Initialize appropriate contracts based on chainId\n const config: StakeSdkConfig = {\n chainId: convertHexToDecimal(chainId),\n };\n\n try {\n this.#stakeSDK = StakeSdk.create(config);\n this.#stakeSDK.pooledStakingContract.connectSignerOrProvider(provider);\n } catch (error) {\n this.#stakeSDK = null;\n // Only log unexpected errors, not unsupported chain errors\n if (\n !(\n error instanceof Error &&\n error.message.includes('Unsupported chainId')\n )\n ) {\n console.error('Stake SDK initialization failed:', error);\n }\n }\n }\n\n #getCurrentAccount() {\n return this.messagingSystem.call('AccountsController:getSelectedAccount');\n }\n\n #getCurrentChainId(): number {\n const { selectedNetworkClientId } = this.messagingSystem.call(\n 'NetworkController:getState',\n );\n const {\n configuration: { chainId },\n } = this.messagingSystem.call(\n 'NetworkController:getNetworkClientById',\n selectedNetworkClientId,\n );\n return convertHexToDecimal(chainId);\n }\n\n /**\n * Refreshes the pooled stakes data for the current account.\n * Fetches updated stake information including lifetime rewards, assets, and exit requests\n * from the staking API service and updates the state.\n *\n * @returns A promise that resolves when the stakes data has been updated\n */\n async refreshPooledStakes(): Promise<void> {\n const currentAccount = this.#getCurrentAccount();\n if (!currentAccount?.address) {\n return;\n }\n\n const chainId = this.#getCurrentChainId();\n\n const { accounts, exchangeRate } =\n await this.#stakingApiService.getPooledStakes(\n [currentAccount.address],\n chainId,\n );\n\n this.update((state) => {\n state.pooled_staking.pooledStakes = accounts[0];\n state.pooled_staking.exchangeRate = exchangeRate;\n });\n }\n\n /**\n * Refreshes the staking eligibility status for the current account.\n * Updates the eligibility status in the controller state based on the location and address blocklist for compliance.\n *\n * @returns A promise that resolves when the eligibility status has been updated\n */\n async refreshStakingEligibility(): Promise<void> {\n const currentAccount = this.#getCurrentAccount();\n if (!currentAccount?.address) {\n return;\n }\n\n const { eligible: isEligible } =\n await this.#stakingApiService.getPooledStakingEligibility([\n currentAccount.address,\n ]);\n\n this.update((state) => {\n state.pooled_staking.isEligible = isEligible;\n });\n }\n\n /**\n * Refreshes vault data for the current chain.\n * Updates the vault data in the controller state including APY, capacity,\n * fee percentage, total assets, and vault address.\n *\n * @returns A promise that resolves when the vault data has been updated\n */\n async refreshVaultData(): Promise<void> {\n const chainId = this.#getCurrentChainId();\n const vaultData = await this.#stakingApiService.getVaultData(chainId);\n\n this.update((state) => {\n state.pooled_staking.vaultData = vaultData;\n });\n }\n\n /**\n * Refreshes all pooled staking related data including stakes, eligibility, and vault data.\n * This method allows partial success, meaning some data may update while other requests fail.\n * All errors are collected and thrown as a single error message.\n *\n * @returns A promise that resolves when all possible data has been updated\n * @throws {Error} If any of the refresh operations fail, with concatenated error messages\n */\n async refreshPooledStakingData(): Promise<void> {\n const errors: Error[] = [];\n\n await Promise.all([\n this.refreshPooledStakes().catch((error) => {\n errors.push(error);\n }),\n this.refreshStakingEligibility().catch((error) => {\n errors.push(error);\n }),\n this.refreshVaultData().catch((error) => {\n errors.push(error);\n }),\n ]);\n\n if (errors.length > 0) {\n throw new Error(\n `Failed to refresh some staking data: ${errors\n .map((e) => e.message)\n .join(', ')}`,\n );\n }\n }\n}\n"]}
package/dist/index.cjs ADDED
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.EarnController = exports.getDefaultEarnControllerState = exports.controllerName = void 0;
4
+ var EarnController_1 = require("./EarnController.cjs");
5
+ Object.defineProperty(exports, "controllerName", { enumerable: true, get: function () { return EarnController_1.controllerName; } });
6
+ Object.defineProperty(exports, "getDefaultEarnControllerState", { enumerable: true, get: function () { return EarnController_1.getDefaultEarnControllerState; } });
7
+ Object.defineProperty(exports, "EarnController", { enumerable: true, get: function () { return EarnController_1.EarnController; } });
8
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAYA,uDAI0B;AAHxB,gHAAA,cAAc,OAAA;AACd,+HAAA,6BAA6B,OAAA;AAC7B,gHAAA,cAAc,OAAA","sourcesContent":["export type {\n PooledStakingState,\n StablecoinLendingState,\n StablecoinVault,\n EarnControllerState,\n EarnControllerGetStateAction,\n EarnControllerStateChangeEvent,\n EarnControllerActions,\n EarnControllerEvents,\n EarnControllerMessenger,\n} from './EarnController';\n\nexport {\n controllerName,\n getDefaultEarnControllerState,\n EarnController,\n} from './EarnController';\n"]}
@@ -0,0 +1,3 @@
1
+ export type { PooledStakingState, StablecoinLendingState, StablecoinVault, EarnControllerState, EarnControllerGetStateAction, EarnControllerStateChangeEvent, EarnControllerActions, EarnControllerEvents, EarnControllerMessenger, } from "./EarnController.cjs";
2
+ export { controllerName, getDefaultEarnControllerState, EarnController, } from "./EarnController.cjs";
3
+ //# sourceMappingURL=index.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.cts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,kBAAkB,EAClB,sBAAsB,EACtB,eAAe,EACf,mBAAmB,EACnB,4BAA4B,EAC5B,8BAA8B,EAC9B,qBAAqB,EACrB,oBAAoB,EACpB,uBAAuB,GACxB,6BAAyB;AAE1B,OAAO,EACL,cAAc,EACd,6BAA6B,EAC7B,cAAc,GACf,6BAAyB"}
@@ -0,0 +1,3 @@
1
+ export type { PooledStakingState, StablecoinLendingState, StablecoinVault, EarnControllerState, EarnControllerGetStateAction, EarnControllerStateChangeEvent, EarnControllerActions, EarnControllerEvents, EarnControllerMessenger, } from "./EarnController.mjs";
2
+ export { controllerName, getDefaultEarnControllerState, EarnController, } from "./EarnController.mjs";
3
+ //# sourceMappingURL=index.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.mts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,kBAAkB,EAClB,sBAAsB,EACtB,eAAe,EACf,mBAAmB,EACnB,4BAA4B,EAC5B,8BAA8B,EAC9B,qBAAqB,EACrB,oBAAoB,EACpB,uBAAuB,GACxB,6BAAyB;AAE1B,OAAO,EACL,cAAc,EACd,6BAA6B,EAC7B,cAAc,GACf,6BAAyB"}
package/dist/index.mjs ADDED
@@ -0,0 +1,2 @@
1
+ export { controllerName, getDefaultEarnControllerState, EarnController } from "./EarnController.mjs";
2
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAYA,OAAO,EACL,cAAc,EACd,6BAA6B,EAC7B,cAAc,EACf,6BAAyB","sourcesContent":["export type {\n PooledStakingState,\n StablecoinLendingState,\n StablecoinVault,\n EarnControllerState,\n EarnControllerGetStateAction,\n EarnControllerStateChangeEvent,\n EarnControllerActions,\n EarnControllerEvents,\n EarnControllerMessenger,\n} from './EarnController';\n\nexport {\n controllerName,\n getDefaultEarnControllerState,\n EarnController,\n} from './EarnController';\n"]}
package/package.json ADDED
@@ -0,0 +1,78 @@
1
+ {
2
+ "name": "@metamask-previews/earn-controller",
3
+ "version": "0.0.0-preview-43593c4e",
4
+ "description": "Manages state for earning features and coordinates interactions between staking services, SDK integrations, and other controllers to enable users to participate in various earning opportunities",
5
+ "keywords": [
6
+ "MetaMask",
7
+ "Ethereum"
8
+ ],
9
+ "homepage": "https://github.com/MetaMask/core/tree/main/packages/earn-controller#readme",
10
+ "bugs": {
11
+ "url": "https://github.com/MetaMask/core/issues"
12
+ },
13
+ "repository": {
14
+ "type": "git",
15
+ "url": "https://github.com/MetaMask/core.git"
16
+ },
17
+ "license": "MIT",
18
+ "sideEffects": false,
19
+ "exports": {
20
+ ".": {
21
+ "import": {
22
+ "types": "./dist/index.d.mts",
23
+ "default": "./dist/index.mjs"
24
+ },
25
+ "require": {
26
+ "types": "./dist/index.d.cts",
27
+ "default": "./dist/index.cjs"
28
+ }
29
+ },
30
+ "./package.json": "./package.json"
31
+ },
32
+ "main": "./dist/index.cjs",
33
+ "types": "./dist/index.d.cts",
34
+ "files": [
35
+ "dist/"
36
+ ],
37
+ "scripts": {
38
+ "build": "ts-bridge --project tsconfig.build.json --verbose --clean --no-references",
39
+ "build:docs": "typedoc",
40
+ "changelog:validate": "../../scripts/validate-changelog.sh @metamask/earn-controller",
41
+ "changelog:update": "../../scripts/update-changelog.sh @metamask/earn-controller",
42
+ "publish:preview": "yarn npm publish --tag preview",
43
+ "test": "NODE_OPTIONS=--experimental-vm-modules jest --reporters=jest-silent-reporter",
44
+ "test:clean": "NODE_OPTIONS=--experimental-vm-modules jest --clearCache",
45
+ "test:verbose": "NODE_OPTIONS=--experimental-vm-modules jest --verbose",
46
+ "test:watch": "NODE_OPTIONS=--experimental-vm-modules jest --watch",
47
+ "since-latest-release": "../../scripts/since-latest-release.sh"
48
+ },
49
+ "dependencies": {
50
+ "@ethersproject/providers": "^5.7.0",
51
+ "@metamask/base-controller": "^7.1.1",
52
+ "@metamask/controller-utils": "^11.4.5",
53
+ "@metamask/stake-sdk": "^1.0.0"
54
+ },
55
+ "devDependencies": {
56
+ "@metamask/accounts-controller": "^22.0.0",
57
+ "@metamask/auto-changelog": "^3.4.4",
58
+ "@metamask/network-controller": "^22.1.1",
59
+ "@types/jest": "^27.4.1",
60
+ "deepmerge": "^4.2.2",
61
+ "jest": "^27.5.1",
62
+ "ts-jest": "^27.1.4",
63
+ "typedoc": "^0.24.8",
64
+ "typedoc-plugin-missing-exports": "^2.0.0",
65
+ "typescript": "~5.2.2"
66
+ },
67
+ "peerDependencies": {
68
+ "@metamask/accounts-controller": "^22.0.0",
69
+ "@metamask/network-controller": "^22.1.1"
70
+ },
71
+ "engines": {
72
+ "node": "^18.18 || >=20"
73
+ },
74
+ "publishConfig": {
75
+ "access": "public",
76
+ "registry": "https://registry.npmjs.org/"
77
+ }
78
+ }