@cubist-labs/cubesigner-sdk 0.1.77 → 0.2.15

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 (55) hide show
  1. package/dist/package.json +68 -0
  2. package/dist/src/api.d.ts +493 -0
  3. package/dist/src/api.js +1166 -0
  4. package/dist/src/client.d.ts +534 -10
  5. package/dist/src/client.js +355 -19
  6. package/dist/src/ethers/index.d.ts +34 -9
  7. package/dist/src/ethers/index.js +63 -19
  8. package/dist/src/index.d.ts +51 -70
  9. package/dist/src/index.js +83 -237
  10. package/dist/src/key.d.ts +35 -64
  11. package/dist/src/key.js +32 -96
  12. package/dist/src/mfa.d.ts +85 -14
  13. package/dist/src/mfa.js +146 -40
  14. package/dist/src/org.d.ts +42 -194
  15. package/dist/src/org.js +52 -336
  16. package/dist/src/paginator.js +1 -1
  17. package/dist/src/response.d.ts +101 -0
  18. package/dist/src/response.js +164 -0
  19. package/dist/src/role.d.ts +87 -83
  20. package/dist/src/role.js +79 -136
  21. package/dist/src/schema.d.ts +936 -28
  22. package/dist/src/schema.js +1 -1
  23. package/dist/src/schema_types.d.ts +109 -0
  24. package/dist/src/schema_types.js +3 -0
  25. package/dist/src/session/cognito_manager.d.ts +15 -3
  26. package/dist/src/session/cognito_manager.js +23 -5
  27. package/dist/src/session/session_manager.d.ts +1 -1
  28. package/dist/src/session/session_manager.js +3 -11
  29. package/dist/src/session/session_storage.js +1 -1
  30. package/dist/src/session/signer_session_manager.d.ts +10 -29
  31. package/dist/src/session/signer_session_manager.js +21 -80
  32. package/dist/src/signer_session.d.ts +15 -252
  33. package/dist/src/signer_session.js +25 -424
  34. package/dist/src/user_export.d.ts +52 -0
  35. package/dist/src/user_export.js +129 -0
  36. package/dist/src/util.d.ts +15 -0
  37. package/dist/src/util.js +33 -11
  38. package/package.json +13 -11
  39. package/src/api.ts +1395 -0
  40. package/src/client.ts +413 -12
  41. package/src/ethers/index.ts +74 -28
  42. package/src/index.ts +96 -273
  43. package/src/key.ts +36 -131
  44. package/src/{fido.ts → mfa.ts} +62 -38
  45. package/src/org.ts +54 -405
  46. package/src/response.ts +196 -0
  47. package/src/role.ts +113 -184
  48. package/src/schema.ts +936 -28
  49. package/src/schema_types.ts +110 -0
  50. package/src/session/cognito_manager.ts +33 -6
  51. package/src/session/session_manager.ts +2 -8
  52. package/src/session/signer_session_manager.ts +29 -110
  53. package/src/signer_session.ts +22 -597
  54. package/src/user_export.ts +116 -0
  55. package/src/util.ts +29 -10
