@0xsequence/dapp-client 0.0.0-20250910142613

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/.turbo/turbo-build.log +5 -0
  2. package/CHANGELOG.md +11 -0
  3. package/LICENSE +202 -0
  4. package/README.md +238 -0
  5. package/dist/ChainSessionManager.d.ts +203 -0
  6. package/dist/ChainSessionManager.d.ts.map +1 -0
  7. package/dist/ChainSessionManager.js +742 -0
  8. package/dist/DappClient.d.ts +409 -0
  9. package/dist/DappClient.d.ts.map +1 -0
  10. package/dist/DappClient.js +667 -0
  11. package/dist/DappTransport.d.ts +47 -0
  12. package/dist/DappTransport.d.ts.map +1 -0
  13. package/dist/DappTransport.js +443 -0
  14. package/dist/index.d.ts +11 -0
  15. package/dist/index.d.ts.map +1 -0
  16. package/dist/index.js +7 -0
  17. package/dist/types/index.d.ts +168 -0
  18. package/dist/types/index.d.ts.map +1 -0
  19. package/dist/types/index.js +25 -0
  20. package/dist/utils/constants.d.ts +6 -0
  21. package/dist/utils/constants.d.ts.map +1 -0
  22. package/dist/utils/constants.js +5 -0
  23. package/dist/utils/errors.d.ts +28 -0
  24. package/dist/utils/errors.d.ts.map +1 -0
  25. package/dist/utils/errors.js +54 -0
  26. package/dist/utils/index.d.ts +11 -0
  27. package/dist/utils/index.d.ts.map +1 -0
  28. package/dist/utils/index.js +135 -0
  29. package/dist/utils/storage.d.ts +64 -0
  30. package/dist/utils/storage.d.ts.map +1 -0
  31. package/dist/utils/storage.js +196 -0
  32. package/eslint.config.mjs +4 -0
  33. package/package.json +38 -0
  34. package/src/ChainSessionManager.ts +978 -0
  35. package/src/DappClient.ts +801 -0
  36. package/src/DappTransport.ts +518 -0
  37. package/src/index.ts +47 -0
  38. package/src/types/index.ts +218 -0
  39. package/src/utils/constants.ts +5 -0
  40. package/src/utils/errors.ts +62 -0
  41. package/src/utils/index.ts +158 -0
  42. package/src/utils/storage.ts +272 -0
  43. package/tsconfig.json +10 -0
