@imtbl/sdk 1.6.1 → 1.7.1

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/passport.js CHANGED
@@ -8,10 +8,10 @@ import * as crypto from 'crypto';
8
8
  import { Magic } from 'magic-sdk';
9
9
  import { OpenIdExtension } from '@magic-ext/oidc';
10
10
  import { JsonRpcProvider, Web3Provider } from '@ethersproject/providers';
11
- import { BigNumber, ethers } from 'ethers';
12
11
  import BN from 'bn.js';
13
12
  import * as encUtils from 'enc-utils';
14
13
  import { EventEmitter } from 'events';
14
+ import { BigNumber, ethers } from 'ethers';
15
15
  import { walletContracts } from '@0xsequence/abi';
16
16
  import { v1 } from '@0xsequence/core';
17
17
 
@@ -9146,7 +9146,7 @@ class MultiRollupApiClients {
9146
9146
  }
9147
9147
 
9148
9148
  // eslint-disable-next-line @typescript-eslint/naming-convention
9149
- const defaultHeaders = { 'x-sdk-version': 'ts-immutable-sdk-1.6.1' };
9149
+ const defaultHeaders = { 'x-sdk-version': 'ts-immutable-sdk-1.7.1' };
9150
9150
  const createConfig = ({ basePath, headers, }) => {
9151
9151
  if (!basePath.trim()) {
9152
9152
  throw Error('basePath can not be empty');
@@ -9325,7 +9325,7 @@ const flattenProperties = (properties) => {
9325
9325
  };
9326
9326
 
9327
9327
  // WARNING: DO NOT CHANGE THE STRING BELOW. IT GETS REPLACED AT BUILD TIME.
9328
- const SDK_VERSION = '1.6.1';
9328
+ const SDK_VERSION = '1.7.1';
9329
9329
  const getReferrer = () => {
9330
9330
  if (isNode()) {
9331
9331
  return '';
@@ -9537,7 +9537,7 @@ const createImmutableXConfiguration = ({ basePath, chainID, coreContractAddress,
9537
9537
  chainID,
9538
9538
  coreContractAddress,
9539
9539
  registrationContractAddress,
9540
- sdkVersion: 'ts-immutable-sdk-1.6.1',
9540
+ sdkVersion: 'ts-immutable-sdk-1.7.1',
9541
9541
  });
9542
9542
  /**
9543
9543
  * Sets `sdkVersion` at the time of build
@@ -10040,6 +10040,69 @@ class IMXClient {
10040
10040
  }
10041
10041
  }
10042
10042
 
10043
+ // used to sign message with L1 keys. Used for registration
10044
+ function serializeEthSignature(sig) {
10045
+ // This is because golang appends a recovery param
10046
+ // https://github.com/ethers-io/ethers.js/issues/823
10047
+ return encUtils.addHexPrefix(encUtils.padLeft(sig.r.toString(16), 64)
10048
+ + encUtils.padLeft(sig.s.toString(16), 64)
10049
+ + encUtils.padLeft(sig.recoveryParam?.toString(16) || '', 2));
10050
+ }
10051
+ function importRecoveryParam(v) {
10052
+ const isValidBigNumber = new BN(v, 16).cmp(new BN(27)) !== -1
10053
+ ? new BN(v, 16).sub(new BN(27)).toNumber()
10054
+ : new BN(v, 16).toNumber();
10055
+ return v.trim()
10056
+ ? isValidBigNumber
10057
+ : undefined;
10058
+ }
10059
+ // used chained with serializeEthSignature. serializeEthSignature(deserializeSignature(...))
10060
+ function deserializeSignature(sig, size = 64) {
10061
+ const removedHexPrefixSig = encUtils.removeHexPrefix(sig);
10062
+ return {
10063
+ r: new BN(removedHexPrefixSig.substring(0, size), 'hex'),
10064
+ s: new BN(removedHexPrefixSig.substring(size, size * 2), 'hex'),
10065
+ recoveryParam: importRecoveryParam(removedHexPrefixSig.substring(size * 2, size * 2 + 2)),
10066
+ };
10067
+ }
10068
+ async function signRaw(payload, signer) {
10069
+ const signature = deserializeSignature(await signer.signMessage(payload));
10070
+ return serializeEthSignature(signature);
10071
+ }
10072
+
10073
+ /**
10074
+ * Helper method to convert token type to a SignableToken type
10075
+ * @param token - the token type to convert to a SignableToken type
10076
+ * @returns the converted SignableToken
10077
+ */
10078
+ function convertToSignableToken(token) {
10079
+ switch (token.type) {
10080
+ case 'ERC721':
10081
+ return {
10082
+ type: 'ERC721',
10083
+ data: {
10084
+ token_id: token.tokenId,
10085
+ token_address: token.tokenAddress,
10086
+ },
10087
+ };
10088
+ case 'ERC20':
10089
+ return {
10090
+ type: 'ERC20',
10091
+ data: {
10092
+ token_address: token.tokenAddress,
10093
+ },
10094
+ };
10095
+ case 'ETH':
10096
+ default:
10097
+ return {
10098
+ type: 'ETH',
10099
+ data: {
10100
+ decimals: 18,
10101
+ },
10102
+ };
10103
+ }
10104
+ }
10105
+
10043
10106
  function bind(fn, thisArg) {
10044
10107
  return function wrap() {
10045
10108
  return fn.apply(thisArg, arguments);
@@ -13955,69 +14018,6 @@ const TransactionApprovalRequestChainTypeEnum = {
13955
14018
  Evm: 'evm'
13956
14019
  };
13957
14020
 
13958
- // used to sign message with L1 keys. Used for registration
13959
- function serializeEthSignature(sig) {
13960
- // This is because golang appends a recovery param
13961
- // https://github.com/ethers-io/ethers.js/issues/823
13962
- return encUtils.addHexPrefix(encUtils.padLeft(sig.r.toString(16), 64)
13963
- + encUtils.padLeft(sig.s.toString(16), 64)
13964
- + encUtils.padLeft(sig.recoveryParam?.toString(16) || '', 2));
13965
- }
13966
- function importRecoveryParam(v) {
13967
- const isValidBigNumber = new BN(v, 16).cmp(new BN(27)) !== -1
13968
- ? new BN(v, 16).sub(new BN(27)).toNumber()
13969
- : new BN(v, 16).toNumber();
13970
- return v.trim()
13971
- ? isValidBigNumber
13972
- : undefined;
13973
- }
13974
- // used chained with serializeEthSignature. serializeEthSignature(deserializeSignature(...))
13975
- function deserializeSignature(sig, size = 64) {
13976
- const removedHexPrefixSig = encUtils.removeHexPrefix(sig);
13977
- return {
13978
- r: new BN(removedHexPrefixSig.substring(0, size), 'hex'),
13979
- s: new BN(removedHexPrefixSig.substring(size, size * 2), 'hex'),
13980
- recoveryParam: importRecoveryParam(removedHexPrefixSig.substring(size * 2, size * 2 + 2)),
13981
- };
13982
- }
13983
- async function signRaw(payload, signer) {
13984
- const signature = deserializeSignature(await signer.signMessage(payload));
13985
- return serializeEthSignature(signature);
13986
- }
13987
-
13988
- /**
13989
- * Helper method to convert token type to a SignableToken type
13990
- * @param token - the token type to convert to a SignableToken type
13991
- * @returns the converted SignableToken
13992
- */
13993
- function convertToSignableToken(token) {
13994
- switch (token.type) {
13995
- case 'ERC721':
13996
- return {
13997
- type: 'ERC721',
13998
- data: {
13999
- token_id: token.tokenId,
14000
- token_address: token.tokenAddress,
14001
- },
14002
- };
14003
- case 'ERC20':
14004
- return {
14005
- type: 'ERC20',
14006
- data: {
14007
- token_address: token.tokenAddress,
14008
- },
14009
- };
14010
- case 'ETH':
14011
- default:
14012
- return {
14013
- type: 'ETH',
14014
- data: {
14015
- decimals: 18,
14016
- },
14017
- };
14018
- }
14019
- }
14020
-
14021
14021
  var PassportErrorType;
14022
14022
  (function (PassportErrorType) {
14023
14023
  PassportErrorType["AUTHENTICATION_ERROR"] = "AUTHENTICATION_ERROR";
@@ -14271,6 +14271,17 @@ class AuthManager {
14271
14271
  return AuthManager.mapOidcUserToDomainModel(oidcUser);
14272
14272
  }, PassportErrorType.AUTHENTICATION_ERROR);
14273
14273
  }
14274
+ async getUserOrLogin() {
14275
+ let user = null;
14276
+ try {
14277
+ user = await this.getUser();
14278
+ }
14279
+ catch (err) {
14280
+ // eslint-disable-next-line no-console
14281
+ console.warn('failed to retrieve a cached user session:', err);
14282
+ }
14283
+ return user || this.login();
14284
+ }
14274
14285
  async loginCallback() {
14275
14286
  return withPassportError(async () => this.userManager.signinPopupCallback(), PassportErrorType.AUTHENTICATION_ERROR);
14276
14287
  }
@@ -14518,274 +14529,53 @@ const getStarkSigner = async (signer) => withPassportError(async () => {
14518
14529
  return createStarkSigner(privateKey);
14519
14530
  }, PassportErrorType.WALLET_CONNECTION_ERROR);
14520
14531
 
14521
- const POLL_INTERVAL = 1 * 1000; // every 1 second
14522
- const MAX_RETRIES = 3;
14523
- const wait = (ms) => new Promise((resolve) => {
14524
- setTimeout(() => resolve(), ms);
14525
- });
14526
- const retryWithDelay = async (fn, options) => {
14527
- const { retries = MAX_RETRIES, interval = POLL_INTERVAL, finalErr = Error('Retry failed'), finallyFn = () => { }, } = options || {};
14528
- try {
14529
- return await fn();
14530
- }
14531
- catch (err) {
14532
- if (retries <= 0) {
14533
- return Promise.reject(finalErr);
14534
- }
14535
- await wait(interval);
14536
- return retryWithDelay(fn, { retries: retries - 1, finalErr, finallyFn });
14537
- }
14538
- finally {
14539
- if (retries <= 0) {
14540
- finallyFn();
14541
- }
14542
- }
14543
- };
14532
+ var PassportEvents;
14533
+ (function (PassportEvents) {
14534
+ PassportEvents["LOGGED_OUT"] = "loggedOut";
14535
+ })(PassportEvents || (PassportEvents = {}));
14544
14536
 
14545
- /**
14546
- * ProviderErrors should take priority over RpcErrorCodes
14547
- * https://eips.ethereum.org/EIPS/eip-1193#provider-errors
14548
- * https://eips.ethereum.org/EIPS/eip-1474#error-codes
14549
- */
14550
- var ProviderErrorCode;
14551
- (function (ProviderErrorCode) {
14552
- ProviderErrorCode[ProviderErrorCode["USER_REJECTED_REQUEST"] = 4001] = "USER_REJECTED_REQUEST";
14553
- ProviderErrorCode[ProviderErrorCode["UNAUTHORIZED"] = 4100] = "UNAUTHORIZED";
14554
- ProviderErrorCode[ProviderErrorCode["UNSUPPORTED_METHOD"] = 4200] = "UNSUPPORTED_METHOD";
14555
- ProviderErrorCode[ProviderErrorCode["DISCONNECTED"] = 4900] = "DISCONNECTED";
14556
- })(ProviderErrorCode || (ProviderErrorCode = {}));
14557
- var RpcErrorCode;
14558
- (function (RpcErrorCode) {
14559
- RpcErrorCode[RpcErrorCode["RPC_SERVER_ERROR"] = -32000] = "RPC_SERVER_ERROR";
14560
- RpcErrorCode[RpcErrorCode["INVALID_REQUEST"] = -32600] = "INVALID_REQUEST";
14561
- RpcErrorCode[RpcErrorCode["METHOD_NOT_FOUND"] = -32601] = "METHOD_NOT_FOUND";
14562
- RpcErrorCode[RpcErrorCode["INVALID_PARAMS"] = -32602] = "INVALID_PARAMS";
14563
- RpcErrorCode[RpcErrorCode["INTERNAL_ERROR"] = -32603] = "INTERNAL_ERROR";
14564
- RpcErrorCode[RpcErrorCode["PARSE_ERROR"] = -32700] = "PARSE_ERROR";
14565
- RpcErrorCode[RpcErrorCode["TRANSACTION_REJECTED"] = -32003] = "TRANSACTION_REJECTED";
14566
- })(RpcErrorCode || (RpcErrorCode = {}));
14567
- class JsonRpcError extends Error {
14568
- message;
14569
- code;
14570
- constructor(code, message) {
14571
- super(message);
14572
- this.message = message;
14573
- this.code = code;
14574
- }
14575
- }
14576
-
14577
- const transactionRejectedCrossSdkBridgeError = 'Transaction requires confirmation but this functionality is not'
14578
- + ' supported in this environment. Please contact Immutable support if you need to enable this feature.';
14579
- const convertBigNumberishToString = (value) => BigNumber.from(value).toString();
14580
- const transformGuardianTransactions = (txs) => {
14581
- try {
14582
- return txs.map((t) => ({
14583
- delegateCall: t.delegateCall === true,
14584
- revertOnError: t.revertOnError === true,
14585
- gasLimit: t.gasLimit ? convertBigNumberishToString(t.gasLimit) : '0',
14586
- target: t.to ?? ethers.constants.AddressZero,
14587
- value: t.value ? convertBigNumberishToString(t.value) : '0',
14588
- data: t.data ? t.data.toString() : '0x',
14589
- }));
14590
- }
14591
- catch (error) {
14592
- const errorMessage = error instanceof Error ? error.message : String(error);
14593
- throw new JsonRpcError(RpcErrorCode.INVALID_PARAMS, `Transaction failed to parsing: ${errorMessage}`);
14594
- }
14595
- };
14596
- class GuardianClient {
14597
- transactionAPI;
14598
- messageAPI;
14599
- confirmationScreen;
14600
- crossSdkBridgeEnabled;
14601
- authManager;
14602
- constructor({ confirmationScreen, config, authManager }) {
14603
- const guardianConfiguration = new Configuration({ basePath: config.imxPublicApiDomain });
14604
- this.confirmationScreen = confirmationScreen;
14605
- this.crossSdkBridgeEnabled = config.crossSdkBridgeEnabled;
14606
- this.messageAPI = new MessagesApi(guardianConfiguration);
14607
- this.transactionAPI = new TransactionsApi(guardianConfiguration);
14608
- this.authManager = authManager;
14609
- }
14610
- /**
14611
- * Open confirmation screen and close it automatically if the
14612
- * underlying task fails.
14613
- */
14614
- withConfirmationScreen(popupWindowSize) {
14615
- return (task) => this.withConfirmationScreenTask(popupWindowSize)(task)();
14616
- }
14617
- withConfirmationScreenTask(popupWindowSize) {
14618
- return (task) => async () => {
14619
- this.confirmationScreen.loading(popupWindowSize);
14620
- try {
14621
- return await task();
14622
- }
14623
- catch (err) {
14624
- this.confirmationScreen.closeWindow();
14625
- throw err;
14626
- }
14627
- };
14628
- }
14629
- withDefaultConfirmationScreenTask(task) {
14630
- return this.withConfirmationScreenTask()(task);
14631
- }
14632
- async evaluateImxTransaction({ payloadHash }) {
14633
- const finallyFn = () => {
14634
- this.confirmationScreen.closeWindow();
14635
- };
14636
- const user = await this.authManager.getUser();
14637
- const headers = { Authorization: `Bearer ${user.accessToken}` };
14638
- const transactionRes = await retryWithDelay(async () => this.transactionAPI.getTransactionByID({
14639
- transactionID: payloadHash,
14640
- chainType: 'starkex',
14641
- }, { headers }), { finallyFn });
14642
- if (!transactionRes.data.id) {
14643
- throw new Error("Transaction doesn't exists");
14644
- }
14645
- const evaluateImxRes = await this.transactionAPI.evaluateTransaction({
14646
- id: payloadHash,
14647
- transactionEvaluationRequest: {
14648
- chainType: 'starkex',
14649
- },
14650
- }, { headers });
14651
- const { confirmationRequired } = evaluateImxRes.data;
14652
- if (confirmationRequired) {
14653
- if (this.crossSdkBridgeEnabled) {
14654
- throw new Error(transactionRejectedCrossSdkBridgeError);
14655
- }
14656
- const confirmationResult = await this.confirmationScreen.requestConfirmation(payloadHash, user.imx.ethAddress, TransactionApprovalRequestChainTypeEnum.Starkex);
14657
- if (!confirmationResult.confirmed) {
14658
- throw new Error('Transaction rejected by user');
14659
- }
14660
- }
14661
- else {
14662
- this.confirmationScreen.closeWindow();
14663
- }
14664
- }
14665
- async evaluateEVMTransaction({ chainId, nonce, metaTransactions, }) {
14666
- const user = await this.authManager.getUser();
14667
- const headers = { Authorization: `Bearer ${user.accessToken}` };
14668
- const guardianTransactions = transformGuardianTransactions(metaTransactions);
14669
- try {
14670
- const transactionEvaluationResponseAxiosResponse = await this.transactionAPI.evaluateTransaction({
14671
- id: 'evm',
14672
- transactionEvaluationRequest: {
14673
- chainType: 'evm',
14674
- chainId,
14675
- transactionData: {
14676
- nonce,
14677
- userAddress: user.zkEvm.ethAddress,
14678
- metaTransactions: guardianTransactions,
14679
- },
14680
- },
14681
- }, { headers });
14682
- return transactionEvaluationResponseAxiosResponse.data;
14683
- }
14684
- catch (error) {
14685
- const errorMessage = error instanceof Error ? error.message : String(error);
14686
- throw new JsonRpcError(RpcErrorCode.INTERNAL_ERROR, `Transaction failed to validate with error: ${errorMessage}`);
14687
- }
14688
- }
14689
- async validateEVMTransaction({ chainId, nonce, metaTransactions, }) {
14690
- const transactionEvaluationResponse = await this.evaluateEVMTransaction({
14691
- chainId,
14692
- nonce,
14693
- metaTransactions,
14694
- });
14695
- const { confirmationRequired, transactionId } = transactionEvaluationResponse;
14696
- if (confirmationRequired && this.crossSdkBridgeEnabled) {
14697
- throw new JsonRpcError(RpcErrorCode.TRANSACTION_REJECTED, transactionRejectedCrossSdkBridgeError);
14698
- }
14699
- if (confirmationRequired && !!transactionId) {
14700
- const user = await this.authManager.getUser();
14701
- const confirmationResult = await this.confirmationScreen.requestConfirmation(transactionId, user.zkEvm.ethAddress, TransactionApprovalRequestChainTypeEnum.Evm, chainId);
14702
- if (!confirmationResult.confirmed) {
14703
- throw new JsonRpcError(RpcErrorCode.TRANSACTION_REJECTED, 'Transaction rejected by user');
14704
- }
14705
- }
14706
- else {
14707
- this.confirmationScreen.closeWindow();
14708
- }
14709
- }
14710
- async evaluateMessage({ chainID, payload }) {
14711
- try {
14712
- const user = await this.authManager.getUser();
14713
- if (user === null) {
14714
- throw new PassportError('evaluateMessage requires a valid ID token or refresh token. Please log in first', PassportErrorType.NOT_LOGGED_IN_ERROR);
14715
- }
14716
- const messageEvalResponse = await this.messageAPI.evaluateMessage({ messageEvaluationRequest: { chainID, payload } }, { headers: { Authorization: `Bearer ${user.accessToken}` } });
14717
- return messageEvalResponse.data;
14718
- }
14719
- catch (error) {
14720
- const errorMessage = error instanceof Error ? error.message : String(error);
14721
- throw new JsonRpcError(RpcErrorCode.INTERNAL_ERROR, `Message failed to validate with error: ${errorMessage}`);
14722
- }
14723
- }
14724
- async validateMessage({ chainID, payload }) {
14725
- const { messageId, confirmationRequired } = await this.evaluateMessage({ chainID, payload });
14726
- if (confirmationRequired && this.crossSdkBridgeEnabled) {
14727
- throw new JsonRpcError(RpcErrorCode.TRANSACTION_REJECTED, transactionRejectedCrossSdkBridgeError);
14728
- }
14729
- if (confirmationRequired && !!messageId) {
14730
- const user = await this.authManager.getUser();
14731
- const confirmationResult = await this.confirmationScreen.requestMessageConfirmation(messageId, user.zkEvm.ethAddress);
14732
- if (!confirmationResult.confirmed) {
14733
- throw new JsonRpcError(RpcErrorCode.TRANSACTION_REJECTED, 'Signature rejected by user');
14734
- }
14735
- }
14736
- else {
14737
- this.confirmationScreen.closeWindow();
14738
- }
14739
- }
14740
- }
14741
-
14742
- var PassportEvents;
14743
- (function (PassportEvents) {
14744
- PassportEvents["LOGGED_OUT"] = "loggedOut";
14745
- })(PassportEvents || (PassportEvents = {}));
14746
-
14747
- async function exchangeTransfer({ user, starkSigner, request, exchangesApi, }) {
14748
- return withPassportError(async () => {
14749
- const { ethAddress } = user.imx;
14750
- const transferAmount = request.amount;
14751
- const signableResult = await exchangesApi.getExchangeSignableTransfer({
14752
- id: request.transactionID,
14753
- getSignableTransferRequest: {
14754
- sender: ethAddress,
14755
- token: convertToSignableToken(request),
14756
- amount: transferAmount,
14757
- receiver: request.receiver,
14758
- },
14759
- });
14760
- const starkAddress = await starkSigner.getAddress();
14761
- const { payload_hash: payloadHash } = signableResult.data;
14762
- const starkSignature = await starkSigner.signMessage(payloadHash);
14763
- const transferSigningParams = {
14764
- sender_stark_key: signableResult.data.sender_stark_key || starkAddress,
14765
- sender_vault_id: signableResult.data.sender_vault_id,
14766
- receiver_stark_key: signableResult.data.receiver_stark_key,
14767
- receiver_vault_id: signableResult.data.receiver_vault_id,
14768
- asset_id: signableResult.data.asset_id,
14769
- amount: signableResult.data.amount,
14770
- nonce: signableResult.data.nonce,
14771
- expiration_timestamp: signableResult.data.expiration_timestamp,
14772
- stark_signature: starkSignature,
14773
- };
14774
- const headers = {
14775
- // eslint-disable-next-line @typescript-eslint/naming-convention
14776
- Authorization: `Bearer ${user.accessToken}`,
14777
- };
14778
- const response = await exchangesApi.createExchangeTransfer({
14779
- id: request.transactionID,
14780
- createTransferRequest: transferSigningParams,
14781
- }, { headers });
14782
- return {
14783
- sent_signature: response?.data.sent_signature,
14784
- status: response?.data.status?.toString(),
14785
- time: response?.data.time,
14786
- transfer_id: response?.data.transfer_id,
14787
- };
14788
- }, PassportErrorType.EXCHANGE_TRANSFER_ERROR);
14537
+ async function exchangeTransfer({ user, starkSigner, request, exchangesApi, }) {
14538
+ return withPassportError(async () => {
14539
+ const { ethAddress } = user.imx;
14540
+ const transferAmount = request.amount;
14541
+ const signableResult = await exchangesApi.getExchangeSignableTransfer({
14542
+ id: request.transactionID,
14543
+ getSignableTransferRequest: {
14544
+ sender: ethAddress,
14545
+ token: convertToSignableToken(request),
14546
+ amount: transferAmount,
14547
+ receiver: request.receiver,
14548
+ },
14549
+ });
14550
+ const starkAddress = await starkSigner.getAddress();
14551
+ const { payload_hash: payloadHash } = signableResult.data;
14552
+ const starkSignature = await starkSigner.signMessage(payloadHash);
14553
+ const transferSigningParams = {
14554
+ sender_stark_key: signableResult.data.sender_stark_key || starkAddress,
14555
+ sender_vault_id: signableResult.data.sender_vault_id,
14556
+ receiver_stark_key: signableResult.data.receiver_stark_key,
14557
+ receiver_vault_id: signableResult.data.receiver_vault_id,
14558
+ asset_id: signableResult.data.asset_id,
14559
+ amount: signableResult.data.amount,
14560
+ nonce: signableResult.data.nonce,
14561
+ expiration_timestamp: signableResult.data.expiration_timestamp,
14562
+ stark_signature: starkSignature,
14563
+ };
14564
+ const headers = {
14565
+ // eslint-disable-next-line @typescript-eslint/naming-convention
14566
+ Authorization: `Bearer ${user.accessToken}`,
14567
+ };
14568
+ const response = await exchangesApi.createExchangeTransfer({
14569
+ id: request.transactionID,
14570
+ createTransferRequest: transferSigningParams,
14571
+ }, { headers });
14572
+ return {
14573
+ sent_signature: response?.data.sent_signature,
14574
+ status: response?.data.status?.toString(),
14575
+ time: response?.data.time,
14576
+ transfer_id: response?.data.transfer_id,
14577
+ };
14578
+ }, PassportErrorType.EXCHANGE_TRANSFER_ERROR);
14789
14579
  }
14790
14580
 
14791
14581
  const ERC721$1 = 'ERC721';
@@ -15037,6 +14827,30 @@ async function batchNftTransfer({ user, starkSigner, request, transfersApi, guar
15037
14827
  }), PassportErrorType.TRANSFER_ERROR);
15038
14828
  }
15039
14829
 
14830
+ const POLL_INTERVAL = 1 * 1000; // every 1 second
14831
+ const MAX_RETRIES = 3;
14832
+ const wait = (ms) => new Promise((resolve) => {
14833
+ setTimeout(() => resolve(), ms);
14834
+ });
14835
+ const retryWithDelay = async (fn, options) => {
14836
+ const { retries = MAX_RETRIES, interval = POLL_INTERVAL, finalErr = Error('Retry failed'), finallyFn = () => { }, } = options || {};
14837
+ try {
14838
+ return await fn();
14839
+ }
14840
+ catch (err) {
14841
+ if (retries <= 0) {
14842
+ return Promise.reject(finalErr);
14843
+ }
14844
+ await wait(interval);
14845
+ return retryWithDelay(fn, { retries: retries - 1, finalErr, finallyFn });
14846
+ }
14847
+ finally {
14848
+ if (retries <= 0) {
14849
+ finallyFn();
14850
+ }
14851
+ }
14852
+ };
14853
+
15040
14854
  async function forceUserRefresh(authManager) {
15041
14855
  // User metadata is updated asynchronously. Poll userinfo endpoint until it is updated.
15042
14856
  await retryWithDelay(async () => {
@@ -15082,16 +14896,12 @@ class PassportImxProvider {
15082
14896
  */
15083
14897
  signers;
15084
14898
  signerInitialisationError;
15085
- constructor({ authManager, immutableXClient, confirmationScreen, config, passportEventEmitter, magicAdapter, imxApiClients, }) {
14899
+ constructor({ authManager, immutableXClient, passportEventEmitter, magicAdapter, imxApiClients, guardianClient, }) {
15086
14900
  this.authManager = authManager;
15087
14901
  this.immutableXClient = immutableXClient;
15088
- this.guardianClient = new GuardianClient({
15089
- confirmationScreen,
15090
- config,
15091
- authManager,
15092
- });
15093
14902
  this.magicAdapter = magicAdapter;
15094
14903
  this.imxApiClients = imxApiClients;
14904
+ this.guardianClient = guardianClient;
15095
14905
  this.initialiseSigners();
15096
14906
  passportEventEmitter.on(PassportEvents.LOGGED_OUT, this.handleLogout);
15097
14907
  }
@@ -15254,33 +15064,21 @@ class PassportImxProvider {
15254
15064
 
15255
15065
  class PassportImxProviderFactory {
15256
15066
  authManager;
15257
- config;
15258
- confirmationScreen;
15259
15067
  immutableXClient;
15260
15068
  magicAdapter;
15261
15069
  passportEventEmitter;
15262
15070
  imxApiClients;
15263
- constructor({ authManager, config, confirmationScreen, immutableXClient, magicAdapter, passportEventEmitter, imxApiClients, }) {
15071
+ guardianClient;
15072
+ constructor({ authManager, immutableXClient, magicAdapter, passportEventEmitter, imxApiClients, guardianClient, }) {
15264
15073
  this.authManager = authManager;
15265
- this.config = config;
15266
- this.confirmationScreen = confirmationScreen;
15267
15074
  this.immutableXClient = immutableXClient;
15268
15075
  this.magicAdapter = magicAdapter;
15269
15076
  this.passportEventEmitter = passportEventEmitter;
15270
15077
  this.imxApiClients = imxApiClients;
15078
+ this.guardianClient = guardianClient;
15271
15079
  }
15272
15080
  async getProvider() {
15273
- let user = null;
15274
- try {
15275
- user = await this.authManager.getUser();
15276
- }
15277
- catch (e) {
15278
- // eslint-disable-next-line no-console
15279
- console.warn(e);
15280
- }
15281
- if (!user) {
15282
- user = await this.authManager.login();
15283
- }
15081
+ const user = await this.authManager.getUserOrLogin();
15284
15082
  return this.createProviderInstance(user);
15285
15083
  }
15286
15084
  async getProviderSilent() {
@@ -15295,13 +15093,12 @@ class PassportImxProviderFactory {
15295
15093
  throw new PassportError('Failed to initialise', PassportErrorType.WALLET_CONNECTION_ERROR);
15296
15094
  }
15297
15095
  return new PassportImxProvider({
15298
- config: this.config,
15299
15096
  authManager: this.authManager,
15300
15097
  immutableXClient: this.immutableXClient,
15301
- confirmationScreen: this.confirmationScreen,
15302
15098
  passportEventEmitter: this.passportEventEmitter,
15303
15099
  magicAdapter: this.magicAdapter,
15304
15100
  imxApiClients: this.imxApiClients,
15101
+ guardianClient: this.guardianClient,
15305
15102
  });
15306
15103
  }
15307
15104
  }
@@ -15796,6 +15593,38 @@ class RelayerClient {
15796
15593
  }
15797
15594
  }
15798
15595
 
15596
+ /**
15597
+ * ProviderErrors should take priority over RpcErrorCodes
15598
+ * https://eips.ethereum.org/EIPS/eip-1193#provider-errors
15599
+ * https://eips.ethereum.org/EIPS/eip-1474#error-codes
15600
+ */
15601
+ var ProviderErrorCode;
15602
+ (function (ProviderErrorCode) {
15603
+ ProviderErrorCode[ProviderErrorCode["USER_REJECTED_REQUEST"] = 4001] = "USER_REJECTED_REQUEST";
15604
+ ProviderErrorCode[ProviderErrorCode["UNAUTHORIZED"] = 4100] = "UNAUTHORIZED";
15605
+ ProviderErrorCode[ProviderErrorCode["UNSUPPORTED_METHOD"] = 4200] = "UNSUPPORTED_METHOD";
15606
+ ProviderErrorCode[ProviderErrorCode["DISCONNECTED"] = 4900] = "DISCONNECTED";
15607
+ })(ProviderErrorCode || (ProviderErrorCode = {}));
15608
+ var RpcErrorCode;
15609
+ (function (RpcErrorCode) {
15610
+ RpcErrorCode[RpcErrorCode["RPC_SERVER_ERROR"] = -32000] = "RPC_SERVER_ERROR";
15611
+ RpcErrorCode[RpcErrorCode["INVALID_REQUEST"] = -32600] = "INVALID_REQUEST";
15612
+ RpcErrorCode[RpcErrorCode["METHOD_NOT_FOUND"] = -32601] = "METHOD_NOT_FOUND";
15613
+ RpcErrorCode[RpcErrorCode["INVALID_PARAMS"] = -32602] = "INVALID_PARAMS";
15614
+ RpcErrorCode[RpcErrorCode["INTERNAL_ERROR"] = -32603] = "INTERNAL_ERROR";
15615
+ RpcErrorCode[RpcErrorCode["PARSE_ERROR"] = -32700] = "PARSE_ERROR";
15616
+ RpcErrorCode[RpcErrorCode["TRANSACTION_REJECTED"] = -32003] = "TRANSACTION_REJECTED";
15617
+ })(RpcErrorCode || (RpcErrorCode = {}));
15618
+ class JsonRpcError extends Error {
15619
+ message;
15620
+ code;
15621
+ constructor(code, message) {
15622
+ super(message);
15623
+ this.message = message;
15624
+ this.code = code;
15625
+ }
15626
+ }
15627
+
15799
15628
  const MESSAGE_TO_SIGN = 'Only sign this message from Immutable Passport';
15800
15629
  async function registerZkEvmUser({ authManager, magicProvider, multiRollupApiClients, accessToken, jsonRpcProvider, }) {
15801
15630
  const web3Provider = new Web3Provider(magicProvider);
@@ -15830,17 +15659,7 @@ async function registerZkEvmUser({ authManager, magicProvider, multiRollupApiCli
15830
15659
  }
15831
15660
 
15832
15661
  const loginZkEvmUser = async ({ authManager, magicAdapter, multiRollupApiClients, jsonRpcProvider, }) => {
15833
- let user = null;
15834
- try {
15835
- user = await authManager.getUser();
15836
- }
15837
- catch (err) {
15838
- // eslint-disable-next-line no-console
15839
- console.warn('eth_requestAccounts` failed to retrieve a cached user session:', err);
15840
- }
15841
- if (!user) {
15842
- user = await authManager.login();
15843
- }
15662
+ const user = await authManager.getUserOrLogin();
15844
15663
  if (!user.idToken) {
15845
15664
  throw new Error('User is missing idToken');
15846
15665
  }
@@ -15865,6 +15684,171 @@ const loginZkEvmUser = async ({ authManager, magicAdapter, multiRollupApiClients
15865
15684
  };
15866
15685
  };
15867
15686
 
15687
+ const transactionRejectedCrossSdkBridgeError = 'Transaction requires confirmation but this functionality is not'
15688
+ + ' supported in this environment. Please contact Immutable support if you need to enable this feature.';
15689
+ const convertBigNumberishToString = (value) => BigNumber.from(value).toString();
15690
+ const transformGuardianTransactions = (txs) => {
15691
+ try {
15692
+ return txs.map((t) => ({
15693
+ delegateCall: t.delegateCall === true,
15694
+ revertOnError: t.revertOnError === true,
15695
+ gasLimit: t.gasLimit ? convertBigNumberishToString(t.gasLimit) : '0',
15696
+ target: t.to ?? ethers.constants.AddressZero,
15697
+ value: t.value ? convertBigNumberishToString(t.value) : '0',
15698
+ data: t.data ? t.data.toString() : '0x',
15699
+ }));
15700
+ }
15701
+ catch (error) {
15702
+ const errorMessage = error instanceof Error ? error.message : String(error);
15703
+ throw new JsonRpcError(RpcErrorCode.INVALID_PARAMS, `Transaction failed to parsing: ${errorMessage}`);
15704
+ }
15705
+ };
15706
+ class GuardianClient {
15707
+ transactionAPI;
15708
+ messageAPI;
15709
+ confirmationScreen;
15710
+ crossSdkBridgeEnabled;
15711
+ authManager;
15712
+ constructor({ confirmationScreen, config, authManager }) {
15713
+ const guardianConfiguration = new Configuration({ basePath: config.imxPublicApiDomain });
15714
+ this.confirmationScreen = confirmationScreen;
15715
+ this.crossSdkBridgeEnabled = config.crossSdkBridgeEnabled;
15716
+ this.messageAPI = new MessagesApi(guardianConfiguration);
15717
+ this.transactionAPI = new TransactionsApi(guardianConfiguration);
15718
+ this.authManager = authManager;
15719
+ }
15720
+ /**
15721
+ * Open confirmation screen and close it automatically if the
15722
+ * underlying task fails.
15723
+ */
15724
+ withConfirmationScreen(popupWindowSize) {
15725
+ return (task) => this.withConfirmationScreenTask(popupWindowSize)(task)();
15726
+ }
15727
+ withConfirmationScreenTask(popupWindowSize) {
15728
+ return (task) => async () => {
15729
+ this.confirmationScreen.loading(popupWindowSize);
15730
+ try {
15731
+ return await task();
15732
+ }
15733
+ catch (err) {
15734
+ this.confirmationScreen.closeWindow();
15735
+ throw err;
15736
+ }
15737
+ };
15738
+ }
15739
+ withDefaultConfirmationScreenTask(task) {
15740
+ return this.withConfirmationScreenTask()(task);
15741
+ }
15742
+ async evaluateImxTransaction({ payloadHash }) {
15743
+ const finallyFn = () => {
15744
+ this.confirmationScreen.closeWindow();
15745
+ };
15746
+ const user = await this.authManager.getUser();
15747
+ const headers = { Authorization: `Bearer ${user.accessToken}` };
15748
+ const transactionRes = await retryWithDelay(async () => this.transactionAPI.getTransactionByID({
15749
+ transactionID: payloadHash,
15750
+ chainType: 'starkex',
15751
+ }, { headers }), { finallyFn });
15752
+ if (!transactionRes.data.id) {
15753
+ throw new Error("Transaction doesn't exists");
15754
+ }
15755
+ const evaluateImxRes = await this.transactionAPI.evaluateTransaction({
15756
+ id: payloadHash,
15757
+ transactionEvaluationRequest: {
15758
+ chainType: 'starkex',
15759
+ },
15760
+ }, { headers });
15761
+ const { confirmationRequired } = evaluateImxRes.data;
15762
+ if (confirmationRequired) {
15763
+ if (this.crossSdkBridgeEnabled) {
15764
+ throw new Error(transactionRejectedCrossSdkBridgeError);
15765
+ }
15766
+ const confirmationResult = await this.confirmationScreen.requestConfirmation(payloadHash, user.imx.ethAddress, TransactionApprovalRequestChainTypeEnum.Starkex);
15767
+ if (!confirmationResult.confirmed) {
15768
+ throw new Error('Transaction rejected by user');
15769
+ }
15770
+ }
15771
+ else {
15772
+ this.confirmationScreen.closeWindow();
15773
+ }
15774
+ }
15775
+ async evaluateEVMTransaction({ chainId, nonce, metaTransactions, }) {
15776
+ const user = await this.authManager.getUser();
15777
+ const headers = { Authorization: `Bearer ${user.accessToken}` };
15778
+ const guardianTransactions = transformGuardianTransactions(metaTransactions);
15779
+ try {
15780
+ const transactionEvaluationResponseAxiosResponse = await this.transactionAPI.evaluateTransaction({
15781
+ id: 'evm',
15782
+ transactionEvaluationRequest: {
15783
+ chainType: 'evm',
15784
+ chainId,
15785
+ transactionData: {
15786
+ nonce,
15787
+ userAddress: user.zkEvm.ethAddress,
15788
+ metaTransactions: guardianTransactions,
15789
+ },
15790
+ },
15791
+ }, { headers });
15792
+ return transactionEvaluationResponseAxiosResponse.data;
15793
+ }
15794
+ catch (error) {
15795
+ const errorMessage = error instanceof Error ? error.message : String(error);
15796
+ throw new JsonRpcError(RpcErrorCode.INTERNAL_ERROR, `Transaction failed to validate with error: ${errorMessage}`);
15797
+ }
15798
+ }
15799
+ async validateEVMTransaction({ chainId, nonce, metaTransactions, }) {
15800
+ const transactionEvaluationResponse = await this.evaluateEVMTransaction({
15801
+ chainId,
15802
+ nonce,
15803
+ metaTransactions,
15804
+ });
15805
+ const { confirmationRequired, transactionId } = transactionEvaluationResponse;
15806
+ if (confirmationRequired && this.crossSdkBridgeEnabled) {
15807
+ throw new JsonRpcError(RpcErrorCode.TRANSACTION_REJECTED, transactionRejectedCrossSdkBridgeError);
15808
+ }
15809
+ if (confirmationRequired && !!transactionId) {
15810
+ const user = await this.authManager.getUser();
15811
+ const confirmationResult = await this.confirmationScreen.requestConfirmation(transactionId, user.zkEvm.ethAddress, TransactionApprovalRequestChainTypeEnum.Evm, chainId);
15812
+ if (!confirmationResult.confirmed) {
15813
+ throw new JsonRpcError(RpcErrorCode.TRANSACTION_REJECTED, 'Transaction rejected by user');
15814
+ }
15815
+ }
15816
+ else {
15817
+ this.confirmationScreen.closeWindow();
15818
+ }
15819
+ }
15820
+ async evaluateMessage({ chainID, payload }) {
15821
+ try {
15822
+ const user = await this.authManager.getUser();
15823
+ if (user === null) {
15824
+ throw new PassportError('evaluateMessage requires a valid ID token or refresh token. Please log in first', PassportErrorType.NOT_LOGGED_IN_ERROR);
15825
+ }
15826
+ const messageEvalResponse = await this.messageAPI.evaluateMessage({ messageEvaluationRequest: { chainID, payload } }, { headers: { Authorization: `Bearer ${user.accessToken}` } });
15827
+ return messageEvalResponse.data;
15828
+ }
15829
+ catch (error) {
15830
+ const errorMessage = error instanceof Error ? error.message : String(error);
15831
+ throw new JsonRpcError(RpcErrorCode.INTERNAL_ERROR, `Message failed to validate with error: ${errorMessage}`);
15832
+ }
15833
+ }
15834
+ async validateMessage({ chainID, payload }) {
15835
+ const { messageId, confirmationRequired } = await this.evaluateMessage({ chainID, payload });
15836
+ if (confirmationRequired && this.crossSdkBridgeEnabled) {
15837
+ throw new JsonRpcError(RpcErrorCode.TRANSACTION_REJECTED, transactionRejectedCrossSdkBridgeError);
15838
+ }
15839
+ if (confirmationRequired && !!messageId) {
15840
+ const user = await this.authManager.getUser();
15841
+ const confirmationResult = await this.confirmationScreen.requestMessageConfirmation(messageId, user.zkEvm.ethAddress);
15842
+ if (!confirmationResult.confirmed) {
15843
+ throw new JsonRpcError(RpcErrorCode.TRANSACTION_REJECTED, 'Signature rejected by user');
15844
+ }
15845
+ }
15846
+ else {
15847
+ this.confirmationScreen.closeWindow();
15848
+ }
15849
+ }
15850
+ }
15851
+
15868
15852
  const MAX_TRANSACTION_HASH_RETRIEVAL_RETRIES = 30;
15869
15853
  const TRANSACTION_HASH_RETRIEVAL_WAIT = 1000;
15870
15854
  const getMetaTransactions = async (metaTransaction, nonce, chainId, walletAddress, signer, relayerClient) => {
@@ -16009,7 +15993,6 @@ const signTypedDataV4 = async ({ params, method, magicProvider, jsonRpcProvider,
16009
15993
  class ZkEvmProvider {
16010
15994
  authManager;
16011
15995
  config;
16012
- confirmationScreen;
16013
15996
  magicAdapter;
16014
15997
  multiRollupApiClients;
16015
15998
  jsonRpcProvider; // Used for read
@@ -16019,11 +16002,11 @@ class ZkEvmProvider {
16019
16002
  magicProvider; // Used for signing
16020
16003
  zkevmAddress;
16021
16004
  isPassport = true;
16022
- constructor({ authManager, magicAdapter, config, confirmationScreen, multiRollupApiClients, passportEventEmitter, }) {
16005
+ constructor({ authManager, magicAdapter, config, multiRollupApiClients, passportEventEmitter, guardianClient, }) {
16023
16006
  this.authManager = authManager;
16024
16007
  this.magicAdapter = magicAdapter;
16025
16008
  this.config = config;
16026
- this.confirmationScreen = confirmationScreen;
16009
+ this.guardianClient = guardianClient;
16027
16010
  if (config.crossSdkBridgeEnabled) {
16028
16011
  // JsonRpcProvider by default sets the referrer as "client".
16029
16012
  // On Unreal 4 this errors as the browser used is expecting a valid URL.
@@ -16043,7 +16026,6 @@ class ZkEvmProvider {
16043
16026
  const shouldEmitAccountsChanged = this.isLoggedIn();
16044
16027
  this.magicProvider = undefined;
16045
16028
  this.relayerClient = undefined;
16046
- this.guardianClient = undefined;
16047
16029
  if (shouldEmitAccountsChanged) {
16048
16030
  this.eventEmitter.emit(ProviderEvent.ACCOUNTS_CHANGED, []);
16049
16031
  }
@@ -16051,8 +16033,7 @@ class ZkEvmProvider {
16051
16033
  isLoggedIn() {
16052
16034
  return this.magicProvider !== undefined
16053
16035
  && this.zkevmAddress !== undefined
16054
- && this.relayerClient !== undefined
16055
- && this.guardianClient !== undefined;
16036
+ && this.relayerClient !== undefined;
16056
16037
  }
16057
16038
  async performRequest(request) {
16058
16039
  switch (request.method) {
@@ -16062,7 +16043,6 @@ class ZkEvmProvider {
16062
16043
  }
16063
16044
  const { magicProvider, user } = await loginZkEvmUser({
16064
16045
  authManager: this.authManager,
16065
- config: this.config,
16066
16046
  magicAdapter: this.magicAdapter,
16067
16047
  multiRollupApiClients: this.multiRollupApiClients,
16068
16048
  jsonRpcProvider: this.jsonRpcProvider,
@@ -16073,11 +16053,6 @@ class ZkEvmProvider {
16073
16053
  jsonRpcProvider: this.jsonRpcProvider,
16074
16054
  authManager: this.authManager,
16075
16055
  });
16076
- this.guardianClient = new GuardianClient({
16077
- confirmationScreen: this.confirmationScreen,
16078
- config: this.config,
16079
- authManager: this.authManager,
16080
- });
16081
16056
  this.zkevmAddress = user.zkEvm.ethAddress;
16082
16057
  this.eventEmitter.emit(ProviderEvent.ACCOUNTS_CHANGED, [user.zkEvm.ethAddress]);
16083
16058
  return [user.zkEvm.ethAddress];
@@ -16256,15 +16231,19 @@ const buildPrivateVars = (passportModuleConfiguration) => {
16256
16231
  const immutableXClient = passportModuleConfiguration.overrides
16257
16232
  ? passportModuleConfiguration.overrides.immutableXClient
16258
16233
  : new IMXClient({ baseConfig: passportModuleConfiguration.baseConfig });
16234
+ const guardianClient = new GuardianClient({
16235
+ confirmationScreen,
16236
+ config,
16237
+ authManager,
16238
+ });
16259
16239
  const imxApiClients = buildImxApiClients(passportModuleConfiguration);
16260
16240
  const passportImxProviderFactory = new PassportImxProviderFactory({
16261
16241
  authManager,
16262
- config,
16263
- confirmationScreen,
16264
16242
  immutableXClient,
16265
16243
  magicAdapter,
16266
16244
  passportEventEmitter,
16267
16245
  imxApiClients,
16246
+ guardianClient,
16268
16247
  });
16269
16248
  return {
16270
16249
  config,
@@ -16275,6 +16254,7 @@ const buildPrivateVars = (passportModuleConfiguration) => {
16275
16254
  multiRollupApiClients,
16276
16255
  passportEventEmitter,
16277
16256
  passportImxProviderFactory,
16257
+ guardianClient,
16278
16258
  };
16279
16259
  };
16280
16260
  class Passport {
@@ -16286,6 +16266,7 @@ class Passport {
16286
16266
  multiRollupApiClients;
16287
16267
  passportImxProviderFactory;
16288
16268
  passportEventEmitter;
16269
+ guardianClient;
16289
16270
  constructor(passportModuleConfiguration) {
16290
16271
  const privateVars = buildPrivateVars(passportModuleConfiguration);
16291
16272
  this.config = privateVars.config;
@@ -16296,6 +16277,7 @@ class Passport {
16296
16277
  this.multiRollupApiClients = privateVars.multiRollupApiClients;
16297
16278
  this.passportEventEmitter = privateVars.passportEventEmitter;
16298
16279
  this.passportImxProviderFactory = privateVars.passportImxProviderFactory;
16280
+ this.guardianClient = privateVars.guardianClient;
16299
16281
  setPassportClientId(passportModuleConfiguration.clientId);
16300
16282
  track('passport', 'initialised');
16301
16283
  }
@@ -16315,8 +16297,8 @@ class Passport {
16315
16297
  authManager: this.authManager,
16316
16298
  magicAdapter: this.magicAdapter,
16317
16299
  config: this.config,
16318
- confirmationScreen: this.confirmationScreen,
16319
16300
  multiRollupApiClients: this.multiRollupApiClients,
16301
+ guardianClient: this.guardianClient,
16320
16302
  });
16321
16303
  }
16322
16304
  /**