package/src/client.ts CHANGED
@@ -1,12 +1,413 @@
1
- import createClient from "openapi-fetch";
2
- import { paths } from "./schema";
3
-
4
- /** Type of http client.
5
- * @internal
6
- * */
7
- export type Client = ReturnType<typeof createClient<paths>>;
8
-
9
- /** Re-export schema.
10
- * @internal
11
- * */
12
- export * from "./schema";
1
+ import { SignerSessionManager, SignerSessionStorage } from "./session/signer_session_manager";
2
+ import { CognitoSessionManager } from "./session/cognito_manager";
3
+ import { CubeSignerApi, OidcClient } from "./api";
4
+ import { KeyType, Key } from "./key";
5
+ import { OrgInfo, RatchetConfig } from "./schema_types";
6
+ import { MfaReceipt } from "./mfa";
7
+ import { PageOpts } from "./paginator";
8
+ import { Role } from "./role";
9
+
10
+ // used in doc comments
11
+ // eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars
12
+ import { AddFidoChallenge, MfaFidoChallenge, TotpChallenge } from "./mfa";
13
+ import { MemorySessionStorage } from "./session/session_storage";
14
+
15
+ /** Options for logging in with OIDC token */
16
+ export interface OidcAuthOptions {
17
+ /** Optional token lifetimes */
18
+ lifetimes?: RatchetConfig;
19
+ /** Optional MFA receipt */
20
+ mfaReceipt?: MfaReceipt;
21
+ /** Optional storage to use for the returned session (defaults to {@link MemorySessionStorage}) */
22
+ storage?: SignerSessionStorage;
23
+ }
24
+
25
+ /**
26
+ * Client to use to send requests to CubeSigner services
27
+ * when authenticating using a CubeSigner session token.
28
+ */
29
+ export class CubeSignerClient extends CubeSignerApi {
30
+ /**
31
+ * Constructor.
32
+ * @param {SignerSessionManager} sessionMgr The session manager to use
33
+ * @param {string?} orgId Optional organization ID; if omitted, uses the org ID from the session manager.
34
+ */
35
+ constructor(sessionMgr: SignerSessionManager, orgId?: string) {
36
+ super(sessionMgr, orgId);
37
+ }
38
+
39
+ /**
40
+ * Returns a new instance of this class using the same session manager but targeting a different organization.
41
+ *
42
+ * @param {string} orgId The organization ID.
43
+ * @return {CubeSignerClient} A new instance of this class using the same session manager but targeting different organization.
44
+ */
45
+ withOrg(orgId?: string): CubeSignerClient {
46
+ return orgId ? new CubeSignerClient(this.sessionMgr, orgId) : this;
47
+ }
48
+
49
+ /**
50
+ * Loads an existing management session and creates a {@link CubeSignerClient} instance.
51
+ *
52
+ * @return {Promise<CubeSignerClient>} New CubeSigner instance
53
+ */
54
+ static async loadManagementSession(): Promise<CubeSignerClient> {
55
+ const mgr = await CognitoSessionManager.loadManagementSession();
56
+ // HACK: Ignore that sessionMgr may be a CognitoSessionManager and pretend that it
57
+ // is a SignerSessionManager; that's fine because the CubeSignerClient will
58
+ // almost always just call `await token()` on it, which works in both cases.
59
+ // NOTE: This will go away once `cs login` starts producing signer sessions.
60
+ return new CubeSignerClient(mgr as unknown as SignerSessionManager);
61
+ }
62
+
63
+ /**
64
+ * Create a new signing key.
65
+ * @param {KeyType} type The type of key to create.
66
+ * @param {string?} ownerId The owner of the key. Defaults to the session's user.
67
+ * @return {Key[]} The new keys.
68
+ */
69
+ async createKey(type: KeyType, ownerId?: string): Promise<Key> {
70
+ return (await this.createKeys(type, 1, ownerId))[0];
71
+ }
72
+
73
+ /**
74
+ * Create new signing keys.
75
+ * @param {KeyType} type The type of key to create.
76
+ * @param {number} count The number of keys to create.
77
+ * @param {string?} ownerId The owner of the keys. Defaults to the session's user.
78
+ * @return {Key[]} The new keys.
79
+ */
80
+ async createKeys(type: KeyType, count: number, ownerId?: string): Promise<Key[]> {
81
+ const keys = await this.keysCreate(type, count, ownerId);
82
+ return keys.map((k) => new Key(this, k));
83
+ }
84
+
85
+ /**
86
+ * Derive a key of the given type using the given derivation path and mnemonic.
87
+ * The owner of the derived key will be the owner of the mnemonic.
88
+ *
89
+ * @param {KeyType} type Type of key to derive from the mnemonic.
90
+ * @param {string} derivationPath Mnemonic derivation path used to generate new key.
91
+ * @param {string} mnemonicId materialId of mnemonic key used to derive the new key.
92
+ *
93
+ * @return {Key} newly derived key or undefined if it already exists.
94
+ */
95
+ async deriveKey(
96
+ type: KeyType,
97
+ derivationPath: string,
98
+ mnemonicId: string,
99
+ ): Promise<Key | undefined> {
100
+ return (await this.deriveKeys(type, [derivationPath], mnemonicId))[0];
101
+ }
102
+
103
+ /**
104
+ * Derive a set of keys of the given type using the given derivation paths and mnemonic.
105
+ *
106
+ * The owner of the derived keys will be the owner of the mnemonic.
107
+ *
108
+ * @param {KeyType} type Type of key to derive from the mnemonic.
109
+ * @param {string[]} derivationPaths Mnemonic derivation paths used to generate new key.
110
+ * @param {string} mnemonicId materialId of mnemonic key used to derive the new key.
111
+ *
112
+ * @return {Key[]} newly derived keys.
113
+ */
114
+ async deriveKeys(type: KeyType, derivationPaths: string[], mnemonicId: string): Promise<Key[]> {
115
+ const keys = await this.keysDerive(type, derivationPaths, mnemonicId);
116
+ return keys.map((k) => new Key(this, k));
117
+ }
118
+
119
+ /**
120
+ * Create a new {@link OidcClient} that will use a given OIDC token for auth.
121
+ * @param {string} oidcToken The authentication token to use
122
+ * @return {OidcClient} New OIDC client.
123
+ */
124
+ newOidcClient(oidcToken: string): OidcClient {
125
+ return new OidcClient(this.sessionMgr.env, this.orgId, oidcToken);
126
+ }
127
+
128
+ /**
129
+ * Authenticate an OIDC user and create a new session manager for them.
130
+ *
131
+ * @param {string} oidcToken The OIDC token
132
+ * @param {List<string>} scopes The scopes of the resulting session
133
+ * @param {OidcAuthOptions} options Options.
134
+ * @return {Promise<SignerSessionManager>} The signer session manager
135
+ */
136
+ async oidcAuth(
137
+ oidcToken: string,
138
+ scopes: Array<string>,
139
+ options?: OidcAuthOptions,
140
+ ): Promise<SignerSessionManager> {
141
+ const oidcClient = this.newOidcClient(oidcToken);
142
+ const resp = await oidcClient.sessionCreate(scopes, options?.lifetimes, options?.mfaReceipt);
143
+ return await SignerSessionManager.loadFromStorage(new MemorySessionStorage(resp.data()));
144
+ }
145
+
146
+ /**
147
+ * Create a new user in the organization and sends an invitation to that user.
148
+ *
149
+ * Same as {@link orgUserInvite}.
150
+ */
151
+ get createUser() {
152
+ return this.orgUserInvite.bind(this);
153
+ }
154
+
155
+ /**
156
+ * Create a new OIDC user.
157
+ *
158
+ * Same as {@link orgUserCreateOidc}.
159
+ */
160
+ get createOidcUser() {
161
+ return this.orgUserCreateOidc.bind(this);
162
+ }
163
+
164
+ /**
165
+ * Delete an existing OIDC user.
166
+ *
167
+ * Same as {@link orgUserDeleteOidc}.
168
+ */
169
+ get deleteOidcUser() {
170
+ return this.orgUserDeleteOidc.bind(this);
171
+ }
172
+
173
+ /**
174
+ * List users in the organization.
175
+ *
176
+ * Same as {@link orgUsersList}
177
+ */
178
+ get users() {
179
+ return this.orgUsersList.bind(this);
180
+ }
181
+
182
+ /**
183
+ * Obtain information about the current user.
184
+ *
185
+ * Same as {@link userGet}
186
+ */
187
+ get user() {
188
+ return this.userGet.bind(this);
189
+ }
190
+
191
+ /**
192
+ * Get information about a specific org.
193
+ *
194
+ * @param {string?} orgId The ID or name of the org
195
+ * @return {Promise<OrgInfo>} CubeSigner client for the requested org.
196
+ */
197
+ async org(orgId?: string): Promise<OrgInfo> {
198
+ return await this.withOrg(orgId).orgGet();
199
+ }
200
+
201
+ /**
202
+ * Obtain information about the current user.
203
+ *
204
+ * Same as {@link userGet}
205
+ */
206
+ get aboutMe() {
207
+ return this.userGet.bind(this);
208
+ }
209
+
210
+ /**
211
+ * Get a key by id.
212
+ *
213
+ * @param {string} keyId The id of the key to get.
214
+ * @return {Key} The key.
215
+ */
216
+ async getKey(keyId: string): Promise<Key> {
217
+ const keyInfo = await this.keyGet(keyId);
218
+ return new Key(this, keyInfo);
219
+ }
220
+
221
+ /**
222
+ * Get all keys in the org.
223
+ *
224
+ * @param {KeyType?} type Optional key type to filter list for.
225
+ * @param {PageOpts} page Pagination options. Defaults to fetching the entire result set.
226
+ * @return {Promise<Key[]>} The keys.
227
+ */
228
+ async orgKeys(type?: KeyType, page?: PageOpts): Promise<Key[]> {
229
+ const paginator = this.keysList(type, page);
230
+ const keys = await paginator.fetch();
231
+ return keys.map((k) => new Key(this, k));
232
+ }
233
+
234
+ /**
235
+ * Create a new role.
236
+ *
237
+ * @param {string?} name The name of the role.
238
+ * @return {Role} The new role.
239
+ */
240
+ async createRole(name?: string): Promise<Role> {
241
+ const roleId = await this.roleCreate(name);
242
+ const roleInfo = await this.roleGet(roleId);
243
+ return new Role(this, roleInfo);
244
+ }
245
+
246
+ /**
247
+ * Get a role by id or name.
248
+ *
249
+ * @param {string} roleId The id or name of the role to get.
250
+ * @return {Role} The role.
251
+ */
252
+ async getRole(roleId: string): Promise<Role> {
253
+ const roleInfo = await this.roleGet(roleId);
254
+ return new Role(this, roleInfo);
255
+ }
256
+
257
+ /**
258
+ * List all roles in the org.
259
+ *
260
+ * @param {PageOpts} page Pagination options. Defaults to fetching the entire result set.
261
+ * @return {Role[]} The roles.
262
+ */
263
+ async listRoles(page?: PageOpts): Promise<Role[]> {
264
+ const roles = await this.rolesList(page).fetch();
265
+ return roles.map((r) => new Role(this, r));
266
+ }
267
+
268
+ /**
269
+ * List all users in the org.
270
+ *
271
+ * Same as {@link orgUsersList}
272
+ */
273
+ get listUsers() {
274
+ return this.orgUsersList.bind(this);
275
+ }
276
+
277
+ /**
278
+ * Approve a pending MFA request.
279
+ *
280
+ * Same as {@link mfaApprove}
281
+ */
282
+ get approveMfaRequest() {
283
+ return this.mfaApprove.bind(this);
284
+ }
285
+
286
+ /**
287
+ * Approve a pending MFA request using TOTP.
288
+ *
289
+ * Same as {@link mfaApproveTotp}
290
+ */
291
+ get totpApprove() {
292
+ return this.mfaApproveTotp.bind(this);
293
+ }
294
+
295
+ /**
296
+ * Initiate approval of an existing MFA request using FIDO.
297
+ *
298
+ * Returns a {@link MfaFidoChallenge} that must be answered by calling
299
+ * {@link MfaFidoChallenge.answer} or {@link fidoApproveComplete}.
300
+ *
301
+ * Same as {@link mfaApproveFidoInit}
302
+ */
303
+ get fidoApproveStart() {
304
+ return this.mfaApproveFidoInit.bind(this);
305
+ }
306
+
307
+ /**
308
+ * Answer the MFA approval with FIDO challenge issued by {@link fidoApproveStart}.
309
+ *
310
+ * Same as {@link mfaApproveFidoComplete}
311
+ */
312
+ get fidoApproveComplete() {
313
+ return this.mfaApproveFidoComplete.bind(this);
314
+ }
315
+
316
+ /**
317
+ * Get a pending MFA request by its id.
318
+ *
319
+ * Same as {@link CubeSignerClient.getMfaInfo}
320
+ */
321
+ get getMfaInfo() {
322
+ return this.mfaGet.bind(this);
323
+ }
324
+
325
+ /**
326
+ * List pending MFA requests accessible to the current user.
327
+ *
328
+ * Same as {@link CubeSignerClient.mfaList}
329
+ */
330
+ get listMfaInfos() {
331
+ return this.mfaList.bind(this);
332
+ }
333
+
334
+ /**
335
+ * Obtain a proof of authentication.
336
+ *
337
+ * Same as {@link CubeSignerClient.identityProve}
338
+ */
339
+ get proveIdentity() {
340
+ return this.identityProve.bind(this);
341
+ }
342
+
343
+ /**
344
+ * Check if a given proof of OIDC authentication is valid.
345
+ *
346
+ * Same as {@link CubeSignerClient.identityVerify}
347
+ */
348
+ get verifyIdentity() {
349
+ return this.identityVerify.bind(this);
350
+ }
351
+
352
+ /**
353
+ * Creates a request to add a new FIDO device.
354
+ *
355
+ * Returns a {@link AddFidoChallenge} that must be answered by calling {@link AddFidoChallenge.answer}.
356
+ *
357
+ * MFA may be required.
358
+ *
359
+ * Same as {@link CubeSignerClient.userRegisterFidoInit}
360
+ */
361
+ get addFidoStart() {
362
+ return this.userRegisterFidoInit.bind(this);
363
+ }
364
+
365
+ /**
366
+ * Creates a request to change user's TOTP. Returns a {@link TotpChallenge}
367
+ * that must be answered by calling {@link TotpChallenge.answer} or
368
+ * {@link resetTotpComplete}.
369
+ *
370
+ * Same as {@link userResetTotpInit}
371
+ */
372
+ get resetTotpStart() {
373
+ return this.userResetTotpInit.bind(this);
374
+ }
375
+
376
+ /**
377
+ * Answer the TOTP challenge issued by {@link resetTotpStart}. If successful,
378
+ * user's TOTP configuration will be updated to that of the TOTP challenge.
379
+ *
380
+ * Same as {@link userResetTotpComplete}
381
+ */
382
+ get resetTotpComplete() {
383
+ return this.userResetTotpComplete.bind(this);
384
+ }
385
+
386
+ /**
387
+ * Verifies a given TOTP code against the current user's TOTP configuration.
388
+ * Throws an error if the verification fails.
389
+ *
390
+ * Same as {@link userVerifyTotp}
391
+ */
392
+ get verifyTotp() {
393
+ return this.userVerifyTotp.bind(this);
394
+ }
395
+
396
+ /**
397
+ * Sign a stake request.
398
+ *
399
+ * Same as {@link signStake}
400
+ */
401
+ get stake() {
402
+ return this.signStake.bind(this);
403
+ }
404
+
405
+ /**
406
+ * Sign an unstake request.
407
+ *
408
+ * Same as {@link signUnstake}
409
+ */
410
+ get unstake() {
411
+ return this.signUnstake.bind(this);
412
+ }
413
+ }
@@ -7,15 +7,10 @@ import {
7
7
  getBytes,
8
8
  toBeHex,
9
9
  } from "ethers";