@@ -0,0 +1,218 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+ import { Attestation, Payload } from '@0xsequence/wallet-primitives'
3
+ import { Signers } from '@0xsequence/wallet-core'
4
+ import { Address, Hex } from 'ox'
5
+ import type { TypedData } from 'ox/TypedData'
6
+
7
+ // --- Public Interfaces and Constants ---
8
+
9
+ export const RequestActionType = {
10
+ CREATE_NEW_SESSION: 'createNewSession',
11
+ ADD_EXPLICIT_SESSION: 'addExplicitSession',
12
+ MODIFY_EXPLICIT_SESSION: 'modifyExplicitSession',
13
+ SIGN_MESSAGE: 'signMessage',
14
+ SIGN_TYPED_DATA: 'signTypedData',
15
+ SEND_WALLET_TRANSACTION: 'sendWalletTransaction',
16
+ } as const
17
+
18
+ export type LoginMethod = 'google' | 'apple' | 'email' | 'passkey' | 'mnemonic'
19
+
20
+ export interface GuardConfig {
21
+ url: string
22
+ address: Address.Address
23
+ }
24
+
25
+ // --- Payloads for Transport ---
26
+
27
+ export interface CreateNewSessionPayload {
28
+ sessionAddress: Address.Address
29
+ origin: string
30
+ permissions?: Signers.Session.ExplicitParams
31
+ includeImplicitSession?: boolean
32
+ preferredLoginMethod?: LoginMethod
33
+ email?: string
34
+ }
35
+
36
+ export interface AddExplicitSessionPayload {
37
+ sessionAddress: Address.Address
38
+ permissions: Signers.Session.ExplicitParams
39
+ preferredLoginMethod?: LoginMethod
40
+ email?: string
41
+ }
42
+
43
+ export interface ModifySessionPayload {
44
+ walletAddress: Address.Address
45
+ sessionAddress: Address.Address
46
+ permissions: Signers.Session.ExplicitParams
47
+ }
48
+
49
+ export interface SignMessagePayload {
50
+ address: Address.Address
51
+ message: string
52
+ chainId: number
53
+ }
54
+
55
+ export interface SignTypedDataPayload {
56
+ address: Address.Address
57
+ typedData: TypedData
58
+ chainId: number
59
+ }
60
+
61
+ export type TransactionRequest = {
62
+ to: Address.Address
63
+ value?: bigint
64
+ data?: Hex.Hex
65
+ gasLimit?: bigint
66
+ }
67
+
68
+ export interface SendWalletTransactionPayload {
69
+ address: Address.Address
70
+ transactionRequest: TransactionRequest
71
+ chainId: number
72
+ }
73
+
74
+ export interface ConnectSuccessResponsePayload {
75
+ walletAddress: string
76
+ attestation?: Attestation.Attestation
77
+ signature?: Hex.Hex
78
+ userEmail?: string
79
+ loginMethod?: LoginMethod
80
+ guard?: GuardConfig
81
+ }
82
+
83
+ export interface ModifySessionSuccessResponsePayload {
84
+ walletAddress: string
85
+ sessionAddress: string
86
+ }
87
+
88
+ export interface SignatureSuccessResponse {
89
+ signature: Hex.Hex
90
+ walletAddress: string
91
+ }
92
+
93
+ export interface SendWalletTransactionSuccessResponse {
94
+ transactionHash: Hex.Hex
95
+ walletAddress: string
96
+ }
97
+
98
+ export type WalletActionResponse = SignatureSuccessResponse | SendWalletTransactionSuccessResponse
99
+
100
+ export interface ExplicitSessionResponsePayload {
101
+ walletAddress: string
102
+ sessionAddress: string
103
+ }
104
+
105
+ // --- Dapp-facing Types ---
106
+
107
+ export type RandomPrivateKeyFn = () => Hex.Hex | Promise<Hex.Hex>
108
+
109
+ type RequiredKeys = 'to' | 'data' | 'value'
110
+
111
+ export type Transaction =
112
+ // Required properties from Payload.Call
113
+ Pick<Payload.Call, RequiredKeys> &
114
+ // All other properties from Payload.Call, but optional
115
+ Partial<Omit<Payload.Call, RequiredKeys>>
116
+
117
+ export type Session = {
118
+ address: Address.Address
119
+ isImplicit: boolean
120
+ permissions?: Signers.Session.ExplicitParams
121
+ chainId?: number
122
+ }
123
+
124
+ // --- Event Types ---
125
+
126
+ export type ChainSessionManagerEvent = 'sessionsUpdated' | 'explicitSessionResponse'
127
+
128
+ export type ExplicitSessionEventListener = (data: {
129
+ action: (typeof RequestActionType)['ADD_EXPLICIT_SESSION' | 'MODIFY_EXPLICIT_SESSION']
130
+ response?: ExplicitSessionResponsePayload
131
+ error?: any
132
+ }) => void
133
+
134
+ // A generic listener for events from the DappClient
135
+ export type DappClientEventListener = (data?: any) => void
136
+
137
+ export type DappClientWalletActionEventListener = (data: {
138
+ action: (typeof RequestActionType)['SIGN_MESSAGE' | 'SIGN_TYPED_DATA' | 'SEND_WALLET_TRANSACTION']
139
+ response?: WalletActionResponse
140
+ error?: any
141
+ chainId: number
142
+ }) => void
143
+
144
+ export type DappClientExplicitSessionEventListener = (data: {
145
+ action: (typeof RequestActionType)['ADD_EXPLICIT_SESSION' | 'MODIFY_EXPLICIT_SESSION']
146
+ response?: ExplicitSessionResponsePayload
147
+ error?: any
148
+ chainId: number
149
+ }) => void
150
+
151
+ // --- DappTransport Types ---
152
+
153
+ export interface SequenceSessionStorage {
154
+ getItem(key: string): string | null | Promise<string | null>
155
+ setItem(key: string, value: string): void | Promise<void>
156
+ removeItem(key: string): void | Promise<void>
157
+ }
158
+
159
+ export enum MessageType {
160
+ WALLET_OPENED = 'WALLET_OPENED',
161
+ INIT = 'INIT',
162
+ REQUEST = 'REQUEST',
163
+ RESPONSE = 'RESPONSE',
164
+ }
165
+
166
+ export enum TransportMode {
167
+ POPUP = 'popup',
168
+ REDIRECT = 'redirect',
169
+ }
170
+
171
+ export interface PopupModeOptions {
172
+ requestTimeoutMs?: number
173
+ handshakeTimeoutMs?: number
174
+ }
175
+
176
+ export interface TransportMessage<T = any> {
177
+ id: string
178
+ type: MessageType
179
+ sessionId?: string
180
+ action?: string
181
+ payload?: T
182
+ error?: any
183
+ }
184
+
185
+ export interface BaseRequest {
186
+ type: string
187
+ }
188
+
189
+ export interface MessageSignatureRequest extends BaseRequest {
190
+ type: 'message_signature'
191
+ message: string
192
+ address: Address.Address
193
+ chainId: number
194
+ }
195
+
196
+ export interface TypedDataSignatureRequest extends BaseRequest {
197
+ type: 'typed_data_signature'
198
+ typedData: unknown
199
+ address: Address.Address
200
+ chainId: number
201
+ }
202
+
203
+ export const WalletSize = {
204
+ width: 380,
205
+ height: 600,
206
+ }
207
+
208
+ export interface PendingRequest {
209
+ resolve: (payload: any) => void
210
+ reject: (error: any) => void
211
+ timer: number
212
+ action: string
213
+ }
214
+
215
+ export interface SendRequestOptions {
216
+ timeout?: number
217
+ path?: string
218
+ }
@@ -0,0 +1,5 @@
1
+ export const CACHE_DB_NAME = 'sequence-cache'
2
+ export const NODES_URL = 'https://nodes.sequence.app/{network}'
3
+ export const RELAYER_URL = 'https://dev-{network}-relayer.sequence.app'
4
+ export const KEYMACHINE_URL = 'https://v3-keymachine.sequence-dev.app'
5
+ export const VALUE_FORWARDER_ADDRESS = '0xABAAd93EeE2a569cF0632f39B10A9f5D734777ca'
@@ -0,0 +1,62 @@
1
+ export class InitializationError extends Error {
2
+ constructor(message: string) {
3
+ super(message)
4
+ this.name = 'InitializationError'
5
+ }
6
+ }
7
+
8
+ export class SigningError extends Error {
9
+ constructor(message: string) {
10
+ super(message)
11
+ this.name = 'SigningError'
12
+ }
13
+ }
14
+
15
+ export class TransactionError extends Error {
16
+ constructor(message: string) {
17
+ super(message)
18
+ this.name = 'TransactionError'
19
+ }
20
+ }
21
+
22
+ export class ModifyExplicitSessionError extends Error {
23
+ constructor(message: string) {
24
+ super(message)
25
+ this.name = 'ModifyExplicitSessionError'
26
+ }
27
+ }
28
+
29
+ export class ConnectionError extends Error {
30
+ constructor(message: string) {
31
+ super(message)
32
+ this.name = 'ConnectionError'
33
+ }
34
+ }
35
+
36
+ export class AddExplicitSessionError extends Error {
37
+ constructor(message: string) {
38
+ super(message)
39
+ this.name = 'AddExplicitSessionError'
40
+ }
41
+ }
42
+
43
+ export class FeeOptionError extends Error {
44
+ constructor(message: string) {
45
+ super(message)
46
+ this.name = 'FeeOptionError'
47
+ }
48
+ }
49
+
50
+ export class WalletRedirectError extends Error {
51
+ constructor(message: string) {
52
+ super(message)
53
+ this.name = 'WalletRedirectError'
54
+ }
55
+ }
56
+
57
+ export class SessionConfigError extends Error {
58
+ constructor(message: string) {
59
+ super(message)
60
+ this.name = 'SessionConfigError'
61
+ }
62
+ }
@@ -0,0 +1,158 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+ import { Network } from '@0xsequence/wallet-primitives'
3
+ import { Bytes, Hex } from 'ox'
4
+ import { NODES_URL, RELAYER_URL } from './constants.js'
5
+
6
+ type JsonReplacer = (key: string, value: any) => any
7
+ type JsonReviver = (key: string, value: any) => any
8
+
9
+ /**
10
+ * Creates a single JSON replacer by chaining multiple replacers.
11
+ * The first replacer to transform a value wins.
12
+ */
13
+ function chainReplacers(replacers: JsonReplacer[]): JsonReplacer {
14
+ return function (key: string, value: any): any {
15
+ for (const replacer of replacers) {
16
+ const replacedValue = replacer(key, value)
17
+ if (replacedValue !== value) {
18
+ return replacedValue
19
+ }
20
+ }
21
+ return value
22
+ }
23
+ }
24
+
25
+ /**
26
+ * Creates a single JSON reviver by chaining multiple revivers.
27
+ * The output of one reviver becomes the input for the next.
28
+ */
29
+ function chainRevivers(revivers: JsonReviver[]): JsonReviver {
30
+ return function (key: string, value: any): any {
31
+ let currentValue = value
32
+ for (const reviver of revivers) {
33
+ currentValue = reviver(key, currentValue)
34
+ }
35
+ return currentValue
36
+ }
37
+ }
38
+
39
+ /**
40
+ * A JSON replacer that serializes BigInt values into a structured object.
41
+ */
42
+ const bigIntReplacer: JsonReplacer = (_key, value) => {
43
+ if (typeof value === 'bigint') {
44
+ return {
45
+ _isBigInt: true,
46
+ data: value.toString(),
47
+ }
48
+ }
49
+ return value
50
+ }
51
+
52
+ /**
53
+ * A JSON replacer that serializes Uint8Array values into a structured object.
54
+ */
55
+ const uint8ArrayReplacer: JsonReplacer = (_key, value) => {
56
+ if (value instanceof Uint8Array) {
57
+ return {
58
+ _isUint8Array: true,
59
+ data: Hex.from(value),
60
+ }
61
+ }
62
+ return value
63
+ }
64
+
65
+ /**
66
+ * A JSON reviver that deserializes a structured object back into a BigInt.
67
+ */
68
+ const bigIntReviver: JsonReviver = (key, value) => {
69
+ if (value !== null && typeof value === 'object' && value._isBigInt === true && typeof value.data === 'string') {
70
+ try {
71
+ return BigInt(value.data)
72
+ } catch (e) {
73
+ console.error(`Failed to revive BigInt for key "${key}":`, e)
74
+ return value // Return original object if revival fails
75
+ }
76
+ }
77
+ return value
78
+ }
79
+
80
+ /**
81
+ * A JSON reviver that deserializes a structured object back into a Uint8Array.
82
+ */
83
+ const uint8ArrayReviver: JsonReviver = (key, value) => {
84
+ if (value !== null && typeof value === 'object' && value._isUint8Array === true && typeof value.data === 'string') {
85
+ try {
86
+ return Bytes.from(value.data)
87
+ } catch (e) {
88
+ console.error(`Failed to revive Uint8Array for key "${key}":`, e)
89
+ return value // Return original object if revival fails
90
+ }
91
+ }
92
+ return value
93
+ }
94
+
95
+ export const jsonReplacers = chainReplacers([bigIntReplacer, uint8ArrayReplacer])
96
+
97
+ export const jsonRevivers = chainRevivers([bigIntReviver, uint8ArrayReviver])
98
+
99
+ /**
100
+ * Apply a template to a string.
101
+ *
102
+ * Example:
103
+ * applyTemplate('https://v3-{network}-relayer.sequence.app', { network: 'arbitrum' })
104
+ * returns 'https://v3-arbitrum-relayer.sequence.app'
105
+ *
106
+ * @param template - The template to apply.
107
+ * @param values - The values to apply to the template.
108
+ * @returns The template with the values applied.
109
+ */
110
+ function applyTemplate(template: string, values: Record<string, string>) {
111
+ return template.replace(/{(.*?)}/g, (_, key) => {
112
+ const value = values[key]
113
+ if (value === undefined) {
114
+ throw new Error(`Missing template value for ${template}: ${key}`)
115
+ }
116
+ return value
117
+ })
118
+ }
119
+
120
+ export const getNetwork = (chainId: Network.ChainId | bigint | number) => {
121
+ const network = Network.getNetworkFromChainId(chainId)
122
+
123
+ if (!network) {
124
+ throw new Error(`Network with chainId ${chainId} not found`)
125
+ }
126
+
127
+ return network
128
+ }
129
+
130
+ export const getRpcUrl = (chainId: Network.ChainId | bigint | number, nodesUrl: string, projectAccessKey: string) => {
131
+ const network = getNetwork(chainId)
132
+
133
+ let url = applyTemplate(nodesUrl, { network: network.name })
134
+
135
+ if (nodesUrl.includes('sequence')) {
136
+ url = `${url}/${projectAccessKey}`
137
+ }
138
+
139
+ return url
140
+ }
141
+
142
+ export const getRelayerUrl = (chainId: Network.ChainId | bigint | number, relayerUrl: string) => {
143
+ const network = getNetwork(chainId)
144
+
145
+ const url = applyTemplate(relayerUrl, { network: network.name })
146
+
147
+ return url
148
+ }
149
+
150
+ export const getExplorerUrl = (chainId: Network.ChainId | bigint | number, txHash: string) => {
151
+ const network = getNetwork(chainId)
152
+ const explorerUrl = network.blockExplorer?.url
153
+ if (!explorerUrl) {
154
+ throw new Error(`Explorer URL not found for chainId ${chainId}`)
155
+ }
156
+
157
+ return `${explorerUrl}/tx/${txHash}`
158
+ }
@@ -0,0 +1,272 @@
1
+ import { Attestation } from '@0xsequence/wallet-primitives'
2
+ import { Address, Hex } from 'ox'
3
+ import { jsonReplacers, jsonRevivers } from './index.js'
4
+ import {
5
+ AddExplicitSessionPayload,
6
+ CreateNewSessionPayload,
7
+ ModifySessionPayload,
8
+ LoginMethod,
9
+ SignMessagePayload,
10
+ SignTypedDataPayload,
11
+ GuardConfig,
12
+ SendWalletTransactionPayload,
13
+ } from '../types/index.js'
14
+
15
+ export interface ExplicitSessionData {
16
+ pk: Hex.Hex
17
+ walletAddress: Address.Address
18
+ chainId: number
19
+ loginMethod?: LoginMethod
20
+ userEmail?: string
21
+ guard?: GuardConfig
22
+ }
23
+
24
+ export interface ImplicitSessionData {
25
+ pk: Hex.Hex
26
+ walletAddress: Address.Address
27
+ attestation: Attestation.Attestation
28
+ identitySignature: Hex.Hex
29
+ chainId: number
30
+ loginMethod?: LoginMethod
31
+ userEmail?: string
32
+ guard?: GuardConfig
33
+ }
34
+
35
+ export type PendingPayload =
36
+ | CreateNewSessionPayload
37
+ | AddExplicitSessionPayload
38
+ | ModifySessionPayload
39
+ | SignMessagePayload
40
+ | SignTypedDataPayload
41
+ | SendWalletTransactionPayload
42
+
43
+ export interface PendingRequestContext {
44
+ chainId: number
45
+ action: string
46
+ payload: PendingPayload
47
+ }
48
+
49
+ export interface SequenceStorage {
50
+ setPendingRedirectRequest(isPending: boolean): Promise<void>
51
+ isRedirectRequestPending(): Promise<boolean>
52
+
53
+ saveTempSessionPk(pk: Hex.Hex): Promise<void>
54
+ getAndClearTempSessionPk(): Promise<Hex.Hex | null>
55
+
56
+ savePendingRequest(context: PendingRequestContext): Promise<void>
57
+ getAndClearPendingRequest(): Promise<PendingRequestContext | null>
58
+ peekPendingRequest(): Promise<PendingRequestContext | null>
59
+
60
+ saveExplicitSession(sessionData: ExplicitSessionData): Promise<void>
61
+ getExplicitSessions(): Promise<ExplicitSessionData[]>
62
+ clearExplicitSessions(): Promise<void>
63
+
64
+ saveImplicitSession(sessionData: ImplicitSessionData): Promise<void>
65
+ getImplicitSession(): Promise<ImplicitSessionData | null>
66
+ clearImplicitSession(): Promise<void>
67
+
68
+ clearAllData(): Promise<void>
69
+ }
70
+
71
+ const DB_NAME = 'SequenceDappStorage'
72
+ const DB_VERSION = 1
73
+ const STORE_NAME = 'userKeys'
74
+ const IMPLICIT_SESSIONS_IDB_KEY = 'SequenceImplicitSession'
75
+ const EXPLICIT_SESSIONS_IDB_KEY = 'SequenceExplicitSession'
76
+
77
+ const PENDING_REDIRECT_REQUEST_KEY = 'SequencePendingRedirect'
78
+ const TEMP_SESSION_PK_KEY = 'SequencePendingTempSessionPk'
79
+ const PENDING_REQUEST_CONTEXT_KEY = 'SequencePendingRequestContext'
80
+
81
+ export class WebStorage implements SequenceStorage {
82
+ private openDB(): Promise<IDBDatabase> {
83
+ return new Promise((resolve, reject) => {
84
+ const request = indexedDB.open(DB_NAME, DB_VERSION)
85
+ request.onerror = (event) => reject(`IndexedDB error: ${(event.target as IDBRequest).error}`)
86
+ request.onsuccess = (event) => resolve((event.target as IDBRequest).result as IDBDatabase)
87
+ request.onupgradeneeded = (event) => {
88
+ const db = (event.target as IDBRequest).result as IDBDatabase
89
+ if (!db.objectStoreNames.contains(STORE_NAME)) {
90
+ db.createObjectStore(STORE_NAME)
91
+ }
92
+ }
93
+ })
94
+ }
95
+
96
+ private async getIDBItem<T>(key: IDBValidKey): Promise<T | undefined> {
97
+ const db = await this.openDB()
98
+ return new Promise((resolve, reject) => {
99
+ const request = db.transaction(STORE_NAME, 'readonly').objectStore(STORE_NAME).get(key)
100
+ request.onerror = (event) => reject(`Failed to retrieve item: ${(event.target as IDBRequest).error}`)
101
+ request.onsuccess = (event) => resolve((event.target as IDBRequest).result as T | undefined)
102
+ })
103
+ }
104
+
105
+ private async setIDBItem(key: IDBValidKey, value: unknown): Promise<void> {
106
+ const db = await this.openDB()
107
+ return new Promise((resolve, reject) => {
108
+ const request = db.transaction(STORE_NAME, 'readwrite').objectStore(STORE_NAME).put(value, key)
109
+ request.onerror = (event) => reject(`Failed to save item: ${(event.target as IDBRequest).error}`)
110
+ request.onsuccess = () => resolve()
111
+ })
112
+ }
113
+
114
+ private async deleteIDBItem(key: IDBValidKey): Promise<void> {
115
+ const db = await this.openDB()
116
+ return new Promise((resolve, reject) => {
117
+ const request = db.transaction(STORE_NAME, 'readwrite').objectStore(STORE_NAME).delete(key)
118
+ request.onerror = (event) => reject(`Failed to delete item: ${(event.target as IDBRequest).error}`)
119
+ request.onsuccess = () => resolve()
120
+ })
121
+ }
122
+
123
+ async setPendingRedirectRequest(isPending: boolean): Promise<void> {
124
+ try {
125
+ if (isPending) {
126
+ sessionStorage.setItem(PENDING_REDIRECT_REQUEST_KEY, 'true')
127
+ } else {
128
+ sessionStorage.removeItem(PENDING_REDIRECT_REQUEST_KEY)
129
+ }
130
+ } catch (error) {
131
+ console.error('Failed to set pending redirect flag:', error)
132
+ }
133
+ }
134
+
135
+ async isRedirectRequestPending(): Promise<boolean> {
136
+ try {
137
+ return sessionStorage.getItem(PENDING_REDIRECT_REQUEST_KEY) === 'true'
138
+ } catch (error) {
139
+ console.error('Failed to check pending redirect flag:', error)
140
+ return false
141
+ }
142
+ }
143
+
144
+ async saveTempSessionPk(pk: Hex.Hex): Promise<void> {
145
+ try {
146
+ sessionStorage.setItem(TEMP_SESSION_PK_KEY, pk)
147
+ } catch (error) {
148
+ console.error('Failed to save temp session PK:', error)
149
+ }
150
+ }
151
+
152
+ async getAndClearTempSessionPk(): Promise<Hex.Hex | null> {
153
+ try {
154
+ const pk = sessionStorage.getItem(TEMP_SESSION_PK_KEY)
155
+ sessionStorage.removeItem(TEMP_SESSION_PK_KEY)
156
+ return pk as Hex.Hex | null
157
+ } catch (error) {
158
+ console.error('Failed to retrieve temp session PK:', error)
159
+ return null
160
+ }
161
+ }
162
+
163
+ async savePendingRequest(context: PendingRequestContext): Promise<void> {
164
+ try {
165
+ sessionStorage.setItem(PENDING_REQUEST_CONTEXT_KEY, JSON.stringify(context, jsonReplacers))
166
+ } catch (error) {
167
+ console.error('Failed to save pending request context:', error)
168
+ }
169
+ }
170
+
171
+ async getAndClearPendingRequest(): Promise<PendingRequestContext | null> {
172
+ try {
173
+ const context = sessionStorage.getItem(PENDING_REQUEST_CONTEXT_KEY)
174
+ if (!context) return null
175
+ sessionStorage.removeItem(PENDING_REQUEST_CONTEXT_KEY)
176
+ return JSON.parse(context, jsonRevivers)
177
+ } catch (error) {
178
+ console.error('Failed to retrieve pending request context:', error)
179
+ return null
180
+ }
181
+ }
182
+
183
+ async peekPendingRequest(): Promise<PendingRequestContext | null> {
184
+ try {
185
+ const context = sessionStorage.getItem(PENDING_REQUEST_CONTEXT_KEY)
186
+ if (!context) return null
187
+ return JSON.parse(context, jsonRevivers)
188
+ } catch (error) {
189
+ console.error('Failed to peek at pending request context:', error)
190
+ return null
191
+ }
192
+ }
193
+
194
+ async saveExplicitSession(sessionData: ExplicitSessionData): Promise<void> {
195
+ try {
196
+ const existingSessions = (await this.getExplicitSessions()).filter(
197
+ (s) =>
198
+ !(
199
+ Address.isEqual(s.walletAddress, sessionData.walletAddress) &&
200
+ s.pk === sessionData.pk &&
201
+ s.chainId === sessionData.chainId
202
+ ),
203
+ )
204
+ await this.setIDBItem(EXPLICIT_SESSIONS_IDB_KEY, [...existingSessions, sessionData])
205
+ } catch (error) {
206
+ console.error('Failed to save explicit session:', error)
207
+ throw error
208
+ }
209
+ }
210
+
211
+ async getExplicitSessions(): Promise<ExplicitSessionData[]> {
212
+ try {
213
+ const sessions = await this.getIDBItem<ExplicitSessionData[]>(EXPLICIT_SESSIONS_IDB_KEY)
214
+ return sessions && Array.isArray(sessions) ? sessions : []
215
+ } catch (error) {
216
+ console.error('Failed to retrieve explicit sessions:', error)
217
+ return []
218
+ }
219
+ }
220
+
221
+ async clearExplicitSessions(): Promise<void> {
222
+ try {
223
+ await this.deleteIDBItem(EXPLICIT_SESSIONS_IDB_KEY)
224
+ } catch (error) {
225
+ console.error('Failed to clear explicit sessions:', error)
226
+ throw error
227
+ }
228
+ }
229
+
230
+ async saveImplicitSession(sessionData: ImplicitSessionData): Promise<void> {
231
+ try {
232
+ await this.setIDBItem(IMPLICIT_SESSIONS_IDB_KEY, sessionData)
233
+ } catch (error) {
234
+ console.error('Failed to save implicit session:', error)
235
+ throw error
236
+ }
237
+ }
238
+
239
+ async getImplicitSession(): Promise<ImplicitSessionData | null> {
240
+ try {
241
+ return (await this.getIDBItem<ImplicitSessionData>(IMPLICIT_SESSIONS_IDB_KEY)) ?? null
242
+ } catch (error) {
243
+ console.error('Failed to retrieve implicit session:', error)
244
+ return null
245
+ }
246
+ }
247
+
248
+ async clearImplicitSession(): Promise<void> {
249
+ try {
250
+ await this.deleteIDBItem(IMPLICIT_SESSIONS_IDB_KEY)
251
+ } catch (error) {
252
+ console.error('Failed to clear implicit session:', error)
253
+ throw error
254
+ }
255
+ }
256
+
257
+ async clearAllData(): Promise<void> {
258
+ try {
259
+ // Clear all session storage items
260
+ sessionStorage.removeItem(PENDING_REDIRECT_REQUEST_KEY)
261
+ sessionStorage.removeItem(TEMP_SESSION_PK_KEY)
262
+ sessionStorage.removeItem(PENDING_REQUEST_CONTEXT_KEY)
263
+
264
+ // Clear all IndexedDB items
265
+ await this.clearExplicitSessions()
266
+ await this.clearImplicitSession()
267
+ } catch (error) {
268
+ console.error('Failed to clear all data:', error)
269
+ throw error
270
+ }
271
+ }
272
+ }