@tuwaio/satellite-evm 0.5.0 β 0.6.1
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 +37 -111
- package/dist/index.d.mts +210 -89
- package/dist/index.d.ts +210 -89
- package/dist/index.js +1 -1
- package/dist/index.mjs +1 -1
- package/package.json +14 -14
package/README.md
CHANGED
|
@@ -1,154 +1,80 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @tuwaio/satellite-evm
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/@tuwaio/satellite-evm)
|
|
4
|
-
[](
|
|
5
|
-
[](https://github.com/TuwaIO/satellite-connect/actions)
|
|
4
|
+
[](https://github.com/TuwaIO/satellite-connect/blob/main/packages/satellite-evm/LICENSE)
|
|
6
5
|
|
|
7
|
-
|
|
6
|
+
`@tuwaio/satellite-evm` is the EVM Layer 4 (L4) package of **Satellite Connect**, the wallet connection project of TUWA Stage 2 ("State & Connection", next to Pulsar). Built on **`@wagmi/core`**, **`viem`** and **`@tuwaio/orbit-evm`**, it provides the EVM adapter for [`@tuwaio/satellite-core`](https://satellite.docs.tuwa.io/packages/satellite-core), a watcher that keeps the store in sync with wagmi, and helpers for your wagmi config: default transports, Safe connector options and a connector that impersonates any address for testing. It does not use `ethers.js` or `web3.js`.
|
|
8
7
|
|
|
9
8
|
---
|
|
10
9
|
|
|
11
|
-
## ποΈ
|
|
10
|
+
## ποΈ Core Capabilities
|
|
12
11
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
---
|
|
18
|
-
|
|
19
|
-
## β¨ Engineering Features
|
|
20
|
-
|
|
21
|
-
- **Direct Wagmi Alignment:** Deep integration with `@wagmi/core` configurations, avoiding wrapper bloat.
|
|
22
|
-
- **Custom Connection Watchers:** Low-level event listener hooks tracking provider chain changes, account switches, and connection states.
|
|
23
|
-
- **Contract Wallet Checks:** Native, caching verification utilities (`checkIsWalletAddressContract`) for verifying EIP-1271 signatures or contract accounts.
|
|
24
|
-
- **RPC Transport Isolation:** Isolated transport rules with explicit path mapping using the `createDefaultTransports` controller.
|
|
12
|
+
- **Adapter:** `satelliteEVMAdapter(wagmiConfig, chains)` connects the connectors of your wagmi config (injected wallets found through EIP-6963, WalletConnect, Safe, Coinbaseβ¦) by connector type, such as `"evm:metamask"`. It asks the wallet to switch chains, reads native balances, builds block explorer links (`undefined` when the chain has no explorer), resolves ENS names and avatars on Ethereum Mainnet, and checks whether the account is a smart contract.
|
|
13
|
+
- **Watcher:** `createEVMConnectionsWatcher` subscribes to wagmi's `watchConnections` and copies account and chain changes made in the wallet into the store; it disconnects the store when the wallet disconnects. With a SIWX session it also disconnects when the sign-in is rejected or fails, or when the wallet switches to another account or chain than the session. Pass the store's `getState`, and the watcher reads the current state on every event. React apps use `EVMConnectorsWatcher` from [`@tuwaio/satellite-react/evm`](https://satellite.docs.tuwa.io/packages/satellite-react) instead.
|
|
14
|
+
- **Contract accounts:** `checkIsWalletAddressContract` reads the account's code on the requested chain and caches the result in memory per chain and address.
|
|
15
|
+
- **wagmi config helpers:** `createDefaultTransports(chains)` creates an `http()` transport for every chain; `safeSdkOptions` are the options for the `safe` connector of `@wagmi/connectors`, with anchored Safe{Wallet} origins; `impersonated()` is a development connector that acts as a wallet for the address saved in `localStorage` and forwards requests to your node.
|
|
25
16
|
|
|
26
17
|
---
|
|
27
18
|
|
|
28
19
|
## πΎ Installation
|
|
29
20
|
|
|
30
|
-
### Requirements
|
|
31
|
-
|
|
32
|
-
- Node.js 20-24
|
|
33
|
-
- TypeScript 5.9+
|
|
34
|
-
|
|
35
21
|
```bash
|
|
36
22
|
pnpm add @tuwaio/satellite-evm @tuwaio/satellite-core @tuwaio/orbit-core @tuwaio/orbit-evm @wagmi/core viem zustand immer
|
|
37
23
|
```
|
|
38
24
|
|
|
39
|
-
|
|
25
|
+
> [!IMPORTANT]
|
|
26
|
+
> `@tuwaio/satellite-core` (>=0.5), `@tuwaio/orbit-core` (>=0.3), `@tuwaio/orbit-evm` (>=0.3), `@wagmi/core` (3.x) and `viem` (2.x) are peer dependencies and must be installed alongside `@tuwaio/satellite-evm`. `zustand` and `immer` are the peer dependencies of `@tuwaio/satellite-core`. Add `@wagmi/connectors` if you use the `safe`, `walletConnect` or `coinbaseWallet` connectors.
|
|
40
27
|
|
|
41
|
-
|
|
28
|
+
---
|
|
42
29
|
|
|
43
|
-
|
|
30
|
+
## π Usage
|
|
44
31
|
|
|
45
32
|
```typescript
|
|
46
|
-
import { createDefaultTransports } from '@tuwaio/satellite-evm';
|
|
47
|
-
import {
|
|
48
|
-
import { injected } from '@wagmi/
|
|
33
|
+
import { createDefaultTransports, impersonated, safeSdkOptions, satelliteEVMAdapter } from '@tuwaio/satellite-evm';
|
|
34
|
+
import { safe } from '@wagmi/connectors';
|
|
35
|
+
import { createConfig, injected } from '@wagmi/core';
|
|
49
36
|
import { mainnet, sepolia } from 'viem/chains';
|
|
50
|
-
import type { Chain } from 'viem/chains';
|
|
51
37
|
|
|
52
|
-
export const
|
|
38
|
+
export const appChains = [mainnet, sepolia] as const;
|
|
53
39
|
|
|
54
40
|
export const wagmiConfig = createConfig({
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
ssr: true,
|
|
41
|
+
chains: appChains,
|
|
42
|
+
connectors: [injected(), safe({ ...safeSdkOptions }), impersonated({})],
|
|
43
|
+
transports: createDefaultTransports(appChains), // public RPC URLs: pass your own http(url) transports in production
|
|
44
|
+
ssr: true,
|
|
59
45
|
});
|
|
60
|
-
```
|
|
61
|
-
|
|
62
|
-
---
|
|
63
|
-
|
|
64
|
-
## π Using the EVM Adapter
|
|
65
46
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
### Creating the Adapter
|
|
69
|
-
|
|
70
|
-
You create the adapter by passing your `wagmiConfig` to the `satelliteEVMAdapter` function.
|
|
71
|
-
|
|
72
|
-
```typescript
|
|
73
|
-
import { satelliteEVMAdapter } from '@tuwaio/satellite-evm';
|
|
74
|
-
import { wagmiConfig } from './your-wagmi-config'; // Import your configured wagmiConfig
|
|
75
|
-
|
|
76
|
-
export const evmAdapter = satelliteEVMAdapter(wagmiConfig, appEVMChains);
|
|
47
|
+
export const evmAdapter = satelliteEVMAdapter(wagmiConfig, appChains);
|
|
77
48
|
```
|
|
78
49
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
Use the created adapter within the `SatelliteConnectProvider` from `@tuwaio/satellite-react`.
|
|
82
|
-
|
|
83
|
-
```tsx
|
|
84
|
-
import { SatelliteConnectProvider, EVMConnectorsWatcher } from '@tuwaio/satellite-react';
|
|
85
|
-
import { WagmiProvider } from 'wagmi';
|
|
86
|
-
import { satelliteEVMAdapter } from '@tuwaio/satellite-evm';
|
|
87
|
-
import { wagmiConfig } from './your-wagmi-config';
|
|
88
|
-
import { evmAdapter } from './your-evm-adapter';
|
|
89
|
-
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; // Wagmi requires react-query
|
|
90
|
-
|
|
91
|
-
const queryClient = new QueryClient();
|
|
92
|
-
|
|
93
|
-
function AppProviders({ children }: { children: React.ReactNode }) {
|
|
94
|
-
return (
|
|
95
|
-
<WagmiProvider config={wagmiConfig}>
|
|
96
|
-
<QueryClientProvider client={queryClient}>
|
|
97
|
-
<SatelliteConnectProvider
|
|
98
|
-
adapter={evmAdapter} // Pass the EVM adapter
|
|
99
|
-
autoConnect={true} // Optional: enable auto-connect
|
|
100
|
-
>
|
|
101
|
-
<EVMConnectorsWatcher wagmiConfig={wagmiConfig} /> {/* Manages EVM wallet state */}
|
|
102
|
-
{children}
|
|
103
|
-
</SatelliteConnectProvider>
|
|
104
|
-
</QueryClientProvider>
|
|
105
|
-
</WagmiProvider>
|
|
106
|
-
);
|
|
107
|
-
}
|
|
108
|
-
```
|
|
50
|
+
Pass `evmAdapter` to `createSatelliteConnectStore` from [`@tuwaio/satellite-core`](https://satellite.docs.tuwa.io/packages/satellite-core) or to `SatelliteConnectProvider` from [`@tuwaio/satellite-react`](https://satellite.docs.tuwa.io/packages/satellite-react), and run the watcher next to it. The complete React setup is on the **[`@tuwaio/satellite-react`](https://satellite.docs.tuwa.io/packages/satellite-react)** page; the watcher without React is shown on the **[`createEVMConnectionsWatcher`](https://satellite.docs.tuwa.io/packages/satellite-evm/functions/createEVMConnectionsWatcher)** page.
|
|
109
51
|
|
|
110
52
|
---
|
|
111
53
|
|
|
112
|
-
##
|
|
113
|
-
|
|
114
|
-
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.
|
|
54
|
+
## ποΈ Browser Storage
|
|
115
55
|
|
|
116
|
-
|
|
117
|
-
import { useSiwxSession } from '@tuwaio/siwx-react';
|
|
118
|
-
import { SatelliteConnectProvider } from '@tuwaio/satellite-react';
|
|
119
|
-
import { EVMConnectorsWatcher } from '@tuwaio/satellite-react/evm';
|
|
120
|
-
import { satelliteEVMAdapter } from '@tuwaio/satellite-evm';
|
|
121
|
-
|
|
122
|
-
function AppProviders({ children }: { children: ReactNode }) {
|
|
123
|
-
const siwxSession = useSiwxSession();
|
|
124
|
-
|
|
125
|
-
return (
|
|
126
|
-
<SatelliteConnectProvider adapter={satelliteEVMAdapter(wagmiConfig, appEVMChains)} autoConnect={true}>
|
|
127
|
-
<EVMConnectorsWatcher wagmiConfig={wagmiConfig} siwx={siwxSession} />
|
|
128
|
-
{children}
|
|
129
|
-
</SatelliteConnectProvider>
|
|
130
|
-
);
|
|
131
|
-
}
|
|
132
|
-
```
|
|
56
|
+
The package writes nothing to `localStorage`. The `impersonated` connector reads `satellite-connect:impersonatedAddress` (set with `impersonatedHelpers.setImpersonated` from `@tuwaio/orbit-core`, for example by Nova Connect) on every request; `@tuwaio/satellite-core` removes the key on `disconnectAll` and when the last connection is disconnected. The keys that the store writes for EVM connections are listed on the [`@tuwaio/satellite-core`](https://satellite.docs.tuwa.io/packages/satellite-core) page. wagmi keeps its own state in the `storage` of your wagmi config.
|
|
133
57
|
|
|
134
58
|
---
|
|
135
59
|
|
|
136
|
-
##
|
|
60
|
+
## π External Services
|
|
137
61
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
62
|
+
| Helper | Host | Purpose |
|
|
63
|
+
| ---------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------- |
|
|
64
|
+
| Adapter `getBalance`, `checkIsWalletAddressContract` | The wagmi transport of the chain | `eth_getBalance`, `eth_getCode` |
|
|
65
|
+
| Adapter `getName`, `getAvatar`, `getAddress` | The default RPC URL of `mainnet` in your `chains` (viem's `mainnet` definition when it is missing), not your wagmi transports | ENS resolution on Ethereum Mainnet |
|
|
66
|
+
| Adapter `getAvatar` | The URL in the ENS `avatar` record, `ipfs.io` or `arweave.net` for IPFS and Arweave avatars | Avatar metadata (viem's `getEnsAvatar`) |
|
|
67
|
+
| `createDefaultTransports` | The default RPC URL of each chain definition | Every RPC request of the wagmi config |
|
|
68
|
+
| `impersonated` | The default RPC URL of the chain | Every request except accounts and chain switching, including signing |
|
|
141
69
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
## π€ Contributing & Support
|
|
70
|
+
The default RPC URLs of the chain definitions are public and rate-limited. Explorer links point to the `blockExplorers` of the chain definitions and are not requested by the package.
|
|
145
71
|
|
|
146
|
-
|
|
72
|
+
---
|
|
147
73
|
|
|
148
|
-
|
|
74
|
+
## π API Reference
|
|
149
75
|
|
|
150
|
-
|
|
76
|
+
Every export, with signatures and types generated from the source, is documented at **[satellite.docs.tuwa.io/packages/satellite-evm](https://satellite.docs.tuwa.io/packages/satellite-evm)**.
|
|
151
77
|
|
|
152
78
|
## π License
|
|
153
79
|
|
|
154
|
-
|
|
80
|
+
Licensed under the **Apache-2.0 License**. See the [LICENSE](https://github.com/TuwaIO/satellite-connect/blob/main/packages/satellite-evm/LICENSE) file for details.
|
package/dist/index.d.mts
CHANGED
|
@@ -3,183 +3,304 @@ import * as _wagmi_core from '@wagmi/core';
|
|
|
3
3
|
import { Connector, CreateConnectorFn, Config, CreateConfigParameters } from '@wagmi/core';
|
|
4
4
|
import { Chain } from 'viem/chains';
|
|
5
5
|
import { Transport } from 'viem';
|
|
6
|
-
import { ConnectorType } from '@tuwaio/orbit-core';
|
|
6
|
+
import { ConnectorType, TuwaErrorState } from '@tuwaio/orbit-core';
|
|
7
7
|
|
|
8
|
-
/** EVM
|
|
8
|
+
/** A wagmi `Connector` from `@wagmi/core`: the wallet connectors returned by `getConnectors` of the EVM adapter. */
|
|
9
9
|
type ConnectorEVM = Connector<CreateConnectorFn>;
|
|
10
10
|
/**
|
|
11
|
-
*
|
|
11
|
+
* An EVM connection in the Satellite Connect store: `BaseConnector` from `@tuwaio/satellite-core` plus the wagmi
|
|
12
|
+
* connector of the wallet. `chainId` is a numeric chain ID and `signMessage` signs with wagmi's `signMessage`.
|
|
12
13
|
*/
|
|
13
14
|
interface EVMConnection extends BaseConnector {
|
|
14
|
-
/**
|
|
15
|
+
/** The wagmi connector of the connected wallet. */
|
|
15
16
|
connector?: ConnectorEVM;
|
|
16
17
|
}
|
|
17
18
|
|
|
18
19
|
/**
|
|
19
|
-
* Creates
|
|
20
|
+
* Creates the EVM adapter for the Satellite Connect store (`createSatelliteConnectStore` from `@tuwaio/satellite-core`
|
|
21
|
+
* or `SatelliteConnectProvider` from `@tuwaio/satellite-react`). It implements `SatelliteAdapter` with `@wagmi/core`:
|
|
22
|
+
* - `getConnectors` returns the connectors of `config`. A connector matches a `connectorType` such as `"evm:metamask"`
|
|
23
|
+
* through `formatConnectorName` from `@tuwaio/orbit-core` (for example, the `Safe` connector is `"evm:safe"`).
|
|
24
|
+
* - `connect` runs wagmi's `connect` with the requested chain and returns an {@link EVMConnection}: the account (the
|
|
25
|
+
* zero address if wagmi reports none), the chain (`1` if unknown), the first default RPC URL of the chain, the
|
|
26
|
+
* connector icon and a `signMessage` that signs with wagmi's `signMessage`.
|
|
27
|
+
* - `disconnect` disconnects the given connection, or every connector of `config`.
|
|
28
|
+
* - `checkAndSwitchNetwork` asks the wallet to switch chains with `checkAndSwitchChain` from `@tuwaio/orbit-evm`.
|
|
29
|
+
* - `getBalance` reads the native balance through the wagmi transports and formats it with the chain's decimals.
|
|
30
|
+
* - `getExplorerUrl(url?, chainId?)` joins `url` to the block explorer of `chainId` (looked up in `chains`, then in
|
|
31
|
+
* `config`), or of the connected chain when `chainId` is omitted. It returns `undefined` when the chain has no
|
|
32
|
+
* explorer.
|
|
33
|
+
* - `getName`, `getAvatar` and `getAddress` resolve ENS names on Ethereum Mainnet with `@tuwaio/orbit-evm`, which
|
|
34
|
+
* caches the results in memory.
|
|
35
|
+
* - `checkIsContractAddress` is {@link checkIsWalletAddressContract}; `getSafeConnectorChainId` returns the chain of
|
|
36
|
+
* the `Safe` connector; `switchConnection` runs wagmi's `switchConnection`.
|
|
20
37
|
*
|
|
21
|
-
* @
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
* @
|
|
26
|
-
* @param chains - The list of chains to use for ENS client creation and other interactions
|
|
27
|
-
* @param signInWithSiwe - Optional function for signing in with SIWE
|
|
28
|
-
* @returns A configured SatelliteAdapter instance for EVM chains
|
|
29
|
-
* @throws Error if config is not provided
|
|
38
|
+
* @param config - The wagmi config of the app.
|
|
39
|
+
* @param chains - The app chains. ENS lookups use the Ethereum Mainnet entry of this list (its default RPC URL), or
|
|
40
|
+
* viem's `mainnet` when it is missing.
|
|
41
|
+
* @returns The EVM adapter.
|
|
42
|
+
* @throws {Error} `Satellite EVM adapter requires a wagmi config object.` when `config` is missing.
|
|
30
43
|
*
|
|
31
44
|
* @example
|
|
32
|
-
* ```
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
45
|
+
* ```ts
|
|
46
|
+
* import { satelliteEVMAdapter } from '@tuwaio/satellite-evm';
|
|
47
|
+
* import { createConfig, http, injected } from '@wagmi/core';
|
|
48
|
+
* import { mainnet, sepolia } from 'viem/chains';
|
|
49
|
+
*
|
|
50
|
+
* const chains = [mainnet, sepolia] as const;
|
|
51
|
+
* const wagmiConfig = createConfig({
|
|
52
|
+
* chains,
|
|
53
|
+
* connectors: [injected()],
|
|
54
|
+
* transports: { [mainnet.id]: http(), [sepolia.id]: http() },
|
|
36
55
|
* });
|
|
37
56
|
*
|
|
38
|
-
* const
|
|
39
|
-
* const evmAdapter = satelliteEVMAdapter(config, chains);
|
|
57
|
+
* export const evmAdapter = satelliteEVMAdapter(wagmiConfig, chains);
|
|
40
58
|
* ```
|
|
41
59
|
*/
|
|
42
60
|
declare function satelliteEVMAdapter(config: Config, chains: readonly [Chain, ...Chain[]]): SatelliteAdapter<ConnectorEVM, EVMConnection>;
|
|
43
61
|
|
|
44
62
|
/**
|
|
45
|
-
*
|
|
63
|
+
* Options of {@link impersonated}.
|
|
46
64
|
*/
|
|
47
65
|
type ImpersonatedParameters = {
|
|
48
|
-
/**
|
|
66
|
+
/**
|
|
67
|
+
* Simulated wallet errors. `true` throws a `UserRejectedRequestError` from `viem`; an `Error` is thrown as is.
|
|
68
|
+
*/
|
|
49
69
|
features?: {
|
|
50
|
-
/**
|
|
70
|
+
/** Error thrown by `connect`. */
|
|
51
71
|
connectError?: boolean | Error;
|
|
52
|
-
/**
|
|
72
|
+
/** Error thrown by `wallet_switchEthereumChain`. */
|
|
53
73
|
switchChainError?: boolean | Error;
|
|
54
|
-
/**
|
|
74
|
+
/** Error thrown by `personal_sign`. */
|
|
55
75
|
signMessageError?: boolean | Error;
|
|
56
|
-
/**
|
|
76
|
+
/** Error thrown by `eth_signTypedData_v4`. */
|
|
57
77
|
signTypedDataError?: boolean | Error;
|
|
58
|
-
/**
|
|
78
|
+
/** Not used by the connector. */
|
|
59
79
|
reconnect?: boolean;
|
|
60
80
|
};
|
|
61
81
|
};
|
|
82
|
+
/**
|
|
83
|
+
* Creates a wagmi connector that acts as a wallet for any address, for development and testing (for example against
|
|
84
|
+
* an Anvil or Hardhat node that impersonates accounts). Add it to the `connectors` of your wagmi config; it appears
|
|
85
|
+
* in Satellite Connect as `"evm:impersonatedwallet"` and is never reconnected automatically.
|
|
86
|
+
*
|
|
87
|
+
* On every provider request the connector reads the address to impersonate from `localStorage`
|
|
88
|
+
* (`satellite-connect:impersonatedAddress`, set with `impersonatedHelpers.setImpersonated` from `@tuwaio/orbit-core`)
|
|
89
|
+
* and returns it for `eth_accounts` and `eth_requestAccounts` (no account when the key is empty). `eth_chainId` and
|
|
90
|
+
* `wallet_switchEthereumChain` are answered locally (only chains of the wagmi config can be selected). `personal_sign`
|
|
91
|
+
* is sent as `eth_sign`, and every other request (for example `eth_sendTransaction`) is sent as is to the first
|
|
92
|
+
* default RPC URL of the chain wagmi asks for (the first chain of the wagmi config by default). Public RPC nodes reject
|
|
93
|
+
* signing requests; use a local node that impersonates the address.
|
|
94
|
+
*
|
|
95
|
+
* @param parameters - Connector options. `features` simulate wallet errors.
|
|
96
|
+
* @returns A wagmi connector factory with the id `impersonated` and the name `Impersonated Connector`.
|
|
97
|
+
*
|
|
98
|
+
* @example
|
|
99
|
+
* ```ts
|
|
100
|
+
* import { impersonatedHelpers } from '@tuwaio/orbit-core';
|
|
101
|
+
* import { impersonated } from '@tuwaio/satellite-evm';
|
|
102
|
+
* import { createConfig, http } from '@wagmi/core';
|
|
103
|
+
* import { foundry } from 'viem/chains';
|
|
104
|
+
*
|
|
105
|
+
* export const wagmiConfig = createConfig({
|
|
106
|
+
* chains: [foundry],
|
|
107
|
+
* connectors: [impersonated({ features: { signMessageError: false } })],
|
|
108
|
+
* transports: { [foundry.id]: http() },
|
|
109
|
+
* });
|
|
110
|
+
*
|
|
111
|
+
* impersonatedHelpers.setImpersonated('0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266');
|
|
112
|
+
* ```
|
|
113
|
+
*/
|
|
62
114
|
declare function impersonated(parameters: ImpersonatedParameters): _wagmi_core.CreateConnectorFn<provider, properties, storageItem>;
|
|
63
115
|
declare namespace impersonated {
|
|
64
116
|
var type: "impersonated";
|
|
65
117
|
}
|
|
66
118
|
|
|
67
119
|
/**
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
* Defines allowed domains and debug mode for Safe integration
|
|
120
|
+
* Options for the wagmi `safe` connector: `safe({ ...safeSdkOptions })`. They are passed to the Safe Apps SDK, which
|
|
121
|
+
* accepts messages only from a parent window whose origin matches one of `allowedDomains`.
|
|
71
122
|
*/
|
|
72
123
|
declare const safeSdkOptions: {
|
|
73
|
-
/**
|
|
124
|
+
/**
|
|
125
|
+
* Origins of the Safe{Wallet} apps allowed to host the dApp: `https://app.safe.global`, `https://gnosis-safe.io` and
|
|
126
|
+
* `https://metissafe.tech`, including their subdomains. The patterns are anchored, so look-alike domains such as
|
|
127
|
+
* `https://app-safe.global` are rejected.
|
|
128
|
+
*/
|
|
74
129
|
allowedDomains: RegExp[];
|
|
75
|
-
/**
|
|
130
|
+
/** Debug logging of the Safe Apps SDK. */
|
|
76
131
|
debug: boolean;
|
|
77
132
|
};
|
|
78
133
|
|
|
79
134
|
/**
|
|
80
|
-
* Checks
|
|
135
|
+
* Checks whether an address has contract code on a chain, for example to detect smart contract wallets such as Safe.
|
|
136
|
+
* The EVM adapter uses it as `checkIsContractAddress`, and the store saves the result in `isContractAddress`.
|
|
81
137
|
*
|
|
82
|
-
*
|
|
83
|
-
*
|
|
84
|
-
*
|
|
138
|
+
* Side effects: reads the code with wagmi's `getBytecode` through the wagmi transport of `chainId` (an RPC request),
|
|
139
|
+
* and caches the result in memory per chain and address for the lifetime of the page. Addresses with code include
|
|
140
|
+
* EIP-7702 delegated accounts.
|
|
85
141
|
*
|
|
86
|
-
* @param
|
|
87
|
-
* @param
|
|
88
|
-
* @param
|
|
89
|
-
* @param
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
*
|
|
142
|
+
* @param params - Address and chain to check.
|
|
143
|
+
* @param params.config - The wagmi config whose transports are used.
|
|
144
|
+
* @param params.address - The address to check.
|
|
145
|
+
* @param params.chainId - The chain to check it on, as a number or numeric string.
|
|
146
|
+
* @param params.chains - The app chains. When `chainId` is not in this list, nothing is requested and a warning is
|
|
147
|
+
* logged.
|
|
148
|
+
* @returns `true` when the address has code; `false` when it has none or `chainId` is not in `chains`.
|
|
149
|
+
* @throws {Error} When the RPC request fails (the failure is not cached).
|
|
94
150
|
*
|
|
95
151
|
* @example
|
|
96
|
-
* ```
|
|
152
|
+
* ```ts
|
|
153
|
+
* import { checkIsWalletAddressContract } from '@tuwaio/satellite-evm';
|
|
154
|
+
* import { type Config } from '@wagmi/core';
|
|
155
|
+
* import { mainnet } from 'viem/chains';
|
|
156
|
+
*
|
|
157
|
+
* declare const wagmiConfig: Config;
|
|
158
|
+
*
|
|
97
159
|
* const isContract = await checkIsWalletAddressContract({
|
|
98
160
|
* config: wagmiConfig,
|
|
99
|
-
* address:
|
|
100
|
-
* chainId:
|
|
101
|
-
* chains: [mainnet,
|
|
161
|
+
* address: '0xAb5801a7D398351b8bE11C439e05C5B3259aeC9B',
|
|
162
|
+
* chainId: mainnet.id,
|
|
163
|
+
* chains: [mainnet],
|
|
102
164
|
* });
|
|
103
165
|
* ```
|
|
104
|
-
*
|
|
105
|
-
* @throws Will throw an error if getBytecode request fails
|
|
106
166
|
*/
|
|
107
167
|
declare function checkIsWalletAddressContract({ config, address, chainId, chains, }: {
|
|
108
|
-
/** Wagmi configuration for blockchain interaction */
|
|
109
168
|
config: Config;
|
|
110
|
-
/** Ethereum address to check */
|
|
111
169
|
address: string;
|
|
112
|
-
/** Chain ID where the check should be performed */
|
|
113
170
|
chainId: number | string;
|
|
114
|
-
/** Array of supported chain configurations */
|
|
115
171
|
chains: readonly [Chain, ...Chain[]];
|
|
116
172
|
}): Promise<boolean>;
|
|
117
173
|
|
|
118
174
|
/**
|
|
119
|
-
* Creates
|
|
175
|
+
* Creates the `transports` of a wagmi config: a viem `http()` transport without a URL for every chain, so each chain
|
|
176
|
+
* uses the first default RPC URL of its definition (`rpcUrls.default.http[0]`). These are public, rate-limited
|
|
177
|
+
* endpoints: for production, pass `http(yourRpcUrl)` transports instead.
|
|
178
|
+
*
|
|
179
|
+
* @param chains - The chains of the wagmi config.
|
|
180
|
+
* @returns Transports by chain ID.
|
|
181
|
+
*
|
|
182
|
+
* @example
|
|
183
|
+
* ```ts
|
|
184
|
+
* import { createDefaultTransports } from '@tuwaio/satellite-evm';
|
|
185
|
+
* import { createConfig, injected } from '@wagmi/core';
|
|
186
|
+
* import { mainnet, sepolia } from 'viem/chains';
|
|
120
187
|
*
|
|
121
|
-
*
|
|
122
|
-
* @returns Object mapping chain IDs to their corresponding HTTP transport instances
|
|
188
|
+
* const chains = [mainnet, sepolia] as const;
|
|
123
189
|
*
|
|
124
|
-
*
|
|
190
|
+
* export const wagmiConfig = createConfig({
|
|
191
|
+
* chains,
|
|
192
|
+
* connectors: [injected()],
|
|
193
|
+
* transports: createDefaultTransports(chains),
|
|
194
|
+
* });
|
|
195
|
+
* ```
|
|
125
196
|
*/
|
|
126
197
|
declare const createDefaultTransports: (chains: CreateConfigParameters["chains"]) => Record<number, Transport>;
|
|
127
198
|
|
|
128
199
|
/**
|
|
129
|
-
*
|
|
130
|
-
*
|
|
200
|
+
* Store state and actions used by {@link createEVMConnectionsWatcher}. Pass the store's `disconnect` and
|
|
201
|
+
* `updateActiveConnection` and its `getState`, so the watcher always reads the current state. Instead of `getState`
|
|
202
|
+
* you can pass the current `activeConnection` and `connectionError`; they are then read once, when the watcher is
|
|
203
|
+
* created.
|
|
131
204
|
*/
|
|
132
205
|
interface EVMWatcherCallbacks {
|
|
133
|
-
/** The
|
|
134
|
-
activeConnection
|
|
135
|
-
/**
|
|
206
|
+
/** The active connection when the watcher is created. Ignored when `getState` is passed. */
|
|
207
|
+
activeConnection?: EVMConnection;
|
|
208
|
+
/**
|
|
209
|
+
* Disconnects a connection; the store's `disconnect`.
|
|
210
|
+
*
|
|
211
|
+
* @param connectorType - The connector to disconnect.
|
|
212
|
+
*/
|
|
136
213
|
disconnect: (connectorType: ConnectorType) => void;
|
|
137
|
-
/**
|
|
138
|
-
|
|
139
|
-
|
|
214
|
+
/**
|
|
215
|
+
* The store's `connectionError` when the watcher is created. While it is set, wallet changes are not copied to the
|
|
216
|
+
* store. Ignored when `getState` is passed.
|
|
217
|
+
*/
|
|
218
|
+
connectionError?: TuwaErrorState | string;
|
|
219
|
+
/**
|
|
220
|
+
* Merges fields into the active connection; the store's `updateActiveConnection`.
|
|
221
|
+
*
|
|
222
|
+
* @param connection - Fields to merge.
|
|
223
|
+
*/
|
|
140
224
|
updateActiveConnection: (connection: Partial<EVMConnection>) => void;
|
|
225
|
+
/**
|
|
226
|
+
* Returns the current store state, for example the store's `getState`. The watcher calls it when it starts and on
|
|
227
|
+
* every wagmi event, so it does not have to be recreated when the active connection or the error changes.
|
|
228
|
+
*
|
|
229
|
+
* @returns The current `activeConnection` and `connectionError`.
|
|
230
|
+
*/
|
|
231
|
+
getState?: () => {
|
|
232
|
+
/** The active connection. */
|
|
233
|
+
activeConnection?: EVMConnection;
|
|
234
|
+
/** The connection error. */
|
|
235
|
+
connectionError?: TuwaErrorState | string;
|
|
236
|
+
};
|
|
141
237
|
}
|
|
142
238
|
/**
|
|
143
|
-
* Configuration
|
|
239
|
+
* Configuration of {@link createEVMConnectionsWatcher}.
|
|
144
240
|
*/
|
|
145
241
|
interface EVMWatcherConfig {
|
|
146
|
-
/**
|
|
242
|
+
/** The wagmi config used by the EVM adapter. Its connections are watched. */
|
|
147
243
|
wagmiConfig: Config;
|
|
148
|
-
/** Optional
|
|
244
|
+
/** Optional SIWX session state. See `SatelliteSiwxState` from `@tuwaio/satellite-core`. */
|
|
149
245
|
siwx?: SatelliteSiwxState;
|
|
150
|
-
/**
|
|
246
|
+
/**
|
|
247
|
+
* Legacy SIWE state, used only when `siwx` is not passed.
|
|
248
|
+
*
|
|
249
|
+
* @deprecated Pass `siwx` instead.
|
|
250
|
+
*/
|
|
151
251
|
siwe?: {
|
|
252
|
+
/** `false` turns off the disconnect after a rejected sign-in. */
|
|
152
253
|
enabled?: boolean;
|
|
254
|
+
/** Whether the user is signed in. */
|
|
153
255
|
isSignedIn?: boolean;
|
|
256
|
+
/** Whether the sign-in was rejected. */
|
|
154
257
|
isRejected?: boolean;
|
|
155
258
|
};
|
|
156
259
|
}
|
|
157
260
|
/**
|
|
158
|
-
*
|
|
159
|
-
*
|
|
261
|
+
* Keeps the Satellite Connect store in sync with wagmi, without a UI framework. `EVMConnectorsWatcher` from
|
|
262
|
+
* `@tuwaio/satellite-react/evm` runs it in React apps.
|
|
160
263
|
*
|
|
161
|
-
*
|
|
162
|
-
*
|
|
264
|
+
* When created, it disconnects the active connection if the SIWX sign-in was rejected or failed (see
|
|
265
|
+
* `SatelliteSiwxState` from `@tuwaio/satellite-core`), and sets `signMessage` on an active EVM connection that wagmi
|
|
266
|
+
* reports as connected. Then it subscribes to wagmi's `watchConnections`. On every change, unless the active connection
|
|
267
|
+
* belongs to another chain family:
|
|
268
|
+
* - when wagmi has no connection left, the active connection is disconnected;
|
|
269
|
+
* - otherwise, unless `connectionError` is set: while the user is signed in with SIWX, the active connection is
|
|
270
|
+
* disconnected when the wallet account or chain no longer matches the session; in all other cases the connector
|
|
271
|
+
* type, address, chain, RPC URL and `signMessage` of the wagmi connection are merged into the store.
|
|
163
272
|
*
|
|
164
|
-
*
|
|
165
|
-
*
|
|
166
|
-
*
|
|
273
|
+
* Pass the store's `getState` as `callbacks.getState`, so the active connection and the error are read on every event.
|
|
274
|
+
* `config.siwx` is read when the watcher is created: create a new watcher when the SIWX state changes.
|
|
275
|
+
*
|
|
276
|
+
* @param config - The wagmi config and the optional SIWX state.
|
|
277
|
+
* @param callbacks - Store state and actions.
|
|
278
|
+
* @returns A function that unsubscribes from wagmi.
|
|
167
279
|
*
|
|
168
280
|
* @example
|
|
169
|
-
* ```
|
|
170
|
-
*
|
|
171
|
-
*
|
|
172
|
-
*
|
|
173
|
-
*
|
|
281
|
+
* ```ts
|
|
282
|
+
* import { createSatelliteConnectStore } from '@tuwaio/satellite-core';
|
|
283
|
+
* import {
|
|
284
|
+
* type ConnectorEVM,
|
|
285
|
+
* createEVMConnectionsWatcher,
|
|
286
|
+
* type EVMConnection,
|
|
287
|
+
* satelliteEVMAdapter,
|
|
288
|
+
* } from '@tuwaio/satellite-evm';
|
|
289
|
+
* import { type Config } from '@wagmi/core';
|
|
290
|
+
* import { mainnet } from 'viem/chains';
|
|
174
291
|
*
|
|
175
|
-
*
|
|
176
|
-
* unwatch();
|
|
177
|
-
* ```
|
|
292
|
+
* declare const wagmiConfig: Config;
|
|
178
293
|
*
|
|
179
|
-
*
|
|
180
|
-
*
|
|
181
|
-
*
|
|
182
|
-
*
|
|
294
|
+
* const store = createSatelliteConnectStore<ConnectorEVM, EVMConnection>({
|
|
295
|
+
* adapter: satelliteEVMAdapter(wagmiConfig, [mainnet]),
|
|
296
|
+
* });
|
|
297
|
+
*
|
|
298
|
+
* const { disconnect, updateActiveConnection } = store.getState();
|
|
299
|
+
* export const unwatch = createEVMConnectionsWatcher(
|
|
300
|
+
* { wagmiConfig },
|
|
301
|
+
* { disconnect, updateActiveConnection, getState: store.getState },
|
|
302
|
+
* );
|
|
303
|
+
* ```
|
|
183
304
|
*/
|
|
184
305
|
declare function createEVMConnectionsWatcher(config: EVMWatcherConfig, callbacks: EVMWatcherCallbacks): () => void;
|
|
185
306
|
|
package/dist/index.d.ts
CHANGED
|
@@ -3,183 +3,304 @@ import * as _wagmi_core from '@wagmi/core';
|
|
|
3
3
|
import { Connector, CreateConnectorFn, Config, CreateConfigParameters } from '@wagmi/core';
|
|
4
4
|
import { Chain } from 'viem/chains';
|
|
5
5
|
import { Transport } from 'viem';
|
|
6
|
-
import { ConnectorType } from '@tuwaio/orbit-core';
|
|
6
|
+
import { ConnectorType, TuwaErrorState } from '@tuwaio/orbit-core';
|
|
7
7
|
|
|
8
|
-
/** EVM
|
|
8
|
+
/** A wagmi `Connector` from `@wagmi/core`: the wallet connectors returned by `getConnectors` of the EVM adapter. */
|
|
9
9
|
type ConnectorEVM = Connector<CreateConnectorFn>;
|
|
10
10
|
/**
|
|
11
|
-
*
|
|
11
|
+
* An EVM connection in the Satellite Connect store: `BaseConnector` from `@tuwaio/satellite-core` plus the wagmi
|
|
12
|
+
* connector of the wallet. `chainId` is a numeric chain ID and `signMessage` signs with wagmi's `signMessage`.
|
|
12
13
|
*/
|
|
13
14
|
interface EVMConnection extends BaseConnector {
|
|
14
|
-
/**
|
|
15
|
+
/** The wagmi connector of the connected wallet. */
|
|
15
16
|
connector?: ConnectorEVM;
|
|
16
17
|
}
|
|
17
18
|
|
|
18
19
|
/**
|
|
19
|
-
* Creates
|
|
20
|
+
* Creates the EVM adapter for the Satellite Connect store (`createSatelliteConnectStore` from `@tuwaio/satellite-core`
|
|
21
|
+
* or `SatelliteConnectProvider` from `@tuwaio/satellite-react`). It implements `SatelliteAdapter` with `@wagmi/core`:
|
|
22
|
+
* - `getConnectors` returns the connectors of `config`. A connector matches a `connectorType` such as `"evm:metamask"`
|
|
23
|
+
* through `formatConnectorName` from `@tuwaio/orbit-core` (for example, the `Safe` connector is `"evm:safe"`).
|
|
24
|
+
* - `connect` runs wagmi's `connect` with the requested chain and returns an {@link EVMConnection}: the account (the
|
|
25
|
+
* zero address if wagmi reports none), the chain (`1` if unknown), the first default RPC URL of the chain, the
|
|
26
|
+
* connector icon and a `signMessage` that signs with wagmi's `signMessage`.
|
|
27
|
+
* - `disconnect` disconnects the given connection, or every connector of `config`.
|
|
28
|
+
* - `checkAndSwitchNetwork` asks the wallet to switch chains with `checkAndSwitchChain` from `@tuwaio/orbit-evm`.
|
|
29
|
+
* - `getBalance` reads the native balance through the wagmi transports and formats it with the chain's decimals.
|
|
30
|
+
* - `getExplorerUrl(url?, chainId?)` joins `url` to the block explorer of `chainId` (looked up in `chains`, then in
|
|
31
|
+
* `config`), or of the connected chain when `chainId` is omitted. It returns `undefined` when the chain has no
|
|
32
|
+
* explorer.
|
|
33
|
+
* - `getName`, `getAvatar` and `getAddress` resolve ENS names on Ethereum Mainnet with `@tuwaio/orbit-evm`, which
|
|
34
|
+
* caches the results in memory.
|
|
35
|
+
* - `checkIsContractAddress` is {@link checkIsWalletAddressContract}; `getSafeConnectorChainId` returns the chain of
|
|
36
|
+
* the `Safe` connector; `switchConnection` runs wagmi's `switchConnection`.
|
|
20
37
|
*
|
|
21
|
-
* @
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
* @
|
|
26
|
-
* @param chains - The list of chains to use for ENS client creation and other interactions
|
|
27
|
-
* @param signInWithSiwe - Optional function for signing in with SIWE
|
|
28
|
-
* @returns A configured SatelliteAdapter instance for EVM chains
|
|
29
|
-
* @throws Error if config is not provided
|
|
38
|
+
* @param config - The wagmi config of the app.
|
|
39
|
+
* @param chains - The app chains. ENS lookups use the Ethereum Mainnet entry of this list (its default RPC URL), or
|
|
40
|
+
* viem's `mainnet` when it is missing.
|
|
41
|
+
* @returns The EVM adapter.
|
|
42
|
+
* @throws {Error} `Satellite EVM adapter requires a wagmi config object.` when `config` is missing.
|
|
30
43
|
*
|
|
31
44
|
* @example
|
|
32
|
-
* ```
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
45
|
+
* ```ts
|
|
46
|
+
* import { satelliteEVMAdapter } from '@tuwaio/satellite-evm';
|
|
47
|
+
* import { createConfig, http, injected } from '@wagmi/core';
|
|
48
|
+
* import { mainnet, sepolia } from 'viem/chains';
|
|
49
|
+
*
|
|
50
|
+
* const chains = [mainnet, sepolia] as const;
|
|
51
|
+
* const wagmiConfig = createConfig({
|
|
52
|
+
* chains,
|
|
53
|
+
* connectors: [injected()],
|
|
54
|
+
* transports: { [mainnet.id]: http(), [sepolia.id]: http() },
|
|
36
55
|
* });
|
|
37
56
|
*
|
|
38
|
-
* const
|
|
39
|
-
* const evmAdapter = satelliteEVMAdapter(config, chains);
|
|
57
|
+
* export const evmAdapter = satelliteEVMAdapter(wagmiConfig, chains);
|
|
40
58
|
* ```
|
|
41
59
|
*/
|
|
42
60
|
declare function satelliteEVMAdapter(config: Config, chains: readonly [Chain, ...Chain[]]): SatelliteAdapter<ConnectorEVM, EVMConnection>;
|
|
43
61
|
|
|
44
62
|
/**
|
|
45
|
-
*
|
|
63
|
+
* Options of {@link impersonated}.
|
|
46
64
|
*/
|
|
47
65
|
type ImpersonatedParameters = {
|
|
48
|
-
/**
|
|
66
|
+
/**
|
|
67
|
+
* Simulated wallet errors. `true` throws a `UserRejectedRequestError` from `viem`; an `Error` is thrown as is.
|
|
68
|
+
*/
|
|
49
69
|
features?: {
|
|
50
|
-
/**
|
|
70
|
+
/** Error thrown by `connect`. */
|
|
51
71
|
connectError?: boolean | Error;
|
|
52
|
-
/**
|
|
72
|
+
/** Error thrown by `wallet_switchEthereumChain`. */
|
|
53
73
|
switchChainError?: boolean | Error;
|
|
54
|
-
/**
|
|
74
|
+
/** Error thrown by `personal_sign`. */
|
|
55
75
|
signMessageError?: boolean | Error;
|
|
56
|
-
/**
|
|
76
|
+
/** Error thrown by `eth_signTypedData_v4`. */
|
|
57
77
|
signTypedDataError?: boolean | Error;
|
|
58
|
-
/**
|
|
78
|
+
/** Not used by the connector. */
|
|
59
79
|
reconnect?: boolean;
|
|
60
80
|
};
|
|
61
81
|
};
|
|
82
|
+
/**
|
|
83
|
+
* Creates a wagmi connector that acts as a wallet for any address, for development and testing (for example against
|
|
84
|
+
* an Anvil or Hardhat node that impersonates accounts). Add it to the `connectors` of your wagmi config; it appears
|
|
85
|
+
* in Satellite Connect as `"evm:impersonatedwallet"` and is never reconnected automatically.
|
|
86
|
+
*
|
|
87
|
+
* On every provider request the connector reads the address to impersonate from `localStorage`
|
|
88
|
+
* (`satellite-connect:impersonatedAddress`, set with `impersonatedHelpers.setImpersonated` from `@tuwaio/orbit-core`)
|
|
89
|
+
* and returns it for `eth_accounts` and `eth_requestAccounts` (no account when the key is empty). `eth_chainId` and
|
|
90
|
+
* `wallet_switchEthereumChain` are answered locally (only chains of the wagmi config can be selected). `personal_sign`
|
|
91
|
+
* is sent as `eth_sign`, and every other request (for example `eth_sendTransaction`) is sent as is to the first
|
|
92
|
+
* default RPC URL of the chain wagmi asks for (the first chain of the wagmi config by default). Public RPC nodes reject
|
|
93
|
+
* signing requests; use a local node that impersonates the address.
|
|
94
|
+
*
|
|
95
|
+
* @param parameters - Connector options. `features` simulate wallet errors.
|
|
96
|
+
* @returns A wagmi connector factory with the id `impersonated` and the name `Impersonated Connector`.
|
|
97
|
+
*
|
|
98
|
+
* @example
|
|
99
|
+
* ```ts
|
|
100
|
+
* import { impersonatedHelpers } from '@tuwaio/orbit-core';
|
|
101
|
+
* import { impersonated } from '@tuwaio/satellite-evm';
|
|
102
|
+
* import { createConfig, http } from '@wagmi/core';
|
|
103
|
+
* import { foundry } from 'viem/chains';
|
|
104
|
+
*
|
|
105
|
+
* export const wagmiConfig = createConfig({
|
|
106
|
+
* chains: [foundry],
|
|
107
|
+
* connectors: [impersonated({ features: { signMessageError: false } })],
|
|
108
|
+
* transports: { [foundry.id]: http() },
|
|
109
|
+
* });
|
|
110
|
+
*
|
|
111
|
+
* impersonatedHelpers.setImpersonated('0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266');
|
|
112
|
+
* ```
|
|
113
|
+
*/
|
|
62
114
|
declare function impersonated(parameters: ImpersonatedParameters): _wagmi_core.CreateConnectorFn<provider, properties, storageItem>;
|
|
63
115
|
declare namespace impersonated {
|
|
64
116
|
var type: "impersonated";
|
|
65
117
|
}
|
|
66
118
|
|
|
67
119
|
/**
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
* Defines allowed domains and debug mode for Safe integration
|
|
120
|
+
* Options for the wagmi `safe` connector: `safe({ ...safeSdkOptions })`. They are passed to the Safe Apps SDK, which
|
|
121
|
+
* accepts messages only from a parent window whose origin matches one of `allowedDomains`.
|
|
71
122
|
*/
|
|
72
123
|
declare const safeSdkOptions: {
|
|
73
|
-
/**
|
|
124
|
+
/**
|
|
125
|
+
* Origins of the Safe{Wallet} apps allowed to host the dApp: `https://app.safe.global`, `https://gnosis-safe.io` and
|
|
126
|
+
* `https://metissafe.tech`, including their subdomains. The patterns are anchored, so look-alike domains such as
|
|
127
|
+
* `https://app-safe.global` are rejected.
|
|
128
|
+
*/
|
|
74
129
|
allowedDomains: RegExp[];
|
|
75
|
-
/**
|
|
130
|
+
/** Debug logging of the Safe Apps SDK. */
|
|
76
131
|
debug: boolean;
|
|
77
132
|
};
|
|
78
133
|
|
|
79
134
|
/**
|
|
80
|
-
* Checks
|
|
135
|
+
* Checks whether an address has contract code on a chain, for example to detect smart contract wallets such as Safe.
|
|
136
|
+
* The EVM adapter uses it as `checkIsContractAddress`, and the store saves the result in `isContractAddress`.
|
|
81
137
|
*
|
|
82
|
-
*
|
|
83
|
-
*
|
|
84
|
-
*
|
|
138
|
+
* Side effects: reads the code with wagmi's `getBytecode` through the wagmi transport of `chainId` (an RPC request),
|
|
139
|
+
* and caches the result in memory per chain and address for the lifetime of the page. Addresses with code include
|
|
140
|
+
* EIP-7702 delegated accounts.
|
|
85
141
|
*
|
|
86
|
-
* @param
|
|
87
|
-
* @param
|
|
88
|
-
* @param
|
|
89
|
-
* @param
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
*
|
|
142
|
+
* @param params - Address and chain to check.
|
|
143
|
+
* @param params.config - The wagmi config whose transports are used.
|
|
144
|
+
* @param params.address - The address to check.
|
|
145
|
+
* @param params.chainId - The chain to check it on, as a number or numeric string.
|
|
146
|
+
* @param params.chains - The app chains. When `chainId` is not in this list, nothing is requested and a warning is
|
|
147
|
+
* logged.
|
|
148
|
+
* @returns `true` when the address has code; `false` when it has none or `chainId` is not in `chains`.
|
|
149
|
+
* @throws {Error} When the RPC request fails (the failure is not cached).
|
|
94
150
|
*
|
|
95
151
|
* @example
|
|
96
|
-
* ```
|
|
152
|
+
* ```ts
|
|
153
|
+
* import { checkIsWalletAddressContract } from '@tuwaio/satellite-evm';
|
|
154
|
+
* import { type Config } from '@wagmi/core';
|
|
155
|
+
* import { mainnet } from 'viem/chains';
|
|
156
|
+
*
|
|
157
|
+
* declare const wagmiConfig: Config;
|
|
158
|
+
*
|
|
97
159
|
* const isContract = await checkIsWalletAddressContract({
|
|
98
160
|
* config: wagmiConfig,
|
|
99
|
-
* address:
|
|
100
|
-
* chainId:
|
|
101
|
-
* chains: [mainnet,
|
|
161
|
+
* address: '0xAb5801a7D398351b8bE11C439e05C5B3259aeC9B',
|
|
162
|
+
* chainId: mainnet.id,
|
|
163
|
+
* chains: [mainnet],
|
|
102
164
|
* });
|
|
103
165
|
* ```
|
|
104
|
-
*
|
|
105
|
-
* @throws Will throw an error if getBytecode request fails
|
|
106
166
|
*/
|
|
107
167
|
declare function checkIsWalletAddressContract({ config, address, chainId, chains, }: {
|
|
108
|
-
/** Wagmi configuration for blockchain interaction */
|
|
109
168
|
config: Config;
|
|
110
|
-
/** Ethereum address to check */
|
|
111
169
|
address: string;
|
|
112
|
-
/** Chain ID where the check should be performed */
|
|
113
170
|
chainId: number | string;
|
|
114
|
-
/** Array of supported chain configurations */
|
|
115
171
|
chains: readonly [Chain, ...Chain[]];
|
|
116
172
|
}): Promise<boolean>;
|
|
117
173
|
|
|
118
174
|
/**
|
|
119
|
-
* Creates
|
|
175
|
+
* Creates the `transports` of a wagmi config: a viem `http()` transport without a URL for every chain, so each chain
|
|
176
|
+
* uses the first default RPC URL of its definition (`rpcUrls.default.http[0]`). These are public, rate-limited
|
|
177
|
+
* endpoints: for production, pass `http(yourRpcUrl)` transports instead.
|
|
178
|
+
*
|
|
179
|
+
* @param chains - The chains of the wagmi config.
|
|
180
|
+
* @returns Transports by chain ID.
|
|
181
|
+
*
|
|
182
|
+
* @example
|
|
183
|
+
* ```ts
|
|
184
|
+
* import { createDefaultTransports } from '@tuwaio/satellite-evm';
|
|
185
|
+
* import { createConfig, injected } from '@wagmi/core';
|
|
186
|
+
* import { mainnet, sepolia } from 'viem/chains';
|
|
120
187
|
*
|
|
121
|
-
*
|
|
122
|
-
* @returns Object mapping chain IDs to their corresponding HTTP transport instances
|
|
188
|
+
* const chains = [mainnet, sepolia] as const;
|
|
123
189
|
*
|
|
124
|
-
*
|
|
190
|
+
* export const wagmiConfig = createConfig({
|
|
191
|
+
* chains,
|
|
192
|
+
* connectors: [injected()],
|
|
193
|
+
* transports: createDefaultTransports(chains),
|
|
194
|
+
* });
|
|
195
|
+
* ```
|
|
125
196
|
*/
|
|
126
197
|
declare const createDefaultTransports: (chains: CreateConfigParameters["chains"]) => Record<number, Transport>;
|
|
127
198
|
|
|
128
199
|
/**
|
|
129
|
-
*
|
|
130
|
-
*
|
|
200
|
+
* Store state and actions used by {@link createEVMConnectionsWatcher}. Pass the store's `disconnect` and
|
|
201
|
+
* `updateActiveConnection` and its `getState`, so the watcher always reads the current state. Instead of `getState`
|
|
202
|
+
* you can pass the current `activeConnection` and `connectionError`; they are then read once, when the watcher is
|
|
203
|
+
* created.
|
|
131
204
|
*/
|
|
132
205
|
interface EVMWatcherCallbacks {
|
|
133
|
-
/** The
|
|
134
|
-
activeConnection
|
|
135
|
-
/**
|
|
206
|
+
/** The active connection when the watcher is created. Ignored when `getState` is passed. */
|
|
207
|
+
activeConnection?: EVMConnection;
|
|
208
|
+
/**
|
|
209
|
+
* Disconnects a connection; the store's `disconnect`.
|
|
210
|
+
*
|
|
211
|
+
* @param connectorType - The connector to disconnect.
|
|
212
|
+
*/
|
|
136
213
|
disconnect: (connectorType: ConnectorType) => void;
|
|
137
|
-
/**
|
|
138
|
-
|
|
139
|
-
|
|
214
|
+
/**
|
|
215
|
+
* The store's `connectionError` when the watcher is created. While it is set, wallet changes are not copied to the
|
|
216
|
+
* store. Ignored when `getState` is passed.
|
|
217
|
+
*/
|
|
218
|
+
connectionError?: TuwaErrorState | string;
|
|
219
|
+
/**
|
|
220
|
+
* Merges fields into the active connection; the store's `updateActiveConnection`.
|
|
221
|
+
*
|
|
222
|
+
* @param connection - Fields to merge.
|
|
223
|
+
*/
|
|
140
224
|
updateActiveConnection: (connection: Partial<EVMConnection>) => void;
|
|
225
|
+
/**
|
|
226
|
+
* Returns the current store state, for example the store's `getState`. The watcher calls it when it starts and on
|
|
227
|
+
* every wagmi event, so it does not have to be recreated when the active connection or the error changes.
|
|
228
|
+
*
|
|
229
|
+
* @returns The current `activeConnection` and `connectionError`.
|
|
230
|
+
*/
|
|
231
|
+
getState?: () => {
|
|
232
|
+
/** The active connection. */
|
|
233
|
+
activeConnection?: EVMConnection;
|
|
234
|
+
/** The connection error. */
|
|
235
|
+
connectionError?: TuwaErrorState | string;
|
|
236
|
+
};
|
|
141
237
|
}
|
|
142
238
|
/**
|
|
143
|
-
* Configuration
|
|
239
|
+
* Configuration of {@link createEVMConnectionsWatcher}.
|
|
144
240
|
*/
|
|
145
241
|
interface EVMWatcherConfig {
|
|
146
|
-
/**
|
|
242
|
+
/** The wagmi config used by the EVM adapter. Its connections are watched. */
|
|
147
243
|
wagmiConfig: Config;
|
|
148
|
-
/** Optional
|
|
244
|
+
/** Optional SIWX session state. See `SatelliteSiwxState` from `@tuwaio/satellite-core`. */
|
|
149
245
|
siwx?: SatelliteSiwxState;
|
|
150
|
-
/**
|
|
246
|
+
/**
|
|
247
|
+
* Legacy SIWE state, used only when `siwx` is not passed.
|
|
248
|
+
*
|
|
249
|
+
* @deprecated Pass `siwx` instead.
|
|
250
|
+
*/
|
|
151
251
|
siwe?: {
|
|
252
|
+
/** `false` turns off the disconnect after a rejected sign-in. */
|
|
152
253
|
enabled?: boolean;
|
|
254
|
+
/** Whether the user is signed in. */
|
|
153
255
|
isSignedIn?: boolean;
|
|
256
|
+
/** Whether the sign-in was rejected. */
|
|
154
257
|
isRejected?: boolean;
|
|
155
258
|
};
|
|
156
259
|
}
|
|
157
260
|
/**
|
|
158
|
-
*
|
|
159
|
-
*
|
|
261
|
+
* Keeps the Satellite Connect store in sync with wagmi, without a UI framework. `EVMConnectorsWatcher` from
|
|
262
|
+
* `@tuwaio/satellite-react/evm` runs it in React apps.
|
|
160
263
|
*
|
|
161
|
-
*
|
|
162
|
-
*
|
|
264
|
+
* When created, it disconnects the active connection if the SIWX sign-in was rejected or failed (see
|
|
265
|
+
* `SatelliteSiwxState` from `@tuwaio/satellite-core`), and sets `signMessage` on an active EVM connection that wagmi
|
|
266
|
+
* reports as connected. Then it subscribes to wagmi's `watchConnections`. On every change, unless the active connection
|
|
267
|
+
* belongs to another chain family:
|
|
268
|
+
* - when wagmi has no connection left, the active connection is disconnected;
|
|
269
|
+
* - otherwise, unless `connectionError` is set: while the user is signed in with SIWX, the active connection is
|
|
270
|
+
* disconnected when the wallet account or chain no longer matches the session; in all other cases the connector
|
|
271
|
+
* type, address, chain, RPC URL and `signMessage` of the wagmi connection are merged into the store.
|
|
163
272
|
*
|
|
164
|
-
*
|
|
165
|
-
*
|
|
166
|
-
*
|
|
273
|
+
* Pass the store's `getState` as `callbacks.getState`, so the active connection and the error are read on every event.
|
|
274
|
+
* `config.siwx` is read when the watcher is created: create a new watcher when the SIWX state changes.
|
|
275
|
+
*
|
|
276
|
+
* @param config - The wagmi config and the optional SIWX state.
|
|
277
|
+
* @param callbacks - Store state and actions.
|
|
278
|
+
* @returns A function that unsubscribes from wagmi.
|
|
167
279
|
*
|
|
168
280
|
* @example
|
|
169
|
-
* ```
|
|
170
|
-
*
|
|
171
|
-
*
|
|
172
|
-
*
|
|
173
|
-
*
|
|
281
|
+
* ```ts
|
|
282
|
+
* import { createSatelliteConnectStore } from '@tuwaio/satellite-core';
|
|
283
|
+
* import {
|
|
284
|
+
* type ConnectorEVM,
|
|
285
|
+
* createEVMConnectionsWatcher,
|
|
286
|
+
* type EVMConnection,
|
|
287
|
+
* satelliteEVMAdapter,
|
|
288
|
+
* } from '@tuwaio/satellite-evm';
|
|
289
|
+
* import { type Config } from '@wagmi/core';
|
|
290
|
+
* import { mainnet } from 'viem/chains';
|
|
174
291
|
*
|
|
175
|
-
*
|
|
176
|
-
* unwatch();
|
|
177
|
-
* ```
|
|
292
|
+
* declare const wagmiConfig: Config;
|
|
178
293
|
*
|
|
179
|
-
*
|
|
180
|
-
*
|
|
181
|
-
*
|
|
182
|
-
*
|
|
294
|
+
* const store = createSatelliteConnectStore<ConnectorEVM, EVMConnection>({
|
|
295
|
+
* adapter: satelliteEVMAdapter(wagmiConfig, [mainnet]),
|
|
296
|
+
* });
|
|
297
|
+
*
|
|
298
|
+
* const { disconnect, updateActiveConnection } = store.getState();
|
|
299
|
+
* export const unwatch = createEVMConnectionsWatcher(
|
|
300
|
+
* { wagmiConfig },
|
|
301
|
+
* { disconnect, updateActiveConnection, getState: store.getState },
|
|
302
|
+
* );
|
|
303
|
+
* ```
|
|
183
304
|
*/
|
|
184
305
|
declare function createEVMConnectionsWatcher(config: EVMWatcherConfig, callbacks: EVMWatcherCallbacks): () => void;
|
|
185
306
|
|
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
|
|
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 I=new Map;async function M({config:r,address:o,chainId:e,chains:n}){let s=`${Number(e)}:${o.toLowerCase()}`;if(I.has(s))return I.get(s);if(orbitEvm.createViemClient(Number(e),n)){let i=!!await core.getBytecode(r,{address:o,chainId:Number(e)});return I.set(s,i),i}else return false}function Se(r,o){if(!r)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(r).find(t=>orbitCore.getConnectorTypeFromName(orbitCore.OrbitAdapter.EVM,orbitCore.formatConnectorName(t.name))===e);if(!a)throw new Error("Cannot find connector with this wallet type");try{await core.connect(r,{connector:a,chainId:n});let t=core.getConnection(r);return {connectorType:e,address:t.address??viem.zeroAddress,chainId:t.chainId??chains.mainnet.id,rpcURL:t.chain?.rpcUrls.default.http[0]??chains.mainnet.rpcUrls.default.http[0],isConnected:t.isConnected,isContractAddress:!1,icon:a?.icon?.trim(),connector:a,signMessage:i=>core.signMessage(r,{message:i})}}catch(t){throw new Error(t instanceof Error?t.message:String(t),{cause:t})}},disconnect:async e=>{if(e&&e.isConnected)await core.disconnect(r,{connector:e?.connector});else {let n=core.getConnectors(r);await Promise.allSettled(n.map(async s=>{await core.disconnect(r,{connector:s});}));}},getConnectors:()=>{let e=core.getConnectors(r);return {adapter:orbitCore.OrbitAdapter.EVM,connectors:e.map(n=>n)}},checkAndSwitchNetwork:async e=>await orbitEvm.checkAndSwitchChain(Number(e),r),getBalance:async(e,n)=>{let s=await core.getBalance(r,{address:e,chainId:Number(n)});return {value:viem.formatUnits(s.value,s.decimals),symbol:s.symbol}},getExplorerUrl:(e,n)=>{let a=(n===void 0?core.getConnection(r).chain:o.find(t=>t.id===Number(n))??core.getChains(r).find(t=>t.id===Number(n)))?.blockExplorers?.default.url;if(a)return e?`${a.replace(/\/+$/,"")}/${e.replace(/^\/+/,"")}`:a},getName:e=>orbitEvm.getName(e,o),getAvatar:e=>orbitEvm.getAvatar(e,o),getAddress:e=>orbitEvm.getAddress(e,o),checkIsContractAddress:async({address:e,chainId:n})=>{let s=core.getChains(r);return await M({config:r,address:e,chainId:n,chains:s})},getSafeConnectorChainId:async()=>{let n=core.getConnectors(r).find(s=>s.name==="Safe");if(n)return await n.getChainId()},switchConnection:async e=>{let s=core.getConnectors(r).find(a=>orbitCore.getConnectorTypeFromName(orbitCore.OrbitAdapter.EVM,orbitCore.formatConnectorName(a.name))===e);if(!s)throw new Error(`Cannot find connector with type: ${e}`);try{await core.switchConnection(r,{connector:s});}catch(a){throw new Error(`Failed to switch to connector ${e}: ${a instanceof Error?a.message:String(a)}`,{cause:a})}}}}k.type="impersonated";function k(r){let o=r.features??{},e=false,n,s;return core.createConnector(a=>({id:"impersonated",name:"Impersonated Connector",type:k.type,async setup(){n=a.chains[0].id;},async connect({chainId:t}={}){if(o.connectError)throw typeof o.connectError=="boolean"?new viem.UserRejectedRequestError(new Error("Failed to connect.")):o.connectError;let{request:i}=await this.getProvider(),m=await i({method:"eth_requestAccounts"}),p=await this.getChainId();return t&&p!==t&&(p=(await this.switchChain({chainId:t})).id),e=true,{accounts:m,chainId:p}},async disconnect(){e=false,s=void 0;},async getAccounts(){if(!e)throw new Error("Not connected connector");let{request:t}=await this.getProvider();return (await t({method:"eth_accounts"})).map(viem.getAddress)},async getChainId(){let{request:t}=await this.getProvider(),i=await t({method:"eth_chainId"});return viem.fromHex(i,"number")},async isAuthorized(){return e?!!(await this.getAccounts()).length:false},async switchChain({chainId:t}){let i=a.chains.find(p=>p.id===t);if(!i)throw new viem.SwitchChainError(new core.ChainNotConfiguredError);let{request:m}=await this.getProvider();return await m({method:"wallet_switchEthereumChain",params:[{chainId:viem.numberToHex(t)}]}),i},onAccountsChanged(t){t.length===0?this.onDisconnect():a.emitter.emit("change",{accounts:t.map(viem.getAddress)});},onChainChanged(t){let i=Number(t);a.emitter.emit("change",{chainId:i});},async onDisconnect(){a.emitter.emit("disconnect"),e=false,s=void 0;},async getProvider({chainId:t}={}){s=orbitCore.impersonatedHelpers?.getImpersonated()?[orbitCore.impersonatedHelpers.getImpersonated()||viem.zeroAddress]:void 0;let m=(a.chains.find(h=>h.id===t)??a.chains[0]).rpcUrls.default.http[0];return viem.custom({request:async({method:h,params:u})=>{if(h==="eth_chainId")return viem.numberToHex(n);if(h==="eth_requestAccounts"||h==="eth_accounts")return s??[];if(h==="eth_signTypedData_v4"&&o.signTypedDataError)throw typeof o.signTypedDataError=="boolean"?new viem.UserRejectedRequestError(new Error("Failed to sign typed data.")):o.signTypedDataError;if(h==="wallet_switchEthereumChain"){if(o.switchChainError)throw typeof o.switchChainError=="boolean"?new viem.UserRejectedRequestError(new Error("Failed to switch chain.")):o.switchChainError;n=viem.fromHex(u[0].chainId,"number"),this.onChainChanged(n.toString());return}if(h==="personal_sign"){if(o.signMessageError)throw typeof o.signMessageError=="boolean"?new viem.UserRejectedRequestError(new Error("Failed to sign message.")):o.signMessageError;h="eth_sign",u=[u[1],u[0]];}let c={method:h,params:u},{error:l,result:d}=await utils.rpc.http(m,{body:c});if(l)throw new viem.RpcRequestError({body:c,error:l,url:m});return d}})({retryCount:1})}}))}var Re={allowedDomains:[/^https:\/\/([a-z0-9-]+\.)*gnosis-safe\.io$/,/^https:\/\/([a-z0-9-]+\.)*app\.safe\.global$/,/^https:\/\/([a-z0-9-]+\.)*metissafe\.tech$/],debug:false};var ke=r=>r.reduce((o,e)=>{let n=e.id;return o[n]=viem.http(),o},{});function ze(r,o){let{wagmiConfig:e}=r,n=r.siwx??(r.siwe?{enabled:r.siwe.enabled,isSignedIn:r.siwe.isSignedIn,isRejected:r.siwe.isRejected}:void 0),{disconnect:s,updateActiveConnection:a}=o,t=()=>o.getState?.()??o,i=()=>{let u=n?.isRejected||n?.status==="error",c=n?.isSignedIn??n?.isAuthenticated??false;if(n?.enabled!==false&&!c&&u){let{activeConnection:d}=t();d&&s(d.connectorType);}},m=u=>{let{activeConnection:c,connectionError:l}=t();if(c&&orbitCore.getAdapterFromConnectorType(c.connectorType)!==orbitCore.OrbitAdapter.EVM)return;if(u.length===0){c&&s(c.connectorType);return}let d=core.getConnection(e);if(c&&orbitCore.getAdapterFromConnectorType(c.connectorType)!==orbitCore.OrbitAdapter.EVM||!d||l)return;let E=n?.address??n?.session?.address,y=n?.chainId??n?.session?.chainId;if((n?.isSignedIn??n?.isAuthenticated??false)&&c){let b=d.address&&E&&d.address.toLowerCase()!==E.toLowerCase()&&!E.toLowerCase().endsWith(d.address.toLowerCase()),U=d.chainId&&y&&String(d.chainId)!==String(y)&&!y.endsWith(`:${d.chainId}`);if(b||U){s(c.connectorType);return}}let S=d.connector,D={connectorType:S?`${orbitCore.OrbitAdapter.EVM}:${orbitCore.formatConnectorName(S.name)}`:c?.connectorType,address:d.address,chainId:d.chainId,rpcURL:d?.chain?.rpcUrls.default.http[0],isConnected:true,signMessage:b=>core.signMessage(e,{message:b})};a(D);};i();let{activeConnection:p}=t();if(p&&orbitCore.getAdapterFromConnectorType(p.connectorType)===orbitCore.OrbitAdapter.EVM){let u=core.getConnection(e);u&&u.isConnected&&a({signMessage:c=>core.signMessage(e,{message:c})});}return core.watchConnections(e,{onChange:m})}exports.checkIsWalletAddressContract=M;exports.createDefaultTransports=ke;exports.createEVMConnectionsWatcher=ze;exports.impersonated=k;exports.safeSdkOptions=Re;exports.satelliteEVMAdapter=Se;
|
package/dist/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
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
|
|
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 I=new Map;async function M({config:r,address:o,chainId:e,chains:n}){let s=`${Number(e)}:${o.toLowerCase()}`;if(I.has(s))return I.get(s);if(createViemClient(Number(e),n)){let i=!!await getBytecode(r,{address:o,chainId:Number(e)});return I.set(s,i),i}else return false}function Se(r,o){if(!r)throw new Error("Satellite EVM adapter requires a wagmi config object.");return {key:OrbitAdapter.EVM,connect:async({connectorType:e,chainId:n})=>{let a=getConnectors(r).find(t=>getConnectorTypeFromName(OrbitAdapter.EVM,formatConnectorName(t.name))===e);if(!a)throw new Error("Cannot find connector with this wallet type");try{await connect(r,{connector:a,chainId:n});let t=getConnection(r);return {connectorType:e,address:t.address??zeroAddress,chainId:t.chainId??mainnet.id,rpcURL:t.chain?.rpcUrls.default.http[0]??mainnet.rpcUrls.default.http[0],isConnected:t.isConnected,isContractAddress:!1,icon:a?.icon?.trim(),connector:a,signMessage:i=>signMessage(r,{message:i})}}catch(t){throw new Error(t instanceof Error?t.message:String(t),{cause:t})}},disconnect:async e=>{if(e&&e.isConnected)await disconnect(r,{connector:e?.connector});else {let n=getConnectors(r);await Promise.allSettled(n.map(async s=>{await disconnect(r,{connector:s});}));}},getConnectors:()=>{let e=getConnectors(r);return {adapter:OrbitAdapter.EVM,connectors:e.map(n=>n)}},checkAndSwitchNetwork:async e=>await checkAndSwitchChain(Number(e),r),getBalance:async(e,n)=>{let s=await getBalance(r,{address:e,chainId:Number(n)});return {value:formatUnits(s.value,s.decimals),symbol:s.symbol}},getExplorerUrl:(e,n)=>{let a=(n===void 0?getConnection(r).chain:o.find(t=>t.id===Number(n))??getChains(r).find(t=>t.id===Number(n)))?.blockExplorers?.default.url;if(a)return e?`${a.replace(/\/+$/,"")}/${e.replace(/^\/+/,"")}`:a},getName:e=>getName(e,o),getAvatar:e=>getAvatar(e,o),getAddress:e=>getAddress(e,o),checkIsContractAddress:async({address:e,chainId:n})=>{let s=getChains(r);return await M({config:r,address:e,chainId:n,chains:s})},getSafeConnectorChainId:async()=>{let n=getConnectors(r).find(s=>s.name==="Safe");if(n)return await n.getChainId()},switchConnection:async e=>{let s=getConnectors(r).find(a=>getConnectorTypeFromName(OrbitAdapter.EVM,formatConnectorName(a.name))===e);if(!s)throw new Error(`Cannot find connector with type: ${e}`);try{await switchConnection(r,{connector:s});}catch(a){throw new Error(`Failed to switch to connector ${e}: ${a instanceof Error?a.message:String(a)}`,{cause:a})}}}}k.type="impersonated";function k(r){let o=r.features??{},e=false,n,s;return createConnector(a=>({id:"impersonated",name:"Impersonated Connector",type:k.type,async setup(){n=a.chains[0].id;},async connect({chainId:t}={}){if(o.connectError)throw typeof o.connectError=="boolean"?new UserRejectedRequestError(new Error("Failed to connect.")):o.connectError;let{request:i}=await this.getProvider(),m=await i({method:"eth_requestAccounts"}),p=await this.getChainId();return t&&p!==t&&(p=(await this.switchChain({chainId:t})).id),e=true,{accounts:m,chainId:p}},async disconnect(){e=false,s=void 0;},async getAccounts(){if(!e)throw new Error("Not connected connector");let{request:t}=await this.getProvider();return (await t({method:"eth_accounts"})).map(getAddress$1)},async getChainId(){let{request:t}=await this.getProvider(),i=await t({method:"eth_chainId"});return fromHex(i,"number")},async isAuthorized(){return e?!!(await this.getAccounts()).length:false},async switchChain({chainId:t}){let i=a.chains.find(p=>p.id===t);if(!i)throw new SwitchChainError(new ChainNotConfiguredError);let{request:m}=await this.getProvider();return await m({method:"wallet_switchEthereumChain",params:[{chainId:numberToHex(t)}]}),i},onAccountsChanged(t){t.length===0?this.onDisconnect():a.emitter.emit("change",{accounts:t.map(getAddress$1)});},onChainChanged(t){let i=Number(t);a.emitter.emit("change",{chainId:i});},async onDisconnect(){a.emitter.emit("disconnect"),e=false,s=void 0;},async getProvider({chainId:t}={}){s=impersonatedHelpers?.getImpersonated()?[impersonatedHelpers.getImpersonated()||zeroAddress]:void 0;let m=(a.chains.find(h=>h.id===t)??a.chains[0]).rpcUrls.default.http[0];return custom({request:async({method:h,params:u})=>{if(h==="eth_chainId")return numberToHex(n);if(h==="eth_requestAccounts"||h==="eth_accounts")return s??[];if(h==="eth_signTypedData_v4"&&o.signTypedDataError)throw typeof o.signTypedDataError=="boolean"?new UserRejectedRequestError(new Error("Failed to sign typed data.")):o.signTypedDataError;if(h==="wallet_switchEthereumChain"){if(o.switchChainError)throw typeof o.switchChainError=="boolean"?new UserRejectedRequestError(new Error("Failed to switch chain.")):o.switchChainError;n=fromHex(u[0].chainId,"number"),this.onChainChanged(n.toString());return}if(h==="personal_sign"){if(o.signMessageError)throw typeof o.signMessageError=="boolean"?new UserRejectedRequestError(new Error("Failed to sign message.")):o.signMessageError;h="eth_sign",u=[u[1],u[0]];}let c={method:h,params:u},{error:l,result:d}=await rpc.http(m,{body:c});if(l)throw new RpcRequestError({body:c,error:l,url:m});return d}})({retryCount:1})}}))}var Re={allowedDomains:[/^https:\/\/([a-z0-9-]+\.)*gnosis-safe\.io$/,/^https:\/\/([a-z0-9-]+\.)*app\.safe\.global$/,/^https:\/\/([a-z0-9-]+\.)*metissafe\.tech$/],debug:false};var ke=r=>r.reduce((o,e)=>{let n=e.id;return o[n]=http(),o},{});function ze(r,o){let{wagmiConfig:e}=r,n=r.siwx??(r.siwe?{enabled:r.siwe.enabled,isSignedIn:r.siwe.isSignedIn,isRejected:r.siwe.isRejected}:void 0),{disconnect:s,updateActiveConnection:a}=o,t=()=>o.getState?.()??o,i=()=>{let u=n?.isRejected||n?.status==="error",c=n?.isSignedIn??n?.isAuthenticated??false;if(n?.enabled!==false&&!c&&u){let{activeConnection:d}=t();d&&s(d.connectorType);}},m=u=>{let{activeConnection:c,connectionError:l}=t();if(c&&getAdapterFromConnectorType(c.connectorType)!==OrbitAdapter.EVM)return;if(u.length===0){c&&s(c.connectorType);return}let d=getConnection(e);if(c&&getAdapterFromConnectorType(c.connectorType)!==OrbitAdapter.EVM||!d||l)return;let E=n?.address??n?.session?.address,y=n?.chainId??n?.session?.chainId;if((n?.isSignedIn??n?.isAuthenticated??false)&&c){let b=d.address&&E&&d.address.toLowerCase()!==E.toLowerCase()&&!E.toLowerCase().endsWith(d.address.toLowerCase()),U=d.chainId&&y&&String(d.chainId)!==String(y)&&!y.endsWith(`:${d.chainId}`);if(b||U){s(c.connectorType);return}}let S=d.connector,D={connectorType:S?`${OrbitAdapter.EVM}:${formatConnectorName(S.name)}`:c?.connectorType,address:d.address,chainId:d.chainId,rpcURL:d?.chain?.rpcUrls.default.http[0],isConnected:true,signMessage:b=>signMessage(e,{message:b})};a(D);};i();let{activeConnection:p}=t();if(p&&getAdapterFromConnectorType(p.connectorType)===OrbitAdapter.EVM){let u=getConnection(e);u&&u.isConnected&&a({signMessage:c=>signMessage(e,{message:c})});}return watchConnections(e,{onChange:m})}export{M as checkIsWalletAddressContract,ke as createDefaultTransports,ze as createEVMConnectionsWatcher,k as impersonated,Re as safeSdkOptions,Se as satelliteEVMAdapter};
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tuwaio/satellite-evm",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"author": "Oleksandr Tkach",
|
|
6
6
|
"license": "Apache-2.0",
|
|
7
|
-
"description": "
|
|
7
|
+
"description": "L4 EVM package of Satellite Connect (TUWA): wallet adapter and connections watcher for wagmi connectors, a contract account check and wagmi config helpers, built on @wagmi/core and viem.",
|
|
8
8
|
"main": "./dist/index.js",
|
|
9
9
|
"module": "./dist/index.mjs",
|
|
10
10
|
"types": "./dist/index.d.ts",
|
|
@@ -16,13 +16,17 @@
|
|
|
16
16
|
"socket.json"
|
|
17
17
|
],
|
|
18
18
|
"keywords": [
|
|
19
|
+
"tuwa",
|
|
20
|
+
"satellite-connect",
|
|
21
|
+
"wallet-connection",
|
|
19
22
|
"web3",
|
|
20
23
|
"evm",
|
|
21
|
-
"connect",
|
|
22
|
-
"wallet",
|
|
23
24
|
"wagmi",
|
|
24
25
|
"viem",
|
|
25
|
-
"
|
|
26
|
+
"ens",
|
|
27
|
+
"safe",
|
|
28
|
+
"typescript",
|
|
29
|
+
"headless"
|
|
26
30
|
],
|
|
27
31
|
"repository": {
|
|
28
32
|
"type": "git",
|
|
@@ -41,8 +45,6 @@
|
|
|
41
45
|
],
|
|
42
46
|
"peerDependencies": {
|
|
43
47
|
"@wagmi/core": "3.x.x",
|
|
44
|
-
"immer": "11.x.x",
|
|
45
|
-
"zustand": "5.x.x",
|
|
46
48
|
"viem": "2.x.x",
|
|
47
49
|
"@tuwaio/orbit-core": ">=0.3",
|
|
48
50
|
"@tuwaio/orbit-evm": ">=0.3",
|
|
@@ -50,15 +52,13 @@
|
|
|
50
52
|
},
|
|
51
53
|
"devDependencies": {
|
|
52
54
|
"@wagmi/core": "^3.6.5",
|
|
53
|
-
"@tuwaio/orbit-core": "^0.3.
|
|
54
|
-
"@tuwaio/orbit-evm": "^0.3.
|
|
55
|
-
"@tuwaio/satellite-core": "^0.
|
|
56
|
-
"immer": "^11.1.18",
|
|
55
|
+
"@tuwaio/orbit-core": "^0.3.1",
|
|
56
|
+
"@tuwaio/orbit-evm": "^0.3.2",
|
|
57
|
+
"@tuwaio/satellite-core": "^0.6.2",
|
|
57
58
|
"tsup": "^8.5.1",
|
|
58
59
|
"typescript": "6.0.3",
|
|
59
|
-
"viem": "^2.
|
|
60
|
-
"vitest": "^5.0.
|
|
61
|
-
"zustand": "^5.0.15"
|
|
60
|
+
"viem": "^2.57.1",
|
|
61
|
+
"vitest": "^5.0.2"
|
|
62
62
|
},
|
|
63
63
|
"scripts": {
|
|
64
64
|
"start": "tsup src/index.ts --watch",
|