|
1 |
| -import { Signers as CoreSigners, Envelope, Wallet } from '@0xsequence/wallet-core' |
| 1 | +import { Signers as CoreSigners, Envelope } from '@0xsequence/wallet-core' |
2 | 2 | import {
|
3 | 3 | Attestation,
|
| 4 | + Config, |
| 5 | + Constants, |
| 6 | + GenericTree, |
4 | 7 | Payload,
|
5 |
| - SessionConfig, |
6 | 8 | Signature as SequenceSignature,
|
7 |
| - Config, |
| 9 | + SessionConfig, |
8 | 10 | } from '@0xsequence/wallet-primitives'
|
9 |
| -import { Address, Bytes, Hex, Provider, RpcTransport } from 'ox' |
10 |
| -import { SessionController } from '../session/index.js' |
11 |
| -import { IdentityHandler, identityTypeToHex } from './handlers/identity.js' |
12 |
| -import { Shared } from './manager.js' |
13 |
| -import { AuthCodePkceHandler } from './handlers/authcode-pkce.js' |
| 11 | +import { Address, Bytes, Hex } from 'ox' |
14 | 12 | import { IdentityType } from '../identity/index.js'
|
| 13 | +import { AuthCodePkceHandler } from './handlers/authcode-pkce.js' |
| 14 | +import { IdentityHandler, identityTypeToHex } from './handlers/identity.js' |
| 15 | +import { ManagerOptionsDefaults, Shared } from './manager.js' |
| 16 | +import { Actions } from './types/signature-request.js' |
15 | 17 |
|
16 | 18 | export type AuthorizeImplicitSessionArgs = {
|
17 | 19 | target: string
|
18 | 20 | applicationData?: Hex.Hex
|
19 | 21 | }
|
20 | 22 |
|
21 |
| -export class Sessions { |
22 |
| - private readonly _sessionControllers: Map<Address.Address, SessionController> = new Map() |
| 23 | +const DefaultSessionManagerAddresses = [Constants.DefaultSessionManager] |
23 | 24 |
|
24 |
| - constructor(private readonly shared: Shared) {} |
25 |
| - |
26 |
| - getCoreWallet(walletAddress: Address.Address): Wallet { |
27 |
| - return new Wallet(walletAddress, { |
28 |
| - context: this.shared.sequence.context, |
29 |
| - guest: this.shared.sequence.guest, |
30 |
| - stateProvider: this.shared.sequence.stateProvider, |
31 |
| - }) |
32 |
| - } |
| 25 | +export class Sessions { |
| 26 | + constructor( |
| 27 | + private readonly shared: Shared, |
| 28 | + private readonly sessionManagerAddresses: Address.Address[] = DefaultSessionManagerAddresses, |
| 29 | + ) {} |
33 | 30 |
|
34 |
| - async getControllerForWallet(walletAddress: Address.Address, chainId?: bigint): Promise<SessionController> { |
35 |
| - if (this._sessionControllers.has(walletAddress)) { |
36 |
| - return this._sessionControllers.get(walletAddress)! |
| 31 | + async getSessionTopology(walletAddress: Address.Address): Promise<SessionConfig.SessionsTopology> { |
| 32 | + const { modules } = await this.shared.modules.wallets.getConfigurationParts(walletAddress) |
| 33 | + const managerLeaf = modules.find((leaf) => |
| 34 | + this.sessionManagerAddresses.some((addr) => Address.isEqual(addr, leaf.address)), |
| 35 | + ) |
| 36 | + if (!managerLeaf) { |
| 37 | + throw new Error('Session manager not found') |
37 | 38 | }
|
38 |
| - |
39 |
| - // Construct the wallet |
40 |
| - const wallet = this.getCoreWallet(walletAddress) |
41 |
| - |
42 |
| - // Get the provider if available |
43 |
| - let provider: Provider.Provider | undefined |
44 |
| - if (chainId) { |
45 |
| - const network = this.shared.sequence.networks.find((network) => network.chainId === chainId) |
46 |
| - if (network) { |
47 |
| - provider = Provider.from(RpcTransport.fromHttp(network.rpc)) |
48 |
| - } |
| 39 | + const imageHash = managerLeaf.imageHash |
| 40 | + const tree = await this.shared.sequence.stateProvider.getTree(imageHash) |
| 41 | + if (!tree) { |
| 42 | + throw new Error('Session topology not found') |
49 | 43 | }
|
50 |
| - |
51 |
| - // Create the controller |
52 |
| - const controller = new SessionController({ |
53 |
| - wallet, |
54 |
| - provider, |
55 |
| - stateProvider: this.shared.sequence.stateProvider, |
56 |
| - }) |
57 |
| - this._sessionControllers.set(walletAddress, controller) |
58 |
| - return controller |
59 |
| - } |
60 |
| - |
61 |
| - async getSessionTopology(walletAddress: Address.Address): Promise<SessionConfig.SessionsTopology> { |
62 |
| - const controller = await this.getControllerForWallet(walletAddress) |
63 |
| - return controller.getTopology() |
| 44 | + return SessionConfig.configurationTreeToSessionsTopology(tree) |
64 | 45 | }
|
65 | 46 |
|
66 | 47 | async prepareAuthorizeImplicitSession(
|
@@ -171,59 +152,89 @@ export class Sessions {
|
171 | 152 | permissions: CoreSigners.Session.ExplicitParams,
|
172 | 153 | origin?: string,
|
173 | 154 | ): Promise<string> {
|
174 |
| - const controller = await this.getControllerForWallet(walletAddress) |
175 |
| - const envelope = await controller.addExplicitSession(sessionAddress, permissions) |
176 |
| - return this.prepareSessionUpdate(envelope, origin) |
| 155 | + const topology = await this.getSessionTopology(walletAddress) |
| 156 | + const newTopology = SessionConfig.addExplicitSession(topology, { |
| 157 | + ...permissions, |
| 158 | + signer: sessionAddress, |
| 159 | + }) |
| 160 | + return this.prepareSessionUpdate(walletAddress, newTopology, origin) |
177 | 161 | }
|
178 | 162 |
|
179 | 163 | async removeExplicitSession(
|
180 | 164 | walletAddress: Address.Address,
|
181 | 165 | sessionAddress: Address.Address,
|
182 | 166 | origin?: string,
|
183 | 167 | ): Promise<string> {
|
184 |
| - const controller = await this.getControllerForWallet(walletAddress) |
185 |
| - const envelope = await controller.removeExplicitSession(sessionAddress) |
186 |
| - return this.prepareSessionUpdate(envelope, origin) |
| 168 | + const topology = await this.getSessionTopology(walletAddress) |
| 169 | + const newTopology = SessionConfig.removeExplicitSession(topology, sessionAddress) |
| 170 | + if (!newTopology) { |
| 171 | + throw new Error('Session not found') |
| 172 | + } |
| 173 | + return this.prepareSessionUpdate(walletAddress, newTopology, origin) |
187 | 174 | }
|
188 | 175 |
|
189 | 176 | async addBlacklistAddress(
|
190 | 177 | walletAddress: Address.Address,
|
191 | 178 | address: Address.Address,
|
192 | 179 | origin?: string,
|
193 | 180 | ): Promise<string> {
|
194 |
| - const controller = await this.getControllerForWallet(walletAddress) |
195 |
| - const envelope = await controller.addBlacklistAddress(address) |
196 |
| - return this.prepareSessionUpdate(envelope, origin) |
| 181 | + const topology = await this.getSessionTopology(walletAddress) |
| 182 | + const newTopology = SessionConfig.addToImplicitBlacklist(topology, address) |
| 183 | + return this.prepareSessionUpdate(walletAddress, newTopology, origin) |
197 | 184 | }
|
198 | 185 |
|
199 | 186 | async removeBlacklistAddress(
|
200 | 187 | walletAddress: Address.Address,
|
201 | 188 | address: Address.Address,
|
202 | 189 | origin?: string,
|
203 | 190 | ): Promise<string> {
|
204 |
| - const controller = await this.getControllerForWallet(walletAddress) |
205 |
| - const envelope = await controller.removeBlacklistAddress(address) |
206 |
| - return this.prepareSessionUpdate(envelope, origin) |
| 191 | + const topology = await this.getSessionTopology(walletAddress) |
| 192 | + const newTopology = SessionConfig.removeFromImplicitBlacklist(topology, address) |
| 193 | + return this.prepareSessionUpdate(walletAddress, newTopology, origin) |
207 | 194 | }
|
208 | 195 |
|
209 | 196 | private async prepareSessionUpdate(
|
210 |
| - envelope: Envelope.Envelope<Payload.ConfigUpdate>, |
| 197 | + walletAddress: Address.Address, |
| 198 | + topology: SessionConfig.SessionsTopology, |
211 | 199 | origin: string = 'wallet-webapp',
|
212 | 200 | ): Promise<string> {
|
213 |
| - return await this.shared.modules.signatures.request(envelope, 'session-update', { |
| 201 | + // Store the new configuration |
| 202 | + const tree = SessionConfig.sessionsTopologyToConfigurationTree(topology) |
| 203 | + await this.shared.sequence.stateProvider.saveTree(tree) |
| 204 | + const newImageHash = GenericTree.hash(tree) |
| 205 | + |
| 206 | + // Find the session manager in the old configuration |
| 207 | + const { modules } = await this.shared.modules.wallets.getConfigurationParts(walletAddress) |
| 208 | + const managerLeaf = modules.find((leaf) => |
| 209 | + this.sessionManagerAddresses.some((addr) => Address.isEqual(addr, leaf.address)), |
| 210 | + ) |
| 211 | + if (!managerLeaf) { |
| 212 | + // Missing. Add it |
| 213 | + modules.push({ |
| 214 | + ...ManagerOptionsDefaults.defaultSessionsTopology, |
| 215 | + imageHash: newImageHash, |
| 216 | + }) |
| 217 | + } else { |
| 218 | + // Update the configuration to use the new session manager image hash |
| 219 | + managerLeaf.imageHash = newImageHash |
| 220 | + } |
| 221 | + |
| 222 | + return this.shared.modules.wallets.requestConfigurationUpdate( |
| 223 | + walletAddress, |
| 224 | + { |
| 225 | + modules, |
| 226 | + }, |
| 227 | + Actions.SessionUpdate, |
214 | 228 | origin,
|
215 |
| - }) |
| 229 | + ) |
216 | 230 | }
|
217 | 231 |
|
218 |
| - async completeSessionUpdate(walletAddress: Address.Address, requestId: string) { |
219 |
| - const controller = await this.getControllerForWallet(walletAddress) |
| 232 | + async completeSessionUpdate(requestId: string) { |
220 | 233 | const sigRequest = await this.shared.modules.signatures.get(requestId)
|
221 |
| - const envelope = sigRequest.envelope |
222 |
| - if (sigRequest.action !== 'session-update' || !Payload.isConfigUpdate(envelope.payload)) { |
| 234 | + if (sigRequest.action !== 'session-update' || !Payload.isConfigUpdate(sigRequest.envelope.payload)) { |
223 | 235 | throw new Error('Invalid action')
|
224 | 236 | }
|
225 |
| - console.log('Completing session update:', requestId) |
226 |
| - await controller.completeUpdateConfiguration(envelope as Envelope.Signed<Payload.ConfigUpdate>) |
227 |
| - return this.shared.modules.signatures.complete(requestId) |
| 237 | + |
| 238 | + return this.shared.modules.wallets.completeConfigurationUpdate(requestId) |
228 | 239 | }
|
229 | 240 | }
|
0 commit comments