@tuwaio/satellite-evm 0.3.10 → 0.4.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.
package/README.md CHANGED
@@ -110,77 +110,35 @@ function AppProviders({ children }: { children: React.ReactNode }) {
110
110
 
111
111
  ---
112
112
 
113
- ## 🔐 Sign-In with Ethereum (SIWE) Integration
113
+ ## 🔐 Sign-In With X (SIWX) Session Integration
114
114
 
115
- The `satelliteEVMAdapter` seamlessly integrates with SIWE solutions like `@tuwaio/satellite-siwe-next-auth`. You can pass the `signInWithSiwe` function (obtained from the SIWE provider/hook) as the second argument to the adapter.
116
-
117
- This ensures that the SIWE flow is automatically triggered after a successful wallet connection.
115
+ The `EVMConnectorsWatcher` component accepts a `siwx` prop directly compatible with `@tuwaio/siwx-react`. It monitors session status and automatically handles wallet disconnections if the user switches accounts or chains without an active SIWX session.
118
116
 
119
117
  ```tsx
120
- // Example: SIWE integration with @tuwaio/satellite-siwe-next-auth
121
-
122
- import { useSiweAuth, SiweNextAuthProvider } from '@tuwaio/satellite-siwe-next-auth';
118
+ import { useSiwxSession } from '@tuwaio/siwx-react';
123
119
  import { SatelliteConnectProvider } from '@tuwaio/satellite-react';
124
120
  import { EVMConnectorsWatcher } from '@tuwaio/satellite-react/evm';
125
121
  import { satelliteEVMAdapter } from '@tuwaio/satellite-evm';
126
- import { WagmiProvider } from 'wagmi';
127
- import { wagmiConfig } from './your-wagmi-config'; // Your Wagmi config
128
- import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
129
- import { ReactNode, useState } from 'react';
130
122
 
131
- // Step 1: Create SatelliteConnectProvider wrapper that consumes SIWE context
132
- function SatelliteSiweProvider({ children }: { children: ReactNode }) {
133
- // Get SIWE auth state and methods from the provider above
134
- const { signInWithSiwe, enabled: siweEnabled, isRejected, isSignedIn } = useSiweAuth();
123
+ function AppProviders({ children }: { children: ReactNode }) {
124
+ const siwxSession = useSiwxSession();
135
125
 
136
126
  return (
137
- <SatelliteConnectProvider
138
- // Create the adapter, passing signInWithSiwe if SIWE is enabled
139
- adapter={satelliteEVMAdapter(wagmiConfig, appEVMChains, siweEnabled ? signInWithSiwe : undefined)}
140
- autoConnect={true}
141
- >
142
- {/* Pass SIWE state to watcher for handling disconnections on SIWE rejection */}
143
- <EVMConnectorsWatcher wagmiConfig={wagmiConfig} siwe={{ isSignedIn, isRejected, enabled: siweEnabled }} />
127
+ <SatelliteConnectProvider adapter={satelliteEVMAdapter(wagmiConfig, appEVMChains)} autoConnect={true}>
128
+ <EVMConnectorsWatcher wagmiConfig={wagmiConfig} siwx={siwxSession} />
144
129
  {children}
145
130
  </SatelliteConnectProvider>
146
131
  );
147
132
  }
148
-
149
- // Step 2: Compose all providers in the correct order
150
- // Create QueryClient
151
- const queryClient = new QueryClient();
152
-
153
- function Providers({ children }: { children: ReactNode }) {
154
- return (
155
- <WagmiProvider config={wagmiConfig}>
156
- <QueryClientProvider client={queryClient}>
157
- {/* SIWE Provider must wrap SatelliteSiweProvider to provide context */}
158
- <SiweNextAuthProvider wagmiConfig={wagmiConfig} enabled={true}>
159
- <SatelliteSiweProvider>{children}</SatelliteSiweProvider>
160
- </SiweNextAuthProvider>
161
- </QueryClientProvider>
162
- </WagmiProvider>
163
- );
164
- }
165
-
166
- // Step 3: Use Providers in your application layout
167
- function RootLayout({ children }: { children: React.ReactNode }) {
168
- return (
169
- <Providers>
170
- {/* Your application components */}
171
- {children}
172
- </Providers>
173
- );
174
- }
175
133
  ```
176
134
 
177
135
  ---
178
136
 
179
- - ## 🛠️ Core Utilities
137
+ ## 🛠️ Core Utilities
180
138
 
181
139
  - **`createDefaultTransports`**: Helper to create default `http` transports for each chain in your `wagmiConfig`.
182
140
  - **`checkIsWalletAddressContract`**: Utility to check if a connected address is a smart contract address. The result is cached in memory.
183
- - **`createEVMConnectionsWatcher`**: Framework-agnostic utility to monitor wagmi connection changes and synchronize them with the global state store. Handles account switches, network changes, disconnections, and optional SIWE rejection scenarios. Returns a cleanup function.
141
+ - **`createEVMConnectionsWatcher`**: Framework-agnostic utility to monitor wagmi connection changes and synchronize them with the global state store. Handles account switches, network changes, disconnections, and optional SIWX session rejection scenarios. Returns a cleanup function.
184
142
 
185
143
  ---
186
144
 
package/dist/index.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { BaseConnector, SatelliteAdapter } from '@tuwaio/satellite-core';
1
+ import { BaseConnector, SatelliteAdapter, SatelliteSiwxState } from '@tuwaio/satellite-core';
2
2
  import * as _wagmi_core from '@wagmi/core';
3
3
  import { Connector, CreateConnectorFn, Config, CreateConfigParameters } from '@wagmi/core';
4
4
  import { Chain } from 'viem/chains';
@@ -39,7 +39,7 @@ interface EVMConnection extends BaseConnector {
39
39
  * const evmAdapter = satelliteEVMAdapter(config, chains);
40
40
  * ```
41
41
  */
42
- declare function satelliteEVMAdapter(config: Config, chains: readonly [Chain, ...Chain[]], signInWithSiwe?: () => Promise<void>): SatelliteAdapter<ConnectorEVM, EVMConnection>;
42
+ declare function satelliteEVMAdapter(config: Config, chains: readonly [Chain, ...Chain[]]): SatelliteAdapter<ConnectorEVM, EVMConnection>;
43
43
 
44
44
  /**
45
45
  * Configuration parameters for impersonated wallet connector
@@ -145,14 +145,13 @@ interface EVMWatcherCallbacks {
145
145
  interface EVMWatcherConfig {
146
146
  /** Wagmi configuration object required for connection monitoring */
147
147
  wagmiConfig: Config;
148
- /** Optional Sign-In With Ethereum (SIWE) configuration */
148
+ /** Optional Sign-In With X (SIWX) session state */
149
+ siwx?: SatelliteSiwxState;
150
+ /** @deprecated Legacy SIWE prop alias for backwards compatibility */
149
151
  siwe?: {
150
- /** Whether SIWE authentication is enabled */
151
- enabled: boolean;
152
- /** Whether the user is currently signed in via SIWE */
153
- isSignedIn: boolean;
154
- /** Whether the user has rejected the SIWE signature request */
155
- isRejected: boolean;
152
+ enabled?: boolean;
153
+ isSignedIn?: boolean;
154
+ isRejected?: boolean;
156
155
  };
157
156
  }
158
157
  /**
@@ -162,20 +161,25 @@ interface EVMWatcherConfig {
162
161
  * This function provides a pure, framework-agnostic way to watch EVM connections
163
162
  * without being tied to React hooks or components.
164
163
  *
165
- * @param config - Configuration object containing wagmi config and optional SIWE settings
164
+ * @param config - Configuration object containing wagmi config and optional SIWX session settings
166
165
  * @param callbacks - Callback functions for interacting with the global state
167
166
  * @returns A cleanup function to stop watching connections
168
167
  *
169
168
  * @example
170
169
  * ```typescript
171
170
  * const unwatch = createEVMConnectionsWatcher(
172
- * { wagmiConfig, siwe: { enabled: true, isSignedIn: true, isRejected: false } },
171
+ * { wagmiConfig, siwx: { enabled: true, isSignedIn: true, isRejected: false } },
173
172
  * { activeConnection, disconnect, connectionError, updateActiveConnection }
174
173
  * );
175
174
  *
176
175
  * // Later, when you need to stop watching:
177
176
  * unwatch();
178
177
  * ```
178
+ *
179
+ * @remarks
180
+ * Evaluates session parity on account and network switches. If `siwx` is enabled and
181
+ * the active session address or chainId does not match the newly connected wallet state,
182
+ * it automatically triggers a `disconnect()` to prevent stale session attacks.
179
183
  */
180
184
  declare function createEVMConnectionsWatcher(config: EVMWatcherConfig, callbacks: EVMWatcherCallbacks): () => void;
181
185
 
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { BaseConnector, SatelliteAdapter } from '@tuwaio/satellite-core';
1
+ import { BaseConnector, SatelliteAdapter, SatelliteSiwxState } from '@tuwaio/satellite-core';
2
2
  import * as _wagmi_core from '@wagmi/core';
3
3
  import { Connector, CreateConnectorFn, Config, CreateConfigParameters } from '@wagmi/core';
4
4
  import { Chain } from 'viem/chains';
@@ -39,7 +39,7 @@ interface EVMConnection extends BaseConnector {
39
39
  * const evmAdapter = satelliteEVMAdapter(config, chains);
40
40
  * ```
41
41
  */
42
- declare function satelliteEVMAdapter(config: Config, chains: readonly [Chain, ...Chain[]], signInWithSiwe?: () => Promise<void>): SatelliteAdapter<ConnectorEVM, EVMConnection>;
42
+ declare function satelliteEVMAdapter(config: Config, chains: readonly [Chain, ...Chain[]]): SatelliteAdapter<ConnectorEVM, EVMConnection>;
43
43
 
44
44
  /**
45
45
  * Configuration parameters for impersonated wallet connector
@@ -145,14 +145,13 @@ interface EVMWatcherCallbacks {
145
145
  interface EVMWatcherConfig {
146
146
  /** Wagmi configuration object required for connection monitoring */
147
147
  wagmiConfig: Config;
148
- /** Optional Sign-In With Ethereum (SIWE) configuration */
148
+ /** Optional Sign-In With X (SIWX) session state */
149
+ siwx?: SatelliteSiwxState;
150
+ /** @deprecated Legacy SIWE prop alias for backwards compatibility */
149
151
  siwe?: {
150
- /** Whether SIWE authentication is enabled */
151
- enabled: boolean;
152
- /** Whether the user is currently signed in via SIWE */
153
- isSignedIn: boolean;
154
- /** Whether the user has rejected the SIWE signature request */
155
- isRejected: boolean;
152
+ enabled?: boolean;
153
+ isSignedIn?: boolean;
154
+ isRejected?: boolean;
156
155
  };
157
156
  }
158
157
  /**
@@ -162,20 +161,25 @@ interface EVMWatcherConfig {
162
161
  * This function provides a pure, framework-agnostic way to watch EVM connections
163
162
  * without being tied to React hooks or components.
164
163
  *
165
- * @param config - Configuration object containing wagmi config and optional SIWE settings
164
+ * @param config - Configuration object containing wagmi config and optional SIWX session settings
166
165
  * @param callbacks - Callback functions for interacting with the global state
167
166
  * @returns A cleanup function to stop watching connections
168
167
  *
169
168
  * @example
170
169
  * ```typescript
171
170
  * const unwatch = createEVMConnectionsWatcher(
172
- * { wagmiConfig, siwe: { enabled: true, isSignedIn: true, isRejected: false } },
171
+ * { wagmiConfig, siwx: { enabled: true, isSignedIn: true, isRejected: false } },
173
172
  * { activeConnection, disconnect, connectionError, updateActiveConnection }
174
173
  * );
175
174
  *
176
175
  * // Later, when you need to stop watching:
177
176
  * unwatch();
178
177
  * ```
178
+ *
179
+ * @remarks
180
+ * Evaluates session parity on account and network switches. If `siwx` is enabled and
181
+ * the active session address or chainId does not match the newly connected wallet state,
182
+ * it automatically triggers a `disconnect()` to prevent stale session attacks.
179
183
  */
180
184
  declare function createEVMConnectionsWatcher(config: EVMWatcherConfig, callbacks: EVMWatcherCallbacks): () => void;
181
185
 
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- 'use strict';var orbitCore=require('@tuwaio/orbit-core'),orbitEvm=require('@tuwaio/orbit-evm'),core=require('@wagmi/core'),viem=require('viem'),chains=require('viem/chains'),utils=require('viem/utils');var g=new Map;async function b({config:a,address:o,chainId:s,chains:e}){if(g.has(o))return g.get(o);if(orbitEvm.createViemClient(s,e)){let n=!!await core.getBytecode(a,{address:o});return g.set(o,n),n}else return false}function Ce(a,o,s){if(!a)throw new Error("Satellite EVM adapter requires a wagmi config object.");return {key:orbitCore.OrbitAdapter.EVM,connect:async({connectorType:e,chainId:t})=>{let n=core.getConnectors(a).find(c=>orbitCore.getConnectorTypeFromName(orbitCore.OrbitAdapter.EVM,orbitCore.formatConnectorName(c.name))===e);if(!n)throw new Error("Cannot find connector with this wallet type");try{await core.connect(a,{connector:n,chainId:t}),s&&!orbitCore.isSafeApp&&orbitCore.formatConnectorName(n.name)!=="porto"&&orbitCore.formatConnectorName(n.name)!=="geminiwallet"&&orbitCore.formatConnectorName(n.name)!=="impersonatedwallet"&&await s();let c=core.getConnection(a);return {connectorType:e,address:c.address??viem.zeroAddress,chainId:c.chainId??chains.mainnet.id,rpcURL:c.chain?.rpcUrls.default.http[0]??chains.mainnet.rpcUrls.default.http[0],isConnected:c.isConnected,isContractAddress:!1,icon:n?.icon?.trim(),connector:n}}catch(c){throw new Error(c instanceof Error?c.message:String(c),{cause:c})}},disconnect:async e=>{if(e&&e.isConnected)await core.disconnect(a,{connector:e?.connector});else {let t=core.getConnectors(a);await Promise.allSettled(t.map(async r=>{await core.disconnect(a,{connector:r});}));}},getConnectors:()=>{let e=core.getConnectors(a);return {adapter:orbitCore.OrbitAdapter.EVM,connectors:e.map(t=>t)}},checkAndSwitchNetwork:async e=>await orbitEvm.checkAndSwitchChain(Number(e),a),getBalance:async(e,t)=>{let r=await core.getBalance(a,{address:e,chainId:Number(t)});return {value:viem.formatUnits(r.value,r.decimals),symbol:r.symbol}},getExplorerUrl:e=>{let{chain:t}=core.getConnection(a),r=t?.blockExplorers?.default.url;return e?`${r}/${e}`:r},getName:e=>orbitEvm.getName(e,o),getAvatar:e=>orbitEvm.getAvatar(e,o),getAddress:e=>orbitEvm.getAddress(e,o),checkIsContractAddress:async({address:e,chainId:t})=>{let r=core.getChains(a);return await b({config:a,address:e,chainId:t,chains:r})},getSafeConnectorChainId:async()=>{let t=core.getConnectors(a).find(r=>r.name==="Safe");if(t)return await t.getChainId()},switchConnection:async e=>{let r=core.getConnectors(a).find(n=>orbitCore.getConnectorTypeFromName(orbitCore.OrbitAdapter.EVM,orbitCore.formatConnectorName(n.name))===e);if(!r)throw new Error(`Cannot find connector with type: ${e}`);try{await core.switchConnection(a,{connector:r});}catch(n){throw new Error(`Failed to switch to connector ${e}: ${n instanceof Error?n.message:String(n)}`,{cause:n})}}}}S.type="impersonated";function S(a){let o=a.features??{},s=false,e,t;return core.createConnector(r=>({id:"impersonated",name:"Impersonated Connector",type:S.type,async setup(){e=r.chains[0].id;},async connect({chainId:n}={}){if(o.connectError)throw typeof o.connectError=="boolean"?new viem.UserRejectedRequestError(new Error("Failed to connect.")):o.connectError;let{request:c}=await this.getProvider(),d=await c({method:"eth_requestAccounts"}),h=await this.getChainId();return n&&h!==n&&(h=(await this.switchChain({chainId:n})).id),s=true,{accounts:d,chainId:h}},async disconnect(){s=false,t=void 0;},async getAccounts(){if(!s)throw new Error("Not connected connector");let{request:n}=await this.getProvider();return (await n({method:"eth_accounts"})).map(viem.getAddress)},async getChainId(){let{request:n}=await this.getProvider(),c=await n({method:"eth_chainId"});return viem.fromHex(c,"number")},async isAuthorized(){return s?!!(await this.getAccounts()).length:false},async switchChain({chainId:n}){let c=r.chains.find(h=>h.id===n);if(!c)throw new viem.SwitchChainError(new core.ChainNotConfiguredError);let{request:d}=await this.getProvider();return await d({method:"wallet_switchEthereumChain",params:[{chainId:viem.numberToHex(n)}]}),c},onAccountsChanged(n){n.length===0?this.onDisconnect():r.emitter.emit("change",{accounts:n.map(viem.getAddress)});},onChainChanged(n){let c=Number(n);r.emitter.emit("change",{chainId:c});},async onDisconnect(){r.emitter.emit("disconnect"),s=false,t=void 0;},async getProvider({chainId:n}={}){t=orbitCore.impersonatedHelpers?.getImpersonated()?[orbitCore.impersonatedHelpers.getImpersonated()||viem.zeroAddress]:void 0;let d=(r.chains.find(i=>i.id===n)??r.chains[0]).rpcUrls.default.http[0];return viem.custom({request:async({method:i,params:p})=>{if(i==="eth_chainId")return viem.numberToHex(e);if(i==="eth_requestAccounts")return t;if(i==="eth_signTypedData_v4"&&o.signTypedDataError)throw typeof o.signTypedDataError=="boolean"?new viem.UserRejectedRequestError(new Error("Failed to sign typed data.")):o.signTypedDataError;if(i==="wallet_switchEthereumChain"){if(o.switchChainError)throw typeof o.switchChainError=="boolean"?new viem.UserRejectedRequestError(new Error("Failed to switch chain.")):o.switchChainError;e=viem.fromHex(p[0].chainId,"number"),this.onChainChanged(e.toString());return}if(i==="personal_sign"){if(o.signMessageError)throw typeof o.signMessageError=="boolean"?new viem.UserRejectedRequestError(new Error("Failed to sign message.")):o.signMessageError;i="eth_sign",p=[p[1],p[0]];}let m={method:i,params:p},{error:u,result:y}=await utils.rpc.http(d,{body:m});if(u)throw new viem.RpcRequestError({body:m,error:u,url:d});return y}})({retryCount:1})}}))}var Ie={allowedDomains:[/gnosis-safe.io$/,/app.safe.global$/,/metissafe.tech$/],debug:false};var Ve=a=>a.reduce((o,s)=>{let e=s.id;return o[e]=viem.http(),o},{});function We(a,o){let{wagmiConfig:s,siwe:e}=a,{activeConnection:t,disconnect:r,connectionError:n,updateActiveConnection:c}=o,d=()=>{e?.enabled&&!e?.isSignedIn&&e?.isRejected&&t&&r(t.connectorType);},h=p=>{if(t&&orbitCore.getAdapterFromConnectorType(t.connectorType)!==orbitCore.OrbitAdapter.EVM)return;if(p.length===0){t&&r(t.connectorType);return}let m=core.getConnection(s);if(t&&orbitCore.getAdapterFromConnectorType(t.connectorType)!==orbitCore.OrbitAdapter.EVM||!m||n)return;let u=m.connector,k={connectorType:u?`${orbitCore.OrbitAdapter.EVM}:${orbitCore.formatConnectorName(u.name)}`:t?.connectorType,address:m.address,chainId:m.chainId,rpcURL:m?.chain?.rpcUrls.default.http[0],isConnected:true};c(k);};return d(),core.watchConnections(s,{onChange:h})}exports.checkIsWalletAddressContract=b;exports.createDefaultTransports=Ve;exports.createEVMConnectionsWatcher=We;exports.impersonated=S;exports.safeSdkOptions=Ie;exports.satelliteEVMAdapter=Ce;
1
+ 'use strict';var orbitCore=require('@tuwaio/orbit-core'),orbitEvm=require('@tuwaio/orbit-evm'),core=require('@wagmi/core'),viem=require('viem'),chains=require('viem/chains'),utils=require('viem/utils');var y=new Map;async function A({config:o,address:s,chainId:e,chains:n}){if(y.has(s))return y.get(s);if(orbitEvm.createViemClient(e,n)){let r=!!await core.getBytecode(o,{address:s});return y.set(s,r),r}else return false}function Ie(o,s){if(!o)throw new Error("Satellite EVM adapter requires a wagmi config object.");return {key:orbitCore.OrbitAdapter.EVM,connect:async({connectorType:e,chainId:n})=>{let a=core.getConnectors(o).find(r=>orbitCore.getConnectorTypeFromName(orbitCore.OrbitAdapter.EVM,orbitCore.formatConnectorName(r.name))===e);if(!a)throw new Error("Cannot find connector with this wallet type");try{await core.connect(o,{connector:a,chainId:n});let r=core.getConnection(o);return {connectorType:e,address:r.address??viem.zeroAddress,chainId:r.chainId??chains.mainnet.id,rpcURL:r.chain?.rpcUrls.default.http[0]??chains.mainnet.rpcUrls.default.http[0],isConnected:r.isConnected,isContractAddress:!1,icon:a?.icon?.trim(),connector:a,signMessage:c=>core.signMessage(o,{message:c})}}catch(r){throw new Error(r instanceof Error?r.message:String(r),{cause:r})}},disconnect:async e=>{if(e&&e.isConnected)await core.disconnect(o,{connector:e?.connector});else {let n=core.getConnectors(o);await Promise.allSettled(n.map(async t=>{await core.disconnect(o,{connector:t});}));}},getConnectors:()=>{let e=core.getConnectors(o);return {adapter:orbitCore.OrbitAdapter.EVM,connectors:e.map(n=>n)}},checkAndSwitchNetwork:async e=>await orbitEvm.checkAndSwitchChain(Number(e),o),getBalance:async(e,n)=>{let t=await core.getBalance(o,{address:e,chainId:Number(n)});return {value:viem.formatUnits(t.value,t.decimals),symbol:t.symbol}},getExplorerUrl:e=>{let{chain:n}=core.getConnection(o),t=n?.blockExplorers?.default.url;return e?`${t}/${e}`:t},getName:e=>orbitEvm.getName(e,s),getAvatar:e=>orbitEvm.getAvatar(e,s),getAddress:e=>orbitEvm.getAddress(e,s),checkIsContractAddress:async({address:e,chainId:n})=>{let t=core.getChains(o);return await A({config:o,address:e,chainId:n,chains:t})},getSafeConnectorChainId:async()=>{let n=core.getConnectors(o).find(t=>t.name==="Safe");if(n)return await n.getChainId()},switchConnection:async e=>{let t=core.getConnectors(o).find(a=>orbitCore.getConnectorTypeFromName(orbitCore.OrbitAdapter.EVM,orbitCore.formatConnectorName(a.name))===e);if(!t)throw new Error(`Cannot find connector with type: ${e}`);try{await core.switchConnection(o,{connector:t});}catch(a){throw new Error(`Failed to switch to connector ${e}: ${a instanceof Error?a.message:String(a)}`,{cause:a})}}}}q.type="impersonated";function q(o){let s=o.features??{},e=false,n,t;return core.createConnector(a=>({id:"impersonated",name:"Impersonated Connector",type:q.type,async setup(){n=a.chains[0].id;},async connect({chainId:r}={}){if(s.connectError)throw typeof s.connectError=="boolean"?new viem.UserRejectedRequestError(new Error("Failed to connect.")):s.connectError;let{request:c}=await this.getProvider(),p=await c({method:"eth_requestAccounts"}),u=await this.getChainId();return r&&u!==r&&(u=(await this.switchChain({chainId:r})).id),e=true,{accounts:p,chainId:u}},async disconnect(){e=false,t=void 0;},async getAccounts(){if(!e)throw new Error("Not connected connector");let{request:r}=await this.getProvider();return (await r({method:"eth_accounts"})).map(viem.getAddress)},async getChainId(){let{request:r}=await this.getProvider(),c=await r({method:"eth_chainId"});return viem.fromHex(c,"number")},async isAuthorized(){return e?!!(await this.getAccounts()).length:false},async switchChain({chainId:r}){let c=a.chains.find(u=>u.id===r);if(!c)throw new viem.SwitchChainError(new core.ChainNotConfiguredError);let{request:p}=await this.getProvider();return await p({method:"wallet_switchEthereumChain",params:[{chainId:viem.numberToHex(r)}]}),c},onAccountsChanged(r){r.length===0?this.onDisconnect():a.emitter.emit("change",{accounts:r.map(viem.getAddress)});},onChainChanged(r){let c=Number(r);a.emitter.emit("change",{chainId:c});},async onDisconnect(){a.emitter.emit("disconnect"),e=false,t=void 0;},async getProvider({chainId:r}={}){t=orbitCore.impersonatedHelpers?.getImpersonated()?[orbitCore.impersonatedHelpers.getImpersonated()||viem.zeroAddress]:void 0;let p=(a.chains.find(h=>h.id===r)??a.chains[0]).rpcUrls.default.http[0];return viem.custom({request:async({method:h,params:d})=>{if(h==="eth_chainId")return viem.numberToHex(n);if(h==="eth_requestAccounts")return t;if(h==="eth_signTypedData_v4"&&s.signTypedDataError)throw typeof s.signTypedDataError=="boolean"?new viem.UserRejectedRequestError(new Error("Failed to sign typed data.")):s.signTypedDataError;if(h==="wallet_switchEthereumChain"){if(s.switchChainError)throw typeof s.switchChainError=="boolean"?new viem.UserRejectedRequestError(new Error("Failed to switch chain.")):s.switchChainError;n=viem.fromHex(d[0].chainId,"number"),this.onChainChanged(n.toString());return}if(h==="personal_sign"){if(s.signMessageError)throw typeof s.signMessageError=="boolean"?new viem.UserRejectedRequestError(new Error("Failed to sign message.")):s.signMessageError;h="eth_sign",d=[d[1],d[0]];}let i={method:h,params:d},{error:m,result:l}=await utils.rpc.http(p,{body:i});if(m)throw new viem.RpcRequestError({body:i,error:m,url:p});return l}})({retryCount:1})}}))}var Pe={allowedDomains:[/gnosis-safe.io$/,/app.safe.global$/,/metissafe.tech$/],debug:false};var Ne=o=>o.reduce((s,e)=>{let n=e.id;return s[n]=viem.http(),s},{});function je(o,s){let{wagmiConfig:e}=o,n=o.siwx??(o.siwe?{enabled:o.siwe.enabled,isSignedIn:o.siwe.isSignedIn,isRejected:o.siwe.isRejected}:void 0),{activeConnection:t,disconnect:a,connectionError:r,updateActiveConnection:c}=s,p=()=>{let d=n?.isRejected||n?.status==="error",i=n?.isSignedIn??n?.isAuthenticated??false;n?.enabled!==false&&!i&&d&&t&&a(t.connectorType);},u=d=>{if(t&&orbitCore.getAdapterFromConnectorType(t.connectorType)!==orbitCore.OrbitAdapter.EVM)return;if(d.length===0){t&&a(t.connectorType);return}let i=core.getConnection(e);if(t&&orbitCore.getAdapterFromConnectorType(t.connectorType)!==orbitCore.OrbitAdapter.EVM||!i||r)return;let m=n?.address??n?.session?.address,l=n?.chainId??n?.session?.chainId;if((n?.isSignedIn??n?.isAuthenticated??false)&&t){let E=i.address&&m&&i.address.toLowerCase()!==m.toLowerCase()&&!m.toLowerCase().endsWith(i.address.toLowerCase()),_=i.chainId&&l&&String(i.chainId)!==String(l)&&!l.endsWith(`:${i.chainId}`);if(E||_){a(t.connectorType);return}}let I=i.connector,$={connectorType:I?`${orbitCore.OrbitAdapter.EVM}:${orbitCore.formatConnectorName(I.name)}`:t?.connectorType,address:i.address,chainId:i.chainId,rpcURL:i?.chain?.rpcUrls.default.http[0],isConnected:true,signMessage:E=>core.signMessage(e,{message:E})};c($);};if(p(),t&&orbitCore.getAdapterFromConnectorType(t.connectorType)===orbitCore.OrbitAdapter.EVM){let d=core.getConnection(e);d&&d.isConnected&&c({signMessage:i=>core.signMessage(e,{message:i})});}return core.watchConnections(e,{onChange:u})}exports.checkIsWalletAddressContract=A;exports.createDefaultTransports=Ne;exports.createEVMConnectionsWatcher=je;exports.impersonated=q;exports.safeSdkOptions=Pe;exports.satelliteEVMAdapter=Ie;
package/dist/index.mjs CHANGED
@@ -1 +1 @@
1
- import {OrbitAdapter,getConnectorTypeFromName,formatConnectorName,isSafeApp,impersonatedHelpers,getAdapterFromConnectorType}from'@tuwaio/orbit-core';import {createViemClient,getAddress,getAvatar,getName,checkAndSwitchChain}from'@tuwaio/orbit-evm';import {getBytecode,getConnectors,switchConnection,getChains,getConnection,getBalance,disconnect,connect,createConnector,ChainNotConfiguredError,watchConnections}from'@wagmi/core';import {formatUnits,zeroAddress,custom,numberToHex,UserRejectedRequestError,fromHex,RpcRequestError,getAddress as getAddress$1,SwitchChainError,http}from'viem';import {mainnet}from'viem/chains';import {rpc}from'viem/utils';var g=new Map;async function b({config:a,address:o,chainId:s,chains:e}){if(g.has(o))return g.get(o);if(createViemClient(s,e)){let n=!!await getBytecode(a,{address:o});return g.set(o,n),n}else return false}function Ce(a,o,s){if(!a)throw new Error("Satellite EVM adapter requires a wagmi config object.");return {key:OrbitAdapter.EVM,connect:async({connectorType:e,chainId:t})=>{let n=getConnectors(a).find(c=>getConnectorTypeFromName(OrbitAdapter.EVM,formatConnectorName(c.name))===e);if(!n)throw new Error("Cannot find connector with this wallet type");try{await connect(a,{connector:n,chainId:t}),s&&!isSafeApp&&formatConnectorName(n.name)!=="porto"&&formatConnectorName(n.name)!=="geminiwallet"&&formatConnectorName(n.name)!=="impersonatedwallet"&&await s();let c=getConnection(a);return {connectorType:e,address:c.address??zeroAddress,chainId:c.chainId??mainnet.id,rpcURL:c.chain?.rpcUrls.default.http[0]??mainnet.rpcUrls.default.http[0],isConnected:c.isConnected,isContractAddress:!1,icon:n?.icon?.trim(),connector:n}}catch(c){throw new Error(c instanceof Error?c.message:String(c),{cause:c})}},disconnect:async e=>{if(e&&e.isConnected)await disconnect(a,{connector:e?.connector});else {let t=getConnectors(a);await Promise.allSettled(t.map(async r=>{await disconnect(a,{connector:r});}));}},getConnectors:()=>{let e=getConnectors(a);return {adapter:OrbitAdapter.EVM,connectors:e.map(t=>t)}},checkAndSwitchNetwork:async e=>await checkAndSwitchChain(Number(e),a),getBalance:async(e,t)=>{let r=await getBalance(a,{address:e,chainId:Number(t)});return {value:formatUnits(r.value,r.decimals),symbol:r.symbol}},getExplorerUrl:e=>{let{chain:t}=getConnection(a),r=t?.blockExplorers?.default.url;return e?`${r}/${e}`:r},getName:e=>getName(e,o),getAvatar:e=>getAvatar(e,o),getAddress:e=>getAddress(e,o),checkIsContractAddress:async({address:e,chainId:t})=>{let r=getChains(a);return await b({config:a,address:e,chainId:t,chains:r})},getSafeConnectorChainId:async()=>{let t=getConnectors(a).find(r=>r.name==="Safe");if(t)return await t.getChainId()},switchConnection:async e=>{let r=getConnectors(a).find(n=>getConnectorTypeFromName(OrbitAdapter.EVM,formatConnectorName(n.name))===e);if(!r)throw new Error(`Cannot find connector with type: ${e}`);try{await switchConnection(a,{connector:r});}catch(n){throw new Error(`Failed to switch to connector ${e}: ${n instanceof Error?n.message:String(n)}`,{cause:n})}}}}S.type="impersonated";function S(a){let o=a.features??{},s=false,e,t;return createConnector(r=>({id:"impersonated",name:"Impersonated Connector",type:S.type,async setup(){e=r.chains[0].id;},async connect({chainId:n}={}){if(o.connectError)throw typeof o.connectError=="boolean"?new UserRejectedRequestError(new Error("Failed to connect.")):o.connectError;let{request:c}=await this.getProvider(),d=await c({method:"eth_requestAccounts"}),h=await this.getChainId();return n&&h!==n&&(h=(await this.switchChain({chainId:n})).id),s=true,{accounts:d,chainId:h}},async disconnect(){s=false,t=void 0;},async getAccounts(){if(!s)throw new Error("Not connected connector");let{request:n}=await this.getProvider();return (await n({method:"eth_accounts"})).map(getAddress$1)},async getChainId(){let{request:n}=await this.getProvider(),c=await n({method:"eth_chainId"});return fromHex(c,"number")},async isAuthorized(){return s?!!(await this.getAccounts()).length:false},async switchChain({chainId:n}){let c=r.chains.find(h=>h.id===n);if(!c)throw new SwitchChainError(new ChainNotConfiguredError);let{request:d}=await this.getProvider();return await d({method:"wallet_switchEthereumChain",params:[{chainId:numberToHex(n)}]}),c},onAccountsChanged(n){n.length===0?this.onDisconnect():r.emitter.emit("change",{accounts:n.map(getAddress$1)});},onChainChanged(n){let c=Number(n);r.emitter.emit("change",{chainId:c});},async onDisconnect(){r.emitter.emit("disconnect"),s=false,t=void 0;},async getProvider({chainId:n}={}){t=impersonatedHelpers?.getImpersonated()?[impersonatedHelpers.getImpersonated()||zeroAddress]:void 0;let d=(r.chains.find(i=>i.id===n)??r.chains[0]).rpcUrls.default.http[0];return custom({request:async({method:i,params:p})=>{if(i==="eth_chainId")return numberToHex(e);if(i==="eth_requestAccounts")return t;if(i==="eth_signTypedData_v4"&&o.signTypedDataError)throw typeof o.signTypedDataError=="boolean"?new UserRejectedRequestError(new Error("Failed to sign typed data.")):o.signTypedDataError;if(i==="wallet_switchEthereumChain"){if(o.switchChainError)throw typeof o.switchChainError=="boolean"?new UserRejectedRequestError(new Error("Failed to switch chain.")):o.switchChainError;e=fromHex(p[0].chainId,"number"),this.onChainChanged(e.toString());return}if(i==="personal_sign"){if(o.signMessageError)throw typeof o.signMessageError=="boolean"?new UserRejectedRequestError(new Error("Failed to sign message.")):o.signMessageError;i="eth_sign",p=[p[1],p[0]];}let m={method:i,params:p},{error:u,result:y}=await rpc.http(d,{body:m});if(u)throw new RpcRequestError({body:m,error:u,url:d});return y}})({retryCount:1})}}))}var Ie={allowedDomains:[/gnosis-safe.io$/,/app.safe.global$/,/metissafe.tech$/],debug:false};var Ve=a=>a.reduce((o,s)=>{let e=s.id;return o[e]=http(),o},{});function We(a,o){let{wagmiConfig:s,siwe:e}=a,{activeConnection:t,disconnect:r,connectionError:n,updateActiveConnection:c}=o,d=()=>{e?.enabled&&!e?.isSignedIn&&e?.isRejected&&t&&r(t.connectorType);},h=p=>{if(t&&getAdapterFromConnectorType(t.connectorType)!==OrbitAdapter.EVM)return;if(p.length===0){t&&r(t.connectorType);return}let m=getConnection(s);if(t&&getAdapterFromConnectorType(t.connectorType)!==OrbitAdapter.EVM||!m||n)return;let u=m.connector,k={connectorType:u?`${OrbitAdapter.EVM}:${formatConnectorName(u.name)}`:t?.connectorType,address:m.address,chainId:m.chainId,rpcURL:m?.chain?.rpcUrls.default.http[0],isConnected:true};c(k);};return d(),watchConnections(s,{onChange:h})}export{b as checkIsWalletAddressContract,Ve as createDefaultTransports,We as createEVMConnectionsWatcher,S as impersonated,Ie as safeSdkOptions,Ce as satelliteEVMAdapter};
1
+ import {OrbitAdapter,getConnectorTypeFromName,formatConnectorName,impersonatedHelpers,getAdapterFromConnectorType}from'@tuwaio/orbit-core';import {createViemClient,getAddress,getAvatar,getName,checkAndSwitchChain}from'@tuwaio/orbit-evm';import {getBytecode,getConnectors,switchConnection,getChains,getConnection,getBalance,disconnect,connect,signMessage,createConnector,ChainNotConfiguredError,watchConnections}from'@wagmi/core';import {formatUnits,zeroAddress,custom,numberToHex,UserRejectedRequestError,fromHex,RpcRequestError,getAddress as getAddress$1,SwitchChainError,http}from'viem';import {mainnet}from'viem/chains';import {rpc}from'viem/utils';var y=new Map;async function A({config:o,address:s,chainId:e,chains:n}){if(y.has(s))return y.get(s);if(createViemClient(e,n)){let r=!!await getBytecode(o,{address:s});return y.set(s,r),r}else return false}function Ie(o,s){if(!o)throw new Error("Satellite EVM adapter requires a wagmi config object.");return {key:OrbitAdapter.EVM,connect:async({connectorType:e,chainId:n})=>{let a=getConnectors(o).find(r=>getConnectorTypeFromName(OrbitAdapter.EVM,formatConnectorName(r.name))===e);if(!a)throw new Error("Cannot find connector with this wallet type");try{await connect(o,{connector:a,chainId:n});let r=getConnection(o);return {connectorType:e,address:r.address??zeroAddress,chainId:r.chainId??mainnet.id,rpcURL:r.chain?.rpcUrls.default.http[0]??mainnet.rpcUrls.default.http[0],isConnected:r.isConnected,isContractAddress:!1,icon:a?.icon?.trim(),connector:a,signMessage:c=>signMessage(o,{message:c})}}catch(r){throw new Error(r instanceof Error?r.message:String(r),{cause:r})}},disconnect:async e=>{if(e&&e.isConnected)await disconnect(o,{connector:e?.connector});else {let n=getConnectors(o);await Promise.allSettled(n.map(async t=>{await disconnect(o,{connector:t});}));}},getConnectors:()=>{let e=getConnectors(o);return {adapter:OrbitAdapter.EVM,connectors:e.map(n=>n)}},checkAndSwitchNetwork:async e=>await checkAndSwitchChain(Number(e),o),getBalance:async(e,n)=>{let t=await getBalance(o,{address:e,chainId:Number(n)});return {value:formatUnits(t.value,t.decimals),symbol:t.symbol}},getExplorerUrl:e=>{let{chain:n}=getConnection(o),t=n?.blockExplorers?.default.url;return e?`${t}/${e}`:t},getName:e=>getName(e,s),getAvatar:e=>getAvatar(e,s),getAddress:e=>getAddress(e,s),checkIsContractAddress:async({address:e,chainId:n})=>{let t=getChains(o);return await A({config:o,address:e,chainId:n,chains:t})},getSafeConnectorChainId:async()=>{let n=getConnectors(o).find(t=>t.name==="Safe");if(n)return await n.getChainId()},switchConnection:async e=>{let t=getConnectors(o).find(a=>getConnectorTypeFromName(OrbitAdapter.EVM,formatConnectorName(a.name))===e);if(!t)throw new Error(`Cannot find connector with type: ${e}`);try{await switchConnection(o,{connector:t});}catch(a){throw new Error(`Failed to switch to connector ${e}: ${a instanceof Error?a.message:String(a)}`,{cause:a})}}}}q.type="impersonated";function q(o){let s=o.features??{},e=false,n,t;return createConnector(a=>({id:"impersonated",name:"Impersonated Connector",type:q.type,async setup(){n=a.chains[0].id;},async connect({chainId:r}={}){if(s.connectError)throw typeof s.connectError=="boolean"?new UserRejectedRequestError(new Error("Failed to connect.")):s.connectError;let{request:c}=await this.getProvider(),p=await c({method:"eth_requestAccounts"}),u=await this.getChainId();return r&&u!==r&&(u=(await this.switchChain({chainId:r})).id),e=true,{accounts:p,chainId:u}},async disconnect(){e=false,t=void 0;},async getAccounts(){if(!e)throw new Error("Not connected connector");let{request:r}=await this.getProvider();return (await r({method:"eth_accounts"})).map(getAddress$1)},async getChainId(){let{request:r}=await this.getProvider(),c=await r({method:"eth_chainId"});return fromHex(c,"number")},async isAuthorized(){return e?!!(await this.getAccounts()).length:false},async switchChain({chainId:r}){let c=a.chains.find(u=>u.id===r);if(!c)throw new SwitchChainError(new ChainNotConfiguredError);let{request:p}=await this.getProvider();return await p({method:"wallet_switchEthereumChain",params:[{chainId:numberToHex(r)}]}),c},onAccountsChanged(r){r.length===0?this.onDisconnect():a.emitter.emit("change",{accounts:r.map(getAddress$1)});},onChainChanged(r){let c=Number(r);a.emitter.emit("change",{chainId:c});},async onDisconnect(){a.emitter.emit("disconnect"),e=false,t=void 0;},async getProvider({chainId:r}={}){t=impersonatedHelpers?.getImpersonated()?[impersonatedHelpers.getImpersonated()||zeroAddress]:void 0;let p=(a.chains.find(h=>h.id===r)??a.chains[0]).rpcUrls.default.http[0];return custom({request:async({method:h,params:d})=>{if(h==="eth_chainId")return numberToHex(n);if(h==="eth_requestAccounts")return t;if(h==="eth_signTypedData_v4"&&s.signTypedDataError)throw typeof s.signTypedDataError=="boolean"?new UserRejectedRequestError(new Error("Failed to sign typed data.")):s.signTypedDataError;if(h==="wallet_switchEthereumChain"){if(s.switchChainError)throw typeof s.switchChainError=="boolean"?new UserRejectedRequestError(new Error("Failed to switch chain.")):s.switchChainError;n=fromHex(d[0].chainId,"number"),this.onChainChanged(n.toString());return}if(h==="personal_sign"){if(s.signMessageError)throw typeof s.signMessageError=="boolean"?new UserRejectedRequestError(new Error("Failed to sign message.")):s.signMessageError;h="eth_sign",d=[d[1],d[0]];}let i={method:h,params:d},{error:m,result:l}=await rpc.http(p,{body:i});if(m)throw new RpcRequestError({body:i,error:m,url:p});return l}})({retryCount:1})}}))}var Pe={allowedDomains:[/gnosis-safe.io$/,/app.safe.global$/,/metissafe.tech$/],debug:false};var Ne=o=>o.reduce((s,e)=>{let n=e.id;return s[n]=http(),s},{});function je(o,s){let{wagmiConfig:e}=o,n=o.siwx??(o.siwe?{enabled:o.siwe.enabled,isSignedIn:o.siwe.isSignedIn,isRejected:o.siwe.isRejected}:void 0),{activeConnection:t,disconnect:a,connectionError:r,updateActiveConnection:c}=s,p=()=>{let d=n?.isRejected||n?.status==="error",i=n?.isSignedIn??n?.isAuthenticated??false;n?.enabled!==false&&!i&&d&&t&&a(t.connectorType);},u=d=>{if(t&&getAdapterFromConnectorType(t.connectorType)!==OrbitAdapter.EVM)return;if(d.length===0){t&&a(t.connectorType);return}let i=getConnection(e);if(t&&getAdapterFromConnectorType(t.connectorType)!==OrbitAdapter.EVM||!i||r)return;let m=n?.address??n?.session?.address,l=n?.chainId??n?.session?.chainId;if((n?.isSignedIn??n?.isAuthenticated??false)&&t){let E=i.address&&m&&i.address.toLowerCase()!==m.toLowerCase()&&!m.toLowerCase().endsWith(i.address.toLowerCase()),_=i.chainId&&l&&String(i.chainId)!==String(l)&&!l.endsWith(`:${i.chainId}`);if(E||_){a(t.connectorType);return}}let I=i.connector,$={connectorType:I?`${OrbitAdapter.EVM}:${formatConnectorName(I.name)}`:t?.connectorType,address:i.address,chainId:i.chainId,rpcURL:i?.chain?.rpcUrls.default.http[0],isConnected:true,signMessage:E=>signMessage(e,{message:E})};c($);};if(p(),t&&getAdapterFromConnectorType(t.connectorType)===OrbitAdapter.EVM){let d=getConnection(e);d&&d.isConnected&&c({signMessage:i=>signMessage(e,{message:i})});}return watchConnections(e,{onChange:u})}export{A as checkIsWalletAddressContract,Ne as createDefaultTransports,je as createEVMConnectionsWatcher,q as impersonated,Pe as safeSdkOptions,Ie as satelliteEVMAdapter};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tuwaio/satellite-evm",
3
- "version": "0.3.10",
3
+ "version": "0.4.0",
4
4
  "private": false,
5
5
  "author": "Oleksandr Tkach",
6
6
  "license": "Apache-2.0",
@@ -51,18 +51,18 @@
51
51
  "devDependencies": {
52
52
  "@wagmi/core": "^3.6.4",
53
53
  "@tuwaio/orbit-core": "^0.2.15",
54
- "@tuwaio/orbit-evm": "^0.2.21",
55
- "immer": "^11.1.15",
54
+ "@tuwaio/orbit-evm": "^0.2.22",
55
+ "immer": "^11.1.16",
56
56
  "tsup": "^8.5.1",
57
57
  "typescript": "^6.0.3",
58
- "viem": "^2.55.10",
58
+ "viem": "^2.55.11",
59
59
  "vitest": "^4.1.10",
60
60
  "zustand": "^5.0.14",
61
- "@tuwaio/satellite-core": "^0.3.8"
61
+ "@tuwaio/satellite-core": "^0.4.0"
62
62
  },
63
63
  "scripts": {
64
64
  "start": "tsup src/index.ts --watch",
65
65
  "build": "tsup",
66
- "test": "vitest"
66
+ "test": "vitest run --passWithNoTests"
67
67
  }
68
68
  }