@metamask-previews/bridge-status-controller 1.0.0-preview-dbdf1da

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.
Files changed (52) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/LICENSE +20 -0
  3. package/README.md +15 -0
  4. package/dist/bridge-status-controller.cjs +247 -0
  5. package/dist/bridge-status-controller.cjs.map +1 -0
  6. package/dist/bridge-status-controller.d.cts +44 -0
  7. package/dist/bridge-status-controller.d.cts.map +1 -0
  8. package/dist/bridge-status-controller.d.mts +44 -0
  9. package/dist/bridge-status-controller.d.mts.map +1 -0
  10. package/dist/bridge-status-controller.mjs +243 -0
  11. package/dist/bridge-status-controller.mjs.map +1 -0
  12. package/dist/constants.cjs +12 -0
  13. package/dist/constants.cjs.map +1 -0
  14. package/dist/constants.d.cts +10 -0
  15. package/dist/constants.d.cts.map +1 -0
  16. package/dist/constants.d.mts +10 -0
  17. package/dist/constants.d.mts.map +1 -0
  18. package/dist/constants.mjs +9 -0
  19. package/dist/constants.mjs.map +1 -0
  20. package/dist/index.cjs +17 -0
  21. package/dist/index.cjs.map +1 -0
  22. package/dist/index.d.cts +5 -0
  23. package/dist/index.d.cts.map +1 -0
  24. package/dist/index.d.mts +5 -0
  25. package/dist/index.d.mts.map +1 -0
  26. package/dist/index.mjs +5 -0
  27. package/dist/index.mjs.map +1 -0
  28. package/dist/types.cjs +42 -0
  29. package/dist/types.cjs.map +1 -0
  30. package/dist/types.d.cts +240 -0
  31. package/dist/types.d.cts.map +1 -0
  32. package/dist/types.d.mts +240 -0
  33. package/dist/types.d.mts.map +1 -0
  34. package/dist/types.mjs +39 -0
  35. package/dist/types.mjs.map +1 -0
  36. package/dist/utils/bridge-status.cjs +53 -0
  37. package/dist/utils/bridge-status.cjs.map +1 -0
  38. package/dist/utils/bridge-status.d.cts +10 -0
  39. package/dist/utils/bridge-status.d.cts.map +1 -0
  40. package/dist/utils/bridge-status.d.mts +10 -0
  41. package/dist/utils/bridge-status.d.mts.map +1 -0
  42. package/dist/utils/bridge-status.mjs +46 -0
  43. package/dist/utils/bridge-status.mjs.map +1 -0
  44. package/dist/utils/validators.cjs +161 -0
  45. package/dist/utils/validators.cjs.map +1 -0
  46. package/dist/utils/validators.d.cts +15 -0
  47. package/dist/utils/validators.d.cts.map +1 -0
  48. package/dist/utils/validators.d.mts +15 -0
  49. package/dist/utils/validators.d.mts.map +1 -0
  50. package/dist/utils/validators.mjs +155 -0
  51. package/dist/utils/validators.mjs.map +1 -0
  52. package/package.json +85 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,17 @@
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
+ ## [1.0.0]
11
+
12
+ ### Added
13
+
14
+ - Initial release ([#5317](https://github.com/MetaMask/core/pull/5317))
15
+
16
+ [Unreleased]: https://github.com/MetaMask/core/compare/@metamask/bridge-status-controller@1.0.0...HEAD
17
+ [1.0.0]: https://github.com/MetaMask/core/releases/tag/@metamask/bridge-status-controller@1.0.0
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/bridge-status-controller`
2
+
3
+ Manages bridge-related status fetching functionality for MetaMask.
4
+
5
+ ## Installation
6
+
7
+ `yarn add @metamask/bridge-status-controller`
8
+
9
+ or
10
+
11
+ `npm install @metamask/bridge-status-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,247 @@
1
+ "use strict";
2
+ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
3
+ if (kind === "m") throw new TypeError("Private method is not writable");
4
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
5
+ 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");
6
+ return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
7
+ };
8
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
9
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
10
+ 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");
11
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
12
+ };
13
+ var _BridgeStatusController_instances, _BridgeStatusController_pollingTokensByTxMetaId, _BridgeStatusController_clientId, _BridgeStatusController_fetchFn, _BridgeStatusController_restartPollingForIncompleteHistoryItems, _BridgeStatusController_getSelectedAccount, _BridgeStatusController_fetchBridgeTxStatus, _BridgeStatusController_getSrcTxHash, _BridgeStatusController_updateSrcTxHash, _BridgeStatusController_wipeBridgeStatusByChainId;
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.BridgeStatusController = void 0;
16
+ const polling_controller_1 = require("@metamask/polling-controller");
17
+ const utils_1 = require("@metamask/utils");
18
+ const constants_1 = require("./constants.cjs");
19
+ const types_1 = require("./types.cjs");
20
+ const bridge_status_1 = require("./utils/bridge-status.cjs");
21
+ const metadata = {
22
+ // We want to persist the bridge status state so that we can show the proper data for the Activity list
23
+ // basically match the behavior of TransactionController
24
+ bridgeStatusState: {
25
+ persist: true,
26
+ anonymous: false,
27
+ },
28
+ };
29
+ class BridgeStatusController extends (0, polling_controller_1.StaticIntervalPollingController)() {
30
+ constructor({ messenger, state, clientId, fetchFn, }) {
31
+ super({
32
+ name: constants_1.BRIDGE_STATUS_CONTROLLER_NAME,
33
+ metadata,
34
+ messenger,
35
+ // Restore the persisted state
36
+ state: {
37
+ ...state,
38
+ bridgeStatusState: {
39
+ ...constants_1.DEFAULT_BRIDGE_STATUS_STATE,
40
+ ...state?.bridgeStatusState,
41
+ },
42
+ },
43
+ });
44
+ _BridgeStatusController_instances.add(this);
45
+ _BridgeStatusController_pollingTokensByTxMetaId.set(this, {});
46
+ _BridgeStatusController_clientId.set(this, void 0);
47
+ _BridgeStatusController_fetchFn.set(this, void 0);
48
+ this.resetState = () => {
49
+ this.update((state) => {
50
+ state.bridgeStatusState = {
51
+ ...constants_1.DEFAULT_BRIDGE_STATUS_STATE,
52
+ };
53
+ });
54
+ };
55
+ this.wipeBridgeStatus = ({ address, ignoreNetwork, }) => {
56
+ // Wipe all networks for this address
57
+ if (ignoreNetwork) {
58
+ this.update((state) => {
59
+ state.bridgeStatusState = {
60
+ ...constants_1.DEFAULT_BRIDGE_STATUS_STATE,
61
+ };
62
+ });
63
+ }
64
+ else {
65
+ const { selectedNetworkClientId } = this.messagingSystem.call('NetworkController:getState');
66
+ const selectedNetworkClient = this.messagingSystem.call('NetworkController:getNetworkClientById', selectedNetworkClientId);
67
+ const selectedChainId = selectedNetworkClient.configuration.chainId;
68
+ __classPrivateFieldGet(this, _BridgeStatusController_wipeBridgeStatusByChainId, "f").call(this, address, selectedChainId);
69
+ }
70
+ };
71
+ _BridgeStatusController_restartPollingForIncompleteHistoryItems.set(this, () => {
72
+ // Check for historyItems that do not have a status of complete and restart polling
73
+ const { bridgeStatusState } = this.state;
74
+ const historyItems = Object.values(bridgeStatusState.txHistory);
75
+ const incompleteHistoryItems = historyItems
76
+ .filter((historyItem) => historyItem.status.status === types_1.StatusTypes.PENDING ||
77
+ historyItem.status.status === types_1.StatusTypes.UNKNOWN)
78
+ .filter((historyItem) => {
79
+ // Check if we are already polling this tx, if so, skip restarting polling for that
80
+ const srcTxMetaId = historyItem.txMetaId;
81
+ const pollingToken = __classPrivateFieldGet(this, _BridgeStatusController_pollingTokensByTxMetaId, "f")[srcTxMetaId];
82
+ return !pollingToken;
83
+ });
84
+ incompleteHistoryItems.forEach((historyItem) => {
85
+ const bridgeTxMetaId = historyItem.txMetaId;
86
+ // We manually call startPolling() here rather than go through startPollingForBridgeTxStatus()
87
+ // because we don't want to overwrite the existing historyItem in state
88
+ __classPrivateFieldGet(this, _BridgeStatusController_pollingTokensByTxMetaId, "f")[bridgeTxMetaId] = this.startPolling({
89
+ bridgeTxMetaId,
90
+ });
91
+ });
92
+ });
93
+ this.startPollingForBridgeTxStatus = (startPollingForBridgeTxStatusArgs) => {
94
+ const { bridgeTxMeta, statusRequest, quoteResponse, startTime, slippagePercentage, initialDestAssetBalance, targetContractAddress, } = startPollingForBridgeTxStatusArgs;
95
+ const { address: account } = __classPrivateFieldGet(this, _BridgeStatusController_instances, "m", _BridgeStatusController_getSelectedAccount).call(this);
96
+ // Write all non-status fields to state so we can reference the quote in Activity list without the Bridge API
97
+ // We know it's in progress but not the exact status yet
98
+ const txHistoryItem = {
99
+ txMetaId: bridgeTxMeta.id,
100
+ quote: quoteResponse.quote,
101
+ startTime,
102
+ estimatedProcessingTimeInSeconds: quoteResponse.estimatedProcessingTimeInSeconds,
103
+ slippagePercentage,
104
+ pricingData: {
105
+ amountSent: quoteResponse.sentAmount.amount,
106
+ amountSentInUsd: quoteResponse.sentAmount.usd ?? undefined,
107
+ quotedGasInUsd: quoteResponse.gasFee.usd ?? undefined,
108
+ quotedReturnInUsd: quoteResponse.toTokenAmount.usd ?? undefined,
109
+ },
110
+ initialDestAssetBalance,
111
+ targetContractAddress,
112
+ account,
113
+ status: {
114
+ // We always have a PENDING status when we start polling for a tx, don't need the Bridge API for that
115
+ // Also we know the bare minimum fields for status at this point in time
116
+ status: types_1.StatusTypes.PENDING,
117
+ srcChain: {
118
+ chainId: statusRequest.srcChainId,
119
+ txHash: statusRequest.srcTxHash,
120
+ },
121
+ },
122
+ hasApprovalTx: Boolean(quoteResponse.approval),
123
+ };
124
+ this.update((state) => {
125
+ // Use the txMeta.id as the key so we can reference the txMeta in TransactionController
126
+ state.bridgeStatusState.txHistory[bridgeTxMeta.id] = txHistoryItem;
127
+ });
128
+ __classPrivateFieldGet(this, _BridgeStatusController_pollingTokensByTxMetaId, "f")[bridgeTxMeta.id] = this.startPolling({
129
+ bridgeTxMetaId: bridgeTxMeta.id,
130
+ });
131
+ };
132
+ // This will be called after you call this.startPolling()
133
+ // The args passed in are the args you passed in to startPolling()
134
+ this._executePoll = async (pollingInput) => {
135
+ await __classPrivateFieldGet(this, _BridgeStatusController_fetchBridgeTxStatus, "f").call(this, pollingInput);
136
+ };
137
+ _BridgeStatusController_fetchBridgeTxStatus.set(this, async ({ bridgeTxMetaId, }) => {
138
+ const { bridgeStatusState } = this.state;
139
+ try {
140
+ // We try here because we receive 500 errors from Bridge API if we try to fetch immediately after submitting the source tx
141
+ // Oddly mostly happens on Optimism, never on Arbitrum. By the 2nd fetch, the Bridge API responds properly.
142
+ // Also srcTxHash may not be available immediately for STX, so we don't want to fetch in those cases
143
+ const historyItem = bridgeStatusState.txHistory[bridgeTxMetaId];
144
+ const srcTxHash = __classPrivateFieldGet(this, _BridgeStatusController_getSrcTxHash, "f").call(this, bridgeTxMetaId);
145
+ if (!srcTxHash) {
146
+ return;
147
+ }
148
+ __classPrivateFieldGet(this, _BridgeStatusController_updateSrcTxHash, "f").call(this, bridgeTxMetaId, srcTxHash);
149
+ const statusRequest = (0, bridge_status_1.getStatusRequestWithSrcTxHash)(historyItem.quote, srcTxHash);
150
+ const status = await (0, bridge_status_1.fetchBridgeTxStatus)(statusRequest, __classPrivateFieldGet(this, _BridgeStatusController_clientId, "f"), __classPrivateFieldGet(this, _BridgeStatusController_fetchFn, "f"));
151
+ const newBridgeHistoryItem = {
152
+ ...historyItem,
153
+ status,
154
+ completionTime: status.status === types_1.StatusTypes.COMPLETE ||
155
+ status.status === types_1.StatusTypes.FAILED
156
+ ? Date.now()
157
+ : undefined, // TODO make this more accurate by looking up dest txHash block time
158
+ };
159
+ // No need to purge these on network change or account change, TransactionController does not purge either.
160
+ // TODO In theory we can skip checking status if it's not the current account/network
161
+ // we need to keep track of the account that this is associated with as well so that we don't show it in Activity list for other accounts
162
+ // First stab at this will not stop polling when you are on a different account
163
+ this.update((state) => {
164
+ state.bridgeStatusState.txHistory[bridgeTxMetaId] =
165
+ newBridgeHistoryItem;
166
+ });
167
+ const pollingToken = __classPrivateFieldGet(this, _BridgeStatusController_pollingTokensByTxMetaId, "f")[bridgeTxMetaId];
168
+ if ((status.status === types_1.StatusTypes.COMPLETE ||
169
+ status.status === types_1.StatusTypes.FAILED) &&
170
+ pollingToken) {
171
+ this.stopPollingByPollingToken(pollingToken);
172
+ if (status.status === types_1.StatusTypes.COMPLETE) {
173
+ this.messagingSystem.publish(`${constants_1.BRIDGE_STATUS_CONTROLLER_NAME}:bridgeTransactionComplete`, { bridgeHistoryItem: newBridgeHistoryItem });
174
+ }
175
+ if (status.status === types_1.StatusTypes.FAILED) {
176
+ this.messagingSystem.publish(`${constants_1.BRIDGE_STATUS_CONTROLLER_NAME}:bridgeTransactionFailed`, { bridgeHistoryItem: newBridgeHistoryItem });
177
+ }
178
+ }
179
+ }
180
+ catch (e) {
181
+ console.log('Failed to fetch bridge tx status', e);
182
+ }
183
+ });
184
+ _BridgeStatusController_getSrcTxHash.set(this, (bridgeTxMetaId) => {
185
+ const { bridgeStatusState } = this.state;
186
+ // Prefer the srcTxHash from bridgeStatusState so we don't have to l ook up in TransactionController
187
+ // But it is possible to have bridgeHistoryItem in state without the srcTxHash yet when it is an STX
188
+ const srcTxHash = bridgeStatusState.txHistory[bridgeTxMetaId].status.srcChain.txHash;
189
+ if (srcTxHash) {
190
+ return srcTxHash;
191
+ }
192
+ // Look up in TransactionController if txMeta has been updated with the srcTxHash
193
+ const txControllerState = this.messagingSystem.call('TransactionController:getState');
194
+ const txMeta = txControllerState.transactions.find((tx) => tx.id === bridgeTxMetaId);
195
+ return txMeta?.hash;
196
+ });
197
+ _BridgeStatusController_updateSrcTxHash.set(this, (bridgeTxMetaId, srcTxHash) => {
198
+ const { bridgeStatusState } = this.state;
199
+ if (bridgeStatusState.txHistory[bridgeTxMetaId].status.srcChain.txHash) {
200
+ return;
201
+ }
202
+ this.update((state) => {
203
+ state.bridgeStatusState.txHistory[bridgeTxMetaId].status.srcChain.txHash =
204
+ srcTxHash;
205
+ });
206
+ });
207
+ // Wipes the bridge status for the given address and chainId
208
+ // Will match only source chainId to the selectedChainId
209
+ _BridgeStatusController_wipeBridgeStatusByChainId.set(this, (address, selectedChainId) => {
210
+ const sourceTxMetaIdsToDelete = Object.keys(this.state.bridgeStatusState.txHistory).filter((txMetaId) => {
211
+ const bridgeHistoryItem = this.state.bridgeStatusState.txHistory[txMetaId];
212
+ const hexSourceChainId = (0, utils_1.numberToHex)(bridgeHistoryItem.quote.srcChainId);
213
+ return (bridgeHistoryItem.account === address &&
214
+ hexSourceChainId === selectedChainId);
215
+ });
216
+ sourceTxMetaIdsToDelete.forEach((sourceTxMetaId) => {
217
+ const pollingToken = __classPrivateFieldGet(this, _BridgeStatusController_pollingTokensByTxMetaId, "f")[sourceTxMetaId];
218
+ if (pollingToken) {
219
+ this.stopPollingByPollingToken(__classPrivateFieldGet(this, _BridgeStatusController_pollingTokensByTxMetaId, "f")[sourceTxMetaId]);
220
+ }
221
+ });
222
+ this.update((state) => {
223
+ state.bridgeStatusState.txHistory = sourceTxMetaIdsToDelete.reduce((acc, sourceTxMetaId) => {
224
+ delete acc[sourceTxMetaId];
225
+ return acc;
226
+ }, state.bridgeStatusState.txHistory);
227
+ });
228
+ });
229
+ __classPrivateFieldSet(this, _BridgeStatusController_clientId, clientId, "f");
230
+ __classPrivateFieldSet(this, _BridgeStatusController_fetchFn, fetchFn, "f");
231
+ // Register action handlers
232
+ this.messagingSystem.registerActionHandler(`${constants_1.BRIDGE_STATUS_CONTROLLER_NAME}:startPollingForBridgeTxStatus`, this.startPollingForBridgeTxStatus.bind(this));
233
+ this.messagingSystem.registerActionHandler(`${constants_1.BRIDGE_STATUS_CONTROLLER_NAME}:wipeBridgeStatus`, this.wipeBridgeStatus.bind(this));
234
+ this.messagingSystem.registerActionHandler(`${constants_1.BRIDGE_STATUS_CONTROLLER_NAME}:resetState`, this.resetState.bind(this));
235
+ // Set interval
236
+ this.setIntervalLength(constants_1.REFRESH_INTERVAL_MS);
237
+ // If you close the extension, but keep the browser open, the polling continues
238
+ // If you close the browser, the polling stops
239
+ // Check for historyItems that do not have a status of complete and restart polling
240
+ __classPrivateFieldGet(this, _BridgeStatusController_restartPollingForIncompleteHistoryItems, "f").call(this);
241
+ }
242
+ }
243
+ exports.BridgeStatusController = BridgeStatusController;
244
+ _BridgeStatusController_pollingTokensByTxMetaId = new WeakMap(), _BridgeStatusController_clientId = new WeakMap(), _BridgeStatusController_fetchFn = new WeakMap(), _BridgeStatusController_restartPollingForIncompleteHistoryItems = new WeakMap(), _BridgeStatusController_fetchBridgeTxStatus = new WeakMap(), _BridgeStatusController_getSrcTxHash = new WeakMap(), _BridgeStatusController_updateSrcTxHash = new WeakMap(), _BridgeStatusController_wipeBridgeStatusByChainId = new WeakMap(), _BridgeStatusController_instances = new WeakSet(), _BridgeStatusController_getSelectedAccount = function _BridgeStatusController_getSelectedAccount() {
245
+ return this.messagingSystem.call('AccountsController:getSelectedAccount');
246
+ };
247
+ //# sourceMappingURL=bridge-status-controller.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bridge-status-controller.cjs","sourceRoot":"","sources":["../src/bridge-status-controller.ts"],"names":[],"mappings":"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","sourcesContent":["import type { StateMetadata } from '@metamask/base-controller';\nimport type { BridgeClientId } from '@metamask/bridge-controller';\nimport { StaticIntervalPollingController } from '@metamask/polling-controller';\nimport { numberToHex, type Hex } from '@metamask/utils';\n\nimport {\n BRIDGE_STATUS_CONTROLLER_NAME,\n DEFAULT_BRIDGE_STATUS_STATE,\n REFRESH_INTERVAL_MS,\n} from './constants';\nimport { StatusTypes, type BridgeStatusControllerMessenger } from './types';\nimport type {\n BridgeStatusControllerState,\n StartPollingForBridgeTxStatusArgsSerialized,\n BridgeStatusState,\n FetchFunction,\n} from './types';\nimport {\n fetchBridgeTxStatus,\n getStatusRequestWithSrcTxHash,\n} from './utils/bridge-status';\n\nconst metadata: StateMetadata<BridgeStatusControllerState> = {\n // We want to persist the bridge status state so that we can show the proper data for the Activity list\n // basically match the behavior of TransactionController\n bridgeStatusState: {\n persist: true,\n anonymous: false,\n },\n};\n\n/** The input to start polling for the {@link BridgeStatusController} */\ntype BridgeStatusPollingInput = FetchBridgeTxStatusArgs;\n\ntype SrcTxMetaId = string;\nexport type FetchBridgeTxStatusArgs = {\n bridgeTxMetaId: string;\n};\nexport class BridgeStatusController extends StaticIntervalPollingController<BridgeStatusPollingInput>()<\n typeof BRIDGE_STATUS_CONTROLLER_NAME,\n BridgeStatusControllerState,\n BridgeStatusControllerMessenger\n> {\n #pollingTokensByTxMetaId: Record<SrcTxMetaId, string> = {};\n\n readonly #clientId: BridgeClientId;\n\n readonly #fetchFn: FetchFunction;\n\n constructor({\n messenger,\n state,\n clientId,\n fetchFn,\n }: {\n messenger: BridgeStatusControllerMessenger;\n state?: { bridgeStatusState?: Partial<BridgeStatusState> };\n clientId: BridgeClientId;\n fetchFn: FetchFunction;\n }) {\n super({\n name: BRIDGE_STATUS_CONTROLLER_NAME,\n metadata,\n messenger,\n // Restore the persisted state\n state: {\n ...state,\n bridgeStatusState: {\n ...DEFAULT_BRIDGE_STATUS_STATE,\n ...state?.bridgeStatusState,\n },\n },\n });\n\n this.#clientId = clientId;\n this.#fetchFn = fetchFn;\n\n // Register action handlers\n this.messagingSystem.registerActionHandler(\n `${BRIDGE_STATUS_CONTROLLER_NAME}:startPollingForBridgeTxStatus`,\n this.startPollingForBridgeTxStatus.bind(this),\n );\n this.messagingSystem.registerActionHandler(\n `${BRIDGE_STATUS_CONTROLLER_NAME}:wipeBridgeStatus`,\n this.wipeBridgeStatus.bind(this),\n );\n this.messagingSystem.registerActionHandler(\n `${BRIDGE_STATUS_CONTROLLER_NAME}:resetState`,\n this.resetState.bind(this),\n );\n\n // Set interval\n this.setIntervalLength(REFRESH_INTERVAL_MS);\n\n // If you close the extension, but keep the browser open, the polling continues\n // If you close the browser, the polling stops\n // Check for historyItems that do not have a status of complete and restart polling\n this.#restartPollingForIncompleteHistoryItems();\n }\n\n resetState = () => {\n this.update((state) => {\n state.bridgeStatusState = {\n ...DEFAULT_BRIDGE_STATUS_STATE,\n };\n });\n };\n\n wipeBridgeStatus = ({\n address,\n ignoreNetwork,\n }: {\n address: string;\n ignoreNetwork: boolean;\n }) => {\n // Wipe all networks for this address\n if (ignoreNetwork) {\n this.update((state) => {\n state.bridgeStatusState = {\n ...DEFAULT_BRIDGE_STATUS_STATE,\n };\n });\n } else {\n const { selectedNetworkClientId } = this.messagingSystem.call(\n 'NetworkController:getState',\n );\n const selectedNetworkClient = this.messagingSystem.call(\n 'NetworkController:getNetworkClientById',\n selectedNetworkClientId,\n );\n const selectedChainId = selectedNetworkClient.configuration.chainId;\n\n this.#wipeBridgeStatusByChainId(address, selectedChainId);\n }\n };\n\n readonly #restartPollingForIncompleteHistoryItems = () => {\n // Check for historyItems that do not have a status of complete and restart polling\n const { bridgeStatusState } = this.state;\n const historyItems = Object.values(bridgeStatusState.txHistory);\n const incompleteHistoryItems = historyItems\n .filter(\n (historyItem) =>\n historyItem.status.status === StatusTypes.PENDING ||\n historyItem.status.status === StatusTypes.UNKNOWN,\n )\n .filter((historyItem) => {\n // Check if we are already polling this tx, if so, skip restarting polling for that\n const srcTxMetaId = historyItem.txMetaId;\n const pollingToken = this.#pollingTokensByTxMetaId[srcTxMetaId];\n return !pollingToken;\n });\n\n incompleteHistoryItems.forEach((historyItem) => {\n const bridgeTxMetaId = historyItem.txMetaId;\n\n // We manually call startPolling() here rather than go through startPollingForBridgeTxStatus()\n // because we don't want to overwrite the existing historyItem in state\n this.#pollingTokensByTxMetaId[bridgeTxMetaId] = this.startPolling({\n bridgeTxMetaId,\n });\n });\n };\n\n startPollingForBridgeTxStatus = (\n startPollingForBridgeTxStatusArgs: StartPollingForBridgeTxStatusArgsSerialized,\n ) => {\n const {\n bridgeTxMeta,\n statusRequest,\n quoteResponse,\n startTime,\n slippagePercentage,\n initialDestAssetBalance,\n targetContractAddress,\n } = startPollingForBridgeTxStatusArgs;\n const { address: account } = this.#getSelectedAccount();\n\n // Write all non-status fields to state so we can reference the quote in Activity list without the Bridge API\n // We know it's in progress but not the exact status yet\n const txHistoryItem = {\n txMetaId: bridgeTxMeta.id,\n quote: quoteResponse.quote,\n startTime,\n estimatedProcessingTimeInSeconds:\n quoteResponse.estimatedProcessingTimeInSeconds,\n slippagePercentage,\n pricingData: {\n amountSent: quoteResponse.sentAmount.amount,\n amountSentInUsd: quoteResponse.sentAmount.usd ?? undefined,\n quotedGasInUsd: quoteResponse.gasFee.usd ?? undefined,\n quotedReturnInUsd: quoteResponse.toTokenAmount.usd ?? undefined,\n },\n initialDestAssetBalance,\n targetContractAddress,\n account,\n status: {\n // We always have a PENDING status when we start polling for a tx, don't need the Bridge API for that\n // Also we know the bare minimum fields for status at this point in time\n status: StatusTypes.PENDING,\n srcChain: {\n chainId: statusRequest.srcChainId,\n txHash: statusRequest.srcTxHash,\n },\n },\n hasApprovalTx: Boolean(quoteResponse.approval),\n };\n this.update((state) => {\n // Use the txMeta.id as the key so we can reference the txMeta in TransactionController\n state.bridgeStatusState.txHistory[bridgeTxMeta.id] = txHistoryItem;\n });\n\n this.#pollingTokensByTxMetaId[bridgeTxMeta.id] = this.startPolling({\n bridgeTxMetaId: bridgeTxMeta.id,\n });\n };\n\n // This will be called after you call this.startPolling()\n // The args passed in are the args you passed in to startPolling()\n _executePoll = async (pollingInput: BridgeStatusPollingInput) => {\n await this.#fetchBridgeTxStatus(pollingInput);\n };\n\n #getSelectedAccount() {\n return this.messagingSystem.call('AccountsController:getSelectedAccount');\n }\n\n readonly #fetchBridgeTxStatus = async ({\n bridgeTxMetaId,\n }: FetchBridgeTxStatusArgs) => {\n const { bridgeStatusState } = this.state;\n\n try {\n // We try here because we receive 500 errors from Bridge API if we try to fetch immediately after submitting the source tx\n // Oddly mostly happens on Optimism, never on Arbitrum. By the 2nd fetch, the Bridge API responds properly.\n // Also srcTxHash may not be available immediately for STX, so we don't want to fetch in those cases\n const historyItem = bridgeStatusState.txHistory[bridgeTxMetaId];\n const srcTxHash = this.#getSrcTxHash(bridgeTxMetaId);\n if (!srcTxHash) {\n return;\n }\n\n this.#updateSrcTxHash(bridgeTxMetaId, srcTxHash);\n\n const statusRequest = getStatusRequestWithSrcTxHash(\n historyItem.quote,\n srcTxHash,\n );\n const status = await fetchBridgeTxStatus(\n statusRequest,\n this.#clientId,\n this.#fetchFn,\n );\n const newBridgeHistoryItem = {\n ...historyItem,\n status,\n completionTime:\n status.status === StatusTypes.COMPLETE ||\n status.status === StatusTypes.FAILED\n ? Date.now()\n : undefined, // TODO make this more accurate by looking up dest txHash block time\n };\n\n // No need to purge these on network change or account change, TransactionController does not purge either.\n // TODO In theory we can skip checking status if it's not the current account/network\n // we need to keep track of the account that this is associated with as well so that we don't show it in Activity list for other accounts\n // First stab at this will not stop polling when you are on a different account\n this.update((state) => {\n state.bridgeStatusState.txHistory[bridgeTxMetaId] =\n newBridgeHistoryItem;\n });\n\n const pollingToken = this.#pollingTokensByTxMetaId[bridgeTxMetaId];\n\n if (\n (status.status === StatusTypes.COMPLETE ||\n status.status === StatusTypes.FAILED) &&\n pollingToken\n ) {\n this.stopPollingByPollingToken(pollingToken);\n\n if (status.status === StatusTypes.COMPLETE) {\n this.messagingSystem.publish(\n `${BRIDGE_STATUS_CONTROLLER_NAME}:bridgeTransactionComplete`,\n { bridgeHistoryItem: newBridgeHistoryItem },\n );\n }\n if (status.status === StatusTypes.FAILED) {\n this.messagingSystem.publish(\n `${BRIDGE_STATUS_CONTROLLER_NAME}:bridgeTransactionFailed`,\n { bridgeHistoryItem: newBridgeHistoryItem },\n );\n }\n }\n } catch (e) {\n console.log('Failed to fetch bridge tx status', e);\n }\n };\n\n readonly #getSrcTxHash = (bridgeTxMetaId: string): string | undefined => {\n const { bridgeStatusState } = this.state;\n // Prefer the srcTxHash from bridgeStatusState so we don't have to l ook up in TransactionController\n // But it is possible to have bridgeHistoryItem in state without the srcTxHash yet when it is an STX\n const srcTxHash =\n bridgeStatusState.txHistory[bridgeTxMetaId].status.srcChain.txHash;\n\n if (srcTxHash) {\n return srcTxHash;\n }\n\n // Look up in TransactionController if txMeta has been updated with the srcTxHash\n const txControllerState = this.messagingSystem.call(\n 'TransactionController:getState',\n );\n const txMeta = txControllerState.transactions.find(\n (tx) => tx.id === bridgeTxMetaId,\n );\n return txMeta?.hash;\n };\n\n readonly #updateSrcTxHash = (bridgeTxMetaId: string, srcTxHash: string) => {\n const { bridgeStatusState } = this.state;\n if (bridgeStatusState.txHistory[bridgeTxMetaId].status.srcChain.txHash) {\n return;\n }\n\n this.update((state) => {\n state.bridgeStatusState.txHistory[bridgeTxMetaId].status.srcChain.txHash =\n srcTxHash;\n });\n };\n\n // Wipes the bridge status for the given address and chainId\n // Will match only source chainId to the selectedChainId\n readonly #wipeBridgeStatusByChainId = (\n address: string,\n selectedChainId: Hex,\n ) => {\n const sourceTxMetaIdsToDelete = Object.keys(\n this.state.bridgeStatusState.txHistory,\n ).filter((txMetaId) => {\n const bridgeHistoryItem =\n this.state.bridgeStatusState.txHistory[txMetaId];\n\n const hexSourceChainId = numberToHex(bridgeHistoryItem.quote.srcChainId);\n\n return (\n bridgeHistoryItem.account === address &&\n hexSourceChainId === selectedChainId\n );\n });\n\n sourceTxMetaIdsToDelete.forEach((sourceTxMetaId) => {\n const pollingToken = this.#pollingTokensByTxMetaId[sourceTxMetaId];\n\n if (pollingToken) {\n this.stopPollingByPollingToken(\n this.#pollingTokensByTxMetaId[sourceTxMetaId],\n );\n }\n });\n\n this.update((state) => {\n state.bridgeStatusState.txHistory = sourceTxMetaIdsToDelete.reduce(\n (acc, sourceTxMetaId) => {\n delete acc[sourceTxMetaId];\n return acc;\n },\n state.bridgeStatusState.txHistory,\n );\n });\n };\n}\n"]}
@@ -0,0 +1,44 @@
1
+ import type { BridgeClientId } from "@metamask/bridge-controller";
2
+ import { BRIDGE_STATUS_CONTROLLER_NAME } from "./constants.cjs";
3
+ import { type BridgeStatusControllerMessenger } from "./types.cjs";
4
+ import type { BridgeStatusControllerState, StartPollingForBridgeTxStatusArgsSerialized, BridgeStatusState, FetchFunction } from "./types.cjs";
5
+ /** The input to start polling for the {@link BridgeStatusController} */
6
+ type BridgeStatusPollingInput = FetchBridgeTxStatusArgs;
7
+ export type FetchBridgeTxStatusArgs = {
8
+ bridgeTxMetaId: string;
9
+ };
10
+ declare const BridgeStatusController_base: (abstract new (...args: any[]) => {
11
+ readonly "__#13@#intervalIds": Record<string, NodeJS.Timeout>;
12
+ "__#13@#intervalLength": number | undefined;
13
+ setIntervalLength(intervalLength: number): void;
14
+ getIntervalLength(): number | undefined;
15
+ _startPolling(input: FetchBridgeTxStatusArgs): void;
16
+ _stopPollingByPollingTokenSetId(key: string): void;
17
+ readonly "__#3@#pollingTokenSets": Map<string, Set<string>>;
18
+ "__#3@#callbacks": Map<string, Set<(input: FetchBridgeTxStatusArgs) => void>>;
19
+ _executePoll(input: FetchBridgeTxStatusArgs): Promise<void>;
20
+ startPolling(input: FetchBridgeTxStatusArgs): string;
21
+ stopAllPolling(): void;
22
+ stopPollingByPollingToken(pollingToken: string): void;
23
+ onPollingComplete(input: FetchBridgeTxStatusArgs, callback: (input: FetchBridgeTxStatusArgs) => void): void;
24
+ }) & typeof import("@metamask/base-controller").BaseController;
25
+ export declare class BridgeStatusController extends BridgeStatusController_base<typeof BRIDGE_STATUS_CONTROLLER_NAME, BridgeStatusControllerState, BridgeStatusControllerMessenger> {
26
+ #private;
27
+ constructor({ messenger, state, clientId, fetchFn, }: {
28
+ messenger: BridgeStatusControllerMessenger;
29
+ state?: {
30
+ bridgeStatusState?: Partial<BridgeStatusState>;
31
+ };
32
+ clientId: BridgeClientId;
33
+ fetchFn: FetchFunction;
34
+ });
35
+ resetState: () => void;
36
+ wipeBridgeStatus: ({ address, ignoreNetwork, }: {
37
+ address: string;
38
+ ignoreNetwork: boolean;
39
+ }) => void;
40
+ startPollingForBridgeTxStatus: (startPollingForBridgeTxStatusArgs: StartPollingForBridgeTxStatusArgsSerialized) => void;
41
+ _executePoll: (pollingInput: BridgeStatusPollingInput) => Promise<void>;
42
+ }
43
+ export {};
44
+ //# sourceMappingURL=bridge-status-controller.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bridge-status-controller.d.cts","sourceRoot":"","sources":["../src/bridge-status-controller.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,oCAAoC;AAIlE,OAAO,EACL,6BAA6B,EAG9B,wBAAoB;AACrB,OAAO,EAAe,KAAK,+BAA+B,EAAE,oBAAgB;AAC5E,OAAO,KAAK,EACV,2BAA2B,EAC3B,2CAA2C,EAC3C,iBAAiB,EACjB,aAAa,EACd,oBAAgB;AAejB,wEAAwE;AACxE,KAAK,wBAAwB,GAAG,uBAAuB,CAAC;AAGxD,MAAM,MAAM,uBAAuB,GAAG;IACpC,cAAc,EAAE,MAAM,CAAC;CACxB,CAAC;;;;;;;;;;;;;;;;AACF,qBAAa,sBAAuB,SAAQ,4BAC1C,OAAO,6BAA6B,EACpC,2BAA2B,EAC3B,+BAA+B,CAChC;;gBAOa,EACV,SAAS,EACT,KAAK,EACL,QAAQ,EACR,OAAO,GACR,EAAE;QACD,SAAS,EAAE,+BAA+B,CAAC;QAC3C,KAAK,CAAC,EAAE;YAAE,iBAAiB,CAAC,EAAE,OAAO,CAAC,iBAAiB,CAAC,CAAA;SAAE,CAAC;QAC3D,QAAQ,EAAE,cAAc,CAAC;QACzB,OAAO,EAAE,aAAa,CAAC;KACxB;IAyCD,UAAU,aAMR;IAEF,gBAAgB;iBAIL,MAAM;uBACA,OAAO;eAqBtB;IA8BF,6BAA6B,sCACQ,2CAA2C,UAkD9E;IAIF,YAAY,iBAAwB,wBAAwB,mBAE1D;CAuJH"}
@@ -0,0 +1,44 @@
1
+ import type { BridgeClientId } from "@metamask/bridge-controller";
2
+ import { BRIDGE_STATUS_CONTROLLER_NAME } from "./constants.mjs";
3
+ import { type BridgeStatusControllerMessenger } from "./types.mjs";
4
+ import type { BridgeStatusControllerState, StartPollingForBridgeTxStatusArgsSerialized, BridgeStatusState, FetchFunction } from "./types.mjs";
5
+ /** The input to start polling for the {@link BridgeStatusController} */
6
+ type BridgeStatusPollingInput = FetchBridgeTxStatusArgs;
7
+ export type FetchBridgeTxStatusArgs = {
8
+ bridgeTxMetaId: string;
9
+ };
10
+ declare const BridgeStatusController_base: (abstract new (...args: any[]) => {
11
+ readonly "__#13@#intervalIds": Record<string, NodeJS.Timeout>;
12
+ "__#13@#intervalLength": number | undefined;
13
+ setIntervalLength(intervalLength: number): void;
14
+ getIntervalLength(): number | undefined;
15
+ _startPolling(input: FetchBridgeTxStatusArgs): void;
16
+ _stopPollingByPollingTokenSetId(key: string): void;
17
+ readonly "__#3@#pollingTokenSets": Map<string, Set<string>>;
18
+ "__#3@#callbacks": Map<string, Set<(input: FetchBridgeTxStatusArgs) => void>>;
19
+ _executePoll(input: FetchBridgeTxStatusArgs): Promise<void>;
20
+ startPolling(input: FetchBridgeTxStatusArgs): string;
21
+ stopAllPolling(): void;
22
+ stopPollingByPollingToken(pollingToken: string): void;
23
+ onPollingComplete(input: FetchBridgeTxStatusArgs, callback: (input: FetchBridgeTxStatusArgs) => void): void;
24
+ }) & typeof import("@metamask/base-controller").BaseController;
25
+ export declare class BridgeStatusController extends BridgeStatusController_base<typeof BRIDGE_STATUS_CONTROLLER_NAME, BridgeStatusControllerState, BridgeStatusControllerMessenger> {
26
+ #private;
27
+ constructor({ messenger, state, clientId, fetchFn, }: {
28
+ messenger: BridgeStatusControllerMessenger;
29
+ state?: {
30
+ bridgeStatusState?: Partial<BridgeStatusState>;
31
+ };
32
+ clientId: BridgeClientId;
33
+ fetchFn: FetchFunction;
34
+ });
35
+ resetState: () => void;
36
+ wipeBridgeStatus: ({ address, ignoreNetwork, }: {
37
+ address: string;
38
+ ignoreNetwork: boolean;
39
+ }) => void;
40
+ startPollingForBridgeTxStatus: (startPollingForBridgeTxStatusArgs: StartPollingForBridgeTxStatusArgsSerialized) => void;
41
+ _executePoll: (pollingInput: BridgeStatusPollingInput) => Promise<void>;
42
+ }
43
+ export {};
44
+ //# sourceMappingURL=bridge-status-controller.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bridge-status-controller.d.mts","sourceRoot":"","sources":["../src/bridge-status-controller.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,oCAAoC;AAIlE,OAAO,EACL,6BAA6B,EAG9B,wBAAoB;AACrB,OAAO,EAAe,KAAK,+BAA+B,EAAE,oBAAgB;AAC5E,OAAO,KAAK,EACV,2BAA2B,EAC3B,2CAA2C,EAC3C,iBAAiB,EACjB,aAAa,EACd,oBAAgB;AAejB,wEAAwE;AACxE,KAAK,wBAAwB,GAAG,uBAAuB,CAAC;AAGxD,MAAM,MAAM,uBAAuB,GAAG;IACpC,cAAc,EAAE,MAAM,CAAC;CACxB,CAAC;;;;;;;;;;;;;;;;AACF,qBAAa,sBAAuB,SAAQ,4BAC1C,OAAO,6BAA6B,EACpC,2BAA2B,EAC3B,+BAA+B,CAChC;;gBAOa,EACV,SAAS,EACT,KAAK,EACL,QAAQ,EACR,OAAO,GACR,EAAE;QACD,SAAS,EAAE,+BAA+B,CAAC;QAC3C,KAAK,CAAC,EAAE;YAAE,iBAAiB,CAAC,EAAE,OAAO,CAAC,iBAAiB,CAAC,CAAA;SAAE,CAAC;QAC3D,QAAQ,EAAE,cAAc,CAAC;QACzB,OAAO,EAAE,aAAa,CAAC;KACxB;IAyCD,UAAU,aAMR;IAEF,gBAAgB;iBAIL,MAAM;uBACA,OAAO;eAqBtB;IA8BF,6BAA6B,sCACQ,2CAA2C,UAkD9E;IAIF,YAAY,iBAAwB,wBAAwB,mBAE1D;CAuJH"}