10
- import {
11
- BlobSignRequest,
12
- EvmSignRequest,
13
- MfaRequestInfo,
14
- SignerSession,
15
- SignResponse,
16
- } from "../signer_session";
10
+ import { SignerSession } from "../signer_session";
11
+ import { CubeSignerResponse } from "../response";
12
+ import { BlobSignRequest, EvmSignRequest, MfaRequestInfo } from "../schema_types";
17
13
  import { KeyInfo } from "../key";
18
- import { CubeSigner } from "..";
19
14
 
20
15
  /** Options for the signer */
21
16
  interface SignerOptions {
@@ -31,8 +26,6 @@ interface SignerOptions {
31
26
  * updates. Default is 1000ms
32
27
  */
33
28
  mfaPollIntervalMs?: number;
34
- /** Optional management session. Used to check for MFA updates */
35
- managementSession?: CubeSigner;
36
29
  }
37
30
 
38
31
  /**
@@ -57,10 +50,8 @@ export class Signer extends ethers.AbstractSigner {
57
50
  /** The amount of time to wait between checks for MFA updates */
58
51
  readonly #mfaPollIntervalMs: number;
59
52
 
60
- /** Optional management session, used for MFA flows */
61
- readonly #managementSession?: CubeSigner;
62
-
63
- /** Create new Signer instance
53
+ /**
54
+ * Create new Signer instance
64
55
  * @param {KeyInfo | string} address The key or the eth address of the account to use.
65
56
  * @param {SignerSession} signerSession The underlying Signer session.
66
57
  * @param {SignerOptions} options The options to use for the Signer instance
@@ -76,7 +67,6 @@ export class Signer extends ethers.AbstractSigner {
76
67
  this.#signerSession = signerSession;
77
68
  this.#onMfaPoll = options?.onMfaPoll ?? ((/* _mfaInfo: MfaRequestInfo */) => {}); // eslint-disable-line @typescript-eslint/no-empty-function
