@injectivelabs/wallet-turnkey 1.16.6-alpha.0 → 1.16.6

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.
@@ -1,33 +1,37 @@
1
1
  import { TurnkeyMetadata, TurnkeyProvider } from '@injectivelabs/wallet-base';
2
2
  import { createAccount } from '@turnkey/viem';
3
3
  import { HttpRestClient } from '@injectivelabs/utils';
4
- import { Turnkey, TurnkeyIndexedDbClient } from '@turnkey/sdk-browser';
4
+ import { Turnkey, TurnkeyIframeClient } from '@turnkey/sdk-browser';
5
5
  export declare class TurnkeyWallet {
6
6
  private otpId?;
7
7
  protected turnkey?: Turnkey;
8
- userOrganizationId?: string;
8
+ organizationId: string;
9
9
  protected client: HttpRestClient;
10
10
  private metadata;
11
- protected indexedDbClient?: TurnkeyIndexedDbClient;
11
+ protected iframeClient?: TurnkeyIframeClient;
12
12
  private accountMap;
13
13
  setMetadata(metadata: Partial<TurnkeyMetadata>): void;
14
14
  constructor(metadata: TurnkeyMetadata);
15
15
  static getTurnkeyInstance(metadata: TurnkeyMetadata): Promise<{
16
16
  turnkey: Turnkey;
17
- indexedDbClient: TurnkeyIndexedDbClient;
17
+ iframeClient: TurnkeyIframeClient;
18
18
  }>;
19
19
  getTurnkey(): Promise<Turnkey>;
20
- getIndexedDbClient(): Promise<TurnkeyIndexedDbClient>;
20
+ getIframeClient(): Promise<TurnkeyIframeClient>;
21
21
  getSession(existingCredentialBundle?: string): Promise<{
22
- session: import("@turnkey/sdk-browser").Session | undefined;
22
+ session: import("@turnkey/sdk-types").Session | undefined;
23
23
  organizationId: string;
24
24
  }>;
25
25
  getAccounts(): Promise<string[]>;
26
- getOrCreateAndGetAccount(address: string): Promise<ReturnType<typeof createAccount>>;
26
+ getOrCreateAndGetAccount(address: string, organizationId: string): Promise<ReturnType<typeof createAccount>>;
27
+ injectAndRefresh(credentialBundle: string, options: {
28
+ expirationSeconds?: string;
29
+ organizationId?: string;
30
+ }): Promise<void>;
27
31
  initOTP(email: string): Promise<import("../types.js").TurnkeyOTPCredentialsResponse>;
28
32
  confirmOTP(otpCode: string): Promise<import("../types.js").TurnkeyConfirmEmailOTPResponse>;
29
33
  initOAuth(provider: TurnkeyProvider.Google | TurnkeyProvider.Apple): Promise<string>;
30
34
  confirmOAuth(provider: TurnkeyProvider.Google | TurnkeyProvider.Apple, oidcToken: string): Promise<string>;
31
35
  refreshSession(): Promise<string>;
32
- private initClient;
36
+ private initFrame;
33
37
  }
@@ -3,8 +3,8 @@ import { WalletAction, TurnkeyProvider, } from '@injectivelabs/wallet-base';
3
3
  import { createAccount } from '@turnkey/viem';
4
4
  import { HttpRestClient } from '@injectivelabs/utils';
5
5
  import { getInjectiveAddress } from '@injectivelabs/sdk-ts';
6
- import { SessionType, Turnkey, } from '@turnkey/sdk-browser';
7
- import { TURNKEY_OAUTH_PATH, TURNKEY_OTP_INIT_PATH, TURNKEY_OTP_VERIFY_PATH, } from '../consts.js';
6
+ import { SessionType, Turnkey } from '@turnkey/sdk-browser';
7
+ import { TURNKEY_OAUTH_PATH, TURNKEY_OTP_INIT_PATH, TURNKEY_OTP_VERIFY_PATH, DEFAULT_TURNKEY_REFRESH_SECONDS, } from '../consts.js';
8
8
  import { TurnkeyOtpWallet } from './otp.js';
9
9
  import { TurnkeyErrorCodes } from '../types.js';
10
10
  import { TurnkeyOauthWallet } from './oauth.js';
