Skip to content

Commit a774491

Browse files
authored
Short circuit 404s (#949)
* skip witness on signers that don't support it * add passkey to test
1 parent ee06442 commit a774491

File tree

3 files changed

+94
-1
lines changed

3 files changed

+94
-1
lines changed

packages/wallet/wdk/src/sequence/manager.ts

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ export type ManagerOptions = {
5959
guardUrl?: string
6060
guardAddresses?: Record<GuardRole, Address.Address>
6161

62+
nonWitnessableSigners?: Address.Address[]
63+
6264
// The default guard topology MUST have a placeholder address for the guard address
6365
defaultGuardTopology?: Config.Topology
6466
defaultRecoverySettings?: RecoverySettings
@@ -123,6 +125,8 @@ export const ManagerOptionsDefaults = {
123125
},
124126
bundlers: [],
125127

128+
nonWitnessableSigners: [] as Address.Address[],
129+
126130
guardUrl: 'https://guard.sequence.app',
127131
guardAddresses: {
128132
wallet: '0x26f3D30F41FA897309Ae804A2AFf15CEb1dA5742',
@@ -183,11 +187,41 @@ export const CreateWalletOptionsDefaults = {
183187
}
184188

185189
export function applyManagerOptionsDefaults(options?: ManagerOptions) {
186-
return {
190+
const merged = {
187191
...ManagerOptionsDefaults,
188192
...options,
189193
identity: { ...ManagerOptionsDefaults.identity, ...options?.identity },
190194
}
195+
196+
// Merge and normalize non-witnessable signers.
197+
// We always include the sessions extension address for the active extensions set.
198+
const nonWitnessable = new Set<string>()
199+
for (const address of ManagerOptionsDefaults.nonWitnessableSigners ?? []) {
200+
nonWitnessable.add(address.toLowerCase())
201+
}
202+
for (const address of options?.nonWitnessableSigners ?? []) {
203+
nonWitnessable.add(address.toLowerCase())
204+
}
205+
nonWitnessable.add(merged.extensions.sessions.toLowerCase())
206+
207+
// Include static signer leaves from the guard topology (e.g. recovery guard signer),
208+
// but ignore the placeholder address that is later replaced per-role.
209+
if (merged.defaultGuardTopology) {
210+
const guardTopologySigners = Config.getSigners(merged.defaultGuardTopology)
211+
for (const signer of guardTopologySigners.signers) {
212+
if (Address.isEqual(signer, Constants.PlaceholderAddress)) {
213+
continue
214+
}
215+
nonWitnessable.add(signer.toLowerCase())
216+
}
217+
for (const signer of guardTopologySigners.sapientSigners) {
218+
nonWitnessable.add(signer.address.toLowerCase())
219+
}
220+
}
221+
222+
merged.nonWitnessableSigners = Array.from(nonWitnessable) as Address.Address[]
223+
224+
return merged
191225
}
192226

193227
export type RecoverySettings = {
@@ -221,6 +255,8 @@ export type Sequence = {
221255
readonly relayers: Relayer.Relayer[]
222256
readonly bundlers: Bundler.Bundler[]
223257

258+
readonly nonWitnessableSigners: ReadonlySet<Address.Address>
259+
224260
readonly defaultGuardTopology: Config.Topology
225261
readonly defaultRecoverySettings: RecoverySettings
226262

@@ -408,6 +444,10 @@ export class Manager {
408444
relayers,
409445
bundlers: ops.bundlers,
410446

447+
nonWitnessableSigners: new Set(
448+
(ops.nonWitnessableSigners ?? []).map((address) => address.toLowerCase() as Address.Address),
449+
),
450+
411451
defaultGuardTopology: ops.defaultGuardTopology,
412452
defaultRecoverySettings: ops.defaultRecoverySettings,
413453

packages/wallet/wdk/src/sequence/signers.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,19 @@ export class Signers {
4848
return Kinds.Guard
4949
}
5050

51+
// Passkeys are a sapient signer module: the address alone identifies the kind.
52+
// Metadata (credential id, public key, etc.) is loaded later by the PasskeysHandler
53+
// via the witness payload, so we can skip the witness probe here.
54+
if (Address.isEqual(this.shared.sequence.extensions.passkeys, address)) {
55+
return Kinds.LoginPasskey
56+
}
57+
58+
// Some signers are known to never publish a witness record (e.g. module signers).
59+
// Skip probing the Sessions/Witness endpoint for them.
60+
if (this.shared.sequence.nonWitnessableSigners.has(address.toLowerCase() as Address.Address)) {
61+
return undefined
62+
}
63+
5164
// We need to use the state provider (and witness) this will tell us the kind of signer
5265
// NOTICE: This looks expensive, but this operation should be cached by the state provider
5366
const witness = await (imageHash
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import { describe, expect, it, vi } from 'vitest'
2+
3+
import { Kinds } from '../src/sequence/index.js'
4+
import { newManager } from './constants.js'
5+
6+
describe('Signers.kindOf', () => {
7+
it('does not probe Sessions/Witness for non-witnessable signers', async () => {
8+
const getWitnessFor = vi.fn().mockResolvedValue(undefined)
9+
const getWitnessForSapient = vi.fn().mockResolvedValue(undefined)
10+
11+
const manager = newManager({
12+
stateProvider: {
13+
getWitnessFor,
14+
getWitnessForSapient,
15+
} as any,
16+
})
17+
18+
const signers = (manager as any).shared.modules.signers
19+
const extensions = (manager as any).shared.sequence.extensions
20+
21+
const wallet = '0x1111111111111111111111111111111111111111'
22+
const imageHash = ('0x' + '00'.repeat(32)) as `0x${string}`
23+
24+
// Sessions extension signer (sapient leaf) never publishes a witness.
25+
await signers.kindOf(wallet, extensions.sessions, imageHash)
26+
27+
// Passkeys module is a known sapient signer kind.
28+
expect(await signers.kindOf(wallet, extensions.passkeys, imageHash)).toBe(Kinds.LoginPasskey)
29+
30+
// Sequence dev multisig (default guard topology leaf) never publishes a witness.
31+
await signers.kindOf(wallet, '0x007a47e6BF40C1e0ed5c01aE42fDC75879140bc4')
32+
33+
expect(getWitnessFor).not.toHaveBeenCalled()
34+
expect(getWitnessForSapient).not.toHaveBeenCalled()
35+
36+
// Unknown signers still rely on a witness probe.
37+
await signers.kindOf(wallet, '0x2222222222222222222222222222222222222222')
38+
expect(getWitnessFor).toHaveBeenCalledTimes(1)
39+
})
40+
})

0 commit comments

Comments
 (0)