@imtbl/sdk 0.9.0 → 0.12.0-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/passport.js CHANGED
@@ -1457,7 +1457,7 @@ class MultiRollupApiClients {
1457
1457
  }
1458
1458
 
1459
1459
  // eslint-disable-next-line @typescript-eslint/naming-convention
1460
- const defaultHeaders = { 'x-sdk-version': 'ts-immutable-sdk-0.0.0' };
1460
+ const defaultHeaders = { 'x-sdk-version': 'ts-immutable-sdk-0.12.0-alpha' };
1461
1461
  const createConfig = ({ basePath, headers, }) => {
1462
1462
  if (!basePath.trim()) {
1463
1463
  throw Error('basePath can not be empty');
@@ -1513,7 +1513,7 @@ const createImmutableXConfiguration = ({ basePath, chainID, coreContractAddress,
1513
1513
  chainID,
1514
1514
  coreContractAddress,
1515
1515
  registrationContractAddress,
1516
- sdkVersion: 'ts-immutable-sdk-0.0.0',
1516
+ sdkVersion: 'ts-immutable-sdk-0.12.0-alpha',
1517
1517
  });
1518
1518
  /**
1519
1519
  * Sets `sdkVersion` at the time of build
@@ -6163,22 +6163,28 @@ class AuthManager {
6163
6163
 
6164
6164
  class MagicAdapter {
6165
6165
  config;
6166
+ magicClient;
6166
6167
  constructor(config) {
6167
6168
  this.config = config;
6168
6169
  }
6169
6170
  async login(idToken, network) {
6170
6171
  return withPassportError(async () => {
6171
- const magicClient = new Magic(this.config.magicPublishableApiKey, {
6172
+ this.magicClient = new Magic(this.config.magicPublishableApiKey, {
6172
6173
  extensions: [new OpenIdExtension()],
6173
6174
  network,
6174
6175
  });
6175
- await magicClient.openid.loginWithOIDC({
6176
+ await this.magicClient.openid.loginWithOIDC({
6176
6177
  jwt: idToken,
6177
6178
  providerId: this.config.magicProviderId,
6178
6179
  });
6179
- return magicClient.rpcProvider;
6180
+ return this.magicClient.rpcProvider;
6180
6181
  }, PassportErrorType.WALLET_CONNECTION_ERROR);
6181
6182
  }
6183
+ async logout() {
6184
+ if (this.magicClient?.user) {
6185
+ await this.magicClient.user.logout();
6186
+ }
6187
+ }
6182
6188
  }
6183
6189
 
6184
6190
  const getStarkSigner = async (signer) => withPassportError(async () => {
@@ -6212,6 +6218,8 @@ const retryWithDelay = async (fn, options) => {
6212
6218
 
6213
6219
  /**
6214
6220
  * ProviderErrors should take priority over RpcErrorCodes
6221
+ * https://eips.ethereum.org/EIPS/eip-1193#provider-errors
6222
+ * https://eips.ethereum.org/EIPS/eip-1474#error-codes
6215
6223
  */
6216
6224
  var ProviderErrorCode;
6217
6225
  (function (ProviderErrorCode) {
@@ -6228,7 +6236,7 @@ var RpcErrorCode;
6228
6236
  RpcErrorCode[RpcErrorCode["INVALID_PARAMS"] = -32602] = "INVALID_PARAMS";
6229
6237
  RpcErrorCode[RpcErrorCode["INTERNAL_ERROR"] = -32603] = "INTERNAL_ERROR";
6230
6238
  RpcErrorCode[RpcErrorCode["PARSE_ERROR"] = -32700] = "PARSE_ERROR";
6231
- RpcErrorCode[RpcErrorCode["USER_REJECTED_REQUEST"] = -32003] = "USER_REJECTED_REQUEST";
6239
+ RpcErrorCode[RpcErrorCode["TRANSACTION_REJECTED"] = -32003] = "TRANSACTION_REJECTED";
6232
6240
  })(RpcErrorCode || (RpcErrorCode = {}));
6233
6241
  class JsonRpcError extends Error {
6234
6242
  message;
@@ -6240,6 +6248,8 @@ class JsonRpcError extends Error {
6240
6248
  }
6241
6249
  }
6242
6250
 
6251
+ const transactionRejectedCrossSdkBridgeError = 'Transaction requires confirmation but this functionality is not'
6252
+ + ' supported in this environment. Please contact Immutable support if you need to enable this feature.';
6243
6253
  const convertBigNumberishToString = (value) => BigNumber.from(value).toString();
6244
6254
  const transformGuardianTransactions = (txs) => {
6245
6255
  try {
@@ -6262,13 +6272,15 @@ class GuardianClient {
6262
6272
  confirmationScreen;
6263
6273
  // TODO: ID-977, make this rollup agnostic
6264
6274
  imxEtherAddress;
6265
- constructor({ imxPublicApiDomain, accessToken, confirmationScreen, imxEtherAddress, }) {
6275
+ crossSdkBridgeEnabled;
6276
+ constructor({ accessToken, confirmationScreen, imxEtherAddress, config, }) {
6266
6277
  this.confirmationScreen = confirmationScreen;
6267
6278
  this.transactionAPI = new TransactionsApi(new Configuration({
6268
6279
  accessToken,
6269
- basePath: imxPublicApiDomain,
6280
+ basePath: config.imxPublicApiDomain,
6270
6281
  }));
6271
6282
  this.imxEtherAddress = imxEtherAddress;
6283
+ this.crossSdkBridgeEnabled = config.crossSdkBridgeEnabled;
6272
6284
  }
6273
6285
  /**
6274
6286
  * Open confirmation screen and close it automatically if the
@@ -6281,8 +6293,7 @@ class GuardianClient {
6281
6293
  return (task) => async () => {
6282
6294
  this.confirmationScreen.loading(popupWindowSize);
6283
6295
  try {
6284
- const result = await task();
6285
- return result;
6296
+ return await task();
6286
6297
  }
6287
6298
  catch (err) {
6288
6299
  this.confirmationScreen.closeWindow();
@@ -6312,7 +6323,10 @@ class GuardianClient {
6312
6323
  });
6313
6324
  const { confirmationRequired } = evaluateStarkexRes.data;
6314
6325
  if (confirmationRequired) {
6315
- const confirmationResult = await this.confirmationScreen.startGuardianTransaction(payloadHash, this.imxEtherAddress, TransactionApprovalRequestChainTypeEnum.Starkex);
6326
+ if (this.crossSdkBridgeEnabled) {
6327
+ throw new Error(transactionRejectedCrossSdkBridgeError);
6328
+ }
6329
+ const confirmationResult = await this.confirmationScreen.requestConfirmation(payloadHash, this.imxEtherAddress, TransactionApprovalRequestChainTypeEnum.Starkex);
6316
6330
  if (!confirmationResult.confirmed) {
6317
6331
  throw new Error('Transaction rejected by user');
6318
6332
  }
@@ -6352,10 +6366,13 @@ class GuardianClient {
6352
6366
  metaTransactions,
6353
6367
  });
6354
6368
  const { confirmationRequired, transactionId } = transactionEvaluationResponse;
6369
+ if (confirmationRequired && this.crossSdkBridgeEnabled) {
6370
+ throw new JsonRpcError(RpcErrorCode.TRANSACTION_REJECTED, transactionRejectedCrossSdkBridgeError);
6371
+ }
6355
6372
  if (confirmationRequired && !!transactionId) {
6356
- const confirmationResult = await this.confirmationScreen.startGuardianTransaction(transactionId, this.imxEtherAddress, TransactionApprovalRequestChainTypeEnum.Evm, chainId);
6373
+ const confirmationResult = await this.confirmationScreen.requestConfirmation(transactionId, this.imxEtherAddress, TransactionApprovalRequestChainTypeEnum.Evm, chainId);
6357
6374
  if (!confirmationResult.confirmed) {
6358
- throw new JsonRpcError(RpcErrorCode.USER_REJECTED_REQUEST, 'Transaction rejected by user ');
6375
+ throw new JsonRpcError(RpcErrorCode.TRANSACTION_REJECTED, 'Transaction rejected by user');
6359
6376
  }
6360
6377
  }
6361
6378
  else {
@@ -6364,6 +6381,16 @@ class GuardianClient {
6364
6381
  }
6365
6382
  }
6366
6383
 
6384
+ var PassportEvents;
6385
+ (function (PassportEvents) {
6386
+ PassportEvents["LOGGED_OUT"] = "loggedOut";
6387
+ })(PassportEvents || (PassportEvents = {}));
6388
+ var Networks;
6389
+ (function (Networks) {
6390
+ Networks["PRODUCTION"] = "mainnet";
6391
+ Networks["SANDBOX"] = "goerli";
6392
+ })(Networks || (Networks = {}));
6393
+
6367
6394
  async function exchangeTransfer({ user, starkSigner, request, exchangesApi, }) {
6368
6395
  return withPassportError(async () => {
6369
6396
  const { ethAddress } = user.imx;
@@ -6668,19 +6695,30 @@ class PassportImxProvider {
6668
6695
  immutableXClient;
6669
6696
  confirmationScreen;
6670
6697
  guardianClient;
6671
- constructor({ user, starkSigner, immutableXClient, imxPublicApiDomain, confirmationScreen, }) {
6698
+ constructor({ user, starkSigner, immutableXClient, confirmationScreen, config, passportEventEmitter, }) {
6672
6699
  this.user = user;
6673
6700
  this.starkSigner = starkSigner;
6674
6701
  this.immutableXClient = immutableXClient;
6675
6702
  this.confirmationScreen = confirmationScreen;
6676
6703
  this.guardianClient = new GuardianClient({
6677
- imxPublicApiDomain,
6678
6704
  accessToken: user.accessToken,
6679
6705
  confirmationScreen,
6680
6706
  imxEtherAddress: user.imx.ethAddress,
6707
+ config,
6681
6708
  });
6709
+ passportEventEmitter.on(PassportEvents.LOGGED_OUT, this.handleLogout);
6710
+ }
6711
+ handleLogout = () => {
6712
+ this.user = undefined;
6713
+ this.starkSigner = undefined;
6714
+ };
6715
+ checkIsLoggedIn() {
6716
+ if (this.user === undefined || this.starkSigner === undefined) {
6717
+ throw new PassportError('User has been logged out', PassportErrorType.NOT_LOGGED_IN_ERROR);
6718
+ }
6682
6719
  }
6683
6720
  async transfer(request) {
6721
+ this.checkIsLoggedIn();
6684
6722
  return transfer({
6685
6723
  request,
6686
6724
  user: this.user,
@@ -6700,6 +6738,7 @@ class PassportImxProvider {
6700
6738
  throw new PassportError('Operation not supported', PassportErrorType.OPERATION_NOT_SUPPORTED_ERROR);
6701
6739
  }
6702
6740
  createOrder(request) {
6741
+ this.checkIsLoggedIn();
6703
6742
  return createOrder({
6704
6743
  request,
6705
6744
  user: this.user,
@@ -6709,6 +6748,7 @@ class PassportImxProvider {
6709
6748
  });
6710
6749
  }
6711
6750
  cancelOrder(request) {
6751
+ this.checkIsLoggedIn();
6712
6752
  return cancelOrder({
6713
6753
  request,
6714
6754
  user: this.user,
@@ -6718,6 +6758,7 @@ class PassportImxProvider {
6718
6758
  });
6719
6759
  }
6720
6760
  createTrade(request) {
6761
+ this.checkIsLoggedIn();
6721
6762
  return createTrade({
6722
6763
  request,
6723
6764
  user: this.user,
@@ -6727,6 +6768,7 @@ class PassportImxProvider {
6727
6768
  });
6728
6769
  }
6729
6770
  batchNftTransfer(request) {
6771
+ this.checkIsLoggedIn();
6730
6772
  return batchNftTransfer({
6731
6773
  request,
6732
6774
  user: this.user,
@@ -6736,6 +6778,7 @@ class PassportImxProvider {
6736
6778
  });
6737
6779
  }
6738
6780
  exchangeTransfer(request) {
6781
+ this.checkIsLoggedIn();
6739
6782
  return exchangeTransfer({
6740
6783
  request,
6741
6784
  user: this.user,
@@ -6763,6 +6806,7 @@ class PassportImxProvider {
6763
6806
  throw new PassportError('Operation not supported', PassportErrorType.OPERATION_NOT_SUPPORTED_ERROR);
6764
6807
  }
6765
6808
  getAddress() {
6809
+ this.checkIsLoggedIn();
6766
6810
  return Promise.resolve(this.user.imx.ethAddress);
6767
6811
  }
6768
6812
  }
@@ -6773,12 +6817,14 @@ class PassportImxProviderFactory {
6773
6817
  confirmationScreen;
6774
6818
  immutableXClient;
6775
6819
  magicAdapter;
6776
- constructor({ authManager, config, confirmationScreen, immutableXClient, magicAdapter, }) {
6820
+ passportEventEmitter;
6821
+ constructor({ authManager, config, confirmationScreen, immutableXClient, magicAdapter, passportEventEmitter, }) {
6777
6822
  this.authManager = authManager;
6778
6823
  this.config = config;
6779
6824
  this.confirmationScreen = confirmationScreen;
6780
6825
  this.immutableXClient = immutableXClient;
6781
6826
  this.magicAdapter = magicAdapter;
6827
+ this.passportEventEmitter = passportEventEmitter;
6782
6828
  }
6783
6829
  async getProvider() {
6784
6830
  const user = await this.authManager.login();
@@ -6820,16 +6866,18 @@ class PassportImxProviderFactory {
6820
6866
  user: userImx,
6821
6867
  starkSigner,
6822
6868
  immutableXClient: this.immutableXClient,
6823
- imxPublicApiDomain: this.config.imxPublicApiDomain,
6824
6869
  confirmationScreen: this.confirmationScreen,
6870
+ config: this.config,
6871
+ passportEventEmitter: this.passportEventEmitter,
6825
6872
  });
6826
6873
  }
6827
6874
  return new PassportImxProvider({
6828
6875
  user: user,
6829
6876
  starkSigner,
6830
6877
  immutableXClient: this.immutableXClient,
6831
- imxPublicApiDomain: this.config.imxPublicApiDomain,
6832
6878
  confirmationScreen: this.confirmationScreen,
6879
+ config: this.config,
6880
+ passportEventEmitter: this.passportEventEmitter,
6833
6881
  });
6834
6882
  }
6835
6883
  async registerStarkEx(userAdminKeySigner, starkSigner, jwt) {
@@ -6853,12 +6901,6 @@ class PassportImxProviderFactory {
6853
6901
  }
6854
6902
  }
6855
6903
 
6856
- var Networks;
6857
- (function (Networks) {
6858
- Networks["PRODUCTION"] = "mainnet";
6859
- Networks["SANDBOX"] = "goerli";
6860
- })(Networks || (Networks = {}));
6861
-
6862
6904
  const validateConfiguration = (configuration, requiredKeys, prefix) => {
6863
6905
  const missingKeys = requiredKeys
6864
6906
  .map((key) => !configuration[key] && key)
@@ -6880,11 +6922,11 @@ class PassportConfiguration {
6880
6922
  magicProviderId;
6881
6923
  oidcConfiguration;
6882
6924
  baseConfig;
6883
- zkEvmChainId;
6884
6925
  zkEvmRpcUrl;
6885
6926
  relayerUrl;
6886
6927
  multiRollupConfig;
6887
- constructor({ baseConfig, overrides, ...oidcConfiguration }) {
6928
+ crossSdkBridgeEnabled;
6929
+ constructor({ baseConfig, overrides, crossSdkBridgeEnabled, ...oidcConfiguration }) {
6888
6930
  validateConfiguration(oidcConfiguration, [
6889
6931
  'clientId',
6890
6932
  'logoutRedirectUri',
@@ -6892,6 +6934,7 @@ class PassportConfiguration {
6892
6934
  ]);
6893
6935
  this.oidcConfiguration = oidcConfiguration;
6894
6936
  this.baseConfig = baseConfig;
6937
+ this.crossSdkBridgeEnabled = crossSdkBridgeEnabled || false;
6895
6938
  if (overrides) {
6896
6939
  validateConfiguration(overrides, [
6897
6940
  'network',
@@ -6900,7 +6943,6 @@ class PassportConfiguration {
6900
6943
  'magicPublishableApiKey',
6901
6944
  'magicProviderId',
6902
6945
  'zkEvmRpcUrl',
6903
- 'zkEvmChainId',
6904
6946
  'relayerUrl',
6905
6947
  'imxPublicApiDomain',
6906
6948
  'indexerMrBasePath',
@@ -6914,7 +6956,6 @@ class PassportConfiguration {
6914
6956
  this.magicPublishableApiKey = overrides.magicPublishableApiKey;
6915
6957
  this.magicProviderId = overrides.magicProviderId;
6916
6958
  this.zkEvmRpcUrl = overrides.zkEvmRpcUrl;
6917
- this.zkEvmChainId = overrides.zkEvmChainId;
6918
6959
  this.relayerUrl = overrides.relayerUrl;
6919
6960
  this.multiRollupConfig = {
6920
6961
  indexer: createConfig({
@@ -6938,7 +6979,6 @@ class PassportConfiguration {
6938
6979
  this.passportDomain = 'https://passport.immutable.com';
6939
6980
  this.imxPublicApiDomain = 'https://api.immutable.com';
6940
6981
  this.zkEvmRpcUrl = ''; // TODO: ID-785 Update once mainnet has been deployed
6941
- this.zkEvmChainId = ''; // TODO: ID-785 Update once mainnet has been deployed
6942
6982
  this.relayerUrl = 'https://api.immutable.com/relayer-mr';
6943
6983
  this.multiRollupConfig = multiRollupConfig.getProduction();
6944
6984
  break;
@@ -6952,7 +6992,6 @@ class PassportConfiguration {
6952
6992
  this.passportDomain = 'https://passport.sandbox.immutable.com';
6953
6993
  this.imxPublicApiDomain = 'https://api.sandbox.immutable.com';
6954
6994
  this.zkEvmRpcUrl = 'https://rpc.testnet.immutable.com';
6955
- this.zkEvmChainId = 'eip155:13472';
6956
6995
  this.relayerUrl = 'https://api.sandbox.immutable.com/relayer-mr';
6957
6996
  this.multiRollupConfig = multiRollupConfig.getSandbox();
6958
6997
  break;
@@ -6968,18 +7007,6 @@ var ReceiveMessage;
6968
7007
  ReceiveMessage["TRANSACTION_CONFIRMED"] = "transaction_confirmed";
6969
7008
  ReceiveMessage["TRANSACTION_ERROR"] = "transaction_error";
6970
7009
  })(ReceiveMessage || (ReceiveMessage = {}));
6971
- var SendMessage;
6972
- (function (SendMessage) {
6973
- SendMessage["TRANSACTION_START"] = "transaction_start";
6974
- })(SendMessage || (SendMessage = {}));
6975
- var TransactionTypes;
6976
- (function (TransactionTypes) {
6977
- TransactionTypes["cancelOrder"] = "delete:v1/orders";
6978
- TransactionTypes["createOrder"] = "post:v1/orders";
6979
- TransactionTypes["createTrade"] = "post:v1/trades";
6980
- TransactionTypes["createTransfer"] = "post:v1/transfers";
6981
- TransactionTypes["createBatchTransfer"] = "post:v2/transfers";
6982
- })(TransactionTypes || (TransactionTypes = {}));
6983
7010
  const PASSPORT_EVENT_TYPE = 'imx_passport_confirmation';
6984
7011
 
6985
7012
  const openPopupCenter = ({ url, title, width, height, }) => {
@@ -7009,60 +7036,7 @@ class ConfirmationScreen {
7009
7036
  constructor(config) {
7010
7037
  this.config = config;
7011
7038
  }
7012
- postMessage(destinationWindow, accessToken, message) {
7013
- destinationWindow.postMessage({
7014
- eventType: PASSPORT_EVENT_TYPE,
7015
- accessToken,
7016
- ...message,
7017
- }, this.config.passportDomain);
7018
- }
7019
- startTransaction(accessToken, transaction, popupOptions) {
7020
- return new Promise((resolve, reject) => {
7021
- const messageHandler = ({ data, origin }) => {
7022
- if (origin !== this.config.passportDomain
7023
- || data.eventType !== PASSPORT_EVENT_TYPE) {
7024
- return;
7025
- }
7026
- switch (data.messageType) {
7027
- case ReceiveMessage.CONFIRMATION_WINDOW_READY: {
7028
- // TODO: remove once fixed
7029
- // eslint-disable-next-line @typescript-eslint/no-use-before-define
7030
- this.postMessage(confirmationWindow, accessToken, {
7031
- messageType: SendMessage.TRANSACTION_START,
7032
- messageData: transaction,
7033
- });
7034
- break;
7035
- }
7036
- case ReceiveMessage.TRANSACTION_CONFIRMED: {
7037
- resolve({ confirmed: true });
7038
- break;
7039
- }
7040
- case ReceiveMessage.TRANSACTION_ERROR: {
7041
- reject(new Error('Transaction error'));
7042
- break;
7043
- }
7044
- default:
7045
- reject(new Error('Unsupported message type'));
7046
- }
7047
- };
7048
- window.addEventListener('message', messageHandler);
7049
- const confirmationWindow = openPopupCenter({
7050
- url: `${this.config.passportDomain}/transaction-confirmation`,
7051
- title: CONFIRMATION_WINDOW_TITLE,
7052
- width: popupOptions?.width || CONFIRMATION_WINDOW_WIDTH,
7053
- height: popupOptions?.height || CONFIRMATION_WINDOW_HEIGHT,
7054
- });
7055
- // https://stackoverflow.com/questions/9388380/capture-the-close-event-of-popup-window-in-javascript/48240128#48240128
7056
- const timer = setInterval(() => {
7057
- if (confirmationWindow.closed) {
7058
- clearInterval(timer);
7059
- window.removeEventListener('message', messageHandler);
7060
- resolve({ confirmed: false });
7061
- }
7062
- }, CONFIRMATION_WINDOW_CLOSED_POLLING_DURATION);
7063
- });
7064
- }
7065
- startGuardianTransaction(transactionId, imxEtherAddress, chainType, chainId) {
7039
+ requestConfirmation(transactionId, imxEtherAddress, chainType, chainId) {
7066
7040
  return new Promise((resolve, reject) => {
7067
7041
  const messageHandler = ({ data, origin }) => {
7068
7042
  if (origin !== this.config.passportDomain
@@ -7090,7 +7064,6 @@ class ConfirmationScreen {
7090
7064
  resolve({ confirmed: false });
7091
7065
  return;
7092
7066
  }
7093
- // eslint-disable-next-line max-len
7094
7067
  let href = '';
7095
7068
  if (chainType === TransactionApprovalRequestChainTypeEnum.Starkex) {
7096
7069
  // eslint-disable-next-line max-len
@@ -7112,6 +7085,10 @@ class ConfirmationScreen {
7112
7085
  });
7113
7086
  }
7114
7087
  loading(popupOptions) {
7088
+ if (this.config.crossSdkBridgeEnabled) {
7089
+ // There is no need to open a confirmation window if cross-sdk bridge is enabled
7090
+ return;
7091
+ }
7115
7092
  this.confirmationWindow = openPopupCenter({
7116
7093
  url: `${this.config.passportDomain}/transaction-confirmation/loading.html`,
7117
7094
  title: CONFIRMATION_WINDOW_TITLE,
@@ -7150,11 +7127,82 @@ class TypedEventEmitter {
7150
7127
  }
7151
7128
  }
7152
7129
 
7130
+ // These are ignored by the Relayer but for consistency we set them to the
7131
+ // appropriate values for a 1/1 wallet.
7132
+ //
7133
+ // Weight of a single signature in the multisig
7134
+ const SIGNATURE_WEIGHT = 1;
7135
+ // Total required weight in the multisig
7136
+ const SIGNATURE_THRESHOLD = 1;
7137
+ const ETH_SIGN_FLAG = '02';
7138
+ const ETH_SIGN_PREFIX = '\x19\x01';
7139
+ const META_TRANSACTIONS_TYPE = `tuple(
7140
+ bool delegateCall,
7141
+ bool revertOnError,
7142
+ uint256 gasLimit,
7143
+ address target,
7144
+ uint256 value,
7145
+ bytes data
7146
+ )[]`;
7147
+ function getNormalisedTransactions(txs) {
7148
+ return txs.map((t) => ({
7149
+ delegateCall: t.delegateCall === true,
7150
+ revertOnError: t.revertOnError === true,
7151
+ gasLimit: t.gasLimit ?? ethers.constants.Zero,
7152
+ target: t.to ?? ethers.constants.AddressZero,
7153
+ value: t.value ?? ethers.constants.Zero,
7154
+ data: t.data ?? [],
7155
+ }));
7156
+ }
7157
+ function digestOfTransactionsAndNonce(nonce, normalisedTransactions) {
7158
+ const packMetaTransactionsNonceData = ethers.utils.defaultAbiCoder.encode(['uint256', META_TRANSACTIONS_TYPE], [nonce, normalisedTransactions]);
7159
+ return ethers.utils.keccak256(packMetaTransactionsNonceData);
7160
+ }
7161
+ const getNonce = async (jsonRpcProvider, smartContractWalletAddress) => {
7162
+ const code = await jsonRpcProvider.send('eth_getCode', [smartContractWalletAddress, 'latest']);
7163
+ if (code && code !== '0x') {
7164
+ const contract = new ethers.Contract(smartContractWalletAddress, walletContracts.mainModule.abi, jsonRpcProvider);
7165
+ return contract.nonce();
7166
+ }
7167
+ return 0;
7168
+ };
7169
+ const getSignedMetaTransactions = async (metaTransactions, nonce, chainId, walletAddress, signer) => {
7170
+ const normalisedMetaTransactions = getNormalisedTransactions(metaTransactions);
7171
+ // Get the hash
7172
+ const digest = digestOfTransactionsAndNonce(nonce, normalisedMetaTransactions);
7173
+ const completePayload = ethers.utils.solidityPack(['string', 'uint256', 'address', 'bytes32'], [ETH_SIGN_PREFIX, chainId, walletAddress, digest]);
7174
+ const hash = ethers.utils.keccak256(completePayload);
7175
+ // Sign the digest
7176
+ const hashArray = ethers.utils.arrayify(hash);
7177
+ const ethsigNoType = await signer.signMessage(hashArray);
7178
+ const signedDigest = `${ethsigNoType}${ETH_SIGN_FLAG}`;
7179
+ // Add metadata
7180
+ const encodedSignature = encodeSignature({
7181
+ threshold: SIGNATURE_THRESHOLD,
7182
+ signers: [
7183
+ {
7184
+ weight: SIGNATURE_WEIGHT,
7185
+ signature: signedDigest,
7186
+ },
7187
+ ],
7188
+ });
7189
+ // Encode the transaction;
7190
+ const walletInterface = new ethers.utils.Interface(walletContracts.mainModule.abi);
7191
+ return walletInterface.encodeFunctionData(walletInterface.getFunction('execute'), [
7192
+ normalisedMetaTransactions,
7193
+ nonce,
7194
+ encodedSignature,
7195
+ ]);
7196
+ };
7197
+ const getEip155ChainId = (chainId) => `eip155:${chainId}`;
7198
+
7153
7199
  class RelayerClient {
7154
7200
  config;
7201
+ jsonRpcProvider;
7155
7202
  user;
7156
- constructor({ config, user }) {
7203
+ constructor({ config, jsonRpcProvider, user }) {
7157
7204
  this.config = config;
7205
+ this.jsonRpcProvider = jsonRpcProvider;
7158
7206
  this.user = user;
7159
7207
  }
7160
7208
  async postToRelayer(request) {
@@ -7178,12 +7226,13 @@ class RelayerClient {
7178
7226
  return jsonResponse;
7179
7227
  }
7180
7228
  async ethSendTransaction(to, data) {
7229
+ const { chainId } = await this.jsonRpcProvider.ready;
7181
7230
  const payload = {
7182
7231
  method: 'eth_sendTransaction',
7183
7232
  params: [{
7184
7233
  to,
7185
7234
  data,
7186
- chainId: this.config.zkEvmChainId.toString(),
7235
+ chainId: getEip155ChainId(chainId),
7187
7236
  }],
7188
7237
  };
7189
7238
  const { result } = await this.postToRelayer(payload);
@@ -7198,12 +7247,13 @@ class RelayerClient {
7198
7247
  return result;
7199
7248
  }
7200
7249
  async imGetFeeOptions(userAddress, data) {
7250
+ const { chainId } = await this.jsonRpcProvider.ready;
7201
7251
  const payload = {
7202
7252
  method: 'im_getFeeOptions',
7203
7253
  params: [{
7204
7254
  userAddress,
7205
7255
  data,
7206
- chainId: this.config.zkEvmChainId.toString(),
7256
+ chainId: getEip155ChainId(chainId),
7207
7257
  }],
7208
7258
  };
7209
7259
  const { result } = await this.postToRelayer(payload);
@@ -7261,90 +7311,16 @@ const loginZkEvmUser = async ({ authManager, config, magicAdapter, multiRollupAp
7261
7311
  };
7262
7312
  };
7263
7313
 
7264
- // These are ignored by the Relayer but for consistency we set them to the
7265
- // appropriate values for a 1/1 wallet.
7266
- //
7267
- // Weight of a single signature in the multisig
7268
- const SIGNATURE_WEIGHT = 1;
7269
- // Total required weight in the multisig
7270
- const SIGNATURE_THRESHOLD = 1;
7271
- const ETH_SIGN_FLAG = '02';
7272
- const ETH_SIGN_PREFIX = '\x19\x01';
7273
- const META_TRANSACTIONS_TYPE = `tuple(
7274
- bool delegateCall,
7275
- bool revertOnError,
7276
- uint256 gasLimit,
7277
- address target,
7278
- uint256 value,
7279
- bytes data
7280
- )[]`;
7281
- function getNormalisedTransactions(txs) {
7282
- return txs.map((t) => ({
7283
- delegateCall: t.delegateCall === true,
7284
- revertOnError: t.revertOnError === true,
7285
- gasLimit: t.gasLimit ?? ethers.constants.Zero,
7286
- target: t.to ?? ethers.constants.AddressZero,
7287
- value: t.value ?? ethers.constants.Zero,
7288
- data: t.data ?? [],
7289
- }));
7290
- }
7291
- function digestOfTransactionsAndNonce(nonce, normalisedTransactions) {
7292
- const packMetaTransactionsNonceData = ethers.utils.defaultAbiCoder.encode(['uint256', META_TRANSACTIONS_TYPE], [nonce, normalisedTransactions]);
7293
- return ethers.utils.keccak256(packMetaTransactionsNonceData);
7294
- }
7295
- const getNonce = async (jsonRpcProvider, smartContractWalletAddress) => {
7296
- const code = await jsonRpcProvider.send('eth_getCode', [smartContractWalletAddress, 'latest']);
7297
- if (code && code !== '0x') {
7298
- const contract = new ethers.Contract(smartContractWalletAddress, walletContracts.mainModule.abi, jsonRpcProvider);
7299
- return contract.nonce();
7300
- }
7301
- return 0;
7302
- };
7303
- const getSignedMetaTransactions = async (metaTransactions, nonce, chainId, walletAddress, signer) => {
7304
- const normalisedMetaTransactions = getNormalisedTransactions(metaTransactions);
7305
- // Get the hash
7306
- const digest = digestOfTransactionsAndNonce(nonce, normalisedMetaTransactions);
7307
- const completePayload = ethers.utils.solidityPack(['string', 'uint256', 'address', 'bytes32'], [ETH_SIGN_PREFIX, chainId, walletAddress, digest]);
7308
- const hash = ethers.utils.keccak256(completePayload);
7309
- // Sign the digest
7310
- const hashArray = ethers.utils.arrayify(hash);
7311
- const ethsigNoType = await signer.signMessage(hashArray);
7312
- const signedDigest = `${ethsigNoType}${ETH_SIGN_FLAG}`;
7313
- // Add metadata
7314
- const encodedSignature = encodeSignature({
7315
- threshold: SIGNATURE_THRESHOLD,
7316
- signers: [
7317
- {
7318
- weight: SIGNATURE_WEIGHT,
7319
- signature: signedDigest,
7320
- },
7321
- ],
7322
- });
7323
- // Encode the transaction;
7324
- const walletInterface = new ethers.utils.Interface(walletContracts.mainModule.abi);
7325
- return walletInterface.encodeFunctionData(walletInterface.getFunction('execute'), [
7326
- normalisedMetaTransactions,
7327
- nonce,
7328
- encodedSignature,
7329
- ]);
7330
- };
7331
- const chainIdNumber = (chainId) => {
7332
- const caip2Components = chainId.split(':');
7333
- if (caip2Components.length !== 2) {
7334
- throw new Error('Invalid configuration, chain ID is not in CAIP-2 format');
7335
- }
7336
- return BigNumber.from(caip2Components[1]);
7337
- };
7338
-
7339
7314
  const MAX_TRANSACTION_HASH_RETRIEVAL_RETRIES = 30;
7340
7315
  const TRANSACTION_HASH_RETRIEVAL_WAIT = 1000;
7341
- const sendTransaction = ({ params, magicProvider, jsonRpcProvider, relayerClient, guardianClient, config, user, }) => guardianClient
7316
+ const sendTransaction = ({ params, magicProvider, jsonRpcProvider, relayerClient, guardianClient, user, }) => guardianClient
7342
7317
  .withConfirmationScreen({ width: 480, height: 520 })(async () => {
7343
7318
  const transactionRequest = params[0];
7344
7319
  if (!transactionRequest.to) {
7345
7320
  throw new JsonRpcError(RpcErrorCode.INVALID_PARAMS, 'eth_sendTransaction requires a "to" field');
7346
7321
  }
7347
- const chainId = chainIdNumber(config.zkEvmChainId);
7322
+ const { chainId } = await jsonRpcProvider.ready;
7323
+ const chainIdBigNumber = BigNumber.from(chainId);
7348
7324
  const magicWeb3Provider = new Web3Provider(magicProvider);
7349
7325
  const signer = magicWeb3Provider.getSigner();
7350
7326
  const nonce = await getNonce(jsonRpcProvider, user.zkEvm.ethAddress);
@@ -7359,7 +7335,7 @@ const sendTransaction = ({ params, magicProvider, jsonRpcProvider, relayerClient
7359
7335
  // accurate estimation of a transaction gas cost is only possible if the smart
7360
7336
  // wallet contract can actually execute it (in a simulated environment) - and
7361
7337
  // it can only execute signed transactions.
7362
- const signedTransaction = await getSignedMetaTransactions([metaTransaction], nonce, chainId, user.zkEvm.ethAddress, signer);
7338
+ const signedTransaction = await getSignedMetaTransactions([metaTransaction], nonce, chainIdBigNumber, user.zkEvm.ethAddress, signer);
7363
7339
  // TODO: ID-698 Add support for non-native gas payments (e.g ERC20, feeTransaction initialisation must change)
7364
7340
  // NOTE: "Fee Options" represent the multiple ways we could pay for the gas
7365
7341
  // used in this transaction. Each fee option has a "recipientAddress" we
@@ -7378,15 +7354,14 @@ const sendTransaction = ({ params, magicProvider, jsonRpcProvider, relayerClient
7378
7354
  revertOnError: true,
7379
7355
  };
7380
7356
  await guardianClient.validateEVMTransaction({
7381
- chainId: config.zkEvmChainId,
7357
+ chainId: getEip155ChainId(chainId),
7382
7358
  nonce: convertBigNumberishToString(nonce),
7383
7359
  user,
7384
7360
  metaTransactions: [metaTransaction, feeMetaTransaction],
7385
7361
  });
7386
7362
  // NOTE: We sign again because we now are adding the fee transaction, so the
7387
7363
  // whole payload is different and needs a new signature.
7388
- const signedTransactions = await getSignedMetaTransactions([metaTransaction, feeMetaTransaction], nonce, chainId, user.zkEvm.ethAddress, signer);
7389
- // TODO: ID-697 Evaluate transactions through Guardian
7364
+ const signedTransactions = await getSignedMetaTransactions([metaTransaction, feeMetaTransaction], nonce, chainIdBigNumber, user.zkEvm.ethAddress, signer);
7390
7365
  const relayerId = await relayerClient.ethSendTransaction(user.zkEvm.ethAddress, signedTransactions);
7391
7366
  const retrieveRelayerTransaction = async () => {
7392
7367
  const tx = await relayerClient.imGetTransactionByHash(relayerId);
@@ -7426,7 +7401,8 @@ class ZkEvmProvider {
7426
7401
  relayerClient;
7427
7402
  magicProvider; // Used for signing
7428
7403
  user;
7429
- constructor({ authManager, magicAdapter, config, confirmationScreen, multiRollupApiClients, }) {
7404
+ isPassport = true;
7405
+ constructor({ authManager, magicAdapter, config, confirmationScreen, multiRollupApiClients, passportEventEmitter, }) {
7430
7406
  this.authManager = authManager;
7431
7407
  this.magicAdapter = magicAdapter;
7432
7408
  this.config = config;
@@ -7437,8 +7413,18 @@ class ZkEvmProvider {
7437
7413
  this.jsonRpcProvider = new JsonRpcProvider(this.config.zkEvmRpcUrl);
7438
7414
  this.multiRollupApiClients = multiRollupApiClients;
7439
7415
  this.eventEmitter = new TypedEventEmitter();
7440
- }
7441
- isPassport = true;
7416
+ passportEventEmitter.on(PassportEvents.LOGGED_OUT, this.handleLogout);
7417
+ }
7418
+ handleLogout = () => {
7419
+ const shouldEmitAccountsChanged = this.isLoggedIn();
7420
+ this.magicProvider = undefined;
7421
+ this.user = undefined;
7422
+ this.relayerClient = undefined;
7423
+ this.guardianClient = undefined;
7424
+ if (shouldEmitAccountsChanged) {
7425
+ this.eventEmitter.emit(ProviderEvent.ACCOUNTS_CHANGED, []);
7426
+ }
7427
+ };
7442
7428
  isLoggedIn() {
7443
7429
  return this.magicProvider !== undefined
7444
7430
  && this.user !== undefined
@@ -7461,13 +7447,19 @@ class ZkEvmProvider {
7461
7447
  this.magicProvider = magicProvider;
7462
7448
  this.relayerClient = new RelayerClient({
7463
7449
  config: this.config,
7450
+ jsonRpcProvider: this.jsonRpcProvider,
7464
7451
  user: this.user,
7465
7452
  });
7466
7453
  this.guardianClient = new GuardianClient({
7467
- imxPublicApiDomain: this.config.imxPublicApiDomain,
7468
7454
  accessToken: this.user.accessToken,
7469
7455
  confirmationScreen: this.confirmationScreen,
7470
7456
  imxEtherAddress: this.user.zkEvm.ethAddress,
7457
+ config: new PassportConfiguration({
7458
+ baseConfig: {},
7459
+ clientId: 'client123',
7460
+ logoutRedirectUri: 'http://localhost:3000/logout',
7461
+ redirectUri: 'http://localhost:3000/redirect',
7462
+ }),
7471
7463
  });
7472
7464
  this.eventEmitter.emit(ProviderEvent.ACCOUNTS_CHANGED, [this.user.zkEvm.ethAddress]);
7473
7465
  return [this.user.zkEvm.ethAddress];
@@ -7481,7 +7473,6 @@ class ZkEvmProvider {
7481
7473
  magicProvider: this.magicProvider,
7482
7474
  guardianClient: this.guardianClient,
7483
7475
  jsonRpcProvider: this.jsonRpcProvider,
7484
- config: this.config,
7485
7476
  relayerClient: this.relayerClient,
7486
7477
  user: this.user,
7487
7478
  });
@@ -7613,8 +7604,9 @@ class Passport {
7613
7604
  confirmationScreen;
7614
7605
  immutableXClient;
7615
7606
  magicAdapter;
7616
- passportImxProviderFactory;
7617
7607
  multiRollupApiClients;
7608
+ passportImxProviderFactory;
7609
+ passportEventEmitter;
7618
7610
  constructor(passportModuleConfiguration) {
7619
7611
  this.config = new PassportConfiguration(passportModuleConfiguration);
7620
7612
  this.authManager = new AuthManager(this.config);
@@ -7625,12 +7617,14 @@ class Passport {
7625
7617
  baseConfig: passportModuleConfiguration.baseConfig,
7626
7618
  });
7627
7619
  this.multiRollupApiClients = new MultiRollupApiClients(this.config.multiRollupConfig);
7620
+ this.passportEventEmitter = new TypedEventEmitter();
7628
7621
  this.passportImxProviderFactory = new PassportImxProviderFactory({
7629
7622
  authManager: this.authManager,
7630
7623
  config: this.config,
7631
7624
  confirmationScreen: this.confirmationScreen,
7632
7625
  immutableXClient: this.immutableXClient,
7633
7626
  magicAdapter: this.magicAdapter,
7627
+ passportEventEmitter: this.passportEventEmitter,
7634
7628
  });
7635
7629
  }
7636
7630
  async connectImxSilent() {
@@ -7665,6 +7659,7 @@ class Passport {
7665
7659
  throw new Error('EVM is not supported on production network');
7666
7660
  }
7667
7661
  return new ZkEvmProvider({
7662
+ passportEventEmitter: this.passportEventEmitter,
7668
7663
  authManager: this.authManager,
7669
7664
  magicAdapter: this.magicAdapter,
7670
7665
  config: this.config,
@@ -7676,7 +7671,10 @@ class Passport {
7676
7671
  return this.authManager.loginCallback();
7677
7672
  }
7678
7673
  async logout() {
7679
- return this.authManager.logout();
7674
+ await this.authManager.logout();
7675
+ // Code after this point is only executed if the logout mode is silent
7676
+ await this.magicAdapter.logout();
7677
+ this.passportEventEmitter.emit(PassportEvents.LOGGED_OUT);
7680
7678
  }
7681
7679
  /**
7682
7680
  * This method should only be called from the logout redirect uri