Skip to content

Commit eee46dc

Browse files
committed
Update sessions configuration updates
1 parent 6f6ae4f commit eee46dc

9 files changed

Lines changed: 181 additions & 34 deletions

File tree

packages/core/src/wallet.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ export class Wallet {
9898
throw new Error(`cannot find configuration details for ${envelope.payload.imageHash}`)
9999
}
100100

101+
// Verify the new configuration is valid
101102
const updatedEnvelope = { ...envelope, configuration: status.configuration }
102103
const { weight, threshold } = Envelope.weightOf(updatedEnvelope)
103104
if (weight < threshold) {

packages/wdk/src/dbs/signatures.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,14 @@ export type ActionToPayload = {
77
[Actions.Logout]: Payload.ConfigUpdate
88
[Actions.Login]: Payload.ConfigUpdate
99
[Actions.SendTransaction]: Payload.Calls
10+
[Actions.SessionUpdate]: Payload.ConfigUpdate
1011
}
1112

1213
export const Actions = {
1314
Logout: 'logout',
1415
Login: 'login',
1516
SendTransaction: 'send-transaction',
17+
SessionUpdate: 'session-update',
1618
} as const
1719

1820
export type Action = (typeof Actions)[keyof typeof Actions]

packages/wdk/src/sequence/manager.ts

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
import { Signers as CoreSigners, Relayer, State } from '@0xsequence/sequence-core'
2-
import { Config, Constants, Context, Extensions, Network, Payload } from '@0xsequence/sequence-primitives'
2+
import {
3+
Config,
4+
Constants,
5+
Context,
6+
Extensions,
7+
Network,
8+
Payload,
9+
SessionConfig,
10+
} from '@0xsequence/sequence-primitives'
311
import { Address } from 'ox'
412
import * as Db from '../dbs'
513
import * as Identity from '../identity'
@@ -246,7 +254,7 @@ export class Manager {
246254

247255
// Signatures
248256

249-
public async listSignatureRequests(): Promise<BaseSignatureRequest[]> {
257+
public async listSignatureRequests(): Promise<SignatureRequest[]> {
250258
return this.shared.modules.signatures.list()
251259
}
252260

@@ -324,4 +332,43 @@ export class Manager {
324332
}
325333
})
326334
}
335+
336+
// Sessions
337+
338+
public async getSessionTopology(walletAddress: Address.Address): Promise<SessionConfig.SessionsTopology> {
339+
return this.shared.modules.sessions.getSessionTopology(walletAddress)
340+
}
341+
342+
public async addImplicitSession(walletAddress: Address.Address, sessionAddress: Address.Address) {
343+
return this.shared.modules.sessions.addImplicitSession(walletAddress, sessionAddress)
344+
}
345+
346+
public async addExplicitSession(
347+
walletAddress: Address.Address,
348+
sessionAddress: Address.Address,
349+
permissions: CoreSigners.Session.ExplicitParams,
350+
): Promise<string> {
351+
return this.shared.modules.sessions.addExplicitSession(walletAddress, sessionAddress, permissions)
352+
}
353+
354+
public async removeExplicitSession(walletAddress: Address.Address, sessionAddress: Address.Address): Promise<string> {
355+
return this.shared.modules.sessions.removeExplicitSession(walletAddress, sessionAddress)
356+
}
357+
358+
public async addBlacklistAddress(walletAddress: Address.Address, address: Address.Address): Promise<string> {
359+
return this.shared.modules.sessions.addBlacklistAddress(walletAddress, address)
360+
}
361+
362+
public async removeBlacklistAddress(walletAddress: Address.Address, address: Address.Address): Promise<string> {
363+
return this.shared.modules.sessions.removeBlacklistAddress(walletAddress, address)
364+
}
365+
366+
public async completeSessionUpdate(requestId: string) {
367+
const sigRequest = await this.shared.modules.signatures.get(requestId)
368+
if (sigRequest.action !== 'session-update' || !Payload.isConfigUpdate(sigRequest.envelope.payload)) {
369+
throw new Error('Invalid action')
370+
}
371+
console.log('Completing session update:', requestId)
372+
return this.shared.modules.sessions.completeSessionUpdate(sigRequest.wallet, requestId)
373+
}
327374
}
Lines changed: 85 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,30 @@
1-
import { Wallet } from '@0xsequence/sequence-core'
2-
import { Config, Constants } from '@0xsequence/sequence-primitives'
1+
import { Signers as CoreSigners, Envelope, Wallet } from '@0xsequence/sequence-core'
2+
import { Config, Constants, Payload, SessionConfig } from '@0xsequence/sequence-primitives'
33
import { Address, Provider, RpcTransport } from 'ox'
44
import { SessionController } from '../session'
55
import { Shared } from './manager'
66

