@tuwaio/orbit-evm 0.3.0 → 0.3.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 CHANGED
@@ -1,20 +1,19 @@
1
1
  # @tuwaio/orbit-evm
2
2
 
3
3
  [![NPM Version](https://img.shields.io/npm/v/@tuwaio/orbit-evm.svg)](https://www.npmjs.com/package/@tuwaio/orbit-evm)
4
- [![License](https://img.shields.io/npm/l/@tuwaio/orbit-evm.svg)](./LICENSE)
4
+ [![License](https://img.shields.io/npm/l/@tuwaio/orbit-evm.svg)](https://github.com/TuwaIO/orbit/blob/main/packages/orbit-evm/LICENSE)
5
5
 
6
- `@tuwaio/orbit-evm` provides concrete implementations of low-level EVM-specific communication primitives for Layer 2 (L2) of the TUWA Orbit stack. Engineered strictly on top of **`@wagmi/core`** and **`viem`**, this package provides deterministic chain switching, custom Viem client generation, cached ENS metadata resolution, and full ERC-4337 Account Abstraction orchestration via Pimlico and Solady smart accounts. It enforces a complete exclusion of legacy libraries like `ethers.js` or `web3.js`.
6
+ `@tuwaio/orbit-evm` is the EVM Layer 2 (L2) package of **Orbit Utils**, the Stage 1 primitives layer of the TUWA ecosystem. It wraps **`viem`** and **`@wagmi/core`** into small, framework-agnostic helpers: cached public clients, chain switching, ENS resolution, and ERC-4337 smart accounts built on Solady with a Pimlico bundler and paymaster. It does not use `ethers.js` or `web3.js`.
7
7
 
8
8
  ---
9
9
 
10
10
  ## 🏛️ Core Capabilities
11
11
 
12
- - **Viem Client Optimization:** Creates and caches high-performance `viem` public clients (`createViemClient`) to minimize request latency and avoid duplicate RPC instantiation.
13
- - **ERC-4337 Account Abstraction (Pimlico & Solady):** High-level client factory (`createPimlicoSmartAccountClient`) and Solady smart account instantiation (`createSoladySmartAccount`) with automatic gas sponsorship (`createPimlicoPaymasterClient`).
14
- - **Deterministic Solady Salt Generation:** Generates right-padded 32-byte salts (`pad(ownerAddress, { dir: 'right', size: 32 })`) compliant with the Solady ERC-4337 factory prefix requirements, preventing `SaltDoesNotStartWith()` exceptions and multi-user address collisions.
15
- - **ENS Metadata Engine:** Direct lookup utilities (`getName`, `getAvatar`, `getAddress`) on Ethereum Mainnet with local in-memory caching.
16
- - **Deterministic Chain Switching:** Low-level utility (`checkAndSwitchChain`) to enforce network alignment with the target blockchain.
17
- - **Strict Compile-Time Types:** Fully integrated with TypeScript standards v5.9+ and native Viem/Wagmi typings.
12
+ - **Public clients:** `createViemClient` returns a cached `viem` public client per chain, so repeated reads do not create new clients.
13
+ - **Chain alignment:** `checkAndSwitchChain` asks the connected wallet to switch networks only when it is on a different chain, and throws a readable error when the user rejects.
14
+ - **ENS resolution:** `getName`, `getAvatar` and `getAddress` always resolve against Ethereum Mainnet and cache results in memory; `isEnsName` tells names and addresses apart.
15
+ - **ERC-4337 smart accounts:** `createPimlicoSmartAccountClient` sets up a Solady smart account (EntryPoint v0.7), a bundler client and an optional gas-sponsoring paymaster in one call. Bundler and paymaster clients are cached in memory per endpoint. The building blocks (`createSoladySmartAccount`, `createBundlerRpcClient`, `createPimlicoPaymasterClient`, `createPimlicoRpcUrl`, `clearBundlerCache`) are exported for custom setups.
16
+ - **Chain lists:** `getEvmChains` and `isEvmChainList` extract and detect EVM chain IDs in mixed multi-chain configurations.
18
17
 
19
18
  ---
20
19
 
@@ -24,119 +23,124 @@
24
23
  pnpm add @tuwaio/orbit-evm @wagmi/core viem
25
24
  ```
26
25
 
27
- If using `@tuwaio/orbit-core` adapters and type definitions alongside EVM primitives:
28
-
29
- ```bash
30
- pnpm add @tuwaio/orbit-evm @tuwaio/orbit-core @wagmi/core viem
31
- ```
32
-
33
26
  > [!IMPORTANT]
34
- > `@wagmi/core` (v3.x) and `viem` (v2.x) are peer dependencies and must be installed alongside `@tuwaio/orbit-evm`.
27
+ > `viem` (2.x) and `@wagmi/core` (3.x) are peer dependencies and must be installed alongside `@tuwaio/orbit-evm`.
35
28
 
36
29
  ---
37
30
 
38
- ## 🚀 Technical Integration
39
-
40
- ### Cached Client Generation & Routing
31
+ ## 🚀 Usage
41
32
 
42
- Create a public client wrapper to route queries to EVM nodes:
33
+ ### Cached public client
43
34
 
44
35
  ```typescript
45
36
  import { createViemClient } from '@tuwaio/orbit-evm';
46
- import { mainnet } from 'viem/chains';
37
+ import { mainnet, sepolia } from 'viem/chains';
38
+
39
+ const chains = [mainnet, sepolia] as const;
47
40
 
48
- // Retrieve cached or new Viem Public Client
49
- const client = createViemClient(mainnet);
41
+ // Returns undefined (and logs a warning) if the chain ID is not in `chains`.
42
+ const client = createViemClient(sepolia.id, chains);
43
+ const blockNumber = await client?.getBlockNumber();
50
44
  ```
51
45
 
52
- ### Deterministic Chain Switcher
46
+ The client uses the chain's default RPC URL. The cache is keyed by chain ID and is rebuilt when that URL changes.
53
47
 
54
- Enforce that the wallet's connection context matches the requested chain:
48
+ ### Chain switching
55
49
 
56
50
  ```typescript
57
- import { checkAndSwitchChain } from '@tuwaio/orbit-evm';
58
51
  import { type Config } from '@wagmi/core';
52
+ import { checkAndSwitchChain } from '@tuwaio/orbit-evm';
59
53
 
60
- declare const config: Config;
54
+ declare const wagmiConfig: Config;
61
55
 
62
- async function switchNetwork(targetChainId: number) {
63
- try {
64
- await checkAndSwitchChain(targetChainId, config);
65
- console.log(`Execution context successfully switched to: ${targetChainId}`);
66
- } catch (error) {
67
- console.error('Chain switch rejected:', error);
68
- }
56
+ try {
57
+ await checkAndSwitchChain(11155111, wagmiConfig);
58
+ } catch (error) {
59
+ // "User rejected the request to switch network." or a generic switch error (original error in `cause`).
60
+ console.error(error);
69
61
  }
70
62
  ```
71
63
 
72
- ### ERC-4337 Smart Account Client (Pimlico & Solady)
73
-
74
- Instantiate a fully configured Solady smart account client with automated Pimlico bundler and paymaster sponsorship:
64
+ ### ENS resolution
75
65
 
76
66
  ```typescript
77
- import { createPimlicoSmartAccountClient } from '@tuwaio/orbit-evm';
78
- import { sepolia } from 'viem/chains';
79
- import { type Config } from '@wagmi/core';
67
+ import { getAddress, getAvatar, getName, isEnsName } from '@tuwaio/orbit-evm';
68
+ import { mainnet } from 'viem/chains';
80
69
 
81
- declare const wagmiConfig: Config;
70
+ const chains = [mainnet] as const;
82
71
 
83
- async function initializeSmartAccount() {
84
- const { account, bundlerClient, publicClient, paymasterClient } = await createPimlicoSmartAccountClient({
85
- chain: sepolia,
86
- wagmiConfig,
87
- apiKey: process.env.NEXT_PUBLIC_PIMLICO_API_KEY, // or custom bundlerUrl
88
- sponsor: true, // Enables Pimlico paymaster gas sponsorship
89
- });
72
+ const name = await getName('0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045', chains); // "vitalik.eth" or null
73
+ const avatar = name ? await getAvatar(name, chains) : null;
90
74
 
91
- console.log('Solady Smart Account counterfactual address:', account.address);
92
- return { account, bundlerClient, publicClient };
93
- }
75
+ const input = 'vitalik.eth';
76
+ const address = isEnsName(input) ? await getAddress(input, chains) : null; // lowercased address or null
94
77
  ```
95
78
 
96
- #### Solady Salt Padding Rule
79
+ Lookups use the mainnet entry of `chains` when present (so your own RPC URL is used), otherwise viem's default mainnet RPC.
97
80
 
98
- When deploying Solady smart accounts, the factory requires the salt to be prefixed with the EOA owner's 20-byte address. To guarantee a deterministic 32-byte representation without failing Solady's `SaltDoesNotStartWith()` check, `createSoladySmartAccount` defaults to right-padding:
81
+ ### ERC-4337 smart account with Pimlico
99
82
 
100
83
  ```typescript
101
- import { pad } from 'viem';
84
+ import { type Config } from '@wagmi/core';
85
+ import { createPimlicoSmartAccountClient } from '@tuwaio/orbit-evm';
86
+ import { sepolia } from 'viem/chains';
87
+
88
+ declare const wagmiConfig: Config;
89
+
90
+ const { account, bundlerClient } = await createPimlicoSmartAccountClient({
91
+ chain: sepolia,
92
+ wagmiConfig, // or pass `walletClient` directly
93
+ apiKey: process.env.NEXT_PUBLIC_PIMLICO_API_KEY, // or `bundlerUrl` for your own bundler endpoint
94
+ });
102
95
 
103
- // Deterministic salt ensuring owner prefix alignment
104
- const accountSalt = pad(ownerAccount.address, { dir: 'right', size: 32 });
96
+ const userOpHash = await bundlerClient.sendUserOperation({
97
+ account,
98
+ calls: [{ to: '0x0000000000000000000000000000000000000000', value: 0n }],
99
+ });
100
+ const receipt = await bundlerClient.waitForUserOperationReceipt({ hash: userOpHash });
105
101
  ```
106
102
 
107
- ### Low-Level Pimlico Bundler Client & RPC URL
103
+ - A connected wallet is required: the EOA signs for the smart account.
104
+ - Gas sponsorship (`sponsor`) is on by default when `apiKey` or `bundlerUrl` is set. Without either, the public Pimlico endpoint is used and no paymaster is attached.
105
+ - The smart account uses viem's Solady implementation with its default EntryPoint v0.7 (`entryPoint07Address`).
106
+ - The account salt defaults to the owner address right-padded to 32 bytes (`pad(owner, { dir: 'right', size: 32 })`). This satisfies the Solady factory's owner-prefix check (`SaltDoesNotStartWith()`) and gives every owner a deterministic, unique account address. Pass `salt` to override it.
108
107
 
109
- Instantiate and cache Viem Bundler clients with automated Pimlico URL resolution:
108
+ > [!WARNING]
109
+ > The Pimlico API key becomes part of the bundler URL used by the browser. Use a key restricted to your domains and sponsorship policies, or proxy the bundler through your backend and pass its address as `bundlerUrl`.
110
+
111
+ ### Low-level bundler building blocks
110
112
 
111
113
  ```typescript
112
- import { createBundlerRpcClient, createPimlicoRpcUrl } from '@tuwaio/orbit-evm';
114
+ import {
115
+ clearBundlerCache,
116
+ createBundlerRpcClient,
117
+ createPimlicoPaymasterClient,
118
+ createPimlicoRpcUrl,
119
+ } from '@tuwaio/orbit-evm';
113
120
 
114
- // Generate or retrieve cached Pimlico RPC endpoint
115
- const rpcUrl = createPimlicoRpcUrl({
116
- chainId: 11155111,
117
- apiKey: 'pim_test_key_123',
118
- });
121
+ // Resolution order: bundlerUrl → https://api.pimlico.io/v2/{chainId}/rpc?apikey=… → https://public.pimlico.io/v2/{chainId}/rpc
122
+ const rpcUrl = createPimlicoRpcUrl({ chainId: 11155111, apiKey: process.env.PIMLICO_API_KEY });
123
+
124
+ // Cached by resolved URL.
125
+ const paymasterClient = createPimlicoPaymasterClient({ chainId: 11155111, apiKey: process.env.PIMLICO_API_KEY });
119
126
 
120
- // Retrieve cached or new Viem Bundler Client (with in-memory cache)
127
+ // Cached by resolved URL, paymaster presence and client presence.
121
128
  const bundlerClient = createBundlerRpcClient({
122
129
  chainId: 11155111,
123
- apiKey: 'pim_test_key_123',
130
+ apiKey: process.env.PIMLICO_API_KEY,
131
+ paymaster: paymasterClient,
124
132
  });
133
+
134
+ // Drops cached URLs, bundler and paymaster clients (e.g. after rotating the API key).
135
+ clearBundlerCache();
125
136
  ```
126
137
 
127
138
  ---
128
139
 
129
- ## 🔧 API & Module Architecture
130
-
131
- `@tuwaio/orbit-evm` exposes the following modules:
140
+ ## 📚 API Reference
132
141
 
133
- - **ERC-4337 Account Abstraction:** `createPimlicoSmartAccountClient`, `createSoladySmartAccount`, `createPimlicoPaymasterClient`, `createPimlicoRpcUrl`, `createBundlerRpcClient`, `clearBundlerCache`.
134
- - **Chain Alignment:** `checkAndSwitchChain`, `normalizeChainId`.
135
- - **Client Factory:** `createViemClient`, `clearViemClientCache`.
136
- - **ENS Resolvers:** `getAddress`, `getAvatar`, `getName`, `isEnsName`.
137
-
138
- ---
142
+ Every export, with signatures and types generated from the source, is documented at **[orbit.docs.tuwa.io/packages/orbit-evm](https://orbit.docs.tuwa.io/packages/orbit-evm)**. For a full walkthrough of sovereign account abstraction with Orbit, Pulsar and Quasar, see the [ERC-4337 guide](https://docs.tuwa.io/guides/erc-4337-sovereign-account-abstraction).
139
143
 
140
144
  ## 📄 License
141
145
 
142
- Licensed under the **Apache-2.0 License**. See the [LICENSE](./LICENSE) file for details.
146
+ Licensed under the **Apache-2.0 License**. See the [LICENSE](https://github.com/TuwaIO/orbit/blob/main/packages/orbit-evm/LICENSE) file for details.
package/dist/index.d.mts CHANGED
@@ -102,10 +102,11 @@ declare function createPimlicoRpcUrl(config: PimlicoUrlConfig): string;
102
102
  */
103
103
  declare function createBundlerRpcClient(config: BundlerRpcClientConfig): BundlerClient<HttpTransport>;
104
104
  /**
105
- * Creates a Viem Paymaster Client configured with the resolved Pimlico RPC endpoint.
105
+ * Creates or retrieves a cached Viem Paymaster Client configured with the resolved Pimlico RPC endpoint.
106
+ * Side effect: stores the client in an in-memory cache keyed by the resolved RPC URL (see `clearBundlerCache`).
106
107
  *
107
108
  * @param config - Pimlico URL configuration.
108
- * @returns PaymasterClient instance configured for Pimlico gas sponsorship.
109
+ * @returns Cached or newly instantiated PaymasterClient configured for Pimlico gas sponsorship.
109
110
  */
110
111
  declare function createPimlicoPaymasterClient(config: PimlicoUrlConfig): PaymasterClient;
111
112
  /**
@@ -125,17 +126,23 @@ declare function createSoladySmartAccount({ client, walletClient, salt, }: Creat
125
126
  */
126
127
  declare function createPimlicoSmartAccountClient(config: PimlicoSmartAccountClientConfig): Promise<PimlicoSmartAccountClientResult>;
127
128
  /**
128
- * Clears the in-memory cache of Pimlico URLs and Bundler clients.
129
- * Useful for testing and resetting runtime state.
129
+ * Clears the in-memory caches of Pimlico URLs, Bundler clients and Paymaster clients.
130
+ * Useful for testing and resetting runtime state (e.g. after rotating an API key).
130
131
  */
131
132
  declare function clearBundlerCache(): void;
132
133
 
133
134
  /**
134
- * Get EVM chain IDs from app chains configuration
135
+ * Gets the EVM chain IDs from an app's viem chain configuration.
136
+ *
137
+ * @param appChains - The viem chains configured in the app.
138
+ * @returns The positive numeric chain IDs, or an empty array if no chains are provided.
135
139
  */
136
140
  declare function getEvmChains(appChains?: readonly [Chain$1, ...Chain$1[]]): number[];
137
141
  /**
138
- * Type guard to check if a chain list contains EVM chain IDs
142
+ * Type guard to check if a chain list contains EVM chain IDs.
143
+ *
144
+ * @param chains - Mixed list of chain identifiers.
145
+ * @returns `true` if the list is non-empty and contains only numbers.
139
146
  */
140
147
  declare function isEvmChainList(chains: (string | number)[]): chains is number[];
141
148
 
package/dist/index.d.ts CHANGED
@@ -102,10 +102,11 @@ declare function createPimlicoRpcUrl(config: PimlicoUrlConfig): string;
102
102
  */
103
103
  declare function createBundlerRpcClient(config: BundlerRpcClientConfig): BundlerClient<HttpTransport>;
104
104
  /**
105
- * Creates a Viem Paymaster Client configured with the resolved Pimlico RPC endpoint.
105
+ * Creates or retrieves a cached Viem Paymaster Client configured with the resolved Pimlico RPC endpoint.
106
+ * Side effect: stores the client in an in-memory cache keyed by the resolved RPC URL (see `clearBundlerCache`).
106
107
  *
107
108
  * @param config - Pimlico URL configuration.
108
- * @returns PaymasterClient instance configured for Pimlico gas sponsorship.
109
+ * @returns Cached or newly instantiated PaymasterClient configured for Pimlico gas sponsorship.
109
110
  */
110
111
  declare function createPimlicoPaymasterClient(config: PimlicoUrlConfig): PaymasterClient;
111
112
  /**
@@ -125,17 +126,23 @@ declare function createSoladySmartAccount({ client, walletClient, salt, }: Creat
125
126
  */
126
127
  declare function createPimlicoSmartAccountClient(config: PimlicoSmartAccountClientConfig): Promise<PimlicoSmartAccountClientResult>;
127
128
  /**
128
- * Clears the in-memory cache of Pimlico URLs and Bundler clients.
129
- * Useful for testing and resetting runtime state.
129
+ * Clears the in-memory caches of Pimlico URLs, Bundler clients and Paymaster clients.
130
+ * Useful for testing and resetting runtime state (e.g. after rotating an API key).
130
131
  */
131
132
  declare function clearBundlerCache(): void;
132
133
 
133
134
  /**
134
- * Get EVM chain IDs from app chains configuration
135
+ * Gets the EVM chain IDs from an app's viem chain configuration.
136
+ *
137
+ * @param appChains - The viem chains configured in the app.
138
+ * @returns The positive numeric chain IDs, or an empty array if no chains are provided.
135
139
  */
136
140
  declare function getEvmChains(appChains?: readonly [Chain$1, ...Chain$1[]]): number[];
137
141
  /**
138
- * Type guard to check if a chain list contains EVM chain IDs
142
+ * Type guard to check if a chain list contains EVM chain IDs.
143
+ *
144
+ * @param chains - Mixed list of chain identifiers.
145
+ * @returns `true` if the list is non-empty and contains only numbers.
139
146
  */
140
147
  declare function isEvmChainList(chains: (string | number)[]): chains is number[];
141
148
 
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- 'use strict';var core=require('@wagmi/core'),viem=require('viem'),accountAbstraction=require('viem/account-abstraction'),accounts=require('viem/accounts'),chains=require('viem/chains'),ens=require('viem/ens');var m=new Map,p=new Map;function H(e){return e.bundlerUrl?`custom:${e.bundlerUrl.trim()}`:`${e.chainId}:${e.apiKey?.trim()??"public"}`}function K(e){let t=C(e),r=!!e.paymaster,n=!!e.client;return `${t}:pm=${r}:client=${n}`}function C(e){let t=H(e),r=m.get(t);if(r)return r;let n;return e.bundlerUrl?n=e.bundlerUrl.trim():e.apiKey?n=`https://api.pimlico.io/v2/${e.chainId}/rpc?apikey=${e.apiKey.trim()}`:n=`https://public.pimlico.io/v2/${e.chainId}/rpc`,m.set(t,n),n}function N(e){let t=K(e),r=p.get(t);if(r)return r;let n=C(e),i={...e};delete i.bundlerUrl,delete i.apiKey;let a=accountAbstraction.createBundlerClient({...i,transport:viem.http(n)});return p.set(t,a),a}function R(e){let t=C(e);return accountAbstraction.createPaymasterClient({transport:viem.http(t)})}async function k({client:e,walletClient:t,salt:r}){if(!t.account)throw new Error("WalletClient must have an active account.");let n=t.account,i=accounts.toAccount({address:n.address,async signMessage({message:o}){return t.signMessage({account:n,message:o})},async signTransaction(o){return t.signTransaction({account:n,...o})},async signTypedData(o){return t.signTypedData({account:n,...o})}}),a=r??viem.pad(n.address,{dir:"right",size:32});return accountAbstraction.toSoladySmartAccount({client:e,owner:i,salt:a})}async function Q(e){let{chain:t,apiKey:r,bundlerUrl:n,rpcUrl:i,salt:a,sponsor:o=!!(r||n)}=e,l=e.walletClient;if(!l&&e.wagmiConfig&&(l=await core.getWalletClient(e.wagmiConfig,{chainId:t.id})),!l||!l.account)throw new Error("Active wallet connection with account is required (provide walletClient or wagmiConfig).");let u=e.client??viem.createPublicClient({chain:t,transport:i?viem.http(i):viem.http()}),A=await k({client:u,walletClient:l,salt:a}),d=o&&(r||n)?R({chainId:t.id,apiKey:r,bundlerUrl:n}):void 0,x=N({chainId:t.id,apiKey:r,bundlerUrl:n,client:u,...d?{paymaster:d}:{}});return {account:A,bundlerClient:x,publicClient:u,paymasterClient:d}}function X(){m.clear(),p.clear();}function M(e){return e!=null&&typeof e=="number"&&e>0}function Z(e){return !e||e.length===0?[]:e.map(t=>t.id).filter(M)}function _(e){return e.length>0&&e.every(t=>typeof t=="number")}async function re(e,t){let{connector:r,chainId:n}=core.getConnection(t);if(r&&n!==e)try{await core.switchChain(t,{chainId:e});}catch(i){throw i?.cause?.name==="UserRejectedRequestError"?new Error("User rejected the request to switch network.",{cause:i}):(console.error("Failed to switch network:",i),new Error("An error occurred while switching the network.",{cause:i}))}}var y=new Map;function f(e,t){let r=t.find(i=>i.id===e),n=y.get(e);if(n&&n.chain?.rpcUrls.default.http[0]===r?.rpcUrls.default.http[0])return n;if(r){let i=viem.createPublicClient({chain:r,transport:viem.http()});return y.set(e,i),i}console.warn(`createViemClient: No chain configuration found for chainId ${e}. A client could not be created.`);}var g=new Map,w=new Map,P=new Map,h=e=>{let t=e.find(r=>r.id===chains.mainnet.id);return t?f(chains.mainnet.id,[t]):f(chains.mainnet.id,[chains.mainnet])},fe=async(e,t)=>{let r=h(t);if(!r)return null;let n=g.get(e);if(n!==void 0)return n;try{let i=await ens.getEnsName(r,{address:e});return g.set(e,i),i}catch(i){return console.error(`ENS name lookup failed for address ${e}:`,i),null}},he=async(e,t)=>{let r=h(t);if(!r)return null;let n=ens.normalize(e),i=w.get(n);if(i!==void 0)return i;try{let a=await ens.getEnsAvatar(r,{name:n});return w.set(n,a),a}catch(a){return console.error(`ENS avatar lookup failed for name ${e}:`,a),null}},ye=async(e,t)=>{let r=h(t);if(!r)return null;let n=ens.normalize(e),i=P.get(n);if(i!==void 0)return i;try{let a=await ens.getEnsAddress(r,{name:n}),o=a?a.toLowerCase():null;return P.set(n,o),o}catch(a){return console.error(`ENS address lookup failed for name ${e}:`,a),null}},ge=e=>e.includes(".")&&!viem.isAddress(e);
2
- exports.checkAndSwitchChain=re;exports.clearBundlerCache=X;exports.createBundlerRpcClient=N;exports.createPimlicoPaymasterClient=R;exports.createPimlicoRpcUrl=C;exports.createPimlicoSmartAccountClient=Q;exports.createSoladySmartAccount=k;exports.createViemClient=f;exports.getAddress=ye;exports.getAvatar=he;exports.getEvmChains=Z;exports.getName=fe;exports.isEnsName=ge;exports.isEvmChainList=_;
1
+ 'use strict';var core=require('@wagmi/core'),viem=require('viem'),accountAbstraction=require('viem/account-abstraction'),accounts=require('viem/accounts'),chains=require('viem/chains'),ens=require('viem/ens');var m=new Map,p=new Map,C=new Map;function K(e){return e.bundlerUrl?`custom:${e.bundlerUrl.trim()}`:`${e.chainId}:${e.apiKey?.trim()??"public"}`}function N(e){let t=f(e),r=!!e.paymaster,n=!!e.client;return `${t}:pm=${r}:client=${n}`}function f(e){let t=K(e),r=m.get(t);if(r)return r;let n;return e.bundlerUrl?n=e.bundlerUrl.trim():e.apiKey?n=`https://api.pimlico.io/v2/${e.chainId}/rpc?apikey=${e.apiKey.trim()}`:n=`https://public.pimlico.io/v2/${e.chainId}/rpc`,m.set(t,n),n}function R(e){let t=N(e),r=p.get(t);if(r)return r;let n=f(e),i={...e};delete i.bundlerUrl,delete i.apiKey;let a=accountAbstraction.createBundlerClient({...i,transport:viem.http(n)});return p.set(t,a),a}function k(e){let t=f(e),r=C.get(t);if(r)return r;let n=accountAbstraction.createPaymasterClient({transport:viem.http(t)});return C.set(t,n),n}async function M({client:e,walletClient:t,salt:r}){if(!t.account)throw new Error("WalletClient must have an active account.");let n=t.account,i=accounts.toAccount({address:n.address,async signMessage({message:o}){return t.signMessage({account:n,message:o})},async signTransaction(o){return t.signTransaction({account:n,...o})},async signTypedData(o){return t.signTypedData({account:n,...o})}}),a=r??viem.pad(n.address,{dir:"right",size:32});return accountAbstraction.toSoladySmartAccount({client:e,owner:i,salt:a})}async function X(e){let{chain:t,apiKey:r,bundlerUrl:n,rpcUrl:i,salt:a,sponsor:o=!!(r||n)}=e,l=e.walletClient;if(!l&&e.wagmiConfig&&(l=await core.getWalletClient(e.wagmiConfig,{chainId:t.id})),!l||!l.account)throw new Error("Active wallet connection with account is required (provide walletClient or wagmiConfig).");let u=e.client??viem.createPublicClient({chain:t,transport:i?viem.http(i):viem.http()}),x=await M({client:u,walletClient:l,salt:a}),d=o&&(r||n)?k({chainId:t.id,apiKey:r,bundlerUrl:n}):void 0,S=R({chainId:t.id,apiKey:r,bundlerUrl:n,client:u,...d?{paymaster:d}:{}});return {account:x,bundlerClient:S,publicClient:u,paymasterClient:d}}function Y(){m.clear(),p.clear(),C.clear();}function I(e){return e!=null&&typeof e=="number"&&e>0}function _(e){return !e||e.length===0?[]:e.map(t=>t.id).filter(I)}function ee(e){return e.length>0&&e.every(t=>typeof t=="number")}async function ie(e,t){let{connector:r,chainId:n}=core.getConnection(t);if(r&&n!==e)try{await core.switchChain(t,{chainId:e});}catch(i){throw i?.cause?.name==="UserRejectedRequestError"?new Error("User rejected the request to switch network.",{cause:i}):(console.error("Failed to switch network:",i),new Error("An error occurred while switching the network.",{cause:i}))}}var g=new Map;function h(e,t){let r=t.find(i=>i.id===e),n=g.get(e);if(n&&n.chain?.rpcUrls.default.http[0]===r?.rpcUrls.default.http[0])return n;if(r){let i=viem.createPublicClient({chain:r,transport:viem.http()});return g.set(e,i),i}console.warn(`createViemClient: No chain configuration found for chainId ${e}. A client could not be created.`);}var w=new Map,P=new Map,b=new Map,y=e=>{let t=e.find(r=>r.id===chains.mainnet.id);return t?h(chains.mainnet.id,[t]):h(chains.mainnet.id,[chains.mainnet])},he=async(e,t)=>{let r=y(t);if(!r)return null;let n=w.get(e);if(n!==void 0)return n;try{let i=await ens.getEnsName(r,{address:e});return w.set(e,i),i}catch(i){return console.error(`ENS name lookup failed for address ${e}:`,i),null}},ye=async(e,t)=>{let r=y(t);if(!r)return null;let n=ens.normalize(e),i=P.get(n);if(i!==void 0)return i;try{let a=await ens.getEnsAvatar(r,{name:n});return P.set(n,a),a}catch(a){return console.error(`ENS avatar lookup failed for name ${e}:`,a),null}},ge=async(e,t)=>{let r=y(t);if(!r)return null;let n=ens.normalize(e),i=b.get(n);if(i!==void 0)return i;try{let a=await ens.getEnsAddress(r,{name:n}),o=a?a.toLowerCase():null;return b.set(n,o),o}catch(a){return console.error(`ENS address lookup failed for name ${e}:`,a),null}},we=e=>e.includes(".")&&!viem.isAddress(e);
2
+ exports.checkAndSwitchChain=ie;exports.clearBundlerCache=Y;exports.createBundlerRpcClient=R;exports.createPimlicoPaymasterClient=k;exports.createPimlicoRpcUrl=f;exports.createPimlicoSmartAccountClient=X;exports.createSoladySmartAccount=M;exports.createViemClient=h;exports.getAddress=ge;exports.getAvatar=ye;exports.getEvmChains=_;exports.getName=he;exports.isEnsName=we;exports.isEvmChainList=ee;
package/dist/index.mjs CHANGED
@@ -1,2 +1,2 @@
1
- import {getWalletClient,getConnection,switchChain}from'@wagmi/core';import {http,pad,createPublicClient,isAddress}from'viem';import {createBundlerClient,createPaymasterClient,toSoladySmartAccount}from'viem/account-abstraction';import {toAccount}from'viem/accounts';import {mainnet}from'viem/chains';import {getEnsName,normalize,getEnsAvatar,getEnsAddress}from'viem/ens';var m=new Map,p=new Map;function H(e){return e.bundlerUrl?`custom:${e.bundlerUrl.trim()}`:`${e.chainId}:${e.apiKey?.trim()??"public"}`}function K(e){let t=C(e),r=!!e.paymaster,n=!!e.client;return `${t}:pm=${r}:client=${n}`}function C(e){let t=H(e),r=m.get(t);if(r)return r;let n;return e.bundlerUrl?n=e.bundlerUrl.trim():e.apiKey?n=`https://api.pimlico.io/v2/${e.chainId}/rpc?apikey=${e.apiKey.trim()}`:n=`https://public.pimlico.io/v2/${e.chainId}/rpc`,m.set(t,n),n}function N(e){let t=K(e),r=p.get(t);if(r)return r;let n=C(e),i={...e};delete i.bundlerUrl,delete i.apiKey;let a=createBundlerClient({...i,transport:http(n)});return p.set(t,a),a}function R(e){let t=C(e);return createPaymasterClient({transport:http(t)})}async function k({client:e,walletClient:t,salt:r}){if(!t.account)throw new Error("WalletClient must have an active account.");let n=t.account,i=toAccount({address:n.address,async signMessage({message:o}){return t.signMessage({account:n,message:o})},async signTransaction(o){return t.signTransaction({account:n,...o})},async signTypedData(o){return t.signTypedData({account:n,...o})}}),a=r??pad(n.address,{dir:"right",size:32});return toSoladySmartAccount({client:e,owner:i,salt:a})}async function Q(e){let{chain:t,apiKey:r,bundlerUrl:n,rpcUrl:i,salt:a,sponsor:o=!!(r||n)}=e,l=e.walletClient;if(!l&&e.wagmiConfig&&(l=await getWalletClient(e.wagmiConfig,{chainId:t.id})),!l||!l.account)throw new Error("Active wallet connection with account is required (provide walletClient or wagmiConfig).");let u=e.client??createPublicClient({chain:t,transport:i?http(i):http()}),A=await k({client:u,walletClient:l,salt:a}),d=o&&(r||n)?R({chainId:t.id,apiKey:r,bundlerUrl:n}):void 0,x=N({chainId:t.id,apiKey:r,bundlerUrl:n,client:u,...d?{paymaster:d}:{}});return {account:A,bundlerClient:x,publicClient:u,paymasterClient:d}}function X(){m.clear(),p.clear();}function M(e){return e!=null&&typeof e=="number"&&e>0}function Z(e){return !e||e.length===0?[]:e.map(t=>t.id).filter(M)}function _(e){return e.length>0&&e.every(t=>typeof t=="number")}async function re(e,t){let{connector:r,chainId:n}=getConnection(t);if(r&&n!==e)try{await switchChain(t,{chainId:e});}catch(i){throw i?.cause?.name==="UserRejectedRequestError"?new Error("User rejected the request to switch network.",{cause:i}):(console.error("Failed to switch network:",i),new Error("An error occurred while switching the network.",{cause:i}))}}var y=new Map;function f(e,t){let r=t.find(i=>i.id===e),n=y.get(e);if(n&&n.chain?.rpcUrls.default.http[0]===r?.rpcUrls.default.http[0])return n;if(r){let i=createPublicClient({chain:r,transport:http()});return y.set(e,i),i}console.warn(`createViemClient: No chain configuration found for chainId ${e}. A client could not be created.`);}var g=new Map,w=new Map,P=new Map,h=e=>{let t=e.find(r=>r.id===mainnet.id);return t?f(mainnet.id,[t]):f(mainnet.id,[mainnet])},fe=async(e,t)=>{let r=h(t);if(!r)return null;let n=g.get(e);if(n!==void 0)return n;try{let i=await getEnsName(r,{address:e});return g.set(e,i),i}catch(i){return console.error(`ENS name lookup failed for address ${e}:`,i),null}},he=async(e,t)=>{let r=h(t);if(!r)return null;let n=normalize(e),i=w.get(n);if(i!==void 0)return i;try{let a=await getEnsAvatar(r,{name:n});return w.set(n,a),a}catch(a){return console.error(`ENS avatar lookup failed for name ${e}:`,a),null}},ye=async(e,t)=>{let r=h(t);if(!r)return null;let n=normalize(e),i=P.get(n);if(i!==void 0)return i;try{let a=await getEnsAddress(r,{name:n}),o=a?a.toLowerCase():null;return P.set(n,o),o}catch(a){return console.error(`ENS address lookup failed for name ${e}:`,a),null}},ge=e=>e.includes(".")&&!isAddress(e);
2
- export{re as checkAndSwitchChain,X as clearBundlerCache,N as createBundlerRpcClient,R as createPimlicoPaymasterClient,C as createPimlicoRpcUrl,Q as createPimlicoSmartAccountClient,k as createSoladySmartAccount,f as createViemClient,ye as getAddress,he as getAvatar,Z as getEvmChains,fe as getName,ge as isEnsName,_ as isEvmChainList};
1
+ import {getWalletClient,getConnection,switchChain}from'@wagmi/core';import {http,pad,createPublicClient,isAddress}from'viem';import {createBundlerClient,createPaymasterClient,toSoladySmartAccount}from'viem/account-abstraction';import {toAccount}from'viem/accounts';import {mainnet}from'viem/chains';import {getEnsName,normalize,getEnsAvatar,getEnsAddress}from'viem/ens';var m=new Map,p=new Map,C=new Map;function K(e){return e.bundlerUrl?`custom:${e.bundlerUrl.trim()}`:`${e.chainId}:${e.apiKey?.trim()??"public"}`}function N(e){let t=f(e),r=!!e.paymaster,n=!!e.client;return `${t}:pm=${r}:client=${n}`}function f(e){let t=K(e),r=m.get(t);if(r)return r;let n;return e.bundlerUrl?n=e.bundlerUrl.trim():e.apiKey?n=`https://api.pimlico.io/v2/${e.chainId}/rpc?apikey=${e.apiKey.trim()}`:n=`https://public.pimlico.io/v2/${e.chainId}/rpc`,m.set(t,n),n}function R(e){let t=N(e),r=p.get(t);if(r)return r;let n=f(e),i={...e};delete i.bundlerUrl,delete i.apiKey;let a=createBundlerClient({...i,transport:http(n)});return p.set(t,a),a}function k(e){let t=f(e),r=C.get(t);if(r)return r;let n=createPaymasterClient({transport:http(t)});return C.set(t,n),n}async function M({client:e,walletClient:t,salt:r}){if(!t.account)throw new Error("WalletClient must have an active account.");let n=t.account,i=toAccount({address:n.address,async signMessage({message:o}){return t.signMessage({account:n,message:o})},async signTransaction(o){return t.signTransaction({account:n,...o})},async signTypedData(o){return t.signTypedData({account:n,...o})}}),a=r??pad(n.address,{dir:"right",size:32});return toSoladySmartAccount({client:e,owner:i,salt:a})}async function X(e){let{chain:t,apiKey:r,bundlerUrl:n,rpcUrl:i,salt:a,sponsor:o=!!(r||n)}=e,l=e.walletClient;if(!l&&e.wagmiConfig&&(l=await getWalletClient(e.wagmiConfig,{chainId:t.id})),!l||!l.account)throw new Error("Active wallet connection with account is required (provide walletClient or wagmiConfig).");let u=e.client??createPublicClient({chain:t,transport:i?http(i):http()}),x=await M({client:u,walletClient:l,salt:a}),d=o&&(r||n)?k({chainId:t.id,apiKey:r,bundlerUrl:n}):void 0,S=R({chainId:t.id,apiKey:r,bundlerUrl:n,client:u,...d?{paymaster:d}:{}});return {account:x,bundlerClient:S,publicClient:u,paymasterClient:d}}function Y(){m.clear(),p.clear(),C.clear();}function I(e){return e!=null&&typeof e=="number"&&e>0}function _(e){return !e||e.length===0?[]:e.map(t=>t.id).filter(I)}function ee(e){return e.length>0&&e.every(t=>typeof t=="number")}async function ie(e,t){let{connector:r,chainId:n}=getConnection(t);if(r&&n!==e)try{await switchChain(t,{chainId:e});}catch(i){throw i?.cause?.name==="UserRejectedRequestError"?new Error("User rejected the request to switch network.",{cause:i}):(console.error("Failed to switch network:",i),new Error("An error occurred while switching the network.",{cause:i}))}}var g=new Map;function h(e,t){let r=t.find(i=>i.id===e),n=g.get(e);if(n&&n.chain?.rpcUrls.default.http[0]===r?.rpcUrls.default.http[0])return n;if(r){let i=createPublicClient({chain:r,transport:http()});return g.set(e,i),i}console.warn(`createViemClient: No chain configuration found for chainId ${e}. A client could not be created.`);}var w=new Map,P=new Map,b=new Map,y=e=>{let t=e.find(r=>r.id===mainnet.id);return t?h(mainnet.id,[t]):h(mainnet.id,[mainnet])},he=async(e,t)=>{let r=y(t);if(!r)return null;let n=w.get(e);if(n!==void 0)return n;try{let i=await getEnsName(r,{address:e});return w.set(e,i),i}catch(i){return console.error(`ENS name lookup failed for address ${e}:`,i),null}},ye=async(e,t)=>{let r=y(t);if(!r)return null;let n=normalize(e),i=P.get(n);if(i!==void 0)return i;try{let a=await getEnsAvatar(r,{name:n});return P.set(n,a),a}catch(a){return console.error(`ENS avatar lookup failed for name ${e}:`,a),null}},ge=async(e,t)=>{let r=y(t);if(!r)return null;let n=normalize(e),i=b.get(n);if(i!==void 0)return i;try{let a=await getEnsAddress(r,{name:n}),o=a?a.toLowerCase():null;return b.set(n,o),o}catch(a){return console.error(`ENS address lookup failed for name ${e}:`,a),null}},we=e=>e.includes(".")&&!isAddress(e);
2
+ export{ie as checkAndSwitchChain,Y as clearBundlerCache,R as createBundlerRpcClient,k as createPimlicoPaymasterClient,f as createPimlicoRpcUrl,X as createPimlicoSmartAccountClient,M as createSoladySmartAccount,h as createViemClient,ge as getAddress,ye as getAvatar,_ as getEvmChains,he as getName,we as isEnsName,ee as isEvmChainList};
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "@tuwaio/orbit-evm",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "private": false,
5
5
  "author": "Oleksandr Tkach",
6
6
  "license": "Apache-2.0",
7
- "description": "Layer 2 (L2) of the TUWA Ecosystem. Low-level EVM-specific communication primitives powered strictly by viem and wagmi.",
7
+ "description": "L2 EVM package of Orbit Utils (TUWA): cached viem clients, chain switching, ENS resolution and ERC-4337 smart accounts (Solady + Pimlico bundler/paymaster), built on viem and @wagmi/core.",
8
8
  "main": "./dist/index.js",
9
9
  "module": "./dist/index.mjs",
10
10
  "types": "./dist/index.d.ts",
@@ -16,9 +16,18 @@
16
16
  "socket.json"
17
17
  ],
18
18
  "keywords": [
19
+ "tuwa",
20
+ "orbit",
19
21
  "web3",
20
- "typescript",
21
22
  "evm",
23
+ "viem",
24
+ "wagmi",
25
+ "ens",
26
+ "erc-4337",
27
+ "account-abstraction",
28
+ "pimlico",
29
+ "solady",
30
+ "typescript",
22
31
  "headless"
23
32
  ],
24
33
  "repository": {
@@ -43,8 +52,8 @@
43
52
  "devDependencies": {
44
53
  "@wagmi/core": "^3.6.5",
45
54
  "tsup": "^8.5.1",
46
- "typescript": "^6.0.3",
47
- "viem": "^2.56.3"
55
+ "typescript": "6.0.3",
56
+ "viem": "^2.56.9"
48
57
  },
49
58
  "scripts": {
50
59
  "start": "tsup src/index.ts --watch",