@thehoneyjar/sigil-lens 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +660 -0
- package/README.md +121 -0
- package/dist/index.d.ts +227 -0
- package/dist/index.js +203 -0
- package/dist/index.js.map +1 -0
- package/dist/wagmi.d.ts +56 -0
- package/dist/wagmi.js +74 -0
- package/dist/wagmi.js.map +1 -0
- package/package.json +78 -0
package/README.md
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
# @sigil/lens
|
|
2
|
+
|
|
3
|
+
Address impersonation for testing different user states. View your app as any address.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pnpm add @sigil/lens
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
### React Hooks
|
|
14
|
+
|
|
15
|
+
```tsx
|
|
16
|
+
import { useLens, useIsImpersonating, useLensActions } from '@sigil/lens'
|
|
17
|
+
|
|
18
|
+
function MyComponent() {
|
|
19
|
+
const { enabled, impersonatedAddress, realAddress } = useLens()
|
|
20
|
+
const isImpersonating = useIsImpersonating()
|
|
21
|
+
const { setImpersonatedAddress, clearImpersonation } = useLensActions()
|
|
22
|
+
|
|
23
|
+
return (
|
|
24
|
+
<div>
|
|
25
|
+
{isImpersonating ? (
|
|
26
|
+
<p>Viewing as: {impersonatedAddress}</p>
|
|
27
|
+
) : (
|
|
28
|
+
<p>Viewing as: {realAddress}</p>
|
|
29
|
+
)}
|
|
30
|
+
<button onClick={() => setImpersonatedAddress('0x...')}>
|
|
31
|
+
Impersonate
|
|
32
|
+
</button>
|
|
33
|
+
<button onClick={clearImpersonation}>
|
|
34
|
+
Stop
|
|
35
|
+
</button>
|
|
36
|
+
</div>
|
|
37
|
+
)
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### Wagmi Integration
|
|
42
|
+
|
|
43
|
+
```tsx
|
|
44
|
+
import { useLensAwareAccount } from '@sigil/lens/wagmi'
|
|
45
|
+
|
|
46
|
+
function WalletInfo() {
|
|
47
|
+
const { address, realAddress, isImpersonating } = useLensAwareAccount()
|
|
48
|
+
|
|
49
|
+
// Use `address` for reading data (respects impersonation)
|
|
50
|
+
const { data: balance } = useBalance({ address })
|
|
51
|
+
|
|
52
|
+
// Use `realAddress` for signing transactions
|
|
53
|
+
return (
|
|
54
|
+
<div>
|
|
55
|
+
<p>Viewing: {address}</p>
|
|
56
|
+
{isImpersonating && <Badge>Lens Active</Badge>}
|
|
57
|
+
</div>
|
|
58
|
+
)
|
|
59
|
+
}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### Service API
|
|
63
|
+
|
|
64
|
+
```typescript
|
|
65
|
+
import { createLensService } from '@sigil/lens'
|
|
66
|
+
|
|
67
|
+
const lensService = createLensService()
|
|
68
|
+
|
|
69
|
+
// Impersonate an address
|
|
70
|
+
lensService.setImpersonatedAddress('0x...')
|
|
71
|
+
|
|
72
|
+
// Get current state
|
|
73
|
+
const state = lensService.getState()
|
|
74
|
+
|
|
75
|
+
// Save addresses for quick access
|
|
76
|
+
lensService.saveAddress({ address: '0x...', label: 'Whale' })
|
|
77
|
+
|
|
78
|
+
// Clear impersonation
|
|
79
|
+
lensService.clearImpersonation()
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## API
|
|
83
|
+
|
|
84
|
+
### Hooks
|
|
85
|
+
|
|
86
|
+
| Hook | Description |
|
|
87
|
+
|------|-------------|
|
|
88
|
+
| `useLens()` | Get full lens state |
|
|
89
|
+
| `useLensContext()` | Get lens context for components |
|
|
90
|
+
| `useIsImpersonating()` | Check if currently impersonating |
|
|
91
|
+
| `useImpersonatedAddress()` | Get impersonated address |
|
|
92
|
+
| `useRealAddress()` | Get real connected address |
|
|
93
|
+
| `useEffectiveAddress()` | Get effective address (impersonated or real) |
|
|
94
|
+
| `useSavedAddresses()` | Get saved addresses and actions |
|
|
95
|
+
| `useLensActions()` | Get lens actions |
|
|
96
|
+
|
|
97
|
+
### Wagmi Hooks
|
|
98
|
+
|
|
99
|
+
| Hook | Description |
|
|
100
|
+
|------|-------------|
|
|
101
|
+
| `useLensAwareAccount()` | Wagmi useAccount that respects lens |
|
|
102
|
+
|
|
103
|
+
### Service
|
|
104
|
+
|
|
105
|
+
| Method | Description |
|
|
106
|
+
|--------|-------------|
|
|
107
|
+
| `getState()` | Get current state |
|
|
108
|
+
| `setImpersonatedAddress(address)` | Set impersonated address |
|
|
109
|
+
| `clearImpersonation()` | Clear impersonation |
|
|
110
|
+
| `saveAddress(entry)` | Save an address |
|
|
111
|
+
| `removeAddress(address)` | Remove saved address |
|
|
112
|
+
| `getContext()` | Get lens context |
|
|
113
|
+
| `subscribe(listener)` | Subscribe to state changes |
|
|
114
|
+
|
|
115
|
+
## Persistence
|
|
116
|
+
|
|
117
|
+
Saved addresses are persisted to localStorage under the key `sigil-lens-storage`.
|
|
118
|
+
|
|
119
|
+
## License
|
|
120
|
+
|
|
121
|
+
MIT
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
import { Address } from 'viem';
|
|
2
|
+
import * as zustand_middleware from 'zustand/middleware';
|
|
3
|
+
import * as zustand from 'zustand';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Lens Service Types
|
|
7
|
+
*
|
|
8
|
+
* Types for address impersonation functionality.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Saved address entry for quick selection
|
|
13
|
+
*/
|
|
14
|
+
interface SavedAddress {
|
|
15
|
+
address: Address;
|
|
16
|
+
label: string;
|
|
17
|
+
addedAt: number;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Lens state
|
|
21
|
+
*/
|
|
22
|
+
interface LensState {
|
|
23
|
+
/** Whether lens impersonation is enabled */
|
|
24
|
+
enabled: boolean;
|
|
25
|
+
/** Currently impersonated address */
|
|
26
|
+
impersonatedAddress: Address | null;
|
|
27
|
+
/** The real connected wallet address */
|
|
28
|
+
realAddress: Address | null;
|
|
29
|
+
/** Saved addresses for quick selection */
|
|
30
|
+
savedAddresses: SavedAddress[];
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Lens context for components
|
|
34
|
+
*/
|
|
35
|
+
interface LensContext {
|
|
36
|
+
/** Whether currently impersonating */
|
|
37
|
+
isImpersonating: boolean;
|
|
38
|
+
/** The impersonated address (if any) */
|
|
39
|
+
impersonatedAddress: Address | null;
|
|
40
|
+
/** The real connected address */
|
|
41
|
+
realAddress: Address | null;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Lens service interface
|
|
45
|
+
*/
|
|
46
|
+
interface LensService {
|
|
47
|
+
/** Get current lens state */
|
|
48
|
+
getState(): LensState;
|
|
49
|
+
/** Enable lens and set impersonated address */
|
|
50
|
+
setImpersonatedAddress(address: Address): void;
|
|
51
|
+
/** Clear impersonation */
|
|
52
|
+
clearImpersonation(): void;
|
|
53
|
+
/** Set the real address (from wallet connection) */
|
|
54
|
+
setRealAddress(address: Address | null): void;
|
|
55
|
+
/** Save an address for quick access */
|
|
56
|
+
saveAddress(entry: Omit<SavedAddress, 'addedAt'>): void;
|
|
57
|
+
/** Remove a saved address */
|
|
58
|
+
removeAddress(address: Address): void;
|
|
59
|
+
/** Get lens context */
|
|
60
|
+
getContext(): LensContext;
|
|
61
|
+
/** Subscribe to state changes */
|
|
62
|
+
subscribe(listener: (state: LensState) => void): () => void;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Lens error
|
|
66
|
+
*/
|
|
67
|
+
declare class LensError extends Error {
|
|
68
|
+
code: string;
|
|
69
|
+
recoverable: boolean;
|
|
70
|
+
constructor(message: string, code: string, recoverable?: boolean);
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Lens error codes
|
|
74
|
+
*/
|
|
75
|
+
declare const LensErrorCodes: {
|
|
76
|
+
readonly INVALID_ADDRESS: "LENS_INVALID_ADDRESS";
|
|
77
|
+
readonly NOT_CONNECTED: "LENS_NOT_CONNECTED";
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Lens store actions
|
|
82
|
+
*/
|
|
83
|
+
interface LensActions {
|
|
84
|
+
setImpersonatedAddress: (address: Address) => void;
|
|
85
|
+
clearImpersonation: () => void;
|
|
86
|
+
setRealAddress: (address: Address | null) => void;
|
|
87
|
+
saveAddress: (entry: Omit<SavedAddress, 'addedAt'>) => void;
|
|
88
|
+
removeAddress: (address: Address) => void;
|
|
89
|
+
reset: () => void;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Combined lens store type
|
|
93
|
+
*/
|
|
94
|
+
type LensStore = LensState & LensActions;
|
|
95
|
+
/**
|
|
96
|
+
* Create the lens store
|
|
97
|
+
*/
|
|
98
|
+
declare const useLensStore: zustand.UseBoundStore<Omit<zustand.StoreApi<LensStore>, "persist"> & {
|
|
99
|
+
persist: {
|
|
100
|
+
setOptions: (options: Partial<zustand_middleware.PersistOptions<LensStore, {
|
|
101
|
+
savedAddresses: SavedAddress[];
|
|
102
|
+
}>>) => void;
|
|
103
|
+
clearStorage: () => void;
|
|
104
|
+
rehydrate: () => void | Promise<void>;
|
|
105
|
+
hasHydrated: () => boolean;
|
|
106
|
+
onHydrate: (fn: (state: LensStore) => void) => () => void;
|
|
107
|
+
onFinishHydration: (fn: (state: LensStore) => void) => () => void;
|
|
108
|
+
getOptions: () => Partial<zustand_middleware.PersistOptions<LensStore, {
|
|
109
|
+
savedAddresses: SavedAddress[];
|
|
110
|
+
}>>;
|
|
111
|
+
};
|
|
112
|
+
}>;
|
|
113
|
+
/**
|
|
114
|
+
* Get lens store state (for non-React contexts)
|
|
115
|
+
*/
|
|
116
|
+
declare function getLensState(): LensState;
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Lens Service Implementation
|
|
120
|
+
*
|
|
121
|
+
* Provides lens functionality for non-React contexts.
|
|
122
|
+
*/
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Create a lens service
|
|
126
|
+
*/
|
|
127
|
+
declare function createLensService(): LensService;
|
|
128
|
+
/**
|
|
129
|
+
* Get the default lens service
|
|
130
|
+
*/
|
|
131
|
+
declare function getLensService(): LensService;
|
|
132
|
+
/**
|
|
133
|
+
* Reset the default lens service
|
|
134
|
+
*/
|
|
135
|
+
declare function resetLensService(): void;
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Lens React Hooks
|
|
139
|
+
*
|
|
140
|
+
* React hooks for accessing lens state.
|
|
141
|
+
*/
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Hook to get the full lens state
|
|
145
|
+
*
|
|
146
|
+
* @example
|
|
147
|
+
* ```tsx
|
|
148
|
+
* function LensStatus() {
|
|
149
|
+
* const { enabled, impersonatedAddress, realAddress } = useLens()
|
|
150
|
+
* return <div>{enabled ? `Impersonating ${impersonatedAddress}` : 'Not impersonating'}</div>
|
|
151
|
+
* }
|
|
152
|
+
* ```
|
|
153
|
+
*/
|
|
154
|
+
declare function useLens(): LensState;
|
|
155
|
+
/**
|
|
156
|
+
* Hook to get lens context for components
|
|
157
|
+
*
|
|
158
|
+
* @example
|
|
159
|
+
* ```tsx
|
|
160
|
+
* function AddressDisplay() {
|
|
161
|
+
* const { isImpersonating, impersonatedAddress, realAddress } = useLensContext()
|
|
162
|
+
* return <div>{isImpersonating ? impersonatedAddress : realAddress}</div>
|
|
163
|
+
* }
|
|
164
|
+
* ```
|
|
165
|
+
*/
|
|
166
|
+
declare function useLensContext(): LensContext;
|
|
167
|
+
/**
|
|
168
|
+
* Hook to check if currently impersonating
|
|
169
|
+
*
|
|
170
|
+
* @example
|
|
171
|
+
* ```tsx
|
|
172
|
+
* function LensBadge() {
|
|
173
|
+
* const isImpersonating = useIsImpersonating()
|
|
174
|
+
* if (!isImpersonating) return null
|
|
175
|
+
* return <Badge variant="warning">Lens Active</Badge>
|
|
176
|
+
* }
|
|
177
|
+
* ```
|
|
178
|
+
*/
|
|
179
|
+
declare function useIsImpersonating(): boolean;
|
|
180
|
+
/**
|
|
181
|
+
* Hook to get the impersonated address (or null if not impersonating)
|
|
182
|
+
*/
|
|
183
|
+
declare function useImpersonatedAddress(): Address | null;
|
|
184
|
+
/**
|
|
185
|
+
* Hook to get the real connected address
|
|
186
|
+
*/
|
|
187
|
+
declare function useRealAddress(): Address | null;
|
|
188
|
+
/**
|
|
189
|
+
* Hook to get the effective address (impersonated if active, otherwise real)
|
|
190
|
+
*/
|
|
191
|
+
declare function useEffectiveAddress(): Address | undefined;
|
|
192
|
+
/**
|
|
193
|
+
* Hook to get saved addresses and actions
|
|
194
|
+
*
|
|
195
|
+
* @example
|
|
196
|
+
* ```tsx
|
|
197
|
+
* function SavedAddressList() {
|
|
198
|
+
* const { savedAddresses, saveAddress, removeAddress, selectAddress } = useSavedAddresses()
|
|
199
|
+
* return (
|
|
200
|
+
* <ul>
|
|
201
|
+
* {savedAddresses.map(({ address, label }) => (
|
|
202
|
+
* <li key={address} onClick={() => selectAddress(address)}>
|
|
203
|
+
* {label}: {address}
|
|
204
|
+
* <button onClick={() => removeAddress(address)}>Remove</button>
|
|
205
|
+
* </li>
|
|
206
|
+
* ))}
|
|
207
|
+
* </ul>
|
|
208
|
+
* )
|
|
209
|
+
* }
|
|
210
|
+
* ```
|
|
211
|
+
*/
|
|
212
|
+
declare function useSavedAddresses(): {
|
|
213
|
+
savedAddresses: SavedAddress[];
|
|
214
|
+
saveAddress: (entry: Omit<SavedAddress, 'addedAt'>) => void;
|
|
215
|
+
removeAddress: (address: `0x${string}`) => void;
|
|
216
|
+
selectAddress: (address: `0x${string}`) => void;
|
|
217
|
+
};
|
|
218
|
+
/**
|
|
219
|
+
* Hook to get lens actions
|
|
220
|
+
*/
|
|
221
|
+
declare function useLensActions(): {
|
|
222
|
+
setImpersonatedAddress: (address: `0x${string}`) => void;
|
|
223
|
+
clearImpersonation: () => void;
|
|
224
|
+
setRealAddress: (address: `0x${string}` | null) => void;
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
export { type LensContext, LensError, LensErrorCodes, type LensService, type LensState, type SavedAddress, createLensService, getLensService, getLensState, resetLensService, useEffectiveAddress, useImpersonatedAddress, useIsImpersonating, useLens, useLensActions, useLensContext, useLensStore, useRealAddress, useSavedAddresses };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
// src/types.ts
|
|
2
|
+
var LensError = class extends Error {
|
|
3
|
+
constructor(message, code, recoverable = true) {
|
|
4
|
+
super(message);
|
|
5
|
+
this.code = code;
|
|
6
|
+
this.recoverable = recoverable;
|
|
7
|
+
this.name = "LensError";
|
|
8
|
+
}
|
|
9
|
+
};
|
|
10
|
+
var LensErrorCodes = {
|
|
11
|
+
INVALID_ADDRESS: "LENS_INVALID_ADDRESS",
|
|
12
|
+
NOT_CONNECTED: "LENS_NOT_CONNECTED"
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
// src/store.ts
|
|
16
|
+
import { create } from "zustand";
|
|
17
|
+
import { persist } from "zustand/middleware";
|
|
18
|
+
var initialState = {
|
|
19
|
+
enabled: false,
|
|
20
|
+
impersonatedAddress: null,
|
|
21
|
+
realAddress: null,
|
|
22
|
+
savedAddresses: []
|
|
23
|
+
};
|
|
24
|
+
var useLensStore = create()(
|
|
25
|
+
persist(
|
|
26
|
+
(set) => ({
|
|
27
|
+
...initialState,
|
|
28
|
+
setImpersonatedAddress: (address) => set({
|
|
29
|
+
enabled: true,
|
|
30
|
+
impersonatedAddress: address
|
|
31
|
+
}),
|
|
32
|
+
clearImpersonation: () => set({
|
|
33
|
+
enabled: false,
|
|
34
|
+
impersonatedAddress: null
|
|
35
|
+
}),
|
|
36
|
+
setRealAddress: (address) => set({ realAddress: address }),
|
|
37
|
+
saveAddress: (entry) => set((state) => {
|
|
38
|
+
if (state.savedAddresses.some((a) => a.address === entry.address)) {
|
|
39
|
+
return state;
|
|
40
|
+
}
|
|
41
|
+
return {
|
|
42
|
+
savedAddresses: [
|
|
43
|
+
...state.savedAddresses,
|
|
44
|
+
{ ...entry, addedAt: Date.now() }
|
|
45
|
+
]
|
|
46
|
+
};
|
|
47
|
+
}),
|
|
48
|
+
removeAddress: (address) => set((state) => ({
|
|
49
|
+
savedAddresses: state.savedAddresses.filter((a) => a.address !== address)
|
|
50
|
+
})),
|
|
51
|
+
reset: () => set(initialState)
|
|
52
|
+
}),
|
|
53
|
+
{
|
|
54
|
+
name: "sigil-lens-storage",
|
|
55
|
+
partialize: (state) => ({
|
|
56
|
+
savedAddresses: state.savedAddresses
|
|
57
|
+
})
|
|
58
|
+
}
|
|
59
|
+
)
|
|
60
|
+
);
|
|
61
|
+
function getLensState() {
|
|
62
|
+
const state = useLensStore.getState();
|
|
63
|
+
return {
|
|
64
|
+
enabled: state.enabled,
|
|
65
|
+
impersonatedAddress: state.impersonatedAddress,
|
|
66
|
+
realAddress: state.realAddress,
|
|
67
|
+
savedAddresses: state.savedAddresses
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// src/service.ts
|
|
72
|
+
function createLensService() {
|
|
73
|
+
const store = useLensStore;
|
|
74
|
+
return {
|
|
75
|
+
getState() {
|
|
76
|
+
return getLensState();
|
|
77
|
+
},
|
|
78
|
+
setImpersonatedAddress(address) {
|
|
79
|
+
store.getState().setImpersonatedAddress(address);
|
|
80
|
+
},
|
|
81
|
+
clearImpersonation() {
|
|
82
|
+
store.getState().clearImpersonation();
|
|
83
|
+
},
|
|
84
|
+
setRealAddress(address) {
|
|
85
|
+
store.getState().setRealAddress(address);
|
|
86
|
+
},
|
|
87
|
+
saveAddress(entry) {
|
|
88
|
+
store.getState().saveAddress(entry);
|
|
89
|
+
},
|
|
90
|
+
removeAddress(address) {
|
|
91
|
+
store.getState().removeAddress(address);
|
|
92
|
+
},
|
|
93
|
+
getContext() {
|
|
94
|
+
const state = getLensState();
|
|
95
|
+
return {
|
|
96
|
+
isImpersonating: state.enabled && state.impersonatedAddress !== null,
|
|
97
|
+
impersonatedAddress: state.impersonatedAddress,
|
|
98
|
+
realAddress: state.realAddress
|
|
99
|
+
};
|
|
100
|
+
},
|
|
101
|
+
subscribe(listener) {
|
|
102
|
+
return store.subscribe((state) => {
|
|
103
|
+
listener({
|
|
104
|
+
enabled: state.enabled,
|
|
105
|
+
impersonatedAddress: state.impersonatedAddress,
|
|
106
|
+
realAddress: state.realAddress,
|
|
107
|
+
savedAddresses: state.savedAddresses
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
var defaultLensService = null;
|
|
114
|
+
function getLensService() {
|
|
115
|
+
if (!defaultLensService) {
|
|
116
|
+
defaultLensService = createLensService();
|
|
117
|
+
}
|
|
118
|
+
return defaultLensService;
|
|
119
|
+
}
|
|
120
|
+
function resetLensService() {
|
|
121
|
+
if (defaultLensService) {
|
|
122
|
+
useLensStore.getState().reset();
|
|
123
|
+
}
|
|
124
|
+
defaultLensService = null;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// src/hooks.ts
|
|
128
|
+
function useLens() {
|
|
129
|
+
return useLensStore((state) => ({
|
|
130
|
+
enabled: state.enabled,
|
|
131
|
+
impersonatedAddress: state.impersonatedAddress,
|
|
132
|
+
realAddress: state.realAddress,
|
|
133
|
+
savedAddresses: state.savedAddresses
|
|
134
|
+
}));
|
|
135
|
+
}
|
|
136
|
+
function useLensContext() {
|
|
137
|
+
return useLensStore((state) => ({
|
|
138
|
+
isImpersonating: state.enabled && state.impersonatedAddress !== null,
|
|
139
|
+
impersonatedAddress: state.impersonatedAddress,
|
|
140
|
+
realAddress: state.realAddress
|
|
141
|
+
}));
|
|
142
|
+
}
|
|
143
|
+
function useIsImpersonating() {
|
|
144
|
+
return useLensStore(
|
|
145
|
+
(state) => state.enabled && state.impersonatedAddress !== null
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
function useImpersonatedAddress() {
|
|
149
|
+
return useLensStore(
|
|
150
|
+
(state) => state.enabled ? state.impersonatedAddress : null
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
function useRealAddress() {
|
|
154
|
+
return useLensStore((state) => state.realAddress);
|
|
155
|
+
}
|
|
156
|
+
function useEffectiveAddress() {
|
|
157
|
+
return useLensStore((state) => {
|
|
158
|
+
if (state.enabled && state.impersonatedAddress) {
|
|
159
|
+
return state.impersonatedAddress;
|
|
160
|
+
}
|
|
161
|
+
return state.realAddress ?? void 0;
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
function useSavedAddresses() {
|
|
165
|
+
const savedAddresses = useLensStore((state) => state.savedAddresses);
|
|
166
|
+
const saveAddress = useLensStore((state) => state.saveAddress);
|
|
167
|
+
const removeAddress = useLensStore((state) => state.removeAddress);
|
|
168
|
+
const setImpersonatedAddress = useLensStore((state) => state.setImpersonatedAddress);
|
|
169
|
+
return {
|
|
170
|
+
savedAddresses,
|
|
171
|
+
saveAddress: (entry) => saveAddress(entry),
|
|
172
|
+
removeAddress,
|
|
173
|
+
selectAddress: setImpersonatedAddress
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
function useLensActions() {
|
|
177
|
+
const setImpersonatedAddress = useLensStore((state) => state.setImpersonatedAddress);
|
|
178
|
+
const clearImpersonation = useLensStore((state) => state.clearImpersonation);
|
|
179
|
+
const setRealAddress = useLensStore((state) => state.setRealAddress);
|
|
180
|
+
return {
|
|
181
|
+
setImpersonatedAddress,
|
|
182
|
+
clearImpersonation,
|
|
183
|
+
setRealAddress
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
export {
|
|
187
|
+
LensError,
|
|
188
|
+
LensErrorCodes,
|
|
189
|
+
createLensService,
|
|
190
|
+
getLensService,
|
|
191
|
+
getLensState,
|
|
192
|
+
resetLensService,
|
|
193
|
+
useEffectiveAddress,
|
|
194
|
+
useImpersonatedAddress,
|
|
195
|
+
useIsImpersonating,
|
|
196
|
+
useLens,
|
|
197
|
+
useLensActions,
|
|
198
|
+
useLensContext,
|
|
199
|
+
useLensStore,
|
|
200
|
+
useRealAddress,
|
|
201
|
+
useSavedAddresses
|
|
202
|
+
};
|
|
203
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/types.ts","../src/store.ts","../src/service.ts","../src/hooks.ts"],"sourcesContent":["/**\n * Lens Service Types\n *\n * Types for address impersonation functionality.\n */\n\nimport type { Address } from 'viem'\n\n/**\n * Saved address entry for quick selection\n */\nexport interface SavedAddress {\n address: Address\n label: string\n addedAt: number\n}\n\n/**\n * Lens state\n */\nexport interface LensState {\n /** Whether lens impersonation is enabled */\n enabled: boolean\n /** Currently impersonated address */\n impersonatedAddress: Address | null\n /** The real connected wallet address */\n realAddress: Address | null\n /** Saved addresses for quick selection */\n savedAddresses: SavedAddress[]\n}\n\n/**\n * Lens context for components\n */\nexport interface LensContext {\n /** Whether currently impersonating */\n isImpersonating: boolean\n /** The impersonated address (if any) */\n impersonatedAddress: Address | null\n /** The real connected address */\n realAddress: Address | null\n}\n\n/**\n * Lens service interface\n */\nexport interface LensService {\n /** Get current lens state */\n getState(): LensState\n /** Enable lens and set impersonated address */\n setImpersonatedAddress(address: Address): void\n /** Clear impersonation */\n clearImpersonation(): void\n /** Set the real address (from wallet connection) */\n setRealAddress(address: Address | null): void\n /** Save an address for quick access */\n saveAddress(entry: Omit<SavedAddress, 'addedAt'>): void\n /** Remove a saved address */\n removeAddress(address: Address): void\n /** Get lens context */\n getContext(): LensContext\n /** Subscribe to state changes */\n subscribe(listener: (state: LensState) => void): () => void\n}\n\n/**\n * Lens error\n */\nexport class LensError extends Error {\n constructor(\n message: string,\n public code: string,\n public recoverable: boolean = true\n ) {\n super(message)\n this.name = 'LensError'\n }\n}\n\n/**\n * Lens error codes\n */\nexport const LensErrorCodes = {\n INVALID_ADDRESS: 'LENS_INVALID_ADDRESS',\n NOT_CONNECTED: 'LENS_NOT_CONNECTED',\n} as const\n","/**\n * Lens Store\n *\n * Zustand store for lens state management.\n */\n\nimport { create } from 'zustand'\nimport { persist } from 'zustand/middleware'\nimport type { Address } from 'viem'\nimport type { LensState, SavedAddress } from './types'\n\n/**\n * Lens store actions\n */\ninterface LensActions {\n setImpersonatedAddress: (address: Address) => void\n clearImpersonation: () => void\n setRealAddress: (address: Address | null) => void\n saveAddress: (entry: Omit<SavedAddress, 'addedAt'>) => void\n removeAddress: (address: Address) => void\n reset: () => void\n}\n\n/**\n * Combined lens store type\n */\nexport type LensStore = LensState & LensActions\n\n/**\n * Initial lens state\n */\nconst initialState: LensState = {\n enabled: false,\n impersonatedAddress: null,\n realAddress: null,\n savedAddresses: [],\n}\n\n/**\n * Create the lens store\n */\nexport const useLensStore = create<LensStore>()(\n persist(\n (set) => ({\n ...initialState,\n\n setImpersonatedAddress: (address: Address) =>\n set({\n enabled: true,\n impersonatedAddress: address,\n }),\n\n clearImpersonation: () =>\n set({\n enabled: false,\n impersonatedAddress: null,\n }),\n\n setRealAddress: (address: Address | null) =>\n set({ realAddress: address }),\n\n saveAddress: (entry: Omit<SavedAddress, 'addedAt'>) =>\n set((state) => {\n // Don't add duplicates\n if (state.savedAddresses.some((a) => a.address === entry.address)) {\n return state\n }\n return {\n savedAddresses: [\n ...state.savedAddresses,\n { ...entry, addedAt: Date.now() },\n ],\n }\n }),\n\n removeAddress: (address: Address) =>\n set((state) => ({\n savedAddresses: state.savedAddresses.filter((a) => a.address !== address),\n })),\n\n reset: () => set(initialState),\n }),\n {\n name: 'sigil-lens-storage',\n partialize: (state) => ({\n savedAddresses: state.savedAddresses,\n }),\n }\n )\n)\n\n/**\n * Get lens store state (for non-React contexts)\n */\nexport function getLensState(): LensState {\n const state = useLensStore.getState()\n return {\n enabled: state.enabled,\n impersonatedAddress: state.impersonatedAddress,\n realAddress: state.realAddress,\n savedAddresses: state.savedAddresses,\n }\n}\n","/**\n * Lens Service Implementation\n *\n * Provides lens functionality for non-React contexts.\n */\n\nimport type { Address } from 'viem'\nimport type { LensService, LensState, LensContext, SavedAddress } from './types'\nimport { useLensStore, getLensState } from './store'\n\n/**\n * Create a lens service\n */\nexport function createLensService(): LensService {\n const store = useLensStore\n\n return {\n getState(): LensState {\n return getLensState()\n },\n\n setImpersonatedAddress(address: Address): void {\n store.getState().setImpersonatedAddress(address)\n },\n\n clearImpersonation(): void {\n store.getState().clearImpersonation()\n },\n\n setRealAddress(address: Address | null): void {\n store.getState().setRealAddress(address)\n },\n\n saveAddress(entry: Omit<SavedAddress, 'addedAt'>): void {\n store.getState().saveAddress(entry)\n },\n\n removeAddress(address: Address): void {\n store.getState().removeAddress(address)\n },\n\n getContext(): LensContext {\n const state = getLensState()\n return {\n isImpersonating: state.enabled && state.impersonatedAddress !== null,\n impersonatedAddress: state.impersonatedAddress,\n realAddress: state.realAddress,\n }\n },\n\n subscribe(listener: (state: LensState) => void): () => void {\n return store.subscribe((state) => {\n listener({\n enabled: state.enabled,\n impersonatedAddress: state.impersonatedAddress,\n realAddress: state.realAddress,\n savedAddresses: state.savedAddresses,\n })\n })\n },\n }\n}\n\n/**\n * Default lens service singleton\n */\nlet defaultLensService: LensService | null = null\n\n/**\n * Get the default lens service\n */\nexport function getLensService(): LensService {\n if (!defaultLensService) {\n defaultLensService = createLensService()\n }\n return defaultLensService\n}\n\n/**\n * Reset the default lens service\n */\nexport function resetLensService(): void {\n if (defaultLensService) {\n useLensStore.getState().reset()\n }\n defaultLensService = null\n}\n","/**\n * Lens React Hooks\n *\n * React hooks for accessing lens state.\n */\n\nimport type { Address } from 'viem'\nimport { useLensStore } from './store'\nimport type { LensState, LensContext, SavedAddress } from './types'\n\n/**\n * Hook to get the full lens state\n *\n * @example\n * ```tsx\n * function LensStatus() {\n * const { enabled, impersonatedAddress, realAddress } = useLens()\n * return <div>{enabled ? `Impersonating ${impersonatedAddress}` : 'Not impersonating'}</div>\n * }\n * ```\n */\nexport function useLens(): LensState {\n return useLensStore((state) => ({\n enabled: state.enabled,\n impersonatedAddress: state.impersonatedAddress,\n realAddress: state.realAddress,\n savedAddresses: state.savedAddresses,\n }))\n}\n\n/**\n * Hook to get lens context for components\n *\n * @example\n * ```tsx\n * function AddressDisplay() {\n * const { isImpersonating, impersonatedAddress, realAddress } = useLensContext()\n * return <div>{isImpersonating ? impersonatedAddress : realAddress}</div>\n * }\n * ```\n */\nexport function useLensContext(): LensContext {\n return useLensStore((state) => ({\n isImpersonating: state.enabled && state.impersonatedAddress !== null,\n impersonatedAddress: state.impersonatedAddress,\n realAddress: state.realAddress,\n }))\n}\n\n/**\n * Hook to check if currently impersonating\n *\n * @example\n * ```tsx\n * function LensBadge() {\n * const isImpersonating = useIsImpersonating()\n * if (!isImpersonating) return null\n * return <Badge variant=\"warning\">Lens Active</Badge>\n * }\n * ```\n */\nexport function useIsImpersonating(): boolean {\n return useLensStore(\n (state) => state.enabled && state.impersonatedAddress !== null\n )\n}\n\n/**\n * Hook to get the impersonated address (or null if not impersonating)\n */\nexport function useImpersonatedAddress(): Address | null {\n return useLensStore((state) =>\n state.enabled ? state.impersonatedAddress : null\n )\n}\n\n/**\n * Hook to get the real connected address\n */\nexport function useRealAddress(): Address | null {\n return useLensStore((state) => state.realAddress)\n}\n\n/**\n * Hook to get the effective address (impersonated if active, otherwise real)\n */\nexport function useEffectiveAddress(): Address | undefined {\n return useLensStore((state) => {\n if (state.enabled && state.impersonatedAddress) {\n return state.impersonatedAddress\n }\n return state.realAddress ?? undefined\n })\n}\n\n/**\n * Hook to get saved addresses and actions\n *\n * @example\n * ```tsx\n * function SavedAddressList() {\n * const { savedAddresses, saveAddress, removeAddress, selectAddress } = useSavedAddresses()\n * return (\n * <ul>\n * {savedAddresses.map(({ address, label }) => (\n * <li key={address} onClick={() => selectAddress(address)}>\n * {label}: {address}\n * <button onClick={() => removeAddress(address)}>Remove</button>\n * </li>\n * ))}\n * </ul>\n * )\n * }\n * ```\n */\nexport function useSavedAddresses() {\n const savedAddresses = useLensStore((state) => state.savedAddresses)\n const saveAddress = useLensStore((state) => state.saveAddress)\n const removeAddress = useLensStore((state) => state.removeAddress)\n const setImpersonatedAddress = useLensStore((state) => state.setImpersonatedAddress)\n\n return {\n savedAddresses,\n saveAddress: (entry: Omit<SavedAddress, 'addedAt'>) => saveAddress(entry),\n removeAddress,\n selectAddress: setImpersonatedAddress,\n }\n}\n\n/**\n * Hook to get lens actions\n */\nexport function useLensActions() {\n const setImpersonatedAddress = useLensStore((state) => state.setImpersonatedAddress)\n const clearImpersonation = useLensStore((state) => state.clearImpersonation)\n const setRealAddress = useLensStore((state) => state.setRealAddress)\n\n return {\n setImpersonatedAddress,\n clearImpersonation,\n setRealAddress,\n }\n}\n"],"mappings":"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","names":[]}
|
package/dist/wagmi.d.ts
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { Address } from 'viem';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Lens Wagmi Integration
|
|
5
|
+
*
|
|
6
|
+
* Wagmi-aware hooks for lens functionality.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Return type for useLensAwareAccount
|
|
11
|
+
*/
|
|
12
|
+
interface LensAwareAccount {
|
|
13
|
+
/** Address for reads - impersonated if lens enabled, otherwise real */
|
|
14
|
+
address: Address | undefined;
|
|
15
|
+
/** The user's real connected address (always available for signing) */
|
|
16
|
+
realAddress: Address | undefined;
|
|
17
|
+
/** Whether currently impersonating another address */
|
|
18
|
+
isImpersonating: boolean;
|
|
19
|
+
/** The impersonated address (if any) */
|
|
20
|
+
impersonatedAddress: Address | null;
|
|
21
|
+
/** Whether the wallet is connected */
|
|
22
|
+
isConnected: boolean;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Hook that returns impersonated address for reads, real address for writes.
|
|
26
|
+
*
|
|
27
|
+
* Use this hook in place of wagmi's `useAccount` when you want components
|
|
28
|
+
* to display data for an impersonated address while still being able to
|
|
29
|
+
* sign transactions with the real connected wallet.
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* ```tsx
|
|
33
|
+
* function WalletInfo() {
|
|
34
|
+
* const { address, realAddress, isImpersonating } = useLensAwareAccount()
|
|
35
|
+
*
|
|
36
|
+
* // Use `address` for reading data (respects impersonation)
|
|
37
|
+
* const { data: balance } = useBalance({ address })
|
|
38
|
+
*
|
|
39
|
+
* // Use `realAddress` for signing transactions
|
|
40
|
+
* const { writeContract } = useWriteContract()
|
|
41
|
+
*
|
|
42
|
+
* return (
|
|
43
|
+
* <div>
|
|
44
|
+
* <p>Viewing: {address}</p>
|
|
45
|
+
* {isImpersonating && <Badge>Lens Active</Badge>}
|
|
46
|
+
* <button onClick={() => writeContract({ ... })}>
|
|
47
|
+
* Sign with {realAddress}
|
|
48
|
+
* </button>
|
|
49
|
+
* </div>
|
|
50
|
+
* )
|
|
51
|
+
* }
|
|
52
|
+
* ```
|
|
53
|
+
*/
|
|
54
|
+
declare function useLensAwareAccount(): LensAwareAccount;
|
|
55
|
+
|
|
56
|
+
export { type LensAwareAccount, useLensAwareAccount };
|