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

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,37 +1,33 @@
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, TurnkeyIframeClient } from '@turnkey/sdk-browser';
4
+ import { Turnkey, TurnkeyIndexedDbClient } from '@turnkey/sdk-browser';
5
5
  export declare class TurnkeyWallet {
6
6
  private otpId?;
7
7
  protected turnkey?: Turnkey;
8
- organizationId: string;
8
+ userOrganizationId?: string;
9
9
  protected client: HttpRestClient;
10
10
  private metadata;
11
- protected iframeClient?: TurnkeyIframeClient;
11
+ protected indexedDbClient?: TurnkeyIndexedDbClient;
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
- iframeClient: TurnkeyIframeClient;
17
+ indexedDbClient: TurnkeyIndexedDbClient;
18
18
  }>;
19
19
  getTurnkey(): Promise<Turnkey>;
20
- getIframeClient(): Promise<TurnkeyIframeClient>;
20
+ getIndexedDbClient(): Promise<TurnkeyIndexedDbClient>;
21
21
  getSession(existingCredentialBundle?: string): Promise<{
22
- session: import("@turnkey/sdk-types").Session | undefined;
22
+ session: import("@turnkey/sdk-browser").Session | undefined;
23
23
  organizationId: string;
24
24
  }>;
25
25
  getAccounts(): Promise<string[]>;
26
- getOrCreateAndGetAccount(address: string, organizationId: string): Promise<ReturnType<typeof createAccount>>;
27
- injectAndRefresh(credentialBundle: string, options: {
28
- expirationSeconds?: string;
29
- organizationId?: string;
30
- }): Promise<void>;
26
+ getOrCreateAndGetAccount(address: string): Promise<ReturnType<typeof createAccount>>;
31
27
  initOTP(email: string): Promise<import("../types.js").TurnkeyOTPCredentialsResponse>;
32
28
  confirmOTP(otpCode: string): Promise<import("../types.js").TurnkeyConfirmEmailOTPResponse>;
33
29
  initOAuth(provider: TurnkeyProvider.Google | TurnkeyProvider.Apple): Promise<string>;
34
30
  confirmOAuth(provider: TurnkeyProvider.Google | TurnkeyProvider.Apple, oidcToken: string): Promise<string>;
35
31
  refreshSession(): Promise<string>;
36
- private initFrame;
32
+ private initClient;
37
33
  }
@@ -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, DEFAULT_TURNKEY_REFRESH_SECONDS, } from '../consts.js';
6
+ import { SessionType, Turnkey, } from '@turnkey/sdk-browser';
7
+ import { TURNKEY_OAUTH_PATH, TURNKEY_OTP_INIT_PATH, TURNKEY_OTP_VERIFY_PATH, } 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,76 +12,66 @@ import { generateGoogleUrl } from '../../utils.js';
12
12
  export class TurnkeyWallet {
13
13
  otpId;
14
14
  turnkey;
15
- organizationId;
15
+ userOrganizationId;
16
16
  client;
17
17
  metadata;
18
- iframeClient;
18
+ indexedDbClient;
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;
26
25
  this.client = new HttpRestClient(metadata.apiServerEndpoint);
27
26
  }
28
27
  static async getTurnkeyInstance(metadata) {
29
- const { turnkey, iframeClient } = await createTurnkeyIFrame(metadata);
28
+ const { turnkey, indexedDbClient } = await createTurnkeyClient(metadata);
30
29
  return {
31
30
  turnkey,
32
- iframeClient,
31
+ indexedDbClient,
33
32
  };
34
33
  }
