@imtbl/sdk 0.17.6 → 0.18.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.
@@ -10546,6 +10546,27 @@ declare class JsonRpcError extends Error {
10546
10546
  constructor(code: ProviderErrorCode | RpcErrorCode, message: string);
10547
10547
  }
10548
10548
 
10549
+ interface TypedDataPayload {
10550
+ types: {
10551
+ EIP712Domain: Array<{
10552
+ name: string;
10553
+ type: string;
10554
+ }>;
10555
+ [key: string]: Array<{
10556
+ name: string;
10557
+ type: string;
10558
+ }>;
10559
+ };
10560
+ domain: {
10561
+ name?: string;
10562
+ version?: string;
10563
+ chainId?: number;
10564
+ verifyingContract?: string;
10565
+ salt?: string;
10566
+ };
10567
+ primaryType: string;
10568
+ message: Record<string, any>;
10569
+ }
10549
10570
  interface RequestArguments {
10550
10571
  method: string;
10551
10572
  params?: Array<any>;
@@ -10613,4 +10634,4 @@ declare class Passport {
10613
10634
  getLinkedAddresses(): Promise<string[]>;
10614
10635
  }
10615
10636
 
10616
- export { AccountsChangedEvent, DeviceConnectResponse, DeviceTokenResponse, JsonRpcError, JsonRpcRequestCallback, JsonRpcRequestPayload, JsonRpcResponsePayload, Networks, Passport, PassportError, PassportModuleConfiguration, PassportOverrides, Provider, ProviderErrorCode, ProviderEvent, RequestArguments, RpcErrorCode, UserProfile };
10637
+ export { AccountsChangedEvent, DeviceConnectResponse, DeviceTokenResponse, JsonRpcError, JsonRpcRequestCallback, JsonRpcRequestPayload, JsonRpcResponsePayload, Networks, Passport, PassportError, PassportModuleConfiguration, PassportOverrides, Provider, ProviderErrorCode, ProviderEvent, RequestArguments, RpcErrorCode, TypedDataPayload, UserProfile };
package/dist/passport.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import globalAxios$1 from 'axios';
2
2
  import { ImmutableX, Config, generateLegacyStarkPrivateKey, createStarkSigner } from '@imtbl/core-sdk';
3
- import { UserManager } from 'oidc-client-ts';
3
+ import { UserManager, WebStorageStateStore } from 'oidc-client-ts';
4
4
  import jwt_decode from 'jwt-decode';
5
5
  import * as crypto from 'crypto';
6
6
  import { Magic } from 'magic-sdk';
@@ -11,7 +11,7 @@ import * as encUtils from 'enc-utils';
11
11
  import { Web3Provider, JsonRpcProvider } from '@ethersproject/providers';
12
12
  import { EventEmitter } from 'events';
13
13
  import { walletContracts } from '@0xsequence/abi';
14
- import { encodeSignature } from '@0xsequence/config';
14
+ import { encodeSignature, decodeSignature } from '@0xsequence/config';
15
15
 
16
16
  /* tslint:disable */
17
17
  /* eslint-disable */
@@ -1709,7 +1709,7 @@ class MultiRollupApiClients {
1709
1709
  }
1710
1710
 
1711
1711
  // eslint-disable-next-line @typescript-eslint/naming-convention
1712
- const defaultHeaders = { 'x-sdk-version': 'ts-immutable-sdk-0.17.6' };
1712
+ const defaultHeaders = { 'x-sdk-version': 'ts-immutable-sdk-0.18.1' };
1713
1713
  const createConfig = ({ basePath, headers, }) => {
1714
1714
  if (!basePath.trim()) {
1715
1715
  throw Error('basePath can not be empty');
@@ -1765,7 +1765,7 @@ const createImmutableXConfiguration = ({ basePath, chainID, coreContractAddress,
1765
1765
  chainID,
1766
1766
  coreContractAddress,
1767
1767
  registrationContractAddress,
1768
- sdkVersion: 'ts-immutable-sdk-0.17.6',
1768
+ sdkVersion: 'ts-immutable-sdk-0.18.1',
1769
1769
  });
1770
1770
  /**
1771
1771
  * Sets `sdkVersion` at the time of build
@@ -5489,6 +5489,8 @@ var globalAxios = axios;
5489
5489
  * https://openapi-generator.tech
5490
5490
  * Do not edit the class manually.
5491
5491
  */
5492
+ // Some imports not used depending on template conditions
5493
+ // @ts-ignore
5492
5494
  const BASE_PATH = "https://guardian.sandbox.imtbl.com".replace(/\/+$/, "");
5493
5495
  /**
5494
5496
  *
@@ -5516,10 +5518,10 @@ class BaseAPI {
5516
5518
  */
5517
5519
  class RequiredError extends Error {
5518
5520
  field;
5521
+ name = "RequiredError";
5519
5522
  constructor(field, msg) {
5520
5523
  super(msg);
5521
5524
  this.field = field;
5522
- this.name = "RequiredError";
5523
5525
  }
5524
5526
  }
5525
5527
 
@@ -5564,8 +5566,6 @@ const setBearerAuthToObject = async function (object, configuration) {
5564
5566
  }
5565
5567
  };
5566
5568
  function setFlattenedQueryParams(urlSearchParams, parameter, key = "") {
5567
- if (parameter == null)
5568
- return;
5569
5569
  if (typeof parameter === "object") {
5570
5570
  if (Array.isArray(parameter)) {
5571
5571
  parameter.forEach(item => setFlattenedQueryParams(urlSearchParams, item, key));
@@ -5623,6 +5623,208 @@ const createRequestFunction = function (axiosArgs, globalAxios, BASE_PATH, confi
5623
5623
  };
5624
5624
  };
5625
5625
 
5626
+ /* tslint:disable */
5627
+ /* eslint-disable */
5628
+ /**
5629
+ * Guardian
5630
+ * Guardian API
5631
+ *
5632
+ * The version of the OpenAPI document: 1.0.0
5633
+ * Contact: support@immutable.com
5634
+ *
5635
+ * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
5636
+ * https://openapi-generator.tech
5637
+ * Do not edit the class manually.
5638
+ */
5639
+ /**
5640
+ * MessagesApi - axios parameter creator
5641
+ * @export
5642
+ */
5643
+ const MessagesApiAxiosParamCreator = function (configuration) {
5644
+ return {
5645
+ /**
5646
+ * Approve a pending evm message
5647
+ * @summary Approve a pending evm message
5648
+ * @param {string} messageID id for the message
5649
+ * @param {*} [options] Override http request option.
5650
+ * @throws {RequiredError}
5651
+ */
5652
+ approvePendingMessage: async (messageID, options = {}) => {
5653
+ // verify required parameter 'messageID' is not null or undefined
5654
+ assertParamExists('approvePendingMessage', 'messageID', messageID);
5655
+ const localVarPath = `/guardian/v1/messages/{messageID}/approve`
5656
+ .replace(`{${"messageID"}}`, encodeURIComponent(String(messageID)));
5657
+ // use dummy base URL string because the URL constructor only accepts absolute URLs.
5658
+ const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
5659
+ let baseOptions;
5660
+ if (configuration) {
5661
+ baseOptions = configuration.baseOptions;
5662
+ }
5663
+ const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options };
5664
+ const localVarHeaderParameter = {};
5665
+ const localVarQueryParameter = {};
5666
+ // authentication BearerAuth required
5667
+ // http bearer authentication required
5668
+ await setBearerAuthToObject(localVarHeaderParameter, configuration);
5669
+ setSearchParams(localVarUrlObj, localVarQueryParameter);
5670
+ let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
5671
+ localVarRequestOptions.headers = { ...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers };
5672
+ return {
5673
+ url: toPathString(localVarUrlObj),
5674
+ options: localVarRequestOptions,
5675
+ };
5676
+ },
5677
+ /**
5678
+ * Check if a given message is valid for EVM
5679
+ * @summary Evaluate an evm message to sign
5680
+ * @param {MessageEvaluationRequest} messageEvaluationRequest Specifies the kind of transaction
5681
+ * @param {*} [options] Override http request option.
5682
+ * @throws {RequiredError}
5683
+ */
5684
+ evaluateMessage: async (messageEvaluationRequest, options = {}) => {
5685
+ // verify required parameter 'messageEvaluationRequest' is not null or undefined
5686
+ assertParamExists('evaluateMessage', 'messageEvaluationRequest', messageEvaluationRequest);
5687
+ const localVarPath = `/guardian/v1/messages/evaluate`;
5688
+ // use dummy base URL string because the URL constructor only accepts absolute URLs.
5689
+ const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
5690
+ let baseOptions;
5691
+ if (configuration) {
5692
+ baseOptions = configuration.baseOptions;
5693
+ }
5694
+ const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options };
5695
+ const localVarHeaderParameter = {};
5696
+ const localVarQueryParameter = {};
5697
+ // authentication BearerAuth required
5698
+ // http bearer authentication required
5699
+ await setBearerAuthToObject(localVarHeaderParameter, configuration);
5700
+ localVarHeaderParameter['Content-Type'] = 'application/json';
5701
+ setSearchParams(localVarUrlObj, localVarQueryParameter);
5702
+ let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
5703
+ localVarRequestOptions.headers = { ...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers };
5704
+ localVarRequestOptions.data = serializeDataIfNeeded(messageEvaluationRequest, localVarRequestOptions, configuration);
5705
+ return {
5706
+ url: toPathString(localVarUrlObj),
5707
+ options: localVarRequestOptions,
5708
+ };
5709
+ },
5710
+ /**
5711
+ * Get an evm message by id
5712
+ * @summary Info for a specific evm message
5713
+ * @param {string} messageID The id of the evm message
5714
+ * @param {*} [options] Override http request option.
5715
+ * @throws {RequiredError}
5716
+ */
5717
+ getMessageByID: async (messageID, options = {}) => {
5718
+ // verify required parameter 'messageID' is not null or undefined
5719
+ assertParamExists('getMessageByID', 'messageID', messageID);
5720
+ const localVarPath = `/guardian/v1/messages/{messageID}`
5721
+ .replace(`{${"messageID"}}`, encodeURIComponent(String(messageID)));
5722
+ // use dummy base URL string because the URL constructor only accepts absolute URLs.
5723
+ const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
5724
+ let baseOptions;
5725
+ if (configuration) {
5726
+ baseOptions = configuration.baseOptions;
5727
+ }
5728
+ const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options };
5729
+ const localVarHeaderParameter = {};
5730
+ const localVarQueryParameter = {};
5731
+ // authentication BearerAuth required
5732
+ // http bearer authentication required
5733
+ await setBearerAuthToObject(localVarHeaderParameter, configuration);
5734
+ setSearchParams(localVarUrlObj, localVarQueryParameter);
5735
+ let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
5736
+ localVarRequestOptions.headers = { ...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers };
5737
+ return {
5738
+ url: toPathString(localVarUrlObj),
5739
+ options: localVarRequestOptions,
5740
+ };
5741
+ },
5742
+ };
5743
+ };
5744
+ /**
5745
+ * MessagesApi - functional programming interface
5746
+ * @export
5747
+ */
5748
+ const MessagesApiFp = function (configuration) {
5749
+ const localVarAxiosParamCreator = MessagesApiAxiosParamCreator(configuration);
5750
+ return {
5751
+ /**
5752
+ * Approve a pending evm message
5753
+ * @summary Approve a pending evm message
5754
+ * @param {string} messageID id for the message
5755
+ * @param {*} [options] Override http request option.
5756
+ * @throws {RequiredError}
5757
+ */
5758
+ async approvePendingMessage(messageID, options) {
5759
+ const localVarAxiosArgs = await localVarAxiosParamCreator.approvePendingMessage(messageID, options);
5760
+ return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
5761
+ },
5762
+ /**
5763
+ * Check if a given message is valid for EVM
5764
+ * @summary Evaluate an evm message to sign
5765
+ * @param {MessageEvaluationRequest} messageEvaluationRequest Specifies the kind of transaction
5766
+ * @param {*} [options] Override http request option.
5767
+ * @throws {RequiredError}
5768
+ */
5769
+ async evaluateMessage(messageEvaluationRequest, options) {
5770
+ const localVarAxiosArgs = await localVarAxiosParamCreator.evaluateMessage(messageEvaluationRequest, options);
5771
+ return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
5772
+ },
5773
+ /**
5774
+ * Get an evm message by id
5775
+ * @summary Info for a specific evm message
5776
+ * @param {string} messageID The id of the evm message
5777
+ * @param {*} [options] Override http request option.
5778
+ * @throws {RequiredError}
5779
+ */
5780
+ async getMessageByID(messageID, options) {
5781
+ const localVarAxiosArgs = await localVarAxiosParamCreator.getMessageByID(messageID, options);
5782
+ return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
5783
+ },
5784
+ };
5785
+ };
5786
+ /**
5787
+ * MessagesApi - object-oriented interface
5788
+ * @export
5789
+ * @class MessagesApi
5790
+ * @extends {BaseAPI}
5791
+ */
5792
+ class MessagesApi extends BaseAPI {
5793
+ /**
5794
+ * Approve a pending evm message
5795
+ * @summary Approve a pending evm message
5796
+ * @param {MessagesApiApprovePendingMessageRequest} requestParameters Request parameters.
5797
+ * @param {*} [options] Override http request option.
5798
+ * @throws {RequiredError}
5799
+ * @memberof MessagesApi
5800
+ */
5801
+ approvePendingMessage(requestParameters, options) {
5802
+ return MessagesApiFp(this.configuration).approvePendingMessage(requestParameters.messageID, options).then((request) => request(this.axios, this.basePath));
5803
+ }
5804
+ /**
5805
+ * Check if a given message is valid for EVM
5806
+ * @summary Evaluate an evm message to sign
5807
+ * @param {MessagesApiEvaluateMessageRequest} requestParameters Request parameters.
5808
+ * @param {*} [options] Override http request option.
5809
+ * @throws {RequiredError}
5810
+ * @memberof MessagesApi
5811
+ */
5812
+ evaluateMessage(requestParameters, options) {
5813
+ return MessagesApiFp(this.configuration).evaluateMessage(requestParameters.messageEvaluationRequest, options).then((request) => request(this.axios, this.basePath));
5814
+ }
5815
+ /**
5816
+ * Get an evm message by id
5817
+ * @summary Info for a specific evm message
5818
+ * @param {MessagesApiGetMessageByIDRequest} requestParameters Request parameters.
5819
+ * @param {*} [options] Override http request option.
5820
+ * @throws {RequiredError}
5821
+ * @memberof MessagesApi
5822
+ */
5823
+ getMessageByID(requestParameters, options) {
5824
+ return MessagesApiFp(this.configuration).getMessageByID(requestParameters.messageID, options).then((request) => request(this.axios, this.basePath));
5825
+ }
5826
+ }
5827
+
5626
5828
  /* tslint:disable */
5627
5829
  /* eslint-disable */
5628
5830
  /**
@@ -5646,7 +5848,7 @@ const TransactionsApiAxiosParamCreator = function (configuration) {
5646
5848
  * Approve a pending transaction
5647
5849
  * @summary Approve a pending transaction given chain
5648
5850
  * @param {string} payloadHash Hash for the payload
5649
- * @param {TransactionApprovalRequest} transactionApprovalRequest request body for approving a pending transactio
5851
+ * @param {TransactionApprovalRequest} transactionApprovalRequest request body for approving a pending transaction
5650
5852
  * @param {*} [options] Override http request option.
5651
5853
  * @throws {RequiredError}
5652
5854
  */
@@ -5771,7 +5973,7 @@ const TransactionsApiFp = function (configuration) {
5771
5973
  * Approve a pending transaction
5772
5974
  * @summary Approve a pending transaction given chain
5773
5975
  * @param {string} payloadHash Hash for the payload
5774
- * @param {TransactionApprovalRequest} transactionApprovalRequest request body for approving a pending transactio
5976
+ * @param {TransactionApprovalRequest} transactionApprovalRequest request body for approving a pending transaction
5775
5977
  * @param {*} [options] Override http request option.
5776
5978
  * @throws {RequiredError}
5777
5979
  */
@@ -6129,6 +6331,7 @@ const getAuthConfiguration = ({ oidcConfiguration, authenticationDomain, }) => {
6129
6331
  mergeClaims: true,
6130
6332
  loadUserInfo: true,
6131
6333
  scope: oidcConfiguration.scope,
6334
+ userStore: new WebStorageStateStore({ store: window.localStorage }),
6132
6335
  };
6133
6336
  if (oidcConfiguration.audience) {
6134
6337
  baseConfiguration.extraQueryParams = {
@@ -6528,18 +6731,18 @@ const transformGuardianTransactions = (txs) => {
6528
6731
  };
6529
6732
  class GuardianClient {
6530
6733
  transactionAPI;
6734
+ messageAPI;
6531
6735
  confirmationScreen;
6532
6736
  // TODO: ID-977, make this rollup agnostic
6533
6737
  imxEtherAddress;
6534
6738
  crossSdkBridgeEnabled;
6535
6739
  constructor({ accessToken, confirmationScreen, imxEtherAddress, config, }) {
6740
+ const guardianConfiguration = new Configuration({ accessToken, basePath: config.imxPublicApiDomain });
6536
6741
  this.confirmationScreen = confirmationScreen;
6537
- this.transactionAPI = new TransactionsApi(new Configuration({
6538
- accessToken,
6539
- basePath: config.imxPublicApiDomain,
6540
- }));
6742
+ this.transactionAPI = new TransactionsApi(new Configuration(guardianConfiguration));
6541
6743
  this.imxEtherAddress = imxEtherAddress;
6542
6744
  this.crossSdkBridgeEnabled = config.crossSdkBridgeEnabled;
6745
+ this.messageAPI = new MessagesApi(guardianConfiguration);
6543
6746
  }
6544
6747
  /**
6545
6748
  * Open confirmation screen and close it automatically if the
@@ -6638,6 +6841,31 @@ class GuardianClient {
6638
6841
  this.confirmationScreen.closeWindow();
6639
6842
  }
6640
6843
  }
6844
+ async evaluateMessage({ chainID, payload, user }) {
6845
+ try {
6846
+ const messageEvalResponse = await this.messageAPI.evaluateMessage({ messageEvaluationRequest: { chainID, payload } }, { headers: { Authorization: `Bearer ${user.accessToken}` } });
6847
+ return messageEvalResponse.data;
6848
+ }
6849
+ catch (error) {
6850
+ const errorMessage = error instanceof Error ? error.message : String(error);
6851
+ throw new JsonRpcError(RpcErrorCode.INTERNAL_ERROR, `Message failed to validate with error: ${errorMessage}`);
6852
+ }
6853
+ }
6854
+ async validateMessage({ chainID, payload, user }) {
6855
+ const { messageId, confirmationRequired } = await this.evaluateMessage({ chainID, payload, user });
6856
+ if (confirmationRequired && this.crossSdkBridgeEnabled) {
6857
+ throw new JsonRpcError(RpcErrorCode.TRANSACTION_REJECTED, transactionRejectedCrossSdkBridgeError);
6858
+ }
6859
+ if (confirmationRequired && !!messageId) {
6860
+ const confirmationResult = await this.confirmationScreen.requestMessageConfirmation(messageId);
6861
+ if (!confirmationResult.confirmed) {
6862
+ throw new JsonRpcError(RpcErrorCode.TRANSACTION_REJECTED, 'Signature rejected by user');
6863
+ }
6864
+ }
6865
+ else {
6866
+ this.confirmationScreen.closeWindow();
6867
+ }
6868
+ }
6641
6869
  }
6642
6870
 
6643
6871
  var PassportEvents;
@@ -7265,6 +7493,8 @@ var ReceiveMessage;
7265
7493
  ReceiveMessage["CONFIRMATION_WINDOW_READY"] = "confirmation_window_ready";
7266
7494
  ReceiveMessage["TRANSACTION_CONFIRMED"] = "transaction_confirmed";
7267
7495
  ReceiveMessage["TRANSACTION_ERROR"] = "transaction_error";
7496
+ ReceiveMessage["MESSAGE_CONFIRMED"] = "message_confirmed";
7497
+ ReceiveMessage["MESSAGE_REJECTED"] = "message_rejected";
7268
7498
  })(ReceiveMessage || (ReceiveMessage = {}));
7269
7499
  const PASSPORT_EVENT_TYPE = 'imx_passport_confirmation';
7270
7500
 
@@ -7318,11 +7548,11 @@ class ConfirmationScreen {
7318
7548
  reject(new Error('Unsupported message type'));
7319
7549
  }
7320
7550
  };
7321
- window.addEventListener('message', messageHandler);
7322
7551
  if (!this.confirmationWindow) {
7323
7552
  resolve({ confirmed: false });
7324
7553
  return;
7325
7554
  }
7555
+ window.addEventListener('message', messageHandler);
7326
7556
  let href = '';
7327
7557
  if (chainType === TransactionApprovalRequestChainTypeEnum.Starkex) {
7328
7558
  // eslint-disable-next-line max-len
@@ -7332,15 +7562,39 @@ class ConfirmationScreen {
7332
7562
  // eslint-disable-next-line max-len
7333
7563
  href = `${this.config.passportDomain}/transaction-confirmation/zkevm?transactionId=${transactionId}&imxEtherAddress=${imxEtherAddress}&chainType=evm&chainId=${chainId}`;
7334
7564
  }
7335
- this.confirmationWindow.location.href = href;
7336
- // https://stackoverflow.com/questions/9388380/capture-the-close-event-of-popup-window-in-javascript/48240128#48240128
7337
- const timer = setInterval(() => {
7338
- if (this.confirmationWindow?.closed) {
7339
- clearInterval(timer);
7340
- window.removeEventListener('message', messageHandler);
7341
- resolve({ confirmed: false });
7565
+ this.showConfirmationScreen(href, messageHandler, resolve);
7566
+ });
7567
+ }
7568
+ requestMessageConfirmation(messageId) {
7569
+ return new Promise((resolve, reject) => {
7570
+ const messageHandler = ({ data, origin }) => {
7571
+ if (origin !== this.config.passportDomain
7572
+ || data.eventType !== PASSPORT_EVENT_TYPE) {
7573
+ return;
7574
+ }
7575
+ switch (data.messageType) {
7576
+ case ReceiveMessage.CONFIRMATION_WINDOW_READY: {
7577
+ break;
7578
+ }
7579
+ case ReceiveMessage.MESSAGE_CONFIRMED: {
7580
+ resolve({ confirmed: true });
7581
+ break;
7582
+ }
7583
+ case ReceiveMessage.MESSAGE_REJECTED: {
7584
+ reject(new Error('Message rejected'));
7585
+ break;
7586
+ }
7587
+ default:
7588
+ reject(new Error('Unsupported message type'));
7342
7589
  }
7343
- }, CONFIRMATION_WINDOW_CLOSED_POLLING_DURATION);
7590
+ };
7591
+ if (!this.confirmationWindow) {
7592
+ resolve({ confirmed: false });
7593
+ return;
7594
+ }
7595
+ window.addEventListener('message', messageHandler);
7596
+ const href = `${this.config.passportDomain}/transaction-confirmation/zkevm/message?messageID=${messageId}`;
7597
+ this.showConfirmationScreen(href, messageHandler, resolve);
7344
7598
  });
7345
7599
  }
7346
7600
  loading(popupOptions) {
@@ -7358,6 +7612,17 @@ class ConfirmationScreen {
7358
7612
  closeWindow() {
7359
7613
  this.confirmationWindow?.close();
7360
7614
  }
7615
+ showConfirmationScreen(href, messageHandler, resolve) {
7616
+ this.confirmationWindow.location.href = href;
7617
+ // https://stackoverflow.com/questions/9388380/capture-the-close-event-of-popup-window-in-javascript/48240128#48240128
7618
+ const timer = setInterval(() => {
7619
+ if (this.confirmationWindow?.closed) {
7620
+ clearInterval(timer);
7621
+ window.removeEventListener('message', messageHandler);
7622
+ resolve({ confirmed: false });
7623
+ }
7624
+ }, CONFIRMATION_WINDOW_CLOSED_POLLING_DURATION);
7625
+ }
7361
7626
  }
7362
7627
 
7363
7628
  var RelayerTransactionStatus;
@@ -7386,13 +7651,9 @@ class TypedEventEmitter {
7386
7651
  }
7387
7652
  }
7388
7653
 
7389
- // These are ignored by the Relayer but for consistency we set them to the
7390
- // appropriate values for a 1/1 wallet.
7391
- //
7392
- // Weight of a single signature in the multisig
7393
- const SIGNATURE_WEIGHT = 1;
7394
- // Total required weight in the multisig
7395
- const SIGNATURE_THRESHOLD = 1;
7654
+ const SIGNATURE_WEIGHT = 1; // Weight of a single signature in the multi-sig
7655
+ const TRANSACTION_SIGNATURE_THRESHOLD = 1; // Total required weight in the multi-sig for a transaction
7656
+ const EIP712_SIGNATURE_THRESHOLD = 2; // Total required weight in the multi-sig for data signing
7396
7657
  const ETH_SIGN_FLAG = '02';
7397
7658
  const ETH_SIGN_PREFIX = '\x19\x01';
7398
7659
  const META_TRANSACTIONS_TYPE = `tuple(
@@ -7425,11 +7686,12 @@ const getNonce = async (jsonRpcProvider, smartContractWalletAddress) => {
7425
7686
  }
7426
7687
  return 0;
7427
7688
  };
7689
+ const encodeMessageSubDigest = (chainId, walletAddress, digest) => (ethers.utils.solidityPack(['string', 'uint256', 'address', 'bytes32'], [ETH_SIGN_PREFIX, chainId, walletAddress, digest]));
7428
7690
  const getSignedMetaTransactions = async (metaTransactions, nonce, chainId, walletAddress, signer) => {
7429
7691
  const normalisedMetaTransactions = getNormalisedTransactions(metaTransactions);
7430
7692
  // Get the hash
7431
7693
  const digest = digestOfTransactionsAndNonce(nonce, normalisedMetaTransactions);
7432
- const completePayload = ethers.utils.solidityPack(['string', 'uint256', 'address', 'bytes32'], [ETH_SIGN_PREFIX, chainId, walletAddress, digest]);
7694
+ const completePayload = encodeMessageSubDigest(chainId, walletAddress, digest);
7433
7695
  const hash = ethers.utils.keccak256(completePayload);
7434
7696
  // Sign the digest
7435
7697
  const hashArray = ethers.utils.arrayify(hash);
@@ -7437,7 +7699,7 @@ const getSignedMetaTransactions = async (metaTransactions, nonce, chainId, walle
7437
7699
  const signedDigest = `${ethsigNoType}${ETH_SIGN_FLAG}`;
7438
7700
  // Add metadata
7439
7701
  const encodedSignature = encodeSignature({
7440
- threshold: SIGNATURE_THRESHOLD,
7702
+ threshold: TRANSACTION_SIGNATURE_THRESHOLD,
7441
7703
  signers: [
7442
7704
  {
7443
7705
  weight: SIGNATURE_WEIGHT,
@@ -7453,6 +7715,35 @@ const getSignedMetaTransactions = async (metaTransactions, nonce, chainId, walle
7453
7715
  encodedSignature,
7454
7716
  ]);
7455
7717
  };
7718
+ const decodeRelayerTypedDataSignature = (relayerSignature) => {
7719
+ const signatureWithThreshold = `0000${relayerSignature}`;
7720
+ return decodeSignature(signatureWithThreshold);
7721
+ };
7722
+ const getSignedTypedData = async (typedData, relayerSignature, chainId, walletAddress, signer) => {
7723
+ // Ethers auto-generates the EIP712Domain type in the TypedDataEncoder, and so it needs to be removed
7724
+ const types = { ...typedData.types };
7725
+ // @ts-ignore
7726
+ delete types.EIP712Domain;
7727
+ // eslint-disable-next-line no-underscore-dangle
7728
+ const digest = ethers.utils._TypedDataEncoder.hash(typedData.domain, types, typedData.message);
7729
+ const completePayload = encodeMessageSubDigest(chainId, walletAddress, digest);
7730
+ const hash = ethers.utils.keccak256(completePayload);
7731
+ // Sign the digest
7732
+ const hashArray = ethers.utils.arrayify(hash);
7733
+ const ethsigNoType = await signer.signMessage(hashArray);
7734
+ const signedDigest = `${ethsigNoType}${ETH_SIGN_FLAG}`;
7735
+ const { signers } = decodeRelayerTypedDataSignature(relayerSignature);
7736
+ return encodeSignature({
7737
+ threshold: EIP712_SIGNATURE_THRESHOLD,
7738
+ signers: [
7739
+ ...signers,
7740
+ {
7741
+ weight: SIGNATURE_WEIGHT,
7742
+ signature: signedDigest,
7743
+ },
7744
+ ],
7745
+ });
7746
+ };
7456
7747
  const getEip155ChainId = (chainId) => `eip155:${chainId}`;
7457
7748
 
7458
7749
  class RelayerClient {
@@ -7518,6 +7809,19 @@ class RelayerClient {
7518
7809
  const { result } = await this.postToRelayer(payload);
7519
7810
  return result;
7520
7811
  }
7812
+ async imSignTypedData(address, eip712Payload) {
7813
+ const { chainId } = await this.jsonRpcProvider.ready;
7814
+ const payload = {
7815
+ method: 'im_signTypedData',
7816
+ params: [{
7817
+ address,
7818
+ eip712Payload,
7819
+ chainId: getEip155ChainId(chainId),
7820
+ }],
7821
+ };
7822
+ const { result } = await this.postToRelayer(payload);
7823
+ return result;
7824
+ }
7521
7825
  }
7522
7826
 
7523
7827
  const MESSAGE_TO_SIGN = 'Only sign this message from Immutable Passport';
@@ -7658,6 +7962,60 @@ const sendTransaction = ({ params, magicProvider, jsonRpcProvider, relayerClient
7658
7962
  return relayerTransaction.hash;
7659
7963
  });
7660
7964
 
7965
+ const REQUIRED_TYPED_DATA_PROPERTIES = ['types', 'domain', 'primaryType', 'message'];
7966
+ const isValidTypedDataPayload = (typedData) => (REQUIRED_TYPED_DATA_PROPERTIES.every((key) => key in typedData));
7967
+ const transformTypedData = (typedData, chainId) => {
7968
+ let transformedTypedData;
7969
+ if (typeof typedData === 'string') {
7970
+ try {
7971
+ transformedTypedData = JSON.parse(typedData);
7972
+ }
7973
+ catch (err) {
7974
+ throw new JsonRpcError(RpcErrorCode.INVALID_PARAMS, `Failed to parse typed data JSON: ${err}`);
7975
+ }
7976
+ }
7977
+ else if (typeof typedData === 'object') {
7978
+ transformedTypedData = typedData;
7979
+ }
7980
+ else {
7981
+ throw new JsonRpcError(RpcErrorCode.INVALID_PARAMS, `Invalid typed data argument: ${typedData}`);
7982
+ }
7983
+ if (!isValidTypedDataPayload(transformedTypedData)) {
7984
+ throw new JsonRpcError(RpcErrorCode.INVALID_PARAMS, `Invalid typed data argument. The following properties are required: ${REQUIRED_TYPED_DATA_PROPERTIES.join(', ')}`);
7985
+ }
7986
+ const providedChainId = transformedTypedData.domain?.chainId;
7987
+ if (providedChainId) {
7988
+ // domain.chainId (if defined) can be a number, string, or hex value, but the relayer & guardian only accept a number.
7989
+ if (typeof providedChainId === 'string') {
7990
+ if (providedChainId.startsWith('0x')) {
7991
+ transformedTypedData.domain.chainId = parseInt(providedChainId, 16);
7992
+ }
7993
+ else {
7994
+ transformedTypedData.domain.chainId = parseInt(providedChainId, 10);
7995
+ }
7996
+ }
7997
+ if (transformedTypedData.domain.chainId !== chainId) {
7998
+ throw new JsonRpcError(RpcErrorCode.INVALID_PARAMS, `Invalid chainId, expected ${chainId}`);
7999
+ }
8000
+ }
8001
+ return transformedTypedData;
8002
+ };
8003
+ const signTypedDataV4 = async ({ params, method, magicProvider, jsonRpcProvider, relayerClient, guardianClient, user, }) => guardianClient
8004
+ .withConfirmationScreen({ width: 480, height: 730 })(async () => {
8005
+ const fromAddress = params[0];
8006
+ const typedDataParam = params[1];
8007
+ if (!fromAddress || !typedDataParam) {
8008
+ throw new JsonRpcError(RpcErrorCode.INVALID_PARAMS, `${method} requires an address and a typed data JSON`);
8009
+ }
8010
+ const { chainId } = await jsonRpcProvider.ready;
8011
+ const typedData = transformTypedData(typedDataParam, chainId);
8012
+ await guardianClient.validateMessage({ chainID: String(chainId), payload: typedData, user });
8013
+ const relayerSignature = await relayerClient.imSignTypedData(fromAddress, typedData);
8014
+ const magicWeb3Provider = new Web3Provider(magicProvider);
8015
+ const signer = magicWeb3Provider.getSigner();
8016
+ return getSignedTypedData(typedData, relayerSignature, BigNumber.from(chainId), fromAddress, signer);
8017
+ });
8018
+
7661
8019
  class ZkEvmProvider {
7662
8020
  authManager;
7663
8021
  config;
@@ -7741,6 +8099,21 @@ class ZkEvmProvider {
7741
8099
  case 'eth_accounts': {
7742
8100
  return this.isLoggedIn() ? [this.user.zkEvm.ethAddress] : [];
7743
8101
  }
8102
+ case 'eth_signTypedData':
8103
+ case 'eth_signTypedData_v4': {
8104
+ if (!this.isLoggedIn()) {
8105
+ throw new JsonRpcError(ProviderErrorCode.UNAUTHORIZED, 'Unauthorised - call eth_requestAccounts first');
8106
+ }
8107
+ return signTypedDataV4({
8108
+ method: request.method,
8109
+ params: request.params || [],
8110
+ magicProvider: this.magicProvider,
8111
+ jsonRpcProvider: this.jsonRpcProvider,
8112
+ relayerClient: this.relayerClient,
8113
+ user: this.user,
8114
+ guardianClient: this.guardianClient,
8115
+ });
8116
+ }
7744
8117
  // Pass through methods
7745
8118
  case 'eth_gasPrice':
7746
8119
  case 'eth_getBalance':
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@imtbl/sdk",
3
3
  "description": "Immutable Unified SDK",
4
- "version": "0.17.6",
4
+ "version": "0.18.1",
5
5
  "author": "Immutable",
6
6
  "browser": "./dist/index.browser.js",
7
7
  "bugs": "https://github.com/immutable/ts-immutable-sdk/issues",