@layerswap/wallet-fuel 1.7.0 → 2.0.0

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 (51) hide show
  1. package/dist/esm/constants.js +1 -1
  2. package/dist/esm/fuelBalanceProvider.js +2 -2
  3. package/dist/esm/fuelGasProvider.js +2 -1
  4. package/dist/esm/index.js +24 -24
  5. package/dist/esm/init.js +38 -0
  6. package/dist/esm/service/FuelConnectionService.js +232 -0
  7. package/dist/esm/service/createFuelConnection.js +45 -0
  8. package/dist/esm/service/fuelStore.js +26 -0
  9. package/dist/esm/service/getFuel.js +13 -0
  10. package/dist/esm/service/syncFuel.js +119 -0
  11. package/dist/esm/transferProvider/{useFuelTransfer.js → createFuelTransfer.js} +6 -6
  12. package/dist/esm/utils/index.js +0 -1
  13. package/dist/esm/utils/resolveIcon.js +3 -8
  14. package/dist/tsconfig.tsbuildinfo +1 -1
  15. package/dist/types/fuelBalanceProvider.d.ts +1 -1
  16. package/dist/types/fuelGasProvider.d.ts +2 -1
  17. package/dist/types/fuelGasProvider.d.ts.map +1 -1
  18. package/dist/types/index.d.ts +10 -7
  19. package/dist/types/index.d.ts.map +1 -1
  20. package/dist/types/init.d.ts +8 -0
  21. package/dist/types/init.d.ts.map +1 -0
  22. package/dist/types/service/FuelConnectionService.d.ts +31 -0
  23. package/dist/types/service/FuelConnectionService.d.ts.map +1 -0
  24. package/dist/types/service/createFuelConnection.d.ts +11 -0
  25. package/dist/types/service/createFuelConnection.d.ts.map +1 -0
  26. package/dist/types/service/fuelStore.d.ts +14 -0
  27. package/dist/types/service/fuelStore.d.ts.map +1 -0
  28. package/dist/types/service/getFuel.d.ts +5 -0
  29. package/dist/types/service/getFuel.d.ts.map +1 -0
  30. package/dist/types/service/syncFuel.d.ts +5 -0
  31. package/dist/types/service/syncFuel.d.ts.map +1 -0
  32. package/dist/types/transferProvider/createFuelTransfer.d.ts +3 -0
  33. package/dist/types/transferProvider/createFuelTransfer.d.ts.map +1 -0
  34. package/dist/types/transferProvider/transactionBuilder.d.ts +1 -1
  35. package/dist/types/transferProvider/transactionBuilder.d.ts.map +1 -1
  36. package/dist/types/utils/index.d.ts +0 -1
  37. package/dist/types/utils/index.d.ts.map +1 -1
  38. package/dist/types/utils/resolveIcon.d.ts +2 -2
  39. package/dist/types/utils/resolveIcon.d.ts.map +1 -1
  40. package/package.json +11 -15
  41. package/dist/esm/FuelProvider.js +0 -36
  42. package/dist/esm/useFuelConnection.js +0 -205
  43. package/dist/esm/utils/KnownFuelConnectors.js +0 -24
  44. package/dist/types/FuelProvider.d.ts +0 -6
  45. package/dist/types/FuelProvider.d.ts.map +0 -1
  46. package/dist/types/transferProvider/useFuelTransfer.d.ts +0 -3
  47. package/dist/types/transferProvider/useFuelTransfer.d.ts.map +0 -1
  48. package/dist/types/useFuelConnection.d.ts +0 -3
  49. package/dist/types/useFuelConnection.d.ts.map +0 -1
  50. package/dist/types/utils/KnownFuelConnectors.d.ts +0 -6
  51. package/dist/types/utils/KnownFuelConnectors.d.ts.map +0 -1
@@ -1,4 +1,4 @@
1
- import { KnownInternalNames } from "@layerswap/widget/internal";
1
+ import { KnownInternalNames } from "@layerswap/utils";
2
2
  export const name = 'Fuel';
3
3
  export const id = 'fuel';