35
34
  async getTurnkey() {
36
- if (!this.iframeClient) {
37
- await this.initFrame();
35
+ if (!this.indexedDbClient) {
36
+ await this.initClient();
38
37
  }
39
38
  if (!this.turnkey) {
40
39
  this.turnkey = new Turnkey(this.metadata);
41
40
  }
42
41
  return this.turnkey;
43
42
  }
44
- async getIframeClient() {
45
- if (!this.iframeClient) {
46
- await this.initFrame();
43
+ async getIndexedDbClient() {
44
+ if (!this.indexedDbClient) {
45
+ await this.initClient();
47
46
  }
48
- if (!this.iframeClient) {
49
- throw new WalletException(new Error('Iframe client not initialized'));
47
+ if (!this.indexedDbClient) {
48
+ throw new WalletException(new Error('Indexed DB client not initialized'));
50
49
  }
51
- return this.iframeClient;
50
+ return this.indexedDbClient;
52
51
  }
53
52
  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
- }
66
53
  try {
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
- ]);
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();
78
67
  const actualOrganizationId = user?.organizationId || session?.organizationId || organizationId;
79
- if (!loginResult) {
68
+ if (!user) {
80
69
  return {
81
70
  session: undefined,
82
71
  organizationId: actualOrganizationId,
83
72
  };
84
73
  }
74
+ this.userOrganizationId = actualOrganizationId;
85
75
  return {
86
76
  session,
87
77
  organizationId: actualOrganizationId,
@@ -92,17 +82,17 @@ export class TurnkeyWallet {
92
82
  }
93
83
  }
94
84
  async getAccounts() {
95
- const iframeClient = await this.getIframeClient();
96
- if (!this.organizationId) {
85
+ const indexedDbClient = await this.getIndexedDbClient();
86
+ if (!this.userOrganizationId) {
97
87
  return [];
98
88
  }
99
89
  try {
100
- const response = await iframeClient.getWallets({
101
- organizationId: this.organizationId,
90
+ const response = await indexedDbClient.getWallets({
91
+ organizationId: this.userOrganizationId,
102
92
  });
103
- const accounts = await Promise.allSettled(response.wallets.map((wallet) => iframeClient.getWalletAccounts({
93
+ const accounts = await Promise.allSettled(response.wallets.map((wallet) => indexedDbClient.getWalletAccounts({
104
94
  walletId: wallet.walletId,
105
- organizationId: this.organizationId,
95
+ organizationId: this.userOrganizationId,
106
96
  })));
107
97
  const filteredAccounts = accounts
108
98
  .filter((account) => account.status === 'fulfilled')
@@ -128,61 +118,33 @@ export class TurnkeyWallet {
128
118
  });
129
119
  }
130
120
  }
131
- async getOrCreateAndGetAccount(address, organizationId) {
121
+ async getOrCreateAndGetAccount(address) {
132
122
  const { accountMap } = this;
133
- const iframeClient = await this.getIframeClient();
123
+ const indexedDbClient = await this.getIndexedDbClient();
124
+ const organizationId = this.userOrganizationId;
134
125
  if (accountMap[address] || accountMap[address.toLowerCase()]) {
135
126
  return accountMap[address] || accountMap[address.toLowerCase()];
136
127
  }
137
128
  if (!organizationId) {
138
129
  throw new WalletException(new Error('Organization ID is required'));
139
130
  }
140
- iframeClient.config.organizationId = organizationId;
131
+ indexedDbClient.config.organizationId = organizationId;
141
132
  if (!address) {
142
133
  throw new WalletException(new Error('Account address not found'));
143
134
  }
144
135
  const turnkeyAccount = await createAccount({
145
136
  organizationId,
146
137
  signWith: address,
147
- client: iframeClient,
138
+ client: indexedDbClient,
148
139
  });
149
140
  this.accountMap[address] = turnkeyAccount;
150
141
  return turnkeyAccount;
151
142
  }
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
- }
181
143
  async initOTP(email) {
182
- const iframeClient = await this.getIframeClient();
144
+ const indexedDbClient = await this.getIndexedDbClient();
183
145
  const result = await TurnkeyOtpWallet.initEmailOTP({
184
146
  client: this.client,
185
- iframeClient,
147
+ indexedDbClient,
186
148
  email,
187
149
  otpInitPath: this.metadata.otpInitPath || TURNKEY_OTP_INIT_PATH,
188
150
  });
@@ -190,7 +152,7 @@ export class TurnkeyWallet {
190
152
  throw new WalletException(new Error('Failed to initialize OTP'));
191
153
  }
192
154
  if (result?.organizationId) {
193
- this.organizationId = result.organizationId;
155
+ this.userOrganizationId = result.organizationId;
194
156
  }
195
157
  if (result?.otpId) {
196
158
  this.otpId = result.otpId;
@@ -198,30 +160,35 @@ export class TurnkeyWallet {
198
160
  return result;
199
161
  }
200
162
  async confirmOTP(otpCode) {
201
- const iframeClient = await this.getIframeClient();
163
+ const indexedDbClient = await this.getIndexedDbClient();
164
+ const targetPublicKey = await indexedDbClient.getPublicKey();
202
165
  if (!this.otpId) {
203
166
  throw new WalletException(new Error('OTP ID is required'));
204
167
  }
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
+ }
205
174
  const result = await TurnkeyOtpWallet.confirmEmailOTP({
206
175
  otpCode,
207
- iframeClient,
208
176
  client: this.client,
209
177
  emailOTPId: this.otpId,
210
- organizationId: this.organizationId,
178
+ organizationId: this.userOrganizationId,
179
+ targetPublicKey,
211
180
  otpVerifyPath: this.metadata.otpVerifyPath || TURNKEY_OTP_VERIFY_PATH,
212
181
  });
213
- if (!result || !result.credentialBundle) {
182
+ if (!result || !result.session) {
214
183
  throw new WalletException(new Error('Failed to confirm OTP'));
215
184
  }
216
- await this.injectAndRefresh(result.credentialBundle, {
217
- organizationId: result.organizationId,
218
- expirationSeconds: this.metadata.expirationSeconds,
219
- });
185
+ await indexedDbClient.loginWithSession(result.session);
186
+ this.userOrganizationId = result.organizationId;
220
187
  return result;
221
188
  }
222
189
  async initOAuth(provider) {
223
- const iframeClient = await this.getIframeClient();
224
- const nonce = await TurnkeyOauthWallet.generateOAuthNonce(iframeClient);
190
+ const indexedDbClient = await this.getIndexedDbClient();
191
+ const nonce = await TurnkeyOauthWallet.generateOAuthNonce(indexedDbClient);
225
192
  if (provider === TurnkeyProvider.Apple) {
226
193
  // TODO: implement the ability to generate Apple OAuth URL
227
194
  return nonce;
@@ -236,10 +203,10 @@ export class TurnkeyWallet {
236
203
  });
237
204
  }
238
205
  async confirmOAuth(provider, oidcToken) {
239
- const iframeClient = await this.getIframeClient();
206
+ const indexedDbClient = await this.getIndexedDbClient();
240
207
  const oauthResult = await TurnkeyOauthWallet.oauthLogin({
241
208
  oidcToken,
242
- iframeClient,
209
+ indexedDbClient,
243
210
  client: this.client,
244
211
  providerName: provider.toString(),
245
212
  oauthLoginPath: this.metadata.oauthLoginPath || TURNKEY_OAUTH_PATH,
@@ -247,53 +214,40 @@ export class TurnkeyWallet {
247
214
  if (!oauthResult || !oauthResult.credentialBundle) {
248
215
  throw new WalletException(new Error('Unexpected OAuth result'));
249
216
  }
250
- await this.injectAndRefresh(oauthResult.credentialBundle, {
251
- organizationId: oauthResult.organizationId,
252
- expirationSeconds: this.metadata.expirationSeconds,
253
- });
217
+ await indexedDbClient.loginWithSession(oauthResult.credentialBundle);
218
+ this.userOrganizationId = oauthResult.organizationId;
254
219
  return oauthResult.credentialBundle;
255
220
  }
256
221
  async refreshSession() {
257
222
  const session = await this.getSession();
223
+ const indexedDbClient = await this.getIndexedDbClient();
258
224
  if (session.session?.token) {
259
- await this.injectAndRefresh(session.session.token, {
260
- expirationSeconds: this.metadata.expirationSeconds || DEFAULT_TURNKEY_REFRESH_SECONDS,
225
+ await indexedDbClient.refreshSession({
226
+ sessionType: SessionType.READ_WRITE,
227
+ expirationSeconds: this.metadata.expirationSeconds,
261
228
  });
229
+ this.userOrganizationId = session.organizationId;
262
230
  return session.session.token;
263
231
  }
264
232
  throw new TurnkeyWalletSessionException(new Error('Session expired. Please login again.'));
265
233
  }
266
- async initFrame() {
234
+ async initClient() {
267
235
  const { metadata } = this;
268
- const { turnkey, iframeClient } = await createTurnkeyIFrame(metadata);
236
+ const { turnkey, indexedDbClient } = await createTurnkeyClient(metadata);
269
237
  this.turnkey = turnkey;
270
- this.iframeClient = iframeClient;
238
+ this.indexedDbClient = indexedDbClient;
239
+ return { turnkey, indexedDbClient };
271
240
  }
272
241
  }
273
- async function createTurnkeyIFrame(metadata) {
242
+ async function createTurnkeyClient(metadata) {
274
243
  const turnkey = new Turnkey(metadata);
275
- const turnkeyAuthIframeElementId = metadata.iframeElementId || 'turnkey-auth-iframe-element-id';
276
- if (!metadata.iframeContainerId) {
277
- throw new GeneralException(new Error('iframeContainerId is required'));
278
- }
244
+ const indexedDbClient = await turnkey.indexedDbClient();
245
+ await indexedDbClient.init();
279
246
  if (!turnkey) {
280
247
  throw new GeneralException(new Error('Turnkey is not initialized'));
281
248
  }
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
- });
295
249
  return {
296
250
  turnkey,
297
- iframeClient,
251
+ indexedDbClient,
298
252
  };
299
253
  }
@@ -17,7 +17,7 @@ export type TurnkeyOTPCredentialsResponse = {
17
17
  organizationId: string;
18
18
  };
19
19
  export type TurnkeyConfirmEmailOTPResponse = {
20
- credentialBundle: string;
20
+ session: 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.5",
4
+ "version": "1.16.6-alpha.0",
5
5
  "sideEffects": false,
6
6
  "publishConfig": {
7
7
  "access": "public"
@@ -44,10 +44,10 @@
44
44
  }
45
45
  },
46
46
  "scripts": {
47
- "build": "yarn build:esm && yarn build:cjs && yarn build:post",
47
+ "build": "pnpm build:esm && pnpm build:cjs && pnpm 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 && yarn build:post",
50
+ "build:watch": "tsc --build -w tsconfig.build.json && tsc -w --build tsconfig.build.esm.json && pnpm 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.5",
63
- "@injectivelabs/sdk-ts": "^1.16.5",
64
- "@injectivelabs/ts-types": "^1.16.5",
65
- "@injectivelabs/utils": "^1.16.5",
66
- "@injectivelabs/wallet-base": "^1.16.5",
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",
67
67
  "@turnkey/sdk-browser": "5.2.3",
68
- "@turnkey/viem": "^0.9.9",
69
- "viem": "^2.31.3"
68
+ "@turnkey/viem": "^0.9.10",
69
+ "viem": "^2.33.2"
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": "59378cbaf7f09e97bd0c8c157fd03abdd0ea35d5"
80
+ "gitHead": "b2bd329705e0848283332658754771d45c09d4f7"
81
81
  }