77
export class Sessions {
8+
private readonly _sessionControllers: Map<Address.Address, SessionController> = new Map()
9+
810
constructor(private readonly shared: Shared) {}
911

10-
async controllerForWallet(address: Address.Address, chainId?: bigint) {
12+
async getControllerForWallet(walletAddress: Address.Address, chainId?: bigint): Promise<SessionController> {
13+
if (this._sessionControllers.has(walletAddress)) {
14+
return this._sessionControllers.get(walletAddress)!
15+
}
16+
17+
//FIXME How do we check the wallet is available? Is it necessary?
1118
// Find the session configuration for the wallet
12-
const wallet = new Wallet(address, {
19+
const wallet = new Wallet(walletAddress, {
1320
context: this.shared.sequence.context,
1421
stateProvider: this.shared.sequence.stateProvider,
1522
guest: this.shared.sequence.guest,
1623
})
17-
const status = await wallet.getStatus()
18-
const walletConfig = status.configuration
19-
const sessionConfigLeaf = Config.findSignerLeaf(walletConfig, Constants.DefaultSessionManager)
24+
const { configuration } = await wallet.getStatus()
25+
const sessionConfigLeaf = Config.findSignerLeaf(configuration, Constants.DefaultSessionManager)
2026
if (!sessionConfigLeaf || !Config.isSapientSignerLeaf(sessionConfigLeaf)) {
21-
throw new Error(`Session module not found for wallet ${address}`)
27+
throw new Error(`Session module not found for wallet ${walletAddress}`)
2228
}
2329

2430
// Get the provider if available
@@ -31,10 +37,80 @@ export class Sessions {
3137
}
3238

3339
// Create the controller
34-
const controller = SessionController.createFromStorage(sessionConfigLeaf.imageHash, {
40+
const controller = await SessionController.createFromStorage(sessionConfigLeaf.imageHash, {
3541
wallet,
3642
provider,
43+
stateProvider: this.shared.sequence.stateProvider,
3744
})
45+
this._sessionControllers.set(walletAddress, controller)
3846
return controller
3947
}
48+
49+
async getSessionTopology(walletAddress: Address.Address): Promise<SessionConfig.SessionsTopology> {
50+
console.log('Getting session topology for:', walletAddress)
51+
const controller = await this.getControllerForWallet(walletAddress)
52+
console.log('Session topology:', controller.topology)
53+
return controller.topology
54+
}
55+
56+
async addImplicitSession(walletAddress: Address.Address, sessionAddress: Address.Address): Promise<string> {
57+
const controller = await this.getControllerForWallet(walletAddress)
58+
// Create attestation params
59+
//FIXME This is a login request?
60+
// const attestationParams: Attestation.Attestation = {
61+
// approvedSigner: sessionAddress,
62+
// }
63+
// const envelope = await controller.addImplicitSession(sessionAddress)
64+
// return this.shared.modules.signatures.request(envelope, 'config-update', {
65+
// origin: 'session-manager',
66+
// })
67+
throw new Error('Not implemented')
68+
}
69+
70+
async addExplicitSession(
71+
walletAddress: Address.Address,
72+
sessionAddress: Address.Address,
73+
permissions: CoreSigners.Session.ExplicitParams,
74+
): Promise<string> {
75+
const controller = await this.getControllerForWallet(walletAddress)
76+
const envelope = await controller.addExplicitSession(sessionAddress, permissions)
77+
return this.prepareSessionUpdate(envelope)
78+
}
79+
80+
async removeExplicitSession(walletAddress: Address.Address, sessionAddress: Address.Address): Promise<string> {
81+
const controller = await this.getControllerForWallet(walletAddress)
82+
const envelope = await controller.removeExplicitSession(sessionAddress)
83+
return this.prepareSessionUpdate(envelope)
84+
}
85+
86+
async addBlacklistAddress(walletAddress: Address.Address, address: Address.Address): Promise<string> {
87+
const controller = await this.getControllerForWallet(walletAddress)
88+
const envelope = await controller.addBlacklistAddress(address)
89+
return this.prepareSessionUpdate(envelope)
90+
}
91+
92+
async removeBlacklistAddress(walletAddress: Address.Address, address: Address.Address): Promise<string> {
93+
const controller = await this.getControllerForWallet(walletAddress)
94+
const envelope = await controller.removeBlacklistAddress(address)
95+
return this.prepareSessionUpdate(envelope)
96+
}
97+
98+
private async prepareSessionUpdate(envelope: Envelope.Envelope<Payload.ConfigUpdate>): Promise<string> {
99+
const requestId = await this.shared.modules.signatures.request(envelope, 'session-update', {
100+
origin: 'wallet-webapp',
101+
})
102+
return requestId
103+
}
104+
105+
async completeSessionUpdate(walletAddress: Address.Address, requestId: string) {
106+
const controller = await this.getControllerForWallet(walletAddress)
107+
const sigRequest = await this.shared.modules.signatures.get(requestId)
108+
const envelope = sigRequest.envelope
109+
if (sigRequest.action !== 'session-update' || !Payload.isConfigUpdate(envelope.payload)) {
110+
throw new Error('Invalid action')
111+
}
112+
console.log('Completing session update:', requestId)
113+
await controller.completeUpdateConfiguration(envelope as Envelope.Signed<Payload.ConfigUpdate>)
114+
return this.shared.modules.signatures.complete(requestId)
115+
}
40116
}

packages/wdk/src/sequence/signatures.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ import { BaseSignatureRequest, SignatureRequest, SignerBase, SignerSigned, Signe
1010
export class Signatures {
1111
constructor(private readonly shared: Shared) {}
1212

13-
async list(): Promise<Db.SignatureRequest[]> {
14-
return this.shared.databases.signatures.list()
13+
async list(): Promise<SignatureRequest[]> {
14+
return this.shared.databases.signatures.list() as any as SignatureRequest[]
1515
}
1616

1717
async get(requestId: string): Promise<SignatureRequest> {
@@ -94,7 +94,7 @@ export class Signatures {
9494
...request,
9595
...Envelope.weightOf(request.envelope),
9696
signers: statuses,
97-
}
97+
} as SignatureRequest
9898
}
9999

100100
onSignatureRequestUpdate(

packages/wdk/src/sequence/transactions.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { Payload } from '@0xsequence/sequence-primitives'
2-
import * as Db from '../dbs'
32
import { Envelope, Wallet } from '@0xsequence/sequence-core'
43
import { Address, Provider, RpcTransport } from 'ox'
54
import { v7 as uuidv7 } from 'uuid'
@@ -161,6 +160,7 @@ export class Transactions {
161160

162161
return signatureId
163162
}
163+
164164
async relay(transactionOrSignatureId: string) {
165165
// First, try to get the transaction directly
166166
let tx: Transaction | undefined

packages/wdk/src/sequence/types/signatureRequest.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,14 @@ export type ActionToPayload = {
77
[Actions.Logout]: Payload.ConfigUpdate
88
[Actions.Login]: Payload.ConfigUpdate
99
[Actions.SendTransaction]: Payload.Calls
10+
[Actions.SessionUpdate]: Payload.ConfigUpdate
1011
}
1112

1213
export const Actions = {
1314
Logout: 'logout',
1415
Login: 'login',
1516
SendTransaction: 'send-transaction',
17+
SessionUpdate: 'session-update',
1618
} as const
1719

1820
export type Action = (typeof Actions)[keyof typeof Actions]

packages/wdk/src/sequence/wallets.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,10 @@ export class Wallets {
204204
return this.shared.databases.manager.get(wallet).then((r) => r !== undefined)
205205
}
206206

207+
public async get(walletAddress: Address.Address): Promise<Wallet | undefined> {
208+
return await this.shared.databases.manager.get(walletAddress)
209+
}
210+
207211
public async list(): Promise<Wallet[]> {
208212
return this.shared.databases.manager.list()
209213
}
@@ -374,7 +378,10 @@ export class Wallets {
374378
if (!args.noSessionManager) {
375379
// FIXME: Calculate image hash with the identity signer
376380
const sessionManagerTopology = SessionConfig.emptySessionsTopology(loginSignerAddress)
381+
// Store this tree in the state provider
377382
const sessionConfigTree = SessionConfig.sessionsTopologyToConfigurationTree(sessionManagerTopology)
383+
this.shared.sequence.stateProvider.saveTree(sessionConfigTree)
384+
// Prepare the configuration leaf
378385
const sessionImageHash = GenericTree.hash(sessionConfigTree)
379386
modules = [
380387
{

packages/wdk/src/session/session-controller.ts

Lines changed: 31 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,6 @@ export class SessionController {
2424
private readonly _identitySigner: IdentitySigner | null
2525
private readonly _stateProvider: State.Provider | null
2626

27-
private _pendingUpdate: {
28-
envelope: Envelope.Envelope<Payload.ConfigUpdate>
29-
topology: SessionConfig.SessionsTopology
30-
} | null = null
31-
3227
constructor(configuration: SessionControllerConfiguration) {
3328
this._manager = new Signers.SessionManager({
3429
topology: configuration.topology,
@@ -132,7 +127,14 @@ export class SessionController {
132127
// Create a new manager with the new topology
133128

134129
// Store the new configuration
135-
await this._stateProvider?.saveTree(SessionConfig.sessionsTopologyToConfigurationTree(topology))
130+
if (!this._stateProvider) {
131+
throw new Error('State provider not provided')
132+
}
133+
const tree = SessionConfig.sessionsTopologyToConfigurationTree(topology)
134+
console.log('prepareUpdateConfiguration Tree:', tree)
135+
const newImageHash = GenericTree.hash(tree)
136+
console.log('New image hash:', newImageHash)
137+
await this._stateProvider.saveTree(tree)
136138

137139
// Get the old wallet configuration
138140
const { configuration } = await this._wallet.getStatus()
@@ -144,26 +146,36 @@ export class SessionController {
144146
throw new Error('Session manager not found in configuration')
145147
}
146148

147-
// Update the configuration to use the new session manager image hash
148-
managerLeaf.imageHash = this.imageHash
149+
console.log('prepareUpdateConfiguration Manager Leaf:', managerLeaf)
150+
console.log('New image hash:', newImageHash)
149151

150-
// Update the wallet configuration
151-
const envelope = await this._wallet.prepareUpdate(configuration)
152-
return envelope
152+
// Update the configuration to use the new session manager image hash
153+
managerLeaf.imageHash = newImageHash
154+
return await this._wallet.prepareUpdate(configuration)
153155
}
154156

155157
// Complete the configuration update
156-
protected async completeUpdateConfiguration(envelope: Envelope.Signed<Payload.ConfigUpdate>): Promise<void> {
157-
// Verify this is the pending configuration update
158-
if (!this._pendingUpdate) {
159-
throw new Error('No pending configuration update')
158+
async completeUpdateConfiguration(envelope: Envelope.Signed<Payload.ConfigUpdate>): Promise<void> {
159+
const configuration = await this._stateProvider?.getConfiguration(envelope.payload.imageHash)
160+
if (!configuration) {
161+
throw new Error('Wallet configuration not found')
162+
}
163+
164+
// Find the session manager in the new configuration
165+
const managerLeaf = Config.findSignerLeaf(configuration, this._manager.address)
166+
if (!managerLeaf || !Config.isSapientSignerLeaf(managerLeaf)) {
167+
throw new Error('Session manager not found in configuration')
160168
}
161-
if (this._pendingUpdate.envelope.payload.imageHash !== envelope.payload.imageHash) {
162-
throw new Error('Invalid configuration update')
169+
const sessionTree = await this._stateProvider?.getTree(managerLeaf.imageHash)
170+
if (!sessionTree) {
171+
throw new Error('Session tree not found')
163172
}
173+
const topology = SessionConfig.configurationTreeToSessionsTopology(sessionTree)
174+
console.log('completeUpdateConfiguration Topology:', topology)
164175

165-
// Update the manager and wallet with the new topology
166-
this._manager = this._manager.withTopology(this._pendingUpdate.topology)
176+
// Update the manager and wallet with the new topology and submit the update
177+
this._manager = this._manager.withTopology(topology)
178+
console.log('Submitting update:', envelope.payload.imageHash)
167179
await this._wallet.submitUpdate(envelope)
168180
}
169181
}

0 commit comments

Comments
 (0)