4
4
  export const commonSupportedNetworks = [
@@ -1,5 +1,5 @@
1
- import { retryWithExponentialBackoff, fetchWithTimeout, KnownInternalNames, formatUnits } from "@layerswap/widget/internal";
2
- import { BalanceProvider } from "@layerswap/widget/types";
1
+ import { retryWithExponentialBackoff, fetchWithTimeout, KnownInternalNames, formatUnits } from "@layerswap/utils";
2
+ import { BalanceProvider } from "@layerswap/widget-types";
3
3
  export class FuelBalanceProvider extends BalanceProvider {
4
4
  constructor() {
5
5
  super(...arguments);
@@ -1,5 +1,6 @@
1
1
  import WatchdogAbi from './jsons/FUELWATCHDOG.json';
2
- import { ErrorHandler, KnownInternalNames, formatUnits } from "@layerswap/widget/internal";
2
+ import { KnownInternalNames, formatUnits } from "@layerswap/utils";
3
+ import { ErrorHandler } from "@layerswap/widget-types";
3
4
  export class FuelGasProvider {
4
5
  supportsNetwork(network) {
5
6
  return (KnownInternalNames.Networks.FuelMainnet.includes(network.name) || KnownInternalNames.Networks.FuelTestnet.includes(network.name));
package/dist/esm/index.js CHANGED
@@ -1,16 +1,24 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
2
1
  import { FuelBalanceProvider } from "./fuelBalanceProvider";
3
2
  import { FuelGasProvider } from "./fuelGasProvider";
4
- import FuelProviderWrapper from "./FuelProvider";
5
- import useFuelConnection from "./useFuelConnection";
6
- import React from "react";
7
- import { useFuelTransfer } from "./transferProvider/useFuelTransfer";
3
+ import { createFuelTransfer } from "./transferProvider/createFuelTransfer";
4
+ import { createFuelConnection } from "./service/createFuelConnection";
5
+ import { initFuelProvider } from "./init";
6
+ import { id } from "./constants";
7
+ // The literal id in the return type lets `defineWalletDescriptor` in
8
+ // `@layerswap/wallets` verify it matches the descriptor id at compile time.
8
9
  export function createFuelProvider(config = {}) {
9
- const { customHook, balanceProviders, gasProviders, transferProviders } = config;
10
- const WrapperComponent = ({ children }) => {
11
- return (_jsx(FuelProviderWrapper, { children: children }));
10
+ const { customConnection, balanceProviders, gasProviders, transferProviders, } = config;
11
+ const init = (_ctx) => {
12
+ initFuelProvider();
13
+ // No-op disposer; init is idempotent across remounts.
14
+ };
15
+ const createConnection = (props) => {
16
+ initFuelProvider();
17
+ if (customConnection) {
18
+ return customConnection(props);
19
+ }
20
+ return createFuelConnection(props);
12
21
  };
13
- const walletConnectionProvider = customHook || useFuelConnection;
14
22
  const defaultBalanceProviders = [new FuelBalanceProvider()];
15
23
  const finalBalanceProviders = balanceProviders !== undefined
16
24
  ? (Array.isArray(balanceProviders) ? balanceProviders : [balanceProviders])
@@ -19,27 +27,19 @@ export function createFuelProvider(config = {}) {
19
27
  const finalGasProviders = gasProviders !== undefined
20
28
  ? (Array.isArray(gasProviders) ? gasProviders : [gasProviders])
21
29
  : defaultGasProviders;
22
- const defaultTransferProviders = [useFuelTransfer];
30
+ const defaultTransferProviders = [createFuelTransfer];
23
31
  const finalTransferProviders = transferProviders !== undefined
24
32
  ? (Array.isArray(transferProviders) ? transferProviders : [transferProviders])
25
33
  : defaultTransferProviders;
26
34
  return {
27
- id: "fuel",
28
- wrapper: WrapperComponent,
29
- walletConnectionProvider,
35
+ id,
36
+ init,
37
+ createConnection,
30
38
  gasProvider: finalGasProviders,
31
39
  balanceProvider: finalBalanceProviders,
32
40
  transferProvider: finalTransferProviders,
33
41
  };
34
42
  }
35
- /**
36
- * @deprecated Use createFuelProvider() instead. This export will be removed in a future version.
37
- */
38
- export const FuelProvider = {
39
- id: "fuel",
40
- wrapper: FuelProviderWrapper,
41
- walletConnectionProvider: useFuelConnection,
42
- gasProvider: [new FuelGasProvider()],
43
- balanceProvider: [new FuelBalanceProvider()],
44
- transferProvider: [useFuelTransfer],
45
- };
43
+ export { createFuelConnection } from "./service/createFuelConnection";
44
+ export { useFuelStore } from "./service/fuelStore";
45
+ export { getFuelInstance, hasFuelInstance } from "./service/getFuel";
@@ -0,0 +1,38 @@
1
+ import { Fuel } from '@fuel-ts/account';
2
+ import { BakoSafeConnector } from './connectors/bako-safe';
3
+ import { BakoRequestAPI } from './connectors/bako-safe/Bako';
4
+ import { FuelWalletConnector } from './connectors/fuel-wallet';
5
+ import { FueletWalletConnector } from './connectors/fuelet-wallet';
6
+ import { hasFuelInstance, setFuelInstance } from './service/getFuel';
7
+ import { attachFuelSync } from './service/syncFuel';
8
+ const HOST_URL = 'https://api.bako.global';
9
+ let _initialized = false;
10
+ /**
11
+ * One-shot initialization of the Fuel SDK + store sync.
12
+ * Safe to call multiple times — subsequent calls are no-ops.
13
+ */
14
+ export function initFuelProvider() {
15
+ if (_initialized)
16
+ return;
17
+ if (typeof window === 'undefined')
18
+ return;
19
+ if (hasFuelInstance()) {
20
+ _initialized = true;
21
+ return;
22
+ }
23
+ const fuel = new Fuel({
24
+ connectors: [
25
+ new FuelWalletConnector(),
26
+ new BakoSafeConnector({ api: new BakoRequestAPI(HOST_URL) }),
27
+ new FueletWalletConnector(),
28
+ ],
29
+ });
30
+ setFuelInstance(fuel);
31
+ attachFuelSync(fuel);
32
+ _initialized = true;
33
+ }
34
+ /** Visible for tests. Resets the singleton init so a fresh init can run. */
35
+ export function _resetFuelInit() {
36
+ _initialized = false;
37
+ setFuelInstance(null);
38
+ }
@@ -0,0 +1,232 @@
1
+ import { sleep } from "@layerswap/utils";
2
+ import { Address } from '@fuel-ts/address';
3
+ import { name as PROVIDER_NAME, id as PROVIDER_ID } from '../constants';
4
+ import { BAKO_STATE } from '../connectors/bako-safe/Bako';
5
+ import { resolveFuelWalletConnectorIcon } from '../utils';
6
+ import { useFuelStore } from './fuelStore';
7
+ export class FuelConnectionService {
8
+ constructor() {
9
+ this._networks = [];
10
+ this._networksKey = '';
11
+ // `connectWallet` mutates the module-global `BAKO_STATE.{state.last_req,
12
+ // period_durtion}` while connecting. Rapid double-clicks or a retry firing
13
+ // while a connect is still in flight would interleave those writes. Chain
14
+ // connects through this promise so they run one-at-a-time.
15
+ this._connectQueue = Promise.resolve();
16
+ }
17
+ setNetworks(networks, networkAdapter) {
18
+ const key = networks.map(network => networkAdapter.getId(network)).join('|');
19
+ if (this._networksKey === key)
20
+ return;
21
+ this._networks = networks;
22
+ this._networkAdapter = networkAdapter;
23
+ this._networksKey = key;
24
+ }
25
+ addWallet(wallet) {
26
+ useFuelStore.getState().connectWallet(wallet);
27
+ }
28
+ removeWallet(connectorName) {
29
+ useFuelStore.getState().disconnectWallet(connectorName);
30
+ }
31
+ getConnectedWallets() {
32
+ return useFuelStore.getState().connectedWallets;
33
+ }
34
+ getSupportedNetworks() {
35
+ return this._networks
36
+ .filter(network => this._networkAdapter?.isFuelNetwork(network))
37
+ .map(network => this._networkAdapter?.getId(network))
38
+ .filter((id) => !!id);
39
+ }
40
+ getNetworkIcon() {
41
+ const network = this._networks.find(item => this._networkAdapter?.isFuelNetwork(item));
42
+ return network && this._networkAdapter ? this._networkAdapter.getIcon(network) : undefined;
43
+ }
44
+ // Connector icons come either as a URL string or as a { dark, light } pair
45
+ // of raw SVG markup; the latter is wrapped as a base64 data URL unless it
46
+ // already is one. Shared by getAvailableConnectors and resolveFuelWallet.
47
+ resolveConnectorImageIcon(image) {
48
+ if (!image)
49
+ return undefined;
50
+ if (typeof image === 'string')
51
+ return image;
52
+ return image.dark.startsWith('data:')
53
+ ? image.dark
54
+ : `data:image/svg+xml;base64,${btoa(image.dark)}`;
55
+ }
56
+ getAvailableConnectors() {
57
+ const connectors = useFuelStore.getState().connectors;
58
+ return connectors.map(c => {
59
+ const icon = this.resolveConnectorImageIcon(c.metadata.image);
60
+ const isInstalled = c.installed && !c['dAppWindow'];
61
+ const isLoadable = c.metadata?.install?.action !== 'Install';
62
+ return {
63
+ name: c.name,
64
+ id: c.name,
65
+ icon,
66
+ type: isInstalled ? 'injected' : 'other',
67
+ installUrl: c.metadata.install.link,
68
+ extensionNotFound: !c.installed,
69
+ isLoadable,
70
+ providerName: PROVIDER_NAME,
71
+ };
72
+ });
73
+ }
74
+ async resolveFuelWallet(connector, address, addresses) {
75
+ const network = await connector.currentNetwork();
76
+ const chainId = network.chainId ?? (network.url.toLowerCase().includes('testnet') ? 0 : 9889);
77
+ const icon = this.resolveConnectorImageIcon(connector.metadata.image);
78
+ return {
79
+ id: connector.name,
80
+ address,
81
+ addresses,
82
+ isActive: true,
83
+ chainId,
84
+ disconnect: () => this.disconnectWallet(connector.name),
85
+ displayName: `${connector.name} - Fuel`,
86
+ providerName: PROVIDER_NAME,
87
+ icon: resolveFuelWalletConnectorIcon({ connector: connector.name, address, iconUrl: icon }),
88
+ autofillSupportedNetworks: this.getSupportedNetworks(),
89
+ withdrawalSupportedNetworks: this.getSupportedNetworks(),
90
+ asSourceSupportedNetworks: this.getSupportedNetworks(),
91
+ networkIcon: this.getNetworkIcon(),
92
+ };
93
+ }
94
+ connectWallet({ connector }) {
95
+ // Serialize against any in-flight connect (see `_connectQueue`). The
96
+ // `.then(run, run)` runs `run` whether the prior connect resolved or
97
+ // rejected, so one failure doesn't wedge the queue.
98
+ const run = () => this.doConnectWallet({ connector });
99
+ const result = this._connectQueue.then(run, run);
100
+ this._connectQueue = result.catch(() => { });
101
+ return result;
102
+ }
103
+ async doConnectWallet({ connector }) {
104
+ const attemptConnection = async (isRetry = false) => {
105
+ try {
106
+ const fuelConnector = useFuelStore.getState().connectors.find(w => w.name === connector.name);
107
+ BAKO_STATE.state.last_req = undefined;
108
+ BAKO_STATE.period_durtion = 120000;
109
+ await fuelConnector?.connect();
110
+ const addresses = (await fuelConnector?.accounts())?.map(a => new Address(a).toB256());
111
+ if (!addresses || !fuelConnector)
112
+ return undefined;
113
+ const result = await this.resolveFuelWallet(fuelConnector, addresses[0], addresses);
114
+ this.addWallet(result);
115
+ await this.switchAccount(result);
116
+ return result;
117
+ }
118
+ catch (e) {
119
+ if (connector.name === 'Bako Safe' && e === false && !isRetry) {
120
+ console.log('Bako Safe connection failed with false, retrying once...');
121
+ await sleep(1000);
122
+ return await attemptConnection(true);
123
+ }
124
+ throw e instanceof Error ? e : new Error(typeof e === 'string' ? e : JSON.stringify(e));
125
+ }
126
+ };
127
+ return attemptConnection();
128
+ }
129
+ async disconnectWallet(connectorName) {
130
+ try {
131
+ const fuelConnector = useFuelStore.getState().connectors.find(c => c.name === connectorName);
132
+ if (!fuelConnector)
133
+ throw new Error('Connector not found');
134
+ await fuelConnector.disconnect();
135
+ }
136
+ catch (e) {
137
+ // Disconnect is best-effort — log but do not rethrow; the wallet is
138
+ // removed from the store in `finally` regardless.
139
+ const msg = e instanceof Error ? e.message : String(e);
140
+ console.error(`[Fuel] Failed to disconnect ${connectorName}: ${msg}`);
141
+ }
142
+ finally {
143
+ this.removeWallet(connectorName);
144
+ }
145
+ }
146
+ async disconnectWallets() {
147
+ try {
148
+ BAKO_STATE.state.last_req = undefined;
149
+ BAKO_STATE.period_durtion = 10000;
150
+ const connectors = useFuelStore.getState().connectors;
151
+ for (const connector of connectors.filter(c => c.connected)) {
152
+ await connector.disconnect();
153
+ this.removeWallet();
154
+ }
155
+ }
156
+ catch (e) {
157
+ // Best-effort bulk disconnect — log but do not rethrow.
158
+ const msg = e instanceof Error ? e.message : String(e);
159
+ console.error(`[Fuel] Failed to disconnect wallets: ${msg}`);
160
+ }
161
+ }
162
+ async switchAccount(wallet) {
163
+ try {
164
+ const fuel = useFuelStore.getState().fuel;
165
+ if (!fuel)
166
+ throw new Error('Fuel instance not available');
167
+ const res = await fuel.selectConnector(wallet.id);
168
+ if (!res)
169
+ throw new Error('Could not switch account');
170
+ }
171
+ catch (e) {
172
+ const msg = e instanceof Error ? e.message : String(e);
173
+ console.error(`[Fuel] Failed to switch account to ${wallet.id}: ${msg}`);
174
+ }
175
+ }
176
+ async switchChain(connector, chainId) {
177
+ try {
178
+ const fuelConnector = useFuelStore.getState().connectors.find(c => c.name === connector.id);
179
+ if (!fuelConnector)
180
+ throw new Error('Connector not found');
181
+ const res = await fuelConnector.selectNetwork({ chainId: Number(chainId) });
182
+ if (!res)
183
+ throw new Error('Could not switch chain');
184
+ }
185
+ catch (e) {
186
+ const msg = e instanceof Error ? e.message : String(e);
187
+ console.error(`[Fuel] Failed to switch chain to ${chainId}: ${msg}`);
188
+ }
189
+ }
190
+ async resolveConnectedWallets() {
191
+ const connectors = useFuelStore.getState().connectors.filter(c => c.connected);
192
+ const wallets = [];
193
+ for (const connector of connectors) {
194
+ try {
195
+ const addresses = (await connector.accounts()).map(a => Address.fromAddressOrString(a).toB256());
196
+ if (connector.connected && addresses.length > 0) {
197
+ const w = await this.resolveFuelWallet(connector, addresses[0], addresses);
198
+ wallets.push(w);
199
+ }
200
+ }
201
+ catch (e) {
202
+ const msg = e instanceof Error ? e.message : String(e);
203
+ console.error(`[Fuel] Failed to resolve connected wallet for ${connector.name}: ${msg}`);
204
+ }
205
+ }
206
+ return wallets;
207
+ }
208
+ async syncConnectedWallets() {
209
+ const wallets = await this.resolveConnectedWallets();
210
+ for (const wallet of wallets) {
211
+ this.addWallet(wallet);
212
+ }
213
+ }
214
+ buildProvider() {
215
+ const connectedWallets = this.getConnectedWallets();
216
+ return {
217
+ connectWallet: this.connectWallet.bind(this),
218
+ disconnectWallets: this.disconnectWallets.bind(this),
219
+ switchAccount: this.switchAccount.bind(this),
220
+ switchChain: this.switchChain.bind(this),
221
+ availableConnectors: this.getAvailableConnectors(),
222
+ autofillSupportedNetworks: this.getSupportedNetworks(),
223
+ withdrawalSupportedNetworks: this.getSupportedNetworks(),
224
+ asSourceSupportedNetworks: this.getSupportedNetworks(),
225
+ activeWallet: connectedWallets[0],
226
+ connectedWallets,
227
+ name: PROVIDER_NAME,
228
+ id: PROVIDER_ID,
229
+ ready: useFuelStore.getState().ready,
230
+ };
231
+ }
232
+ }
@@ -0,0 +1,45 @@
1
+ import { createMemoizedConnectionStore } from "@layerswap/wallet-core";
2
+ import { createFuelTransfer } from '../transferProvider/createFuelTransfer';
3
+ import { FuelConnectionService } from './FuelConnectionService';
4
+ import { registerFuelWalletSynchronizer } from './syncFuel';
5
+ import { useFuelStore } from './fuelStore';
6
+ /**
7
+ * Vanilla external-store factory for the Fuel wallet connection. Replaces the
8
+ * old `useFuelConnection` hook.
9
+ */
10
+ export function createFuelConnection(initialProps, options = {}) {
11
+ const { extraMultiStepHandlers = [] } = options;
12
+ let networks = initialProps.networks;
13
+ let networkAdapter = initialProps.networkAdapter;
14
+ const fuelConnectionService = new FuelConnectionService();
15
+ fuelConnectionService.setNetworks(networks, networkAdapter);
16
+ const unregisterWalletSynchronizer = registerFuelWalletSynchronizer(() => fuelConnectionService.syncConnectedWallets());
17
+ const transferProvider = createFuelTransfer();
18
+ const transfer = transferProvider.executeTransfer;
19
+ return createMemoizedConnectionStore({
20
+ computeInputs: () => {
21
+ const fuelState = useFuelStore.getState();
22
+ return {
23
+ connectors: fuelState.connectors,
24
+ fuel: fuelState.fuel,
25
+ ready: fuelState.ready,
26
+ connectedWallets: fuelState.connectedWallets,
27
+ networks,
28
+ };
29
+ },
30
+ buildSnapshot: () => ({
31
+ ...fuelConnectionService.buildProvider(),
32
+ transfer,
33
+ multiStepHandlers: extraMultiStepHandlers,
34
+ }),
35
+ subscribe: sync => [
36
+ useFuelStore.subscribe(sync),
37
+ ],
38
+ onUpdateProps: nextProps => {
39
+ networks = nextProps.networks;
40
+ networkAdapter = nextProps.networkAdapter;
41
+ fuelConnectionService.setNetworks(networks, networkAdapter);
42
+ },
43
+ onDestroy: unregisterWalletSynchronizer,
44
+ });
45
+ }
@@ -0,0 +1,26 @@
1
+ import { create } from 'zustand';
2
+ export const useFuelStore = create()((set) => ({
3
+ connectors: [],
4
+ fuel: undefined,
5
+ ready: false,
6
+ connectedWallets: [],
7
+ // `fuel.connectors()` returns the SAME array instance on every call and
8
+ // mutates `installed`/`connected` on the connector objects in place. A
9
+ // stored reference would defeat every downstream identity memo — the
10
+ // provider snapshot would never recompute after its first build, freezing
11
+ // stale `extensionNotFound` flags in the UI. Copy on write so every
12
+ // publish is a fresh identity.
13
+ _setConnectors: (connectors) => set({ connectors: [...connectors], ready: connectors.length > 0 }),
14
+ _setFuel: (fuel) => set({ fuel }),
15
+ // Mirrors the global wallet store's dedupe-by-(id,address) semantics so the
16
+ // same connector reconnecting replaces its entry instead of appending.
17
+ connectWallet: (wallet) => set((state) => {
18
+ const filtered = state.connectedWallets.filter(w => !(w.id === wallet.id && w.address === wallet.address));
19
+ return { connectedWallets: [...filtered, wallet] };
20
+ }),
21
+ disconnectWallet: (connectorName) => set((state) => ({
22
+ connectedWallets: connectorName
23
+ ? state.connectedWallets.filter(w => w.id !== connectorName)
24
+ : [],
25
+ })),
26
+ }));
@@ -0,0 +1,13 @@
1
+ let _instance = null;
2
+ export function setFuelInstance(instance) {
3
+ _instance = instance;
4
+ }
5
+ export function getFuelInstance() {
6
+ if (!_instance) {
7
+ throw new Error('Fuel instance requested before initFuelProvider() ran');
8
+ }
9
+ return _instance;
10
+ }
11
+ export function hasFuelInstance() {
12
+ return _instance !== null;
13
+ }
@@ -0,0 +1,119 @@
1
+ import { FuelConnectorEventTypes } from '@fuel-ts/account';
2
+ import { useFuelStore } from './fuelStore';
3
+ let _attached = false;
4
+ let _dispose = null;
5
+ let _fuel = null;
6
+ const walletSynchronizers = new Set();
7
+ export function registerFuelWalletSynchronizer(sync) {
8
+ walletSynchronizers.add(sync);
9
+ return () => {
10
+ walletSynchronizers.delete(sync);
11
+ };
12
+ }
13
+ async function syncConnectedWallets() {
14
+ await Promise.allSettled([...walletSynchronizers].map(sync => sync()));
15
+ }
16
+ export function attachFuelSync(fuel) {
17
+ if (_fuel === fuel && _dispose)
18
+ return _dispose;
19
+ _dispose?.();
20
+ useFuelStore.getState()._setFuel(fuel);
21
+ const refreshConnectors = async () => {
22
+ try {
23
+ const connectors = await fuel.connectors();
24
+ useFuelStore.getState()._setConnectors(connectors);
25
+ // Re-attach per-connector network listeners whenever the list changes
26
+ attachConnectorNetworkListeners(connectors);
27
+ }
28
+ catch {
29
+ // swallow
30
+ }
31
+ };
32
+ const onConnection = () => {
33
+ syncConnectedWallets().catch(() => { });
34
+ };
35
+ const onCurrentConnector = () => {
36
+ syncConnectedWallets().catch(() => { });
37
+ };
38
+ let perConnectorDisposers = [];
39
+ function attachConnectorNetworkListeners(connectors) {
40
+ // Detach previous listeners
41
+ perConnectorDisposers.forEach(fn => fn());
42
+ perConnectorDisposers = [];
43
+ for (const c of connectors) {
44
+ const handler = async () => {
45
+ await syncConnectedWallets();
46
+ };
47
+ c.on(FuelConnectorEventTypes.currentNetwork, handler);
48
+ perConnectorDisposers.push(() => c.off(FuelConnectorEventTypes.currentNetwork, handler));
49
+ }
50
+ }
51
+ // Refresh connector detection flags, then sync sessions the refreshed
52
+ // flags reveal. Shared by initial population, bounded re-detection and
53
+ // the window-focus refresh below.
54
+ const refreshAll = () => refreshConnectors()
55
+ .then(() => syncConnectedWallets())
56
+ .catch(() => { });
57
+ const onConnectors = () => { void refreshAll(); };
58
+ let retryTimers = [];
59
+ const onWindowFocus = () => { void refreshAll(); };
60
+ let disposed = false;
61
+ const dispose = () => {
62
+ if (disposed)
63
+ return;
64
+ disposed = true;
65
+ fuel.off(FuelConnectorEventTypes.connectors, onConnectors);
66
+ fuel.off(FuelConnectorEventTypes.connection, onConnection);
67
+ fuel.off(FuelConnectorEventTypes.currentConnector, onCurrentConnector);
68
+ perConnectorDisposers.forEach(fn => fn());
69
+ perConnectorDisposers = [];
70
+ retryTimers.forEach(t => clearTimeout(t));
71
+ retryTimers = [];
72
+ if (typeof window !== 'undefined')
73
+ window.removeEventListener('focus', onWindowFocus);
74
+ if (_dispose !== dispose)
75
+ return;
76
+ _attached = false;
77
+ _dispose = null;
78
+ _fuel = null;
79
+ };
80
+ try {
81
+ fuel.on(FuelConnectorEventTypes.connectors, onConnectors);
82
+ fuel.on(FuelConnectorEventTypes.connection, onConnection);
83
+ fuel.on(FuelConnectorEventTypes.currentConnector, onCurrentConnector);
84
+ }
85
+ catch (error) {
86
+ dispose();
87
+ throw error;
88
+ }
89
+ _attached = true;
90
+ _fuel = fuel;
91
+ _dispose = dispose;
92
+ // Initial population. The SDK restores the stored connector during Fuel's
93
+ // constructor with `emitEvents: false`, which skips the `connection` event,
94
+ // and its early `currentConnector` emit races the store population above —
95
+ // so no event reliably announces a restored session. `fuel.connectors()`
96
+ // refreshes each connector's `connected` flag, so a sync chained after it
97
+ // picks the restored session up deterministically.
98
+ void refreshAll();
99
+ // Detection is not one-shot-reliable: extension content scripts and MV3
100
+ // service workers can come up after the initial ping, and a wallet that
101
+ // announced itself before our listeners attached is silent forever after.
102
+ // The pre-refactor @fuels/react layer re-polled connectors; mirror that
103
+ // with a bounded re-detection pass (spaced beyond the SDK's 5s ping cache)
104
+ // plus a refresh on window focus — which also picks up an extension the
105
+ // user just installed from the "Install" prompt, or unlocked via popup.
106
+ if (typeof window !== 'undefined') {
107
+ const RETRY_DELAYS_MS = [6000, 14000];
108
+ retryTimers = RETRY_DELAYS_MS.map(delay => setTimeout(() => {
109
+ const connectors = useFuelStore.getState().connectors;
110
+ if (connectors.length === 0 || connectors.some(c => !c.installed))
111
+ void refreshAll();
112
+ }, delay));
113
+ window.addEventListener('focus', onWindowFocus);
114
+ }
115
+ return _dispose;
116
+ }
117
+ export function isFuelSyncAttached() {
118
+ return _attached;
119
+ }
@@ -1,23 +1,23 @@
1
- import { ActionMessageType } from "@layerswap/widget/types";
2
- import { useFuel } from '@fuels/react';
1
+ import { ActionMessageType } from '@layerswap/widget-types';
3
2
  import { Provider } from '@fuel-ts/account';
4
3
  import { transactionBuilder } from "./transactionBuilder";
5
- import { KnownInternalNames } from "@layerswap/widget/internal";
4
+ import { KnownInternalNames } from "@layerswap/utils";
5
+ import { getFuelInstance, hasFuelInstance } from "../service/getFuel";
6
6
  const supportedNetworks = [
7
7
  KnownInternalNames.Networks.FuelTestnet,
8
8
  KnownInternalNames.Networks.FuelDevnet,
9
9
  KnownInternalNames.Networks.FuelMainnet
10
10
  ];
11
- export function useFuelTransfer() {
12
- const { fuel } = useFuel();
11
+ export function createFuelTransfer() {
13
12
  return {
14
13
  supportsNetwork(network) {
15
14
  return supportedNetworks.includes(network.name);
16
15
  },
17
16
  async executeTransfer(params) {
18
- if (!fuel) {
17
+ if (!hasFuelInstance()) {
19
18
  throw new Error("Fuel not initialized");
20
19
  }
20
+ const fuel = getFuelInstance();
21
21
  const { callData, network, selectedWallet, swapId } = params;
22
22
  const fuelProvider = new Provider(network.node_url);
23
23
  const fuelWallet = await fuel.getWallet(selectedWallet.address, fuelProvider);
@@ -1,2 +1 @@
1
- export * from "./KnownFuelConnectors";
2
1
  export * from "./resolveIcon";
@@ -1,9 +1,4 @@
1
- import { walletIconResolver } from "@layerswap/widget/internal";
2
- import KnownFuelConnectors from "./KnownFuelConnectors";
3
- export const resolveFuelWalletConnectorIcon = ({ connector, address, iconUrl }) => {
4
- const knownConnector = KnownFuelConnectors.find(c => c.id.toLowerCase() === connector?.toLowerCase());
5
- if (knownConnector && knownConnector.icon)
6
- return knownConnector.icon;
7
- else
8
- return walletIconResolver(address, iconUrl);
1
+ import { resolveWalletIconString } from "@layerswap/wallet-core";
2
+ export const resolveFuelWalletConnectorIcon = ({ connector, iconUrl }) => {
3
+ return resolveWalletIconString({ id: connector, iconUrl });
9
4
  };