@imtbl/sdk 0.0.1-alpha
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/dist/base.d.ts +18 -0
- package/dist/base.js +15 -0
- package/dist/browser/checkout.js +2 -0
- package/dist/checkout_sdk.d.ts +673 -0
- package/dist/checkout_sdk.js +471 -0
- package/dist/checkout_widgets.d.ts +14444 -0
- package/dist/checkout_widgets.js +185 -0
- package/dist/index.cjs +2576 -0
- package/dist/index.d.ts +23898 -0
- package/dist/index.js +2547 -0
- package/dist/passport.d.ts +736 -0
- package/dist/passport.js +910 -0
- package/dist/provider.d.ts +962 -0
- package/dist/provider.js +1139 -0
- package/dist/starkex.d.ts +8421 -0
- package/dist/starkex.js +10 -0
- package/package.json +97 -0
package/dist/passport.js
ADDED
|
@@ -0,0 +1,910 @@
|
|
|
1
|
+
import { Configuration, UsersApi, TransfersApi, OrdersApi, ExchangesApi, TradesApi, generateLegacyStarkPrivateKey, createStarkSigner } from '@imtbl/core-sdk';
|
|
2
|
+
import { UserManager } from 'oidc-client-ts';
|
|
3
|
+
import { ethers } from 'ethers';
|
|
4
|
+
import { Magic } from 'magic-sdk';
|
|
5
|
+
import { OpenIdExtension } from '@magic-ext/oidc';
|
|
6
|
+
import BN from 'bn.js';
|
|
7
|
+
import * as encUtils from 'enc-utils';
|
|
8
|
+
|
|
9
|
+
// used to sign message with L1 keys. Used for registration
|
|
10
|
+
function serializeEthSignature(sig) {
|
|
11
|
+
// This is because golang appends a recovery param
|
|
12
|
+
// https://github.com/ethers-io/ethers.js/issues/823
|
|
13
|
+
return encUtils.addHexPrefix(encUtils.padLeft(sig.r.toString(16), 64) +
|
|
14
|
+
encUtils.padLeft(sig.s.toString(16), 64) +
|
|
15
|
+
encUtils.padLeft(sig.recoveryParam?.toString(16) || '', 2));
|
|
16
|
+
}
|
|
17
|
+
function importRecoveryParam(v) {
|
|
18
|
+
return v.trim()
|
|
19
|
+
? new BN(v, 16).cmp(new BN(27)) !== -1
|
|
20
|
+
? new BN(v, 16).sub(new BN(27)).toNumber()
|
|
21
|
+
: new BN(v, 16).toNumber()
|
|
22
|
+
: undefined;
|
|
23
|
+
}
|
|
24
|
+
// used chained with serializeEthSignature. serializeEthSignature(deserializeSignature(...))
|
|
25
|
+
function deserializeSignature(sig, size = 64) {
|
|
26
|
+
sig = encUtils.removeHexPrefix(sig);
|
|
27
|
+
return {
|
|
28
|
+
r: new BN(sig.substring(0, size), 'hex'),
|
|
29
|
+
s: new BN(sig.substring(size, size * 2), 'hex'),
|
|
30
|
+
recoveryParam: importRecoveryParam(sig.substring(size * 2, size * 2 + 2)),
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
async function signRaw(payload, signer) {
|
|
34
|
+
const signature = deserializeSignature(await signer.signMessage(payload));
|
|
35
|
+
return serializeEthSignature(signature);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Helper method to convert token type to a SignableToken type
|
|
40
|
+
* @param token - the token type to convert to a SignableToken type
|
|
41
|
+
* @returns the converted SignableToken
|
|
42
|
+
*/
|
|
43
|
+
function convertToSignableToken(token) {
|
|
44
|
+
switch (token.type) {
|
|
45
|
+
case 'ERC721':
|
|
46
|
+
return {
|
|
47
|
+
type: 'ERC721',
|
|
48
|
+
data: {
|
|
49
|
+
token_id: token.tokenId,
|
|
50
|
+
token_address: token.tokenAddress,
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
case 'ERC20':
|
|
54
|
+
return {
|
|
55
|
+
type: 'ERC20',
|
|
56
|
+
data: {
|
|
57
|
+
token_address: token.tokenAddress,
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
case 'ETH':
|
|
61
|
+
return {
|
|
62
|
+
type: 'ETH',
|
|
63
|
+
data: {
|
|
64
|
+
decimals: 18,
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
var Environment;
|
|
71
|
+
(function (Environment) {
|
|
72
|
+
Environment["PRODUCTION"] = "production";
|
|
73
|
+
Environment["SANDBOX"] = "sandbox";
|
|
74
|
+
})(Environment || (Environment = {}));
|
|
75
|
+
|
|
76
|
+
var PassportErrorType;
|
|
77
|
+
(function (PassportErrorType) {
|
|
78
|
+
PassportErrorType["AUTHENTICATION_ERROR"] = "AUTHENTICATION_ERROR";
|
|
79
|
+
PassportErrorType["INVALID_CONFIGURATION"] = "INVALID_CONFIGURATION";
|
|
80
|
+
PassportErrorType["WALLET_CONNECTION_ERROR"] = "WALLET_CONNECTION_ERROR";
|
|
81
|
+
PassportErrorType["NOT_LOGGED_IN_ERROR"] = "NOT_LOGGED_IN_ERROR";
|
|
82
|
+
PassportErrorType["REFRESH_TOKEN_ERROR"] = "REFRESH_TOKEN_ERROR";
|
|
83
|
+
PassportErrorType["USER_REGISTRATION_ERROR"] = "USER_REGISTRATION_ERROR";
|
|
84
|
+
PassportErrorType["LOGOUT_ERROR"] = "LOGOUT_ERROR";
|
|
85
|
+
PassportErrorType["TRANSFER_ERROR"] = "TRANSFER_ERROR";
|
|
86
|
+
PassportErrorType["CREATE_ORDER_ERROR"] = "CREATE_ORDER_ERROR";
|
|
87
|
+
PassportErrorType["CANCEL_ORDER_ERROR"] = "CANCEL_ORDER_ERROR";
|
|
88
|
+
PassportErrorType["EXCHANGE_TRANSFER_ERROR"] = "EXCHANGE_TRANSFER_ERROR";
|
|
89
|
+
PassportErrorType["CREATE_TRADE_ERROR"] = "CREATE_TRADE_ERROR";
|
|
90
|
+
PassportErrorType["OPERATION_NOT_SUPPORTED_ERROR"] = "OPERATION_NOT_SUPPORTED_ERROR";
|
|
91
|
+
})(PassportErrorType || (PassportErrorType = {}));
|
|
92
|
+
class PassportError extends Error {
|
|
93
|
+
type;
|
|
94
|
+
constructor(message, type) {
|
|
95
|
+
super(message);
|
|
96
|
+
this.type = type;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
const withPassportError = async (fn, customErrorType) => {
|
|
100
|
+
try {
|
|
101
|
+
return await fn();
|
|
102
|
+
}
|
|
103
|
+
catch (error) {
|
|
104
|
+
const errorMessage = `${customErrorType}: ${error.message}` || 'UnknownError';
|
|
105
|
+
throw new PassportError(errorMessage, customErrorType);
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
const POLL_INTERVAL = 1 * 1000; // every 1 second
|
|
110
|
+
const MAX_RETRIES = 3;
|
|
111
|
+
const wait = (ms) => new Promise((resolve) => {
|
|
112
|
+
setTimeout(() => resolve(), ms);
|
|
113
|
+
});
|
|
114
|
+
const retryWithDelay = async (fn, options) => {
|
|
115
|
+
const { retries = MAX_RETRIES, interval = POLL_INTERVAL, finalErr = Error('Retry failed'), } = options || {};
|
|
116
|
+
try {
|
|
117
|
+
return await fn();
|
|
118
|
+
}
|
|
119
|
+
catch (err) {
|
|
120
|
+
if (retries <= 0) {
|
|
121
|
+
return Promise.reject(finalErr);
|
|
122
|
+
}
|
|
123
|
+
await wait(interval);
|
|
124
|
+
return retryWithDelay(fn, { retries: retries - 1, finalErr });
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
const getAuthConfiguration = ({ oidcConfiguration, authenticationDomain, }) => {
|
|
129
|
+
const baseConfiguration = {
|
|
130
|
+
authority: authenticationDomain,
|
|
131
|
+
redirect_uri: oidcConfiguration.redirectUri,
|
|
132
|
+
popup_redirect_uri: oidcConfiguration.redirectUri,
|
|
133
|
+
client_id: oidcConfiguration.clientId,
|
|
134
|
+
metadata: {
|
|
135
|
+
authorization_endpoint: `${authenticationDomain}/authorize`,
|
|
136
|
+
token_endpoint: `${authenticationDomain}/oauth/token`,
|
|
137
|
+
userinfo_endpoint: `${authenticationDomain}/userinfo`,
|
|
138
|
+
end_session_endpoint: `${authenticationDomain}/v2/logout` +
|
|
139
|
+
`?returnTo=${encodeURIComponent(oidcConfiguration.logoutRedirectUri)}` +
|
|
140
|
+
`&client_id=${oidcConfiguration.clientId}`,
|
|
141
|
+
},
|
|
142
|
+
mergeClaims: true,
|
|
143
|
+
loadUserInfo: true,
|
|
144
|
+
scope: oidcConfiguration.scope,
|
|
145
|
+
};
|
|
146
|
+
if (oidcConfiguration.audience) {
|
|
147
|
+
baseConfiguration.extraQueryParams = {
|
|
148
|
+
audience: oidcConfiguration.audience,
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
return baseConfiguration;
|
|
152
|
+
};
|
|
153
|
+
class AuthManager {
|
|
154
|
+
userManager;
|
|
155
|
+
config;
|
|
156
|
+
constructor(config) {
|
|
157
|
+
this.config = config;
|
|
158
|
+
this.userManager = new UserManager(getAuthConfiguration(config));
|
|
159
|
+
}
|
|
160
|
+
mapOidcUserToDomainModel = (oidcUser) => {
|
|
161
|
+
const passport = oidcUser.profile?.passport;
|
|
162
|
+
return {
|
|
163
|
+
idToken: oidcUser.id_token,
|
|
164
|
+
accessToken: oidcUser.access_token,
|
|
165
|
+
refreshToken: oidcUser.refresh_token,
|
|
166
|
+
profile: {
|
|
167
|
+
sub: oidcUser.profile.sub,
|
|
168
|
+
email: oidcUser.profile.email,
|
|
169
|
+
nickname: oidcUser.profile.nickname,
|
|
170
|
+
},
|
|
171
|
+
etherKey: passport?.ether_key || '',
|
|
172
|
+
};
|
|
173
|
+
};
|
|
174
|
+
async login() {
|
|
175
|
+
return withPassportError(async () => {
|
|
176
|
+
const popupWindowFeatures = { width: 400, height: 420 };
|
|
177
|
+
const oidcUser = await this.userManager.signinPopup({
|
|
178
|
+
popupWindowFeatures,
|
|
179
|
+
});
|
|
180
|
+
return this.mapOidcUserToDomainModel(oidcUser);
|
|
181
|
+
}, PassportErrorType.AUTHENTICATION_ERROR);
|
|
182
|
+
}
|
|
183
|
+
async loginCallback() {
|
|
184
|
+
return withPassportError(async () => this.userManager.signinPopupCallback(), PassportErrorType.AUTHENTICATION_ERROR);
|
|
185
|
+
}
|
|
186
|
+
async logout() {
|
|
187
|
+
return withPassportError(async () => this.userManager.signoutRedirect(), PassportErrorType.LOGOUT_ERROR);
|
|
188
|
+
}
|
|
189
|
+
async getUser() {
|
|
190
|
+
return withPassportError(async () => {
|
|
191
|
+
const oidcUser = await this.userManager.getUser();
|
|
192
|
+
if (!oidcUser) {
|
|
193
|
+
throw new Error('Failed to retrieve user');
|
|
194
|
+
}
|
|
195
|
+
return this.mapOidcUserToDomainModel(oidcUser);
|
|
196
|
+
}, PassportErrorType.NOT_LOGGED_IN_ERROR);
|
|
197
|
+
}
|
|
198
|
+
async requestRefreshTokenAfterRegistration() {
|
|
199
|
+
return withPassportError(async () => {
|
|
200
|
+
const updatedUser = await retryWithDelay(async () => {
|
|
201
|
+
const user = await this.userManager.signinSilent();
|
|
202
|
+
const passportMetadata = user?.profile?.passport;
|
|
203
|
+
const metadataExists = !!passportMetadata?.ether_key &&
|
|
204
|
+
!!passportMetadata?.stark_key &&
|
|
205
|
+
!!passportMetadata?.user_admin_key;
|
|
206
|
+
if (metadataExists) {
|
|
207
|
+
return user;
|
|
208
|
+
}
|
|
209
|
+
return Promise.reject('user wallet addresses not exist');
|
|
210
|
+
});
|
|
211
|
+
if (!updatedUser) {
|
|
212
|
+
return null;
|
|
213
|
+
}
|
|
214
|
+
return this.mapOidcUserToDomainModel(updatedUser);
|
|
215
|
+
}, PassportErrorType.REFRESH_TOKEN_ERROR);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
class MagicAdapter {
|
|
220
|
+
magicClient;
|
|
221
|
+
config;
|
|
222
|
+
constructor(config) {
|
|
223
|
+
this.config = config;
|
|
224
|
+
this.magicClient = new Magic(config.magicPublishableApiKey, {
|
|
225
|
+
network: config.network,
|
|
226
|
+
extensions: [new OpenIdExtension()],
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
async login(idToken) {
|
|
230
|
+
return withPassportError(async () => {
|
|
231
|
+
await this.magicClient.openid.loginWithOIDC({
|
|
232
|
+
jwt: idToken,
|
|
233
|
+
providerId: this.config.magicProviderId,
|
|
234
|
+
});
|
|
235
|
+
return new ethers.providers.Web3Provider(this.magicClient
|
|
236
|
+
.rpcProvider);
|
|
237
|
+
}, PassportErrorType.WALLET_CONNECTION_ERROR);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
var ReceiveMessage;
|
|
242
|
+
(function (ReceiveMessage) {
|
|
243
|
+
ReceiveMessage["CONFIRMATION_WINDOW_READY"] = "confirmation_window_ready";
|
|
244
|
+
ReceiveMessage["TRANSACTION_CONFIRMED"] = "transaction_confirmed";
|
|
245
|
+
ReceiveMessage["TRANSACTION_ERROR"] = "transaction_error";
|
|
246
|
+
})(ReceiveMessage || (ReceiveMessage = {}));
|
|
247
|
+
var SendMessage;
|
|
248
|
+
(function (SendMessage) {
|
|
249
|
+
SendMessage["TRANSACTION_START"] = "transaction_start";
|
|
250
|
+
})(SendMessage || (SendMessage = {}));
|
|
251
|
+
var TransactionTypes;
|
|
252
|
+
(function (TransactionTypes) {
|
|
253
|
+
TransactionTypes["CancelOrder"] = "delete:v1/orders";
|
|
254
|
+
TransactionTypes["CreateOrder"] = "post:v1/orders";
|
|
255
|
+
TransactionTypes["CreateTrade"] = "post:v1/trades";
|
|
256
|
+
TransactionTypes["CreateTransfer"] = "post:v1/transfers";
|
|
257
|
+
TransactionTypes["CreateBatchTransfer"] = "post:v2/transfers";
|
|
258
|
+
})(TransactionTypes || (TransactionTypes = {}));
|
|
259
|
+
const PassportEventType = 'imx_passport_confirmation';
|
|
260
|
+
|
|
261
|
+
const openPopupCenter = ({ url, title, width, height, }) => {
|
|
262
|
+
const left = Math.max(0, Math.round(window.screenX + (window.outerWidth - width) / 2));
|
|
263
|
+
const top = Math.max(0, Math.round(window.screenY + (window.outerHeight - height) / 2));
|
|
264
|
+
const newWindow = window.open(url, title, `
|
|
265
|
+
scrollbars=yes,
|
|
266
|
+
width=${width},
|
|
267
|
+
height=${height},
|
|
268
|
+
top=${top},
|
|
269
|
+
left=${left}
|
|
270
|
+
`);
|
|
271
|
+
if (!newWindow) {
|
|
272
|
+
throw new Error('Failed to open confirmation screen');
|
|
273
|
+
}
|
|
274
|
+
newWindow.focus();
|
|
275
|
+
return newWindow;
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
const ConfirmationWindowTitle = 'Confirm this transaction';
|
|
279
|
+
const ConfirmationWindowHeight = 380;
|
|
280
|
+
const ConfirmationWindowWidth = 480;
|
|
281
|
+
const ConfirmationWindowClosedPollingDuration = 1000;
|
|
282
|
+
class ConfirmationScreen {
|
|
283
|
+
config;
|
|
284
|
+
constructor(config) {
|
|
285
|
+
this.config = config;
|
|
286
|
+
}
|
|
287
|
+
postMessage(destinationWindow, accessToken, message) {
|
|
288
|
+
destinationWindow.postMessage({
|
|
289
|
+
eventType: PassportEventType,
|
|
290
|
+
accessToken,
|
|
291
|
+
...message,
|
|
292
|
+
}, this.config.passportDomain);
|
|
293
|
+
}
|
|
294
|
+
startTransaction(accessToken, transaction, popupOptions) {
|
|
295
|
+
return new Promise((resolve, reject) => {
|
|
296
|
+
const messageHandler = ({ data, origin }) => {
|
|
297
|
+
if (origin != this.config.passportDomain ||
|
|
298
|
+
data.eventType != PassportEventType) {
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
switch (data.messageType) {
|
|
302
|
+
case ReceiveMessage.CONFIRMATION_WINDOW_READY: {
|
|
303
|
+
this.postMessage(confirmationWindow, accessToken, {
|
|
304
|
+
messageType: SendMessage.TRANSACTION_START,
|
|
305
|
+
messageData: transaction,
|
|
306
|
+
});
|
|
307
|
+
break;
|
|
308
|
+
}
|
|
309
|
+
case ReceiveMessage.TRANSACTION_CONFIRMED: {
|
|
310
|
+
resolve({ confirmed: true });
|
|
311
|
+
break;
|
|
312
|
+
}
|
|
313
|
+
case ReceiveMessage.TRANSACTION_ERROR: {
|
|
314
|
+
reject(new Error('Transaction error'));
|
|
315
|
+
break;
|
|
316
|
+
}
|
|
317
|
+
default:
|
|
318
|
+
reject(new Error('Unsupported message type'));
|
|
319
|
+
}
|
|
320
|
+
};
|
|
321
|
+
window.addEventListener('message', messageHandler);
|
|
322
|
+
const confirmationWindow = openPopupCenter({
|
|
323
|
+
url: `${this.config.passportDomain}/transaction-confirmation`,
|
|
324
|
+
title: ConfirmationWindowTitle,
|
|
325
|
+
width: popupOptions?.width || ConfirmationWindowWidth,
|
|
326
|
+
height: popupOptions?.height || ConfirmationWindowHeight,
|
|
327
|
+
});
|
|
328
|
+
// https://stackoverflow.com/questions/9388380/capture-the-close-event-of-popup-window-in-javascript/48240128#48240128
|
|
329
|
+
const timer = setInterval(function () {
|
|
330
|
+
if (confirmationWindow.closed) {
|
|
331
|
+
clearInterval(timer);
|
|
332
|
+
window.removeEventListener('message', messageHandler);
|
|
333
|
+
resolve({ confirmed: false });
|
|
334
|
+
}
|
|
335
|
+
}, ConfirmationWindowClosedPollingDuration);
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
const ERC721$1 = 'ERC721';
|
|
341
|
+
const transfer = ({ request, transfersApi, starkSigner, user, passportConfig, }) => {
|
|
342
|
+
return withPassportError(async () => {
|
|
343
|
+
const transferAmount = request.type === ERC721$1 ? '1' : request.amount;
|
|
344
|
+
const getSignableTransferRequest = {
|
|
345
|
+
sender: user.etherKey,
|
|
346
|
+
token: convertToSignableToken(request),
|
|
347
|
+
amount: transferAmount,
|
|
348
|
+
receiver: request.receiver,
|
|
349
|
+
};
|
|
350
|
+
const signableResult = await transfersApi.getSignableTransferV1({
|
|
351
|
+
getSignableTransferRequest,
|
|
352
|
+
});
|
|
353
|
+
const confirmationScreen = new ConfirmationScreen(passportConfig);
|
|
354
|
+
const confirmationResult = await confirmationScreen.startTransaction(user.accessToken, {
|
|
355
|
+
transactionType: TransactionTypes.CreateTransfer,
|
|
356
|
+
transactionData: getSignableTransferRequest,
|
|
357
|
+
});
|
|
358
|
+
if (!confirmationResult.confirmed) {
|
|
359
|
+
throw new Error('Transaction rejected by user');
|
|
360
|
+
}
|
|
361
|
+
const signableResultData = signableResult.data;
|
|
362
|
+
const { payload_hash: payloadHash } = signableResultData;
|
|
363
|
+
const starkSignature = await starkSigner.signMessage(payloadHash);
|
|
364
|
+
const senderStarkKey = await starkSigner.getAddress();
|
|
365
|
+
const transferSigningParams = {
|
|
366
|
+
sender_stark_key: signableResultData.sender_stark_key || senderStarkKey,
|
|
367
|
+
sender_vault_id: signableResultData.sender_vault_id,
|
|
368
|
+
receiver_stark_key: signableResultData.receiver_stark_key,
|
|
369
|
+
receiver_vault_id: signableResultData.receiver_vault_id,
|
|
370
|
+
asset_id: signableResultData.asset_id,
|
|
371
|
+
amount: signableResultData.amount,
|
|
372
|
+
nonce: signableResultData.nonce,
|
|
373
|
+
expiration_timestamp: signableResultData.expiration_timestamp,
|
|
374
|
+
stark_signature: starkSignature,
|
|
375
|
+
};
|
|
376
|
+
const createTransferRequest = {
|
|
377
|
+
createTransferRequest: transferSigningParams,
|
|
378
|
+
};
|
|
379
|
+
const headers = {
|
|
380
|
+
Authorization: 'Bearer ' + user.accessToken,
|
|
381
|
+
};
|
|
382
|
+
const { data: responseData } = await transfersApi.createTransferV1(createTransferRequest, { headers });
|
|
383
|
+
return {
|
|
384
|
+
sent_signature: responseData.sent_signature,
|
|
385
|
+
status: responseData.status?.toString(),
|
|
386
|
+
time: responseData.time,
|
|
387
|
+
transfer_id: responseData.transfer_id,
|
|
388
|
+
};
|
|
389
|
+
}, PassportErrorType.TRANSFER_ERROR);
|
|
390
|
+
};
|
|
391
|
+
async function batchNftTransfer({ user, starkSigner, request, transfersApi, passportConfig, }) {
|
|
392
|
+
return withPassportError(async () => {
|
|
393
|
+
const ethAddress = user.etherKey;
|
|
394
|
+
const signableRequests = request.map((nftTransfer) => {
|
|
395
|
+
return {
|
|
396
|
+
amount: '1',
|
|
397
|
+
token: convertToSignableToken({
|
|
398
|
+
type: ERC721$1,
|
|
399
|
+
tokenId: nftTransfer.tokenId,
|
|
400
|
+
tokenAddress: nftTransfer.tokenAddress,
|
|
401
|
+
}),
|
|
402
|
+
receiver: nftTransfer.receiver,
|
|
403
|
+
};
|
|
404
|
+
});
|
|
405
|
+
const getSignableTransferRequestV2 = {
|
|
406
|
+
sender_ether_key: ethAddress,
|
|
407
|
+
signable_requests: signableRequests,
|
|
408
|
+
};
|
|
409
|
+
const signableResult = await transfersApi.getSignableTransfer({
|
|
410
|
+
getSignableTransferRequestV2,
|
|
411
|
+
});
|
|
412
|
+
const confirmationScreen = new ConfirmationScreen(passportConfig);
|
|
413
|
+
const popupWindowSize = { width: 480, height: 784 };
|
|
414
|
+
const confirmationResult = await confirmationScreen.startTransaction(user.accessToken, {
|
|
415
|
+
transactionType: TransactionTypes.CreateBatchTransfer,
|
|
416
|
+
transactionData: getSignableTransferRequestV2,
|
|
417
|
+
}, popupWindowSize);
|
|
418
|
+
if (!confirmationResult.confirmed) {
|
|
419
|
+
throw new Error('Transaction rejected by user');
|
|
420
|
+
}
|
|
421
|
+
const requests = await Promise.all(signableResult.data.signable_responses.map(async (resp) => {
|
|
422
|
+
const starkSignature = await starkSigner.signMessage(resp.payload_hash);
|
|
423
|
+
return {
|
|
424
|
+
sender_vault_id: resp.sender_vault_id,
|
|
425
|
+
receiver_stark_key: resp.receiver_stark_key,
|
|
426
|
+
receiver_vault_id: resp.receiver_vault_id,
|
|
427
|
+
asset_id: resp.asset_id,
|
|
428
|
+
amount: resp.amount,
|
|
429
|
+
nonce: resp.nonce,
|
|
430
|
+
expiration_timestamp: resp.expiration_timestamp,
|
|
431
|
+
stark_signature: starkSignature,
|
|
432
|
+
};
|
|
433
|
+
}));
|
|
434
|
+
const transferSigningParams = {
|
|
435
|
+
sender_stark_key: signableResult.data.sender_stark_key,
|
|
436
|
+
requests,
|
|
437
|
+
};
|
|
438
|
+
const headers = {
|
|
439
|
+
Authorization: 'Bearer ' + user.accessToken,
|
|
440
|
+
};
|
|
441
|
+
const response = await transfersApi.createTransfer({
|
|
442
|
+
createTransferRequestV2: transferSigningParams,
|
|
443
|
+
}, { headers });
|
|
444
|
+
return {
|
|
445
|
+
transfer_ids: response?.data.transfer_ids,
|
|
446
|
+
};
|
|
447
|
+
}, PassportErrorType.TRANSFER_ERROR);
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
const ERC721 = 'ERC721';
|
|
451
|
+
async function createOrder({ starkSigner, user, request, ordersApi, passportConfig, }) {
|
|
452
|
+
return withPassportError(async () => {
|
|
453
|
+
const ethAddress = user.etherKey;
|
|
454
|
+
const amountSell = request.sell.type === ERC721 ? '1' : request.sell.amount;
|
|
455
|
+
const amountBuy = request.buy.type === ERC721 ? '1' : request.buy.amount;
|
|
456
|
+
const getSignableOrderRequestV3 = {
|
|
457
|
+
user: ethAddress,
|
|
458
|
+
amount_buy: amountBuy,
|
|
459
|
+
token_buy: convertToSignableToken(request.buy),
|
|
460
|
+
amount_sell: amountSell,
|
|
461
|
+
token_sell: convertToSignableToken(request.sell),
|
|
462
|
+
fees: request.fees,
|
|
463
|
+
expiration_timestamp: request.expiration_timestamp,
|
|
464
|
+
};
|
|
465
|
+
const getSignableOrderResponse = await ordersApi.getSignableOrder({
|
|
466
|
+
getSignableOrderRequestV3,
|
|
467
|
+
});
|
|
468
|
+
const confirmationScreen = new ConfirmationScreen(passportConfig);
|
|
469
|
+
const confirmationResult = await confirmationScreen.startTransaction(user.accessToken, {
|
|
470
|
+
transactionType: TransactionTypes.CreateOrder,
|
|
471
|
+
transactionData: getSignableOrderRequestV3,
|
|
472
|
+
});
|
|
473
|
+
if (!confirmationResult.confirmed) {
|
|
474
|
+
throw new Error('Transaction rejected by user');
|
|
475
|
+
}
|
|
476
|
+
const { payload_hash: payloadHash } = getSignableOrderResponse.data;
|
|
477
|
+
const starkSignature = await starkSigner.signMessage(payloadHash);
|
|
478
|
+
const signableResultData = getSignableOrderResponse.data;
|
|
479
|
+
const orderParams = {
|
|
480
|
+
createOrderRequest: {
|
|
481
|
+
include_fees: true,
|
|
482
|
+
fees: request.fees,
|
|
483
|
+
stark_signature: starkSignature,
|
|
484
|
+
amount_buy: signableResultData.amount_buy,
|
|
485
|
+
amount_sell: signableResultData.amount_sell,
|
|
486
|
+
asset_id_buy: signableResultData.asset_id_buy,
|
|
487
|
+
asset_id_sell: signableResultData.asset_id_sell,
|
|
488
|
+
expiration_timestamp: signableResultData.expiration_timestamp,
|
|
489
|
+
nonce: signableResultData.nonce,
|
|
490
|
+
stark_key: signableResultData.stark_key,
|
|
491
|
+
vault_id_buy: signableResultData.vault_id_buy,
|
|
492
|
+
vault_id_sell: signableResultData.vault_id_sell,
|
|
493
|
+
},
|
|
494
|
+
};
|
|
495
|
+
const headers = {
|
|
496
|
+
Authorization: 'Bearer ' + user.accessToken,
|
|
497
|
+
};
|
|
498
|
+
const createOrderResponse = await ordersApi.createOrder(orderParams, {
|
|
499
|
+
headers,
|
|
500
|
+
});
|
|
501
|
+
return {
|
|
502
|
+
...createOrderResponse.data,
|
|
503
|
+
};
|
|
504
|
+
}, PassportErrorType.CREATE_ORDER_ERROR);
|
|
505
|
+
}
|
|
506
|
+
async function cancelOrder({ user, starkSigner, request, ordersApi, passportConfig, }) {
|
|
507
|
+
return withPassportError(async () => {
|
|
508
|
+
const getSignableCancelOrderRequest = {
|
|
509
|
+
order_id: request.order_id,
|
|
510
|
+
};
|
|
511
|
+
const getSignableCancelOrderResponse = await ordersApi.getSignableCancelOrder({
|
|
512
|
+
getSignableCancelOrderRequest,
|
|
513
|
+
});
|
|
514
|
+
const confirmationScreen = new ConfirmationScreen(passportConfig);
|
|
515
|
+
const confirmationResult = await confirmationScreen.startTransaction(user.accessToken, {
|
|
516
|
+
transactionType: TransactionTypes.CancelOrder,
|
|
517
|
+
transactionData: getSignableCancelOrderRequest,
|
|
518
|
+
});
|
|
519
|
+
if (!confirmationResult.confirmed) {
|
|
520
|
+
throw new Error('Transaction rejected by user');
|
|
521
|
+
}
|
|
522
|
+
const { payload_hash: payloadHash } = getSignableCancelOrderResponse.data;
|
|
523
|
+
const starkSignature = await starkSigner.signMessage(payloadHash);
|
|
524
|
+
const headers = {
|
|
525
|
+
Authorization: 'Bearer ' + user.accessToken,
|
|
526
|
+
};
|
|
527
|
+
const cancelOrderResponse = await ordersApi.cancelOrder({
|
|
528
|
+
id: request.order_id.toString(),
|
|
529
|
+
cancelOrderRequest: {
|
|
530
|
+
order_id: request.order_id,
|
|
531
|
+
stark_signature: starkSignature,
|
|
532
|
+
},
|
|
533
|
+
}, { headers });
|
|
534
|
+
return {
|
|
535
|
+
order_id: cancelOrderResponse.data.order_id,
|
|
536
|
+
status: cancelOrderResponse.data.status,
|
|
537
|
+
};
|
|
538
|
+
}, PassportErrorType.CANCEL_ORDER_ERROR);
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
async function exchangeTransfer({ user, starkSigner, request, exchangesApi, }) {
|
|
542
|
+
return withPassportError(async () => {
|
|
543
|
+
const ethAddress = user.etherKey;
|
|
544
|
+
const transferAmount = request.amount;
|
|
545
|
+
const signableResult = await exchangesApi.getExchangeSignableTransfer({
|
|
546
|
+
id: request.transactionID,
|
|
547
|
+
getSignableTransferRequest: {
|
|
548
|
+
sender: ethAddress,
|
|
549
|
+
token: convertToSignableToken(request),
|
|
550
|
+
amount: transferAmount,
|
|
551
|
+
receiver: request.receiver,
|
|
552
|
+
},
|
|
553
|
+
});
|
|
554
|
+
const starkAddress = await starkSigner.getAddress();
|
|
555
|
+
const { payload_hash: payloadHash } = signableResult.data;
|
|
556
|
+
const starkSignature = await starkSigner.signMessage(payloadHash);
|
|
557
|
+
const transferSigningParams = {
|
|
558
|
+
sender_stark_key: signableResult.data.sender_stark_key || starkAddress,
|
|
559
|
+
sender_vault_id: signableResult.data.sender_vault_id,
|
|
560
|
+
receiver_stark_key: signableResult.data.receiver_stark_key,
|
|
561
|
+
receiver_vault_id: signableResult.data.receiver_vault_id,
|
|
562
|
+
asset_id: signableResult.data.asset_id,
|
|
563
|
+
amount: signableResult.data.amount,
|
|
564
|
+
nonce: signableResult.data.nonce,
|
|
565
|
+
expiration_timestamp: signableResult.data.expiration_timestamp,
|
|
566
|
+
stark_signature: starkSignature,
|
|
567
|
+
};
|
|
568
|
+
const headers = {
|
|
569
|
+
Authorization: 'Bearer ' + user.accessToken,
|
|
570
|
+
};
|
|
571
|
+
const response = await exchangesApi.createExchangeTransfer({
|
|
572
|
+
id: request.transactionID,
|
|
573
|
+
createTransferRequest: transferSigningParams,
|
|
574
|
+
}, { headers });
|
|
575
|
+
return {
|
|
576
|
+
sent_signature: response?.data.sent_signature,
|
|
577
|
+
status: response?.data.status?.toString(),
|
|
578
|
+
time: response?.data.time,
|
|
579
|
+
transfer_id: response?.data.transfer_id,
|
|
580
|
+
};
|
|
581
|
+
}, PassportErrorType.EXCHANGE_TRANSFER_ERROR);
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
async function createTrade({ request, tradesApi, user, starkSigner, passportConfig, }) {
|
|
585
|
+
return withPassportError(async () => {
|
|
586
|
+
const ethAddress = user.etherKey;
|
|
587
|
+
const getSignableTradeRequest = {
|
|
588
|
+
expiration_timestamp: request.expiration_timestamp,
|
|
589
|
+
fees: request.fees,
|
|
590
|
+
order_id: request.order_id,
|
|
591
|
+
user: ethAddress,
|
|
592
|
+
};
|
|
593
|
+
const getSignableTradeResponse = await tradesApi.getSignableTrade({
|
|
594
|
+
getSignableTradeRequest,
|
|
595
|
+
});
|
|
596
|
+
const confirmationScreen = new ConfirmationScreen(passportConfig);
|
|
597
|
+
const confirmationResult = await confirmationScreen.startTransaction(user.accessToken, {
|
|
598
|
+
transactionType: TransactionTypes.CreateTrade,
|
|
599
|
+
transactionData: getSignableTradeRequest,
|
|
600
|
+
});
|
|
601
|
+
if (!confirmationResult.confirmed) {
|
|
602
|
+
throw new Error('Transaction rejected by user');
|
|
603
|
+
}
|
|
604
|
+
const { payload_hash: payloadHash } = getSignableTradeResponse.data;
|
|
605
|
+
const starkSignature = await starkSigner.signMessage(payloadHash);
|
|
606
|
+
const { data: signableResultData } = getSignableTradeResponse;
|
|
607
|
+
const tradeParams = {
|
|
608
|
+
createTradeRequest: {
|
|
609
|
+
include_fees: true,
|
|
610
|
+
fees: request?.fees,
|
|
611
|
+
stark_signature: starkSignature,
|
|
612
|
+
order_id: request?.order_id,
|
|
613
|
+
fee_info: signableResultData.fee_info,
|
|
614
|
+
amount_buy: signableResultData.amount_buy,
|
|
615
|
+
amount_sell: signableResultData.amount_sell,
|
|
616
|
+
asset_id_buy: signableResultData.asset_id_buy,
|
|
617
|
+
asset_id_sell: signableResultData.asset_id_sell,
|
|
618
|
+
expiration_timestamp: signableResultData.expiration_timestamp,
|
|
619
|
+
nonce: signableResultData.nonce,
|
|
620
|
+
stark_key: signableResultData.stark_key,
|
|
621
|
+
vault_id_buy: signableResultData.vault_id_buy,
|
|
622
|
+
vault_id_sell: signableResultData.vault_id_sell,
|
|
623
|
+
},
|
|
624
|
+
};
|
|
625
|
+
const headers = { Authorization: 'Bearer ' + user.accessToken };
|
|
626
|
+
const { data: createTradeResponse } = await tradesApi.createTrade(tradeParams, {
|
|
627
|
+
headers,
|
|
628
|
+
});
|
|
629
|
+
return createTradeResponse;
|
|
630
|
+
}, PassportErrorType.CREATE_TRADE_ERROR);
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
class PassportImxProvider {
|
|
634
|
+
user;
|
|
635
|
+
starkSigner;
|
|
636
|
+
transfersApi;
|
|
637
|
+
ordersApi;
|
|
638
|
+
passportConfig;
|
|
639
|
+
exchangesApi;
|
|
640
|
+
tradesApi;
|
|
641
|
+
constructor({ user, starkSigner, passportConfig }) {
|
|
642
|
+
this.user = user;
|
|
643
|
+
this.starkSigner = starkSigner;
|
|
644
|
+
this.passportConfig = passportConfig;
|
|
645
|
+
const apiConfig = new Configuration({
|
|
646
|
+
basePath: passportConfig.imxApiBasePath,
|
|
647
|
+
});
|
|
648
|
+
this.transfersApi = new TransfersApi(apiConfig);
|
|
649
|
+
this.ordersApi = new OrdersApi(apiConfig);
|
|
650
|
+
this.exchangesApi = new ExchangesApi(apiConfig);
|
|
651
|
+
this.tradesApi = new TradesApi(apiConfig);
|
|
652
|
+
}
|
|
653
|
+
async transfer(request) {
|
|
654
|
+
return transfer({
|
|
655
|
+
request,
|
|
656
|
+
user: this.user,
|
|
657
|
+
starkSigner: this.starkSigner,
|
|
658
|
+
transfersApi: this.transfersApi,
|
|
659
|
+
passportConfig: this.passportConfig,
|
|
660
|
+
});
|
|
661
|
+
}
|
|
662
|
+
registerOffchain() {
|
|
663
|
+
throw new PassportError('Operation not supported', PassportErrorType.OPERATION_NOT_SUPPORTED_ERROR);
|
|
664
|
+
}
|
|
665
|
+
isRegisteredOnchain() {
|
|
666
|
+
throw new PassportError('Operation not supported', PassportErrorType.OPERATION_NOT_SUPPORTED_ERROR);
|
|
667
|
+
}
|
|
668
|
+
createOrder(request) {
|
|
669
|
+
return createOrder({
|
|
670
|
+
request,
|
|
671
|
+
user: this.user,
|
|
672
|
+
starkSigner: this.starkSigner,
|
|
673
|
+
ordersApi: this.ordersApi,
|
|
674
|
+
passportConfig: this.passportConfig,
|
|
675
|
+
});
|
|
676
|
+
}
|
|
677
|
+
cancelOrder(request) {
|
|
678
|
+
return cancelOrder({
|
|
679
|
+
request,
|
|
680
|
+
user: this.user,
|
|
681
|
+
starkSigner: this.starkSigner,
|
|
682
|
+
ordersApi: this.ordersApi,
|
|
683
|
+
passportConfig: this.passportConfig,
|
|
684
|
+
});
|
|
685
|
+
}
|
|
686
|
+
createTrade(request) {
|
|
687
|
+
return createTrade({
|
|
688
|
+
request,
|
|
689
|
+
user: this.user,
|
|
690
|
+
starkSigner: this.starkSigner,
|
|
691
|
+
tradesApi: this.tradesApi,
|
|
692
|
+
passportConfig: this.passportConfig,
|
|
693
|
+
});
|
|
694
|
+
}
|
|
695
|
+
batchNftTransfer(request) {
|
|
696
|
+
return batchNftTransfer({
|
|
697
|
+
request,
|
|
698
|
+
user: this.user,
|
|
699
|
+
starkSigner: this.starkSigner,
|
|
700
|
+
transfersApi: this.transfersApi,
|
|
701
|
+
passportConfig: this.passportConfig,
|
|
702
|
+
});
|
|
703
|
+
}
|
|
704
|
+
exchangeTransfer(request) {
|
|
705
|
+
return exchangeTransfer({
|
|
706
|
+
request,
|
|
707
|
+
user: this.user,
|
|
708
|
+
starkSigner: this.starkSigner,
|
|
709
|
+
exchangesApi: this.exchangesApi,
|
|
710
|
+
});
|
|
711
|
+
}
|
|
712
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
713
|
+
deposit(deposit) {
|
|
714
|
+
throw new PassportError('Operation not supported', PassportErrorType.OPERATION_NOT_SUPPORTED_ERROR);
|
|
715
|
+
}
|
|
716
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
717
|
+
prepareWithdrawal(request) {
|
|
718
|
+
throw new PassportError('Operation not supported', PassportErrorType.OPERATION_NOT_SUPPORTED_ERROR);
|
|
719
|
+
}
|
|
720
|
+
completeWithdrawal(
|
|
721
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
722
|
+
starkPublicKey,
|
|
723
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
724
|
+
token) {
|
|
725
|
+
throw new PassportError('Operation not supported', PassportErrorType.OPERATION_NOT_SUPPORTED_ERROR);
|
|
726
|
+
}
|
|
727
|
+
getAddress() {
|
|
728
|
+
return Promise.resolve(this.starkSigner.getAddress());
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
var Networks;
|
|
733
|
+
(function (Networks) {
|
|
734
|
+
Networks["PRODUCTION"] = "mainnet";
|
|
735
|
+
Networks["SANDBOX"] = "goerli";
|
|
736
|
+
})(Networks || (Networks = {}));
|
|
737
|
+
|
|
738
|
+
const validateConfiguration = (configuration, requiredKeys, prefix) => {
|
|
739
|
+
const missingKeys = requiredKeys
|
|
740
|
+
.map((key) => !configuration[key] && key)
|
|
741
|
+
.filter((n) => n)
|
|
742
|
+
.join(', ');
|
|
743
|
+
if (missingKeys !== '') {
|
|
744
|
+
const errorMessage = prefix
|
|
745
|
+
? `${prefix} - ${missingKeys} cannot be null`
|
|
746
|
+
: `${missingKeys} cannot be null`;
|
|
747
|
+
throw new PassportError(errorMessage, PassportErrorType.INVALID_CONFIGURATION);
|
|
748
|
+
}
|
|
749
|
+
};
|
|
750
|
+
class PassportConfiguration {
|
|
751
|
+
network;
|
|
752
|
+
authenticationDomain;
|
|
753
|
+
passportDomain;
|
|
754
|
+
magicPublishableApiKey;
|
|
755
|
+
magicProviderId;
|
|
756
|
+
imxApiBasePath;
|
|
757
|
+
oidcConfiguration;
|
|
758
|
+
baseConfig;
|
|
759
|
+
constructor({ baseConfig, overrides, ...oidcConfiguration }) {
|
|
760
|
+
validateConfiguration(oidcConfiguration, [
|
|
761
|
+
'clientId',
|
|
762
|
+
'logoutRedirectUri',
|
|
763
|
+
'redirectUri',
|
|
764
|
+
]);
|
|
765
|
+
this.oidcConfiguration = oidcConfiguration;
|
|
766
|
+
this.baseConfig = baseConfig;
|
|
767
|
+
if (overrides) {
|
|
768
|
+
validateConfiguration(overrides, [
|
|
769
|
+
'network',
|
|
770
|
+
'authenticationDomain',
|
|
771
|
+
'magicPublishableApiKey',
|
|
772
|
+
'magicProviderId',
|
|
773
|
+
'passportDomain',
|
|
774
|
+
'imxApiBasePath',
|
|
775
|
+
], 'overrides');
|
|
776
|
+
this.network = overrides.network;
|
|
777
|
+
this.authenticationDomain = overrides.authenticationDomain;
|
|
778
|
+
this.passportDomain = overrides.passportDomain;
|
|
779
|
+
this.magicPublishableApiKey = overrides.magicPublishableApiKey;
|
|
780
|
+
this.magicProviderId = overrides.magicProviderId;
|
|
781
|
+
this.imxApiBasePath = overrides.imxApiBasePath;
|
|
782
|
+
}
|
|
783
|
+
else {
|
|
784
|
+
switch (baseConfig.environment) {
|
|
785
|
+
case Environment.SANDBOX: {
|
|
786
|
+
this.network = Networks.SANDBOX;
|
|
787
|
+
this.authenticationDomain = 'https://auth.immutable.com';
|
|
788
|
+
this.magicPublishableApiKey = 'pk_live_10F423798A540ED7';
|
|
789
|
+
this.magicProviderId = 'fSMzaRQ4O7p4fttl7pCyGVtJS_G70P8SNsLXtPPGHo0=';
|
|
790
|
+
this.passportDomain = 'https://passport.immutable.com';
|
|
791
|
+
this.imxApiBasePath = 'https://api.x.immutable.com';
|
|
792
|
+
break;
|
|
793
|
+
}
|
|
794
|
+
case Environment.PRODUCTION: {
|
|
795
|
+
this.network = Networks.PRODUCTION;
|
|
796
|
+
this.authenticationDomain = 'https://auth.immutable.com';
|
|
797
|
+
this.magicPublishableApiKey = 'pk_live_10F423798A540ED7';
|
|
798
|
+
this.magicProviderId = 'fSMzaRQ4O7p4fttl7pCyGVtJS_G70P8SNsLXtPPGHo0=';
|
|
799
|
+
this.passportDomain = 'https://passport.sandbox.immutable.com';
|
|
800
|
+
this.imxApiBasePath = 'https://api.sandbox.x.immutable.com';
|
|
801
|
+
break;
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
const getStarkSigner = async (signer) => {
|
|
809
|
+
return withPassportError(async () => {
|
|
810
|
+
const privateKey = await generateLegacyStarkPrivateKey(signer);
|
|
811
|
+
return createStarkSigner(privateKey);
|
|
812
|
+
}, PassportErrorType.WALLET_CONNECTION_ERROR);
|
|
813
|
+
};
|
|
814
|
+
|
|
815
|
+
async function registerPassport({ ethSigner, starkSigner, usersApi }, authorization) {
|
|
816
|
+
return withPassportError(async () => {
|
|
817
|
+
const userAddress = await ethSigner.getAddress();
|
|
818
|
+
const starkPublicKey = await starkSigner.getAddress();
|
|
819
|
+
const signableResult = await usersApi.getSignableRegistrationOffchain({
|
|
820
|
+
getSignableRegistrationRequest: {
|
|
821
|
+
ether_key: userAddress,
|
|
822
|
+
stark_key: starkPublicKey,
|
|
823
|
+
},
|
|
824
|
+
});
|
|
825
|
+
const { signable_message: signableMessage, payload_hash: payloadHash } = signableResult.data;
|
|
826
|
+
const ethSignature = await signRaw(signableMessage, ethSigner);
|
|
827
|
+
const starkSignature = await starkSigner.signMessage(payloadHash);
|
|
828
|
+
const response = await usersApi.registerPassportUser({
|
|
829
|
+
authorization: `Bearer ` + authorization,
|
|
830
|
+
registerPassportUserRequest: {
|
|
831
|
+
eth_signature: ethSignature,
|
|
832
|
+
ether_key: userAddress,
|
|
833
|
+
stark_signature: starkSignature,
|
|
834
|
+
stark_key: starkPublicKey,
|
|
835
|
+
},
|
|
836
|
+
});
|
|
837
|
+
return response.statusText;
|
|
838
|
+
}, PassportErrorType.USER_REGISTRATION_ERROR);
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
class Passport {
|
|
842
|
+
authManager;
|
|
843
|
+
magicAdapter;
|
|
844
|
+
config;
|
|
845
|
+
constructor(passportModuleConfiguration) {
|
|
846
|
+
const passportConfiguration = new PassportConfiguration(passportModuleConfiguration);
|
|
847
|
+
this.config = passportConfiguration;
|
|
848
|
+
this.authManager = new AuthManager(this.config);
|
|
849
|
+
this.magicAdapter = new MagicAdapter(this.config);
|
|
850
|
+
}
|
|
851
|
+
async connectImx() {
|
|
852
|
+
const user = await this.authManager.login();
|
|
853
|
+
if (!user.idToken) {
|
|
854
|
+
throw new PassportError('Failed to initialise', PassportErrorType.WALLET_CONNECTION_ERROR);
|
|
855
|
+
}
|
|
856
|
+
const provider = await this.magicAdapter.login(user.idToken);
|
|
857
|
+
const ethSigner = provider.getSigner();
|
|
858
|
+
const starkSigner = await getStarkSigner(ethSigner);
|
|
859
|
+
if (!user.etherKey) {
|
|
860
|
+
const updatedUser = await this.registerUser(ethSigner, starkSigner, user.accessToken);
|
|
861
|
+
return new PassportImxProvider({
|
|
862
|
+
user: updatedUser,
|
|
863
|
+
starkSigner,
|
|
864
|
+
passportConfig: this.config,
|
|
865
|
+
});
|
|
866
|
+
}
|
|
867
|
+
const userWithEtherKey = user;
|
|
868
|
+
return new PassportImxProvider({
|
|
869
|
+
user: userWithEtherKey,
|
|
870
|
+
starkSigner,
|
|
871
|
+
passportConfig: this.config,
|
|
872
|
+
});
|
|
873
|
+
}
|
|
874
|
+
async loginCallback() {
|
|
875
|
+
return this.authManager.loginCallback();
|
|
876
|
+
}
|
|
877
|
+
async logout() {
|
|
878
|
+
return this.authManager.logout();
|
|
879
|
+
}
|
|
880
|
+
async getUserInfo() {
|
|
881
|
+
const user = await this.authManager.getUser();
|
|
882
|
+
return user.profile;
|
|
883
|
+
}
|
|
884
|
+
async getIdToken() {
|
|
885
|
+
const user = await this.authManager.getUser();
|
|
886
|
+
return user.idToken;
|
|
887
|
+
}
|
|
888
|
+
async getAccessToken() {
|
|
889
|
+
const user = await this.authManager.getUser();
|
|
890
|
+
return user.accessToken;
|
|
891
|
+
}
|
|
892
|
+
async registerUser(userAdminKeySigner, starkSigner, jwt) {
|
|
893
|
+
const configuration = new Configuration({
|
|
894
|
+
basePath: this.config.imxApiBasePath,
|
|
895
|
+
});
|
|
896
|
+
const usersApi = new UsersApi(configuration);
|
|
897
|
+
await registerPassport({
|
|
898
|
+
ethSigner: userAdminKeySigner,
|
|
899
|
+
starkSigner,
|
|
900
|
+
usersApi,
|
|
901
|
+
}, jwt);
|
|
902
|
+
const updatedUser = await this.authManager.requestRefreshTokenAfterRegistration();
|
|
903
|
+
if (!updatedUser) {
|
|
904
|
+
throw new PassportError('Failed to get refresh token', PassportErrorType.REFRESH_TOKEN_ERROR);
|
|
905
|
+
}
|
|
906
|
+
return updatedUser;
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
export { Passport, PassportConfiguration, PassportError };
|