@@ -12,66 +12,76 @@ import { generateGoogleUrl } from '../../utils.js';
12
12
  export class TurnkeyWallet {
13
13
  otpId;
14
14
  turnkey;
15
- userOrganizationId;
15
+ organizationId;
16
16
  client;
17
17
  metadata;
18
- indexedDbClient;
18
+ iframeClient;
19
19
  accountMap = {};
20
20
  setMetadata(metadata) {
21
21
  this.metadata = { ...this.metadata, ...metadata };
22
22
  }
23
23
  constructor(metadata) {
24
24
  this.metadata = metadata;
25
+ this.organizationId = metadata.organizationId;
25
26
  this.client = new HttpRestClient(metadata.apiServerEndpoint);
26
27
  }
27
28
  static async getTurnkeyInstance(metadata) {
28
- const { turnkey, indexedDbClient } = await createTurnkeyClient(metadata);
29
+ const { turnkey, iframeClient } = await createTurnkeyIFrame(metadata);
29
30
  return {
30
31
  turnkey,
31
- indexedDbClient,
32
+ iframeClient,
32
33
  };
33
34
  }
34
35
  async getTurnkey() {
35
- if (!this.indexedDbClient) {
36
- await this.initClient();
36
+ if (!this.iframeClient) {
37
+ await this.initFrame();
37
38
  }
38
39
  if (!this.turnkey) {
39
40
  this.turnkey = new Turnkey(this.metadata);
40
41
  }
41
42
  return this.turnkey;
42
43
  }
43
- async getIndexedDbClient() {
44
- if (!this.indexedDbClient) {
45
- await this.initClient();
44
+ async getIframeClient() {
45
+ if (!this.iframeClient) {
46
+ await this.initFrame();
46
47
  }
47
- if (!this.indexedDbClient) {
48
- throw new WalletException(new Error('Indexed DB client not initialized'));
48
+ if (!this.iframeClient) {
49
+ throw new WalletException(new Error('Iframe client not initialized'));
49
50
  }
50
- return this.indexedDbClient;
51
+ return this.iframeClient;
51
52
  }
52
53
  async getSession(existingCredentialBundle) {
54
+ const { metadata } = this;
55
+ const iframeClient = await this.getIframeClient();
56
+ const turnkey = await this.getTurnkey();
57
+ const currentSession = await turnkey.getSession();
58
+ const organizationId = currentSession?.organizationId || metadata.defaultOrganizationId;
59
+ const credentialBundle = existingCredentialBundle || currentSession?.token;
60
+ if (!credentialBundle) {
61
+ return {
62
+ session: undefined,
63
+ organizationId,
64
+ };
65
+ }
53
66
  try {
54
- const { metadata } = this;
55
- const indexedDbClient = await this.getIndexedDbClient();
56
- const turnkey = await this.getTurnkey();
57
- const session = await turnkey.getSession();
58
- const organizationId = session?.organizationId || metadata.defaultOrganizationId;
59
- const credentialBundle = existingCredentialBundle || session?.token;
60
- if (!credentialBundle) {
61
- return {
62
- session: undefined,
63
- organizationId,
64
- };
65
- }
66
- const user = await indexedDbClient.getWhoami();
67
+ const loginResult = await iframeClient.injectCredentialBundle(credentialBundle);
68
+ // If there is no session, we want to force a refresh to enable to browser SDK to handle key storage and proper session management.
69
+ await iframeClient.refreshSession({
70
+ sessionType: SessionType.READ_WRITE,
71
+ targetPublicKey: iframeClient.iframePublicKey,
72
+ expirationSeconds: this.metadata.expirationSeconds,
73
+ });
74
+ const [session, user] = await Promise.all([
75
+ turnkey.getSession(),
76
+ iframeClient.getWhoami(),
77
+ ]);
67
78
  const actualOrganizationId = user?.organizationId || session?.organizationId || organizationId;
68
- if (!user) {
79
+ if (!loginResult) {
69
80
  return {
70
81
  session: undefined,
71
82
  organizationId: actualOrganizationId,
72
83
  };
73
84
  }
74
- this.userOrganizationId = actualOrganizationId;
75
85
  return {
76
86
  session,
77
87
  organizationId: actualOrganizationId,
@@ -82,17 +92,17 @@ export class TurnkeyWallet {
82
92
  }
83
93
  }
84
94
  async getAccounts() {
85
- const indexedDbClient = await this.getIndexedDbClient();
86
- if (!this.userOrganizationId) {
95
+ const iframeClient = await this.getIframeClient();
96
+ if (!this.organizationId) {
87
97
  return [];
88
98
  }
89
99
  try {
90
- const response = await indexedDbClient.getWallets({
91
- organizationId: this.userOrganizationId,
100
+ const response = await iframeClient.getWallets({
101
+ organizationId: this.organizationId,
92
102
  });
93
- const accounts = await Promise.allSettled(response.wallets.map((wallet) => indexedDbClient.getWalletAccounts({
103
+ const accounts = await Promise.allSettled(response.wallets.map((wallet) => iframeClient.getWalletAccounts({
94
104
  walletId: wallet.walletId,
95
- organizationId: this.userOrganizationId,
105
+ organizationId: this.organizationId,
96
106
  })));
97
107
  const filteredAccounts = accounts
98
108
  .filter((account) => account.status === 'fulfilled')
@@ -118,33 +128,61 @@ export class TurnkeyWallet {
118
128
  });
119
129
  }
120
130
  }
121
- async getOrCreateAndGetAccount(address) {
131
+ async getOrCreateAndGetAccount(address, organizationId) {
122
132
  const { accountMap } = this;
123
- const indexedDbClient = await this.getIndexedDbClient();
124
- const organizationId = this.userOrganizationId;
133
+ const iframeClient = await this.getIframeClient();
125
134
  if (accountMap[address] || accountMap[address.toLowerCase()]) {
126
135
  return accountMap[address] || accountMap[address.toLowerCase()];
127
136
  }
128
137
  if (!organizationId) {
129
138
  throw new WalletException(new Error('Organization ID is required'));
130
139
  }
131
- indexedDbClient.config.organizationId = organizationId;
140
+ iframeClient.config.organizationId = organizationId;
132
141
  if (!address) {
133
142
  throw new WalletException(new Error('Account address not found'));
134
143
  }
135
144
  const turnkeyAccount = await createAccount({
136
145
  organizationId,
137
146
  signWith: address,
138
- client: indexedDbClient,
147
+ client: iframeClient,
139
148
  });
140
149
  this.accountMap[address] = turnkeyAccount;
141
150
  return turnkeyAccount;
142
151
  }
152
+ async injectAndRefresh(credentialBundle, options) {
153
+ const expirationSeconds = options.expirationSeconds || DEFAULT_TURNKEY_REFRESH_SECONDS;
154
+ const iframeClient = await this.getIframeClient();
155
+ await iframeClient.injectCredentialBundle(credentialBundle);
156
+ await iframeClient.loginWithBundle({
157
+ bundle: credentialBundle,
158
+ expirationSeconds,
159
+ });
160
+ await iframeClient.refreshSession({
161
+ sessionType: SessionType.READ_WRITE,
162
+ targetPublicKey: iframeClient.iframePublicKey,
163
+ expirationSeconds,
164
+ });
165
+ const session = await this.turnkey?.getSession();
166
+ if (!session) {
167
+ throw new TurnkeyWalletSessionException(new Error('Session expired. Please login again.'));
168
+ }
169
+ this.organizationId = session.organizationId;
170
+ this.metadata.organizationId = session.organizationId;
171
+ // Refresh the session 2 minutes before it expires
172
+ setTimeout(() => {
173
+ iframeClient.refreshSession({
174
+ expirationSeconds: session?.expiry,
175
+ sessionType: SessionType.READ_WRITE,
176
+ targetPublicKey: iframeClient.iframePublicKey,
177
+ });
178
+ }, (parseInt(expirationSeconds) - 120) * 1000);
179
+ return;
180
+ }
143
181
  async initOTP(email) {
144
- const indexedDbClient = await this.getIndexedDbClient();
182
+ const iframeClient = await this.getIframeClient();
145
183
  const result = await TurnkeyOtpWallet.initEmailOTP({
146
184
  client: this.client,
147
- indexedDbClient,
185
+ iframeClient,
148
186
  email,
149
187
  otpInitPath: this.metadata.otpInitPath || TURNKEY_OTP_INIT_PATH,
150
188
  });
@@ -152,7 +190,7 @@ export class TurnkeyWallet {
152
190
  throw new WalletException(new Error('Failed to initialize OTP'));
153
191
  }
154
192
  if (result?.organizationId) {
155
- this.userOrganizationId = result.organizationId;
193
+ this.organizationId = result.organizationId;
156
194
  }
157
195
  if (result?.otpId) {
158
196
  this.otpId = result.otpId;
@@ -160,35 +198,30 @@ export class TurnkeyWallet {
160
198
  return result;
161
199
  }
162
200
  async confirmOTP(otpCode) {
163
- const indexedDbClient = await this.getIndexedDbClient();
164
- const targetPublicKey = await indexedDbClient.getPublicKey();
201
+ const iframeClient = await this.getIframeClient();
165
202
  if (!this.otpId) {
166
203
  throw new WalletException(new Error('OTP ID is required'));
167
204
  }
168
- if (!targetPublicKey) {
169
- throw new WalletException(new Error('Target public key not found'));
170
- }
171
- if (!this.userOrganizationId) {
172
- throw new WalletException(new Error('Organization ID is required'));
173
- }
174
205
  const result = await TurnkeyOtpWallet.confirmEmailOTP({
175
206
  otpCode,
207
+ iframeClient,
176
208
  client: this.client,
177
209
  emailOTPId: this.otpId,
178
- organizationId: this.userOrganizationId,
179
- targetPublicKey,
210
+ organizationId: this.organizationId,
180
211
  otpVerifyPath: this.metadata.otpVerifyPath || TURNKEY_OTP_VERIFY_PATH,
181
212
  });
182
- if (!result || !result.session) {
213
+ if (!result || !result.credentialBundle) {
183
214
  throw new WalletException(new Error('Failed to confirm OTP'));
184
215
  }
185
- await indexedDbClient.loginWithSession(result.session);
186
- this.userOrganizationId = result.organizationId;
216
+ await this.injectAndRefresh(result.credentialBundle, {
217
+ organizationId: result.organizationId,
218
+ expirationSeconds: this.metadata.expirationSeconds,
219
+ });
187
220
  return result;
188
221
  }
189
222
  async initOAuth(provider) {
190
- const indexedDbClient = await this.getIndexedDbClient();
191
- const nonce = await TurnkeyOauthWallet.generateOAuthNonce(indexedDbClient);
223
+ const iframeClient = await this.getIframeClient();
224
+ const nonce = await TurnkeyOauthWallet.generateOAuthNonce(iframeClient);
192
225
  if (provider === TurnkeyProvider.Apple) {
193
226
  // TODO: implement the ability to generate Apple OAuth URL
194
227
  return nonce;
@@ -203,10 +236,10 @@ export class TurnkeyWallet {
203
236
  });
204
237
  }
205
238
  async confirmOAuth(provider, oidcToken) {
206
- const indexedDbClient = await this.getIndexedDbClient();
239
+ const iframeClient = await this.getIframeClient();
207
240
  const oauthResult = await TurnkeyOauthWallet.oauthLogin({
208
241
  oidcToken,
209
- indexedDbClient,
242
+ iframeClient,
210
243
  client: this.client,
211
244
  providerName: provider.toString(),
212
245
  oauthLoginPath: this.metadata.oauthLoginPath || TURNKEY_OAUTH_PATH,
@@ -214,40 +247,53 @@ export class TurnkeyWallet {
214
247
  if (!oauthResult || !oauthResult.credentialBundle) {
215
248
  throw new WalletException(new Error('Unexpected OAuth result'));
216
249
  }
217
- await indexedDbClient.loginWithSession(oauthResult.credentialBundle);
218
- this.userOrganizationId = oauthResult.organizationId;
250
+ await this.injectAndRefresh(oauthResult.credentialBundle, {
251
+ organizationId: oauthResult.organizationId,
252
+ expirationSeconds: this.metadata.expirationSeconds,
253
+ });
219
254
  return oauthResult.credentialBundle;
220
255
  }
221
256
  async refreshSession() {
222
257
  const session = await this.getSession();
223
- const indexedDbClient = await this.getIndexedDbClient();
224
258
  if (session.session?.token) {
225
- await indexedDbClient.refreshSession({
226
- sessionType: SessionType.READ_WRITE,
227
- expirationSeconds: this.metadata.expirationSeconds,
259
+ await this.injectAndRefresh(session.session.token, {
260
+ expirationSeconds: this.metadata.expirationSeconds || DEFAULT_TURNKEY_REFRESH_SECONDS,
228
261
  });
229
- this.userOrganizationId = session.organizationId;
230
262
  return session.session.token;
231
263
  }
232
264
  throw new TurnkeyWalletSessionException(new Error('Session expired. Please login again.'));
233
265
  }
234
- async initClient() {
266
+ async initFrame() {
235
267
  const { metadata } = this;
236
- const { turnkey, indexedDbClient } = await createTurnkeyClient(metadata);
268
+ const { turnkey, iframeClient } = await createTurnkeyIFrame(metadata);
237
269
  this.turnkey = turnkey;
238
- this.indexedDbClient = indexedDbClient;
239
- return { turnkey, indexedDbClient };
270
+ this.iframeClient = iframeClient;
240
271
  }
241
272
  }
242
- async function createTurnkeyClient(metadata) {
273
+ async function createTurnkeyIFrame(metadata) {
243
274
  const turnkey = new Turnkey(metadata);
244
- const indexedDbClient = await turnkey.indexedDbClient();
245
- await indexedDbClient.init();
275
+ const turnkeyAuthIframeElementId = metadata.iframeElementId || 'turnkey-auth-iframe-element-id';
276
+ if (!metadata.iframeContainerId) {
277
+ throw new GeneralException(new Error('iframeContainerId is required'));
278
+ }
246
279
  if (!turnkey) {
247
280
  throw new GeneralException(new Error('Turnkey is not initialized'));
248
281
  }
282
+ const iframe = document.getElementById(metadata.iframeContainerId);
283
+ if (!iframe) {
284
+ throw new GeneralException(new Error('iframe is null'));
285
+ }
286
+ const existingIframeClient = document.getElementById(turnkeyAuthIframeElementId);
287
+ if (existingIframeClient) {
288
+ existingIframeClient.remove();
289
+ }
290
+ const iframeClient = await turnkey.iframeClient({
291
+ iframeContainer: iframe,
292
+ iframeElementId: turnkeyAuthIframeElementId,
293
+ iframeUrl: metadata?.iframeUrl || 'https://auth.turnkey.com',
294
+ });
249
295
  return {
250
296
  turnkey,
251
- indexedDbClient,
297
+ iframeClient,
252
298
  };
253
299
  }
@@ -17,7 +17,7 @@ export type TurnkeyOTPCredentialsResponse = {
17
17
  organizationId: string;
18
18
  };
19
19
  export type TurnkeyConfirmEmailOTPResponse = {
20
- session: string;
20
+ credentialBundle: string;
21
21
  organizationId: string;
22
22
  };
23
23
  export type TurnkeyOauthLoginResponse = {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@injectivelabs/wallet-turnkey",
3
3
  "description": "Turnkey wallet strategy for use with @injectivelabs/wallet-core.",
4
- "version": "1.16.6-alpha.0",
4
+ "version": "1.16.6",
5
5
  "sideEffects": false,
6
6
  "publishConfig": {
7
7
  "access": "public"
@@ -44,10 +44,10 @@
44
44
  }
45
45
  },
46
46
  "scripts": {
47
- "build": "pnpm build:esm && pnpm build:cjs && pnpm build:post",
47
+ "build": "yarn build:esm && yarn build:cjs && yarn build:post",
48
48
  "build:cjs": "tsc --build --force tsconfig.build.json",
49
49
  "build:esm": "tsc --build --force tsconfig.build.esm.json",
50
- "build:watch": "tsc --build -w tsconfig.build.json && tsc -w --build tsconfig.build.esm.json && pnpm build:post",
50
+ "build:watch": "tsc --build -w tsconfig.build.json && tsc -w --build tsconfig.build.esm.json && yarn build:post",
51
51
  "build:post": "shx cp ../../../etc/stub/package.json.stub dist/cjs/package.json && shx cp ../../../etc/stub/package.esm.json.stub dist/esm/package.json",
52
52
  "clean": "tsc --build tsconfig.build.json --clean && tsc --build tsconfig.build.esm.json --clean && shx rm -rf coverage *.log junit.xml dist && jest --clearCache && shx mkdir -p dist",
53
53
  "test": "jest",
@@ -59,14 +59,14 @@
59
59
  "start": "node dist/index.js"
60
60
  },
61
61
  "dependencies": {
62
- "@injectivelabs/exceptions": "1.16.6-alpha.0",
63
- "@injectivelabs/sdk-ts": "1.16.6-alpha.0",
64
- "@injectivelabs/ts-types": "1.16.6-alpha.0",
65
- "@injectivelabs/utils": "1.16.6-alpha.0",
66
- "@injectivelabs/wallet-base": "1.16.6-alpha.0",
62
+ "@injectivelabs/exceptions": "^1.16.6",
63
+ "@injectivelabs/sdk-ts": "^1.16.6",
64
+ "@injectivelabs/ts-types": "^1.16.6",
65
+ "@injectivelabs/utils": "^1.16.6",
66
+ "@injectivelabs/wallet-base": "^1.16.6",
67
67
  "@turnkey/sdk-browser": "5.2.3",
68
- "@turnkey/viem": "^0.9.10",
69
- "viem": "^2.33.2"
68
+ "@turnkey/viem": "^0.9.9",
69
+ "viem": "^2.31.3"
70
70
  },
71
71
  "devDependencies": {
72
72
  "jest": "^29.0.0",
@@ -77,5 +77,5 @@
77
77
  "tsconfig-paths": "^4.2.0",
78
78
  "typescript": "^5.0.0"
79
79
  },
80
- "gitHead": "b2bd329705e0848283332658754771d45c09d4f7"
80
+ "gitHead": "41794b09a1047a01dbb1d0e0a473a2f26305752d"
81
81
  }