@@ -13,6 +13,8 @@ export type PasskeySignerOptions = {
13
13
y : string
14
14
15
15
chainId : ethers . BigNumberish
16
+ provider ?: ethers . Provider
17
+ reader ?: commons . reader . Reader
16
18
17
19
requireUserValidation : boolean
18
20
requireBackupSanityCheck : boolean
@@ -31,11 +33,14 @@ export type PasskeySignerOptions = {
31
33
32
34
export type PasskeySignerContext = {
33
35
factory : string
34
-
35
36
mainModulePasskeys : string
36
37
guestModule : string
37
38
}
38
39
40
+ export type PasskeySignMetadata = {
41
+ cantValidateBehavior : 'ignore' | 'eip6492' | 'throw' ,
42
+ }
43
+
39
44
function bytesToBase64URL ( bytes : Uint8Array ) : string {
40
45
const base64 = btoa ( String . fromCharCode ( ...bytes ) )
41
46
return base64 . replace ( / \+ / g, '-' ) . replace ( / \/ / g, '_' ) . replace ( / = + $ / , '' )
@@ -49,6 +54,9 @@ export class SequencePasskeySigner implements signers.SapientSigner {
49
54
public readonly requireBackupSanityCheck : boolean
50
55
public readonly chainId : ethers . BigNumberish
51
56
57
+ public readonly provider ?: ethers . Provider
58
+ private _reader ?: commons . reader . Reader
59
+
52
60
public readonly context : PasskeySignerContext
53
61
54
62
private readonly doSign : (
@@ -69,7 +77,16 @@ export class SequencePasskeySigner implements signers.SapientSigner {
69
77
this . requireBackupSanityCheck = options . requireBackupSanityCheck
70
78
this . chainId = options . chainId
71
79
this . context = options . context
80
+ this . provider = options . provider
72
81
this . doSign = options . doSign
82
+
83
+ this . _reader = options . reader
84
+ }
85
+
86
+ reader ( ) : commons . reader . Reader {
87
+ if ( this . _reader ) return this . _reader
88
+ if ( ! this . provider ) throw new Error ( 'call requires a provider' )
89
+ return new commons . reader . OnChainReader ( this . provider )
73
90
}
74
91
75
92
initCodeHash ( ) : string {
@@ -110,7 +127,15 @@ export class SequencePasskeySigner implements signers.SapientSigner {
110
127
111
128
notifyStatusChange ( _id : string , _status : Status , _metadata : object ) : void { }
112
129
113
- async buildDeployTransaction ( metadata : object ) : Promise < commons . transaction . TransactionBundle | undefined > {
130
+ async isDeployed ( ) : Promise < boolean > {
131
+ return this . reader ( ) . isDeployed ( await this . getAddress ( ) )
132
+ }
133
+
134
+ async buildDeployTransaction ( metadata ?: commons . WalletDeployMetadata ) : Promise < commons . transaction . TransactionBundle | undefined > {
135
+ if ( metadata ?. ignoreDeployed && ( await this . isDeployed ( ) ) ) {
136
+ return
137
+ }
138
+
114
139
const factoryInterface = new ethers . Interface ( walletContracts . eternalFactory . abi )
115
140
const imageHash = this . imageHash ( )
116
141
@@ -139,14 +164,20 @@ export class SequencePasskeySigner implements signers.SapientSigner {
139
164
return Promise . resolve ( [ ] )
140
165
}
141
166
142
- decorateTransactions (
167
+ async decorateTransactions (
143
168
bundle : commons . transaction . IntendedTransactionBundle ,
144
- _metadata : object
169
+ metadata ?: commons . WalletDeployMetadata
145
170
) : Promise < commons . transaction . IntendedTransactionBundle > {
171
+ // Add deploy transaction
172
+ const deployTx = await this . buildDeployTransaction ( metadata )
173
+ if ( deployTx ) {
174
+ bundle . transactions . unshift ( ...deployTx . transactions )
175
+ }
176
+
146
177
return Promise . resolve ( bundle )
147
178
}
148
179
149
- async sign ( digest : ethers . BytesLike , _metadata : object ) : Promise < ethers . BytesLike > {
180
+ async sign ( digest : ethers . BytesLike , metadata : PasskeySignMetadata ) : Promise < ethers . BytesLike > {
150
181
const subdigest = subDigestOf ( await this . getAddress ( ) , this . chainId , digest )
151
182
152
183
const signature = await this . doSign ( digest , subdigest )
@@ -197,9 +228,39 @@ export class SequencePasskeySigner implements signers.SapientSigner {
197
228
]
198
229
)
199
230
231
+ if ( ! ! metadata && metadata . cantValidateBehavior !== "ignore" ) {
232
+ let isDeployed = false
233
+ try {
234
+ isDeployed = await this . isDeployed ( )
235
+ } catch ( e ) {
236
+ // Ignore. Handled below
237
+ }
238
+ if ( ! isDeployed && metadata . cantValidateBehavior === "eip6492" ) {
239
+ return this . buildEIP6492Signature ( signatureBytes )
240
+ } else if ( ! isDeployed && metadata . cantValidateBehavior === "throw" ) {
241
+ throw new Error ( 'Cannot sign with a non-deployed passkey signer' )
242
+ }
243
+ }
244
+
200
245
return signatureBytes
201
246
}
202
247
248
+ private async buildEIP6492Signature ( signature : string ) : Promise < string > {
249
+ const deployTransactions = await this . buildDeployTransaction ( )
250
+ if ( ! deployTransactions || deployTransactions ?. transactions . length === 0 ) {
251
+ throw new Error ( 'Cannot build EIP-6492 signature without deploy transaction' )
252
+ }
253
+
254
+ const deployTransaction = deployTransactions . transactions [ 0 ]
255
+
256
+ const encoded = ethers . AbiCoder . defaultAbiCoder ( ) . encode (
257
+ [ 'address' , 'bytes' , 'bytes' ] ,
258
+ [ deployTransaction . to , deployTransaction . data , signature ]
259
+ )
260
+
261
+ return ethers . solidityPacked ( [ 'bytes' , 'bytes32' ] , [ encoded , commons . EIP6492 . EIP_6492_SUFFIX ] )
262
+ }
263
+
203
264
suffix ( ) : ethers . BytesLike {
204
265
return new Uint8Array ( [ 3 ] )
205
266
}
0 commit comments