@injectivelabs/wallet-turnkey 1.15.10
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.
- package/README.md +120 -0
- package/dist/cjs/index.d.ts +4 -0
- package/dist/cjs/index.js +23 -0
- package/dist/cjs/package.json +3 -0
- package/dist/cjs/strategy/consts.d.ts +4 -0
- package/dist/cjs/strategy/consts.js +7 -0
- package/dist/cjs/strategy/strategy/base.d.ts +47 -0
- package/dist/cjs/strategy/strategy/base.js +327 -0
- package/dist/cjs/strategy/strategy/oauth.d.ts +5 -0
- package/dist/cjs/strategy/strategy/oauth.js +14 -0
- package/dist/cjs/strategy/strategy/otp.d.ts +5 -0
- package/dist/cjs/strategy/strategy/otp.js +14 -0
- package/dist/cjs/strategy/turnkey/oauth.d.ts +16 -0
- package/dist/cjs/strategy/turnkey/oauth.js +51 -0
- package/dist/cjs/strategy/turnkey/otp.d.ts +21 -0
- package/dist/cjs/strategy/turnkey/otp.js +64 -0
- package/dist/cjs/strategy/turnkey/turnkey.d.ts +43 -0
- package/dist/cjs/strategy/turnkey/turnkey.js +196 -0
- package/dist/cjs/strategy/types.d.ts +26 -0
- package/dist/cjs/strategy/types.js +7 -0
- package/dist/cjs/utils.d.ts +7 -0
- package/dist/cjs/utils.js +10 -0
- package/dist/esm/index.d.ts +4 -0
- package/dist/esm/index.js +4 -0
- package/dist/esm/package.json +3 -0
- package/dist/esm/strategy/consts.d.ts +4 -0
- package/dist/esm/strategy/consts.js +4 -0
- package/dist/esm/strategy/strategy/base.d.ts +47 -0
- package/dist/esm/strategy/strategy/base.js +323 -0
- package/dist/esm/strategy/strategy/oauth.d.ts +5 -0
- package/dist/esm/strategy/strategy/oauth.js +10 -0
- package/dist/esm/strategy/strategy/otp.d.ts +5 -0
- package/dist/esm/strategy/strategy/otp.js +10 -0
- package/dist/esm/strategy/turnkey/oauth.d.ts +16 -0
- package/dist/esm/strategy/turnkey/oauth.js +47 -0
- package/dist/esm/strategy/turnkey/otp.d.ts +21 -0
- package/dist/esm/strategy/turnkey/otp.js +60 -0
- package/dist/esm/strategy/turnkey/turnkey.d.ts +43 -0
- package/dist/esm/strategy/turnkey/turnkey.js +192 -0
- package/dist/esm/strategy/types.d.ts +26 -0
- package/dist/esm/strategy/types.js +4 -0
- package/dist/esm/utils.d.ts +7 -0
- package/dist/esm/utils.js +7 -0
- package/package.json +78 -0
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import { Turnkey, SessionType } from '@turnkey/sdk-browser';
|
|
2
|
+
import { ErrorType, WalletException, GeneralException, UnspecifiedErrorCode, } from '@injectivelabs/exceptions';
|
|
3
|
+
import { WalletAction } from '@injectivelabs/wallet-base';
|
|
4
|
+
import { getInjectiveAddress } from '@injectivelabs/sdk-ts';
|
|
5
|
+
import { HttpRestClient } from '@injectivelabs/utils';
|
|
6
|
+
import { createAccount } from '@turnkey/viem';
|
|
7
|
+
import { TurnkeyErrorCodes } from '../types.js';
|
|
8
|
+
export class TurnkeyWallet {
|
|
9
|
+
iframeClient;
|
|
10
|
+
turnkey;
|
|
11
|
+
client;
|
|
12
|
+
metadata;
|
|
13
|
+
accountMap = {};
|
|
14
|
+
setMetadata(metadata) {
|
|
15
|
+
this.metadata = { ...this.metadata, ...metadata };
|
|
16
|
+
}
|
|
17
|
+
constructor(metadata) {
|
|
18
|
+
this.metadata = metadata;
|
|
19
|
+
this.client = new HttpRestClient(metadata.apiServerEndpoint);
|
|
20
|
+
}
|
|
21
|
+
static async getTurnkeyInstance(metadata) {
|
|
22
|
+
const { turnkey, iframeClient } = await createTurnkeyIFrame(metadata);
|
|
23
|
+
return {
|
|
24
|
+
turnkey,
|
|
25
|
+
iframeClient,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
async getTurnkey() {
|
|
29
|
+
if (!this.iframeClient) {
|
|
30
|
+
await this.initFrame();
|
|
31
|
+
}
|
|
32
|
+
if (!this.turnkey) {
|
|
33
|
+
this.turnkey = new Turnkey(this.metadata);
|
|
34
|
+
}
|
|
35
|
+
return this.turnkey;
|
|
36
|
+
}
|
|
37
|
+
async getIframeClient() {
|
|
38
|
+
if (!this.iframeClient) {
|
|
39
|
+
await this.initFrame();
|
|
40
|
+
}
|
|
41
|
+
if (!this.iframeClient) {
|
|
42
|
+
throw new WalletException(new Error('Iframe client not initialized'));
|
|
43
|
+
}
|
|
44
|
+
return this.iframeClient;
|
|
45
|
+
}
|
|
46
|
+
async getSession(existingCredentialBundle) {
|
|
47
|
+
const { metadata } = this;
|
|
48
|
+
const iframeClient = await this.getIframeClient();
|
|
49
|
+
const turnkey = await this.getTurnkey();
|
|
50
|
+
const currentSession = await turnkey.getSession();
|
|
51
|
+
const organizationId = currentSession?.organizationId || metadata.defaultOrganizationId;
|
|
52
|
+
const credentialBundle = existingCredentialBundle || currentSession?.token;
|
|
53
|
+
if (!credentialBundle) {
|
|
54
|
+
return {
|
|
55
|
+
session: undefined,
|
|
56
|
+
organizationId,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
try {
|
|
60
|
+
const loginResult = await iframeClient.injectCredentialBundle(credentialBundle);
|
|
61
|
+
// If there is no session, we want to force a refresh to enable to browser SDK to handle key storage and proper session management.
|
|
62
|
+
await iframeClient.refreshSession({
|
|
63
|
+
sessionType: SessionType.READ_WRITE,
|
|
64
|
+
targetPublicKey: iframeClient.iframePublicKey,
|
|
65
|
+
expirationSeconds: '900',
|
|
66
|
+
});
|
|
67
|
+
const [session, user] = await Promise.all([
|
|
68
|
+
turnkey.getSession(),
|
|
69
|
+
iframeClient.getWhoami(),
|
|
70
|
+
]);
|
|
71
|
+
const actualOrganizationId = user?.organizationId || session?.organizationId || organizationId;
|
|
72
|
+
if (!loginResult) {
|
|
73
|
+
return {
|
|
74
|
+
session: undefined,
|
|
75
|
+
organizationId: actualOrganizationId,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
return {
|
|
79
|
+
session,
|
|
80
|
+
organizationId: actualOrganizationId,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
catch (e) {
|
|
84
|
+
throw new WalletException(new Error(e.message), {
|
|
85
|
+
code: UnspecifiedErrorCode,
|
|
86
|
+
type: ErrorType.WalletError,
|
|
87
|
+
contextModule: 'turnkey-wallet-get-session',
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
async getAccounts() {
|
|
92
|
+
const iframeClient = await this.getIframeClient();
|
|
93
|
+
if (!this.metadata.organizationId) {
|
|
94
|
+
return [];
|
|
95
|
+
}
|
|
96
|
+
try {
|
|
97
|
+
const response = await iframeClient.getWallets({
|
|
98
|
+
organizationId: this.metadata.organizationId,
|
|
99
|
+
});
|
|
100
|
+
const accounts = await Promise.allSettled(response.wallets.map((wallet) => iframeClient.getWalletAccounts({
|
|
101
|
+
walletId: wallet.walletId,
|
|
102
|
+
organizationId: this.metadata.organizationId,
|
|
103
|
+
})));
|
|
104
|
+
const filteredAccounts = accounts
|
|
105
|
+
.filter((account) => account.status === 'fulfilled')
|
|
106
|
+
.flatMap((result) => result.value?.accounts)
|
|
107
|
+
.filter((wa) => !!wa &&
|
|
108
|
+
wa.addressFormat === 'ADDRESS_FORMAT_ETHEREUM' &&
|
|
109
|
+
!!wa.address);
|
|
110
|
+
return filteredAccounts.map((account) => getInjectiveAddress(account.address));
|
|
111
|
+
}
|
|
112
|
+
catch (e) {
|
|
113
|
+
if (e.code === TurnkeyErrorCodes.UserLoggedOut) {
|
|
114
|
+
throw new WalletException(new Error('User is not logged in'), {
|
|
115
|
+
code: UnspecifiedErrorCode,
|
|
116
|
+
type: ErrorType.WalletError,
|
|
117
|
+
contextModule: WalletAction.GetAccounts,
|
|
118
|
+
contextCode: TurnkeyErrorCodes.UserLoggedOut,
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
throw new WalletException(new Error(e.message), {
|
|
122
|
+
code: UnspecifiedErrorCode,
|
|
123
|
+
type: ErrorType.WalletError,
|
|
124
|
+
contextModule: 'turnkey-wallet-get-accounts',
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
async getOrCreateAndGetAccount(address, organizationId) {
|
|
129
|
+
const { accountMap } = this;
|
|
130
|
+
const iframeClient = await this.getIframeClient();
|
|
131
|
+
if (accountMap[address] || accountMap[address.toLowerCase()]) {
|
|
132
|
+
return accountMap[address] || accountMap[address.toLowerCase()];
|
|
133
|
+
}
|
|
134
|
+
if (!organizationId) {
|
|
135
|
+
throw new WalletException(new Error('Organization ID is required'));
|
|
136
|
+
}
|
|
137
|
+
iframeClient.config.organizationId = organizationId;
|
|
138
|
+
if (!address) {
|
|
139
|
+
throw new WalletException(new Error('Account address not found'));
|
|
140
|
+
}
|
|
141
|
+
const turnkeyAccount = await createAccount({
|
|
142
|
+
organizationId,
|
|
143
|
+
signWith: address,
|
|
144
|
+
client: iframeClient,
|
|
145
|
+
});
|
|
146
|
+
this.accountMap[address] = turnkeyAccount;
|
|
147
|
+
return turnkeyAccount;
|
|
148
|
+
}
|
|
149
|
+
async injectAndRefresh(credentialBundle) {
|
|
150
|
+
const iframeClient = await this.getIframeClient();
|
|
151
|
+
await iframeClient.injectCredentialBundle(credentialBundle);
|
|
152
|
+
await iframeClient.refreshSession({
|
|
153
|
+
sessionType: SessionType.READ_WRITE,
|
|
154
|
+
targetPublicKey: iframeClient.iframePublicKey,
|
|
155
|
+
expirationSeconds: '900',
|
|
156
|
+
});
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
async initFrame() {
|
|
160
|
+
const { metadata } = this;
|
|
161
|
+
const { turnkey, iframeClient } = await createTurnkeyIFrame(metadata);
|
|
162
|
+
this.turnkey = turnkey;
|
|
163
|
+
this.iframeClient = iframeClient;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
async function createTurnkeyIFrame(metadata) {
|
|
167
|
+
const turnkey = new Turnkey(metadata);
|
|
168
|
+
const turnkeyAuthIframeElementId = metadata.iframeElementId || 'turnkey-auth-iframe-element-id';
|
|
169
|
+
if (!metadata.iframeContainerId) {
|
|
170
|
+
throw new GeneralException(new Error('iframeContainerId is required'));
|
|
171
|
+
}
|
|
172
|
+
if (!turnkey) {
|
|
173
|
+
throw new GeneralException(new Error('Turnkey is not initialized'));
|
|
174
|
+
}
|
|
175
|
+
const iframe = document.getElementById(metadata.iframeContainerId);
|
|
176
|
+
if (!iframe) {
|
|
177
|
+
throw new GeneralException(new Error('iframe is null'));
|
|
178
|
+
}
|
|
179
|
+
const existingIframeClient = document.getElementById(turnkeyAuthIframeElementId);
|
|
180
|
+
if (existingIframeClient) {
|
|
181
|
+
existingIframeClient.remove();
|
|
182
|
+
}
|
|
183
|
+
const iframeClient = await turnkey.iframeClient({
|
|
184
|
+
iframeContainer: iframe,
|
|
185
|
+
iframeElementId: turnkeyAuthIframeElementId,
|
|
186
|
+
iframeUrl: metadata?.iframeUrl || 'https://auth.turnkey.com',
|
|
187
|
+
});
|
|
188
|
+
return {
|
|
189
|
+
turnkey,
|
|
190
|
+
iframeClient,
|
|
191
|
+
};
|
|
192
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export declare enum TurnkeyErrorCodes {
|
|
2
|
+
UserLoggedOut = 7
|
|
3
|
+
}
|
|
4
|
+
export type TurnkeyOAuthArgs = {
|
|
5
|
+
provider: 'google';
|
|
6
|
+
oidcToken: string;
|
|
7
|
+
oauthLoginEndpoint: string;
|
|
8
|
+
};
|
|
9
|
+
export type TurnkeyEmailArgs = {
|
|
10
|
+
provider: 'email';
|
|
11
|
+
email: string;
|
|
12
|
+
initEmailOTPEndpoint: string;
|
|
13
|
+
};
|
|
14
|
+
export type TurnkeyEnableArgs = TurnkeyOAuthArgs | TurnkeyEmailArgs;
|
|
15
|
+
export type TurnkeyOTPCredentialsResponse = {
|
|
16
|
+
otpId: string;
|
|
17
|
+
organizationId: string;
|
|
18
|
+
};
|
|
19
|
+
export type TurnkeyConfirmEmailOTPResponse = {
|
|
20
|
+
credentialBundle: string;
|
|
21
|
+
};
|
|
22
|
+
export type TurnkeyOauthLoginResponse = {
|
|
23
|
+
organizationId: string;
|
|
24
|
+
credentialBundle: string;
|
|
25
|
+
message: string;
|
|
26
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export function generateGoogleUrl({ nonce, clientId, redirectUri, scope = 'openid profile email', prompt = 'consent', }) {
|
|
2
|
+
if (!clientId) {
|
|
3
|
+
throw new Error('Google client ID not found');
|
|
4
|
+
}
|
|
5
|
+
const responseType = 'id_token';
|
|
6
|
+
return `https://accounts.google.com/o/oauth2/v2/auth?prompt=${prompt}&client_id=${clientId}&redirect_uri=${redirectUri}&response_type=${responseType}&scope=${scope}&nonce=${nonce}`;
|
|
7
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@injectivelabs/wallet-turnkey",
|
|
3
|
+
"description": "Turnkey wallet strategy for use with @injectivelabs/wallet-core.",
|
|
4
|
+
"version": "1.15.10",
|
|
5
|
+
"sideEffects": false,
|
|
6
|
+
"type": "module",
|
|
7
|
+
"author": {
|
|
8
|
+
"name": "InjectiveLabs",
|
|
9
|
+
"email": "admin@injectivelabs.org"
|
|
10
|
+
},
|
|
11
|
+
"license": "Apache-2.0",
|
|
12
|
+
"types": "dist/cjs/index.d.ts",
|
|
13
|
+
"main": "dist/cjs/index.js",
|
|
14
|
+
"module": "dist/esm/index.js",
|
|
15
|
+
"files": [
|
|
16
|
+
"dist"
|
|
17
|
+
],
|
|
18
|
+
"_moduleAliases": {
|
|
19
|
+
"~wallet-turnkey": "dist"
|
|
20
|
+
},
|
|
21
|
+
"exports": {
|
|
22
|
+
".": {
|
|
23
|
+
"react-native": {
|
|
24
|
+
"import": "./dist/esm/index.js",
|
|
25
|
+
"require": "./dist/cjs/index.js",
|
|
26
|
+
"types": "./dist/cjs/index.d.ts",
|
|
27
|
+
"default": "./dist/cjs/index.js"
|
|
28
|
+
},
|
|
29
|
+
"require": {
|
|
30
|
+
"types": "./dist/cjs/index.d.ts",
|
|
31
|
+
"default": "./dist/cjs/index.js"
|
|
32
|
+
},
|
|
33
|
+
"import": {
|
|
34
|
+
"types": "./dist/esm/index.d.ts",
|
|
35
|
+
"default": "./dist/esm/index.js"
|
|
36
|
+
},
|
|
37
|
+
"default": {
|
|
38
|
+
"types": "./dist/cjs/index.d.ts",
|
|
39
|
+
"default": "./dist/cjs/index.js"
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
"scripts": {
|
|
44
|
+
"build": "yarn build:esm && yarn build:cjs && yarn build:post",
|
|
45
|
+
"build:cjs": "tsc --build --force tsconfig.build.json",
|
|
46
|
+
"build:esm": "tsc --build --force tsconfig.build.esm.json",
|
|
47
|
+
"build:watch": "tsc --build -w tsconfig.build.json && tsc -w --build tsconfig.build.esm.json && yarn build:post",
|
|
48
|
+
"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",
|
|
49
|
+
"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",
|
|
50
|
+
"test": "jest",
|
|
51
|
+
"test:watch": "jest --watch",
|
|
52
|
+
"test:ci": "jest --coverage --ci --reporters='jest-junit'",
|
|
53
|
+
"coverage": "jest --coverage",
|
|
54
|
+
"coverage:show": "live-server coverage",
|
|
55
|
+
"dev": "ts-node -r tsconfig-paths/register src/index.ts",
|
|
56
|
+
"start": "node dist/index.js"
|
|
57
|
+
},
|
|
58
|
+
"dependencies": {
|
|
59
|
+
"@injectivelabs/exceptions": "^1.15.7",
|
|
60
|
+
"@injectivelabs/sdk-ts": "^1.15.10",
|
|
61
|
+
"@injectivelabs/ts-types": "^1.15.8",
|
|
62
|
+
"@injectivelabs/utils": "^1.15.8",
|
|
63
|
+
"@injectivelabs/wallet-base": "^1.15.10",
|
|
64
|
+
"@turnkey/sdk-browser": "^4.1.0",
|
|
65
|
+
"@turnkey/viem": "^0.9.0",
|
|
66
|
+
"viem": "^2.28.1"
|
|
67
|
+
},
|
|
68
|
+
"devDependencies": {
|
|
69
|
+
"jest": "^29.0.0",
|
|
70
|
+
"live-server": "^1.2.1",
|
|
71
|
+
"shx": "^0.3.4",
|
|
72
|
+
"ts-jest": "^29.0.0",
|
|
73
|
+
"ts-node": "^10.9.1",
|
|
74
|
+
"tsconfig-paths": "^4.2.0",
|
|
75
|
+
"typescript": "^5.0.0"
|
|
76
|
+
},
|
|
77
|
+
"gitHead": "6442ae377bbfb3459d2fb3a44c650630a5b7f445"
|
|
78
|
+
}
|