78
69
  this.#mfaPollIntervalMs = options?.mfaPollIntervalMs ?? 1000;
79
- this.#managementSession = options?.managementSession;
80
70
  }
81
71
 
82
72
  /** Resolves to the signer address. */
@@ -94,11 +84,13 @@ export class Signer extends ethers.AbstractSigner {
94
84
  }
95
85
 
96
86
  /**
97
- * Signs a transaction. This populates the transaction type to `0x02` (EIP-1559) unless set. This method will block if the key requires MFA approval.
98
- * @param {ethers.TransactionRequest} tx The transaction to sign.
99
- * @return {Promise<string>} Hex-encoded RLP encoding of the transaction and its signature.
87
+ * Construct a signing request from a transaction. This populates the transaction
88
+ * type to `0x02` (EIP-1559) unless set.
89
+ *
90
+ * @param {ethers.TransactionRequest} tx The transaction
91
+ * @return {EvmSignRequest} The EVM sign request to be sent to CubeSigner
100
92
  */
101
- async signTransaction(tx: ethers.TransactionRequest): Promise<string> {
93
+ async evmSignRequestFromTx(tx: ethers.TransactionRequest): Promise<EvmSignRequest> {
102
94
  // get the chain id from the network or tx
103
95
  let chainId = tx.chainId;
104
96
  if (chainId === undefined) {
@@ -116,17 +108,26 @@ export class Signer extends ethers.AbstractSigner {
116
108
  JsonRpcApiProvider.prototype.getRpcTransaction.call(null, tx);
117
109
  rpcTx.type = toBeHex(tx.type ?? 0x02, 1); // we expect 0x0[0-2]
118
110
 
119
- const req = <EvmSignRequest>{
111
+ return <EvmSignRequest>{
120
112
  chain_id: Number(chainId),
121
113
  tx: rpcTx,
122
114
  };
115
+ }
123
116
 
117
+ /**
118
+ * Sign a transaction. This method will block if the key requires MFA approval.
119
+ * @param {ethers.TransactionRequest} tx The transaction to sign.
120
+ * @return {Promise<string>} Hex-encoded RLP encoding of the transaction and its signature.
121
+ */
122
+ async signTransaction(tx: ethers.TransactionRequest): Promise<string> {
123
+ const req = await this.evmSignRequestFromTx(tx);
124
124
  const res = await this.#signerSession.signEvm(this.#address, req);
125
125
  const data = await this.#handleMfa(res);
126
126
  return data.rlp_signed_tx;
127
127
  }
128
128
 
129
- /** Signs arbitrary messages. This uses ethers.js's [hashMessage](https://docs.ethers.org/v6/api/hashing/#hashMessage)
129
+ /**
130
+ * Signs arbitrary messages. This uses ethers.js's [hashMessage](https://docs.ethers.org/v6/api/hashing/#hashMessage)
130
131
  * to compute the EIP-191 digest and signs this digest using {@link Key#signBlob}.
131
132
  * The key (for this session) must have the `"AllowRawBlobSigning"` policy attached.
132
133
  * @param {string | Uint8Array} message The message to sign.
@@ -137,7 +138,8 @@ export class Signer extends ethers.AbstractSigner {
137
138
  return this.signBlob(digest);
138
139
  }
139
140
 
140
- /** Signs EIP-712 typed data. This uses ethers.js's
141
+ /**
142
+ * Signs EIP-712 typed data. This uses ethers.js's
141
143
  * [TypedDataEncoder.hash](https://docs.ethers.org/v6/api/hashing/#TypedDataEncoder_hash)
142
144
  * to compute the EIP-712 digest and signs this digest using {@link Key#signBlob}.
143
145
  * The key (for this session) must have the `"AllowRawBlobSigning"` policy attached.
@@ -155,7 +157,8 @@ export class Signer extends ethers.AbstractSigner {
155
157
  return this.signBlob(digest);
156
158
  }
157
159
 
158
- /** Sign arbitrary digest. This uses {@link Key#signBlob}.
160
+ /**
161
+ * Sign arbitrary digest. This uses {@link Key#signBlob}.
159
162
  * @param {string} digest The digest to sign.
160
163
  * @return {Promise<string>} The signature.
161
164
  */
@@ -174,21 +177,64 @@ export class Signer extends ethers.AbstractSigner {
174
177
 
175
178
  const res = await this.#signerSession.signBlob(this.#key.key_id, blobReq);
176
179
  const data = await this.#handleMfa(res);
177
- return data.signature;
180
+
181
+ const v_adj = (parseInt(data.signature.slice(128), 16) + 27).toString(16);
182
+ return data.signature.slice(0, 128) + v_adj;
183
+ }
184
+
185
+ /**
186
+ * Initialize the signing a message using MFA approvals. This method populates
187
+ * missing fields. If the signing does not require MFA, this method throws.
188
+ * @param {ethers.TransactionRequest} tx The transaction to send.
189
+ * @return {string} The MFA id associated with the signing request.
190
+ */
191
+ async sendTransactionMfaInit(tx: ethers.TransactionRequest): Promise<string> {
192
+ const popTx = await this.populateTransaction(tx);
193
+ const req = await this.evmSignRequestFromTx(popTx);
194
+ const res = await this.#signerSession.signEvm(this.#address, req);
195
+ return res.mfaId();
196
+ }
197
+
198
+ /**
199
+ * Send a transaction from an approved MFA request. The MFA request contains
200
+ * information about the approved signing request, which this method will
201
+ * execute.
202
+ * @param {MfaRequestInfo} mfaInfo The approved MFA request.
203
+ * @return {ethers.TransactionResponse} The result of submitting the transaction
204
+ */
205
+ async sendTransactionMfaApproved(mfaInfo: MfaRequestInfo): Promise<ethers.TransactionResponse> {
206
+ if (!mfaInfo.request.path.includes("/eth1/sign/")) {
207
+ throw new Error(`Expected EVM transaction signing request, got ${mfaInfo.request.path}`);
208
+ }
209
+ if (!mfaInfo.request.path.includes(this.#address)) {
210
+ throw new Error(
211
+ `Expected signing request for ${this.#address} but got ${mfaInfo.request.path}`,
212
+ );
213
+ }
214
+
215
+ const signedTx = await this.#signerSession.signEvm(
216
+ this.#address,
217
+ mfaInfo.request.body as EvmSignRequest,
218
+ {
219
+ mfaId: mfaInfo.id,
220
+ mfaOrgId: this.#signerSession.orgId,
221
+ mfaConf: mfaInfo.receipt!.confirmation,
222
+ },
223
+ );
224
+ return await this.provider!.broadcastTransaction(signedTx.data().rlp_signed_tx);
178
225
  }
179
226
 
180
227
  /**
181
228
  * If the sign request requires MFA, this method waits for approvals
182
- *
183
- * @param {SignResponse<U>} res The response of a sign request
229
+ * @param {CubeSignerResponse<U>} res The response of a sign request
184
230
  * @return {Promise<U>} The sign data after MFA approvals
185
231
  */
186
- async #handleMfa<U>(res: SignResponse<U>): Promise<U> {
232
+ async #handleMfa<U>(res: CubeSignerResponse<U>): Promise<U> {
187
233
  while (res.requiresMfa()) {
188
234
  await new Promise((resolve) => setTimeout(resolve, this.#mfaPollIntervalMs));
189
235
 
190
236
  const mfaId = res.mfaId();
191
- const mfaInfo = await this.#signerSession.getMfaInfo(this.#managementSession!, mfaId);
237
+ const mfaInfo = await this.#signerSession.getMfaInfo(mfaId);
192
238
  this.#onMfaPoll(mfaInfo);
193
239
  if (mfaInfo.receipt) {
194
240
  res = await res.signWithMfaApproval({