@imtbl/sdk 0.1.6 → 0.1.7

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/index.cjs CHANGED
@@ -7,6 +7,9 @@ var magicSdk = require('magic-sdk');
7
7
  var oidc = require('@magic-ext/oidc');
8
8
  var BN = require('bn.js');
9
9
  var encUtils = require('enc-utils');
10
+ var units = require('@ethersproject/units');
11
+ var bignumber = require('@ethersproject/bignumber');
12
+ var detectEthereumProvider = require('@metamask/detect-provider');
10
13
 
11
14
  function _interopNamespaceDefault(e) {
12
15
  var n = Object.create(null);
@@ -548,30 +551,40 @@ class ImmutableXClient {
548
551
  function serializeEthSignature(sig) {
549
552
  // This is because golang appends a recovery param
550
553
  // https://github.com/ethers-io/ethers.js/issues/823
551
- return encUtils__namespace.addHexPrefix(encUtils__namespace.padLeft(sig.r.toString(16), 64) +
552
- encUtils__namespace.padLeft(sig.s.toString(16), 64) +
553
- encUtils__namespace.padLeft(sig.recoveryParam?.toString(16) || '', 2));
554
+ return encUtils__namespace.addHexPrefix(encUtils__namespace.padLeft(sig.r.toString(16), 64)
555
+ + encUtils__namespace.padLeft(sig.s.toString(16), 64)
556
+ + encUtils__namespace.padLeft(sig.recoveryParam?.toString(16) || '', 2));
554
557
  }
555
558
  function importRecoveryParam(v) {
559
+ const isValidBigNumber = new BN(v, 16).cmp(new BN(27)) !== -1
560
+ ? new BN(v, 16).sub(new BN(27)).toNumber()
561
+ : new BN(v, 16).toNumber();
556
562
  return v.trim()
557
- ? new BN(v, 16).cmp(new BN(27)) !== -1
558
- ? new BN(v, 16).sub(new BN(27)).toNumber()
559
- : new BN(v, 16).toNumber()
563
+ ? isValidBigNumber
560
564
  : undefined;
561
565
  }
562
566
  // used chained with serializeEthSignature. serializeEthSignature(deserializeSignature(...))
563
567
  function deserializeSignature(sig, size = 64) {
564
- sig = encUtils__namespace.removeHexPrefix(sig);
568
+ const removedHexPrefixSig = encUtils__namespace.removeHexPrefix(sig);
565
569
  return {
566
- r: new BN(sig.substring(0, size), 'hex'),
567
- s: new BN(sig.substring(size, size * 2), 'hex'),
568
- recoveryParam: importRecoveryParam(sig.substring(size * 2, size * 2 + 2)),
570
+ r: new BN(removedHexPrefixSig.substring(0, size), 'hex'),
571
+ s: new BN(removedHexPrefixSig.substring(size, size * 2), 'hex'),
572
+ recoveryParam: importRecoveryParam(removedHexPrefixSig.substring(size * 2, size * 2 + 2)),
569
573
  };
570
574
  }
571
575
  async function signRaw(payload, signer) {
572
576
  const signature = deserializeSignature(await signer.signMessage(payload));
573
577
  return serializeEthSignature(signature);
574
578
  }
579
+ async function signMessage(message, signer) {
580
+ const ethAddress = await signer.getAddress();
581
+ const ethSignature = await signRaw(message, signer);
582
+ return {
583
+ message,
584
+ ethAddress,
585
+ ethSignature,
586
+ };
587
+ }
575
588
 
576
589
  /**
577
590
  * Helper method to convert token type to a SignableToken type
@@ -596,6 +609,7 @@ function convertToSignableToken(token) {
596
609
  },
597
610
  };
598
611
  case 'ETH':
612
+ default:
599
613
  return {
600
614
  type: 'ETH',
601
615
  data: {
@@ -899,7 +913,7 @@ class ConfirmationScreen {
899
913
  }
900
914
 
901
915
  const ERC721$1 = 'ERC721';
902
- const transfer = ({ request, transfersApi, starkSigner, user, passportConfig,
916
+ const transfer$1 = ({ request, transfersApi, starkSigner, user, passportConfig,
903
917
  // TODO: remove this eslint disable once we have a better solution
904
918
  // eslint-disable-next-line max-len
905
919
  }) => withPassportError(async () => {
@@ -1010,7 +1024,7 @@ async function batchNftTransfer({ user, starkSigner, request, transfersApi, pass
1010
1024
  }
1011
1025
 
1012
1026
  const ERC721 = 'ERC721';
1013
- async function createOrder({ starkSigner, user, request, ordersApi, passportConfig, }) {
1027
+ async function createOrder$1({ starkSigner, user, request, ordersApi, passportConfig, }) {
1014
1028
  return withPassportError(async () => {
1015
1029
  const ethAddress = user.etherKey;
1016
1030
  const amountSell = request.sell.type === ERC721 ? '1' : request.sell.amount;
@@ -1066,7 +1080,7 @@ async function createOrder({ starkSigner, user, request, ordersApi, passportConf
1066
1080
  };
1067
1081
  }, PassportErrorType.CREATE_ORDER_ERROR);
1068
1082
  }
1069
- async function cancelOrder({ user, starkSigner, request, ordersApi, passportConfig, }) {
1083
+ async function cancelOrder$1({ user, starkSigner, request, ordersApi, passportConfig, }) {
1070
1084
  return withPassportError(async () => {
1071
1085
  const getSignableCancelOrderRequest = {
1072
1086
  order_id: request.order_id,
@@ -1102,7 +1116,7 @@ async function cancelOrder({ user, starkSigner, request, ordersApi, passportConf
1102
1116
  }, PassportErrorType.CANCEL_ORDER_ERROR);
1103
1117
  }
1104
1118
 
1105
- async function exchangeTransfer({ user, starkSigner, request, exchangesApi, }) {
1119
+ async function exchangeTransfer$1({ user, starkSigner, request, exchangesApi, }) {
1106
1120
  return withPassportError(async () => {
1107
1121
  const ethAddress = user.etherKey;
1108
1122
  const transferAmount = request.amount;
@@ -1146,7 +1160,7 @@ async function exchangeTransfer({ user, starkSigner, request, exchangesApi, }) {
1146
1160
  }, PassportErrorType.EXCHANGE_TRANSFER_ERROR);
1147
1161
  }
1148
1162
 
1149
- async function createTrade({ request, tradesApi, user, starkSigner, passportConfig, }) {
1163
+ async function createTrade$1({ request, tradesApi, user, starkSigner, passportConfig, }) {
1150
1164
  return withPassportError(async () => {
1151
1165
  const ethAddress = user.etherKey;
1152
1166
  const getSignableTradeRequest = {
@@ -1208,7 +1222,7 @@ class PassportImxProvider {
1208
1222
  this.immutableXClient = immutableXClient;
1209
1223
  }
1210
1224
  async transfer(request) {
1211
- return transfer({
1225
+ return transfer$1({
1212
1226
  request,
1213
1227
  user: this.user,
1214
1228
  starkSigner: this.starkSigner,
@@ -1227,7 +1241,7 @@ class PassportImxProvider {
1227
1241
  throw new PassportError('Operation not supported', PassportErrorType.OPERATION_NOT_SUPPORTED_ERROR);
1228
1242
  }
1229
1243
  createOrder(request) {
1230
- return createOrder({
1244
+ return createOrder$1({
1231
1245
  request,
1232
1246
  user: this.user,
1233
1247
  starkSigner: this.starkSigner,
@@ -1236,7 +1250,7 @@ class PassportImxProvider {
1236
1250
  });
1237
1251
  }
1238
1252
  cancelOrder(request) {
1239
- return cancelOrder({
1253
+ return cancelOrder$1({
1240
1254
  request,
1241
1255
  user: this.user,
1242
1256
  starkSigner: this.starkSigner,
@@ -1245,7 +1259,7 @@ class PassportImxProvider {
1245
1259
  });
1246
1260
  }
1247
1261
  createTrade(request) {
1248
- return createTrade({
1262
+ return createTrade$1({
1249
1263
  request,
1250
1264
  user: this.user,
1251
1265
  starkSigner: this.starkSigner,
@@ -1263,7 +1277,7 @@ class PassportImxProvider {
1263
1277
  });
1264
1278
  }
1265
1279
  exchangeTransfer(request) {
1266
- return exchangeTransfer({
1280
+ return exchangeTransfer$1({
1267
1281
  request,
1268
1282
  user: this.user,
1269
1283
  starkSigner: this.starkSigner,
@@ -1290,7 +1304,7 @@ class PassportImxProvider {
1290
1304
  throw new PassportError('Operation not supported', PassportErrorType.OPERATION_NOT_SUPPORTED_ERROR);
1291
1305
  }
1292
1306
  getAddress() {
1293
- return Promise.resolve(this.starkSigner.getAddress());
1307
+ return Promise.resolve(this.user.etherKey);
1294
1308
  }
1295
1309
  }
1296
1310
 
@@ -1400,6 +1414,7 @@ async function registerPassport({ ethSigner, starkSigner, usersApi }, authorizat
1400
1414
  }
1401
1415
 
1402
1416
  // TODO: Remove this once the dependency has been added
1417
+ // eslint-disable-next-line import/no-extraneous-dependencies
1403
1418
  class Passport {
1404
1419
  authManager;
1405
1420
  magicAdapter;
@@ -1481,6 +1496,1062 @@ class Passport {
1481
1496
  }
1482
1497
  }
1483
1498
 
1499
+ function isChainValid(chainID, config) {
1500
+ return chainID === config.ethConfiguration.chainID;
1501
+ }
1502
+ async function validateChain(signer, config) {
1503
+ const chainID = await signer.getChainId();
1504
+ if (!isChainValid(chainID, config)) {
1505
+ throw new Error('The wallet used for this operation is not connected to the correct network.');
1506
+ }
1507
+ }
1508
+
1509
+ async function transfer({ signers: { ethSigner, starkSigner }, request, config, }) {
1510
+ await validateChain(ethSigner, config.immutableXConfig);
1511
+ const ethAddress = await ethSigner.getAddress();
1512
+ const transfersApi = new coreSdk.TransfersApi(config.immutableXConfig.apiConfiguration);
1513
+ const transferAmount = request.type === 'ERC721' ? '1' : request.amount;
1514
+ const signableResult = await transfersApi.getSignableTransferV1({
1515
+ getSignableTransferRequest: {
1516
+ sender: ethAddress,
1517
+ token: convertToSignableToken(request),
1518
+ amount: transferAmount,
1519
+ receiver: request.receiver,
1520
+ },
1521
+ });
1522
+ const { signable_message: signableMessage, payload_hash: payloadHash } = signableResult.data;
1523
+ const ethSignature = await signRaw(signableMessage, ethSigner);
1524
+ const starkSignature = await starkSigner.signMessage(payloadHash);
1525
+ const transferSigningParams = {
1526
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
1527
+ sender_stark_key: signableResult.data.sender_stark_key,
1528
+ sender_vault_id: signableResult.data.sender_vault_id,
1529
+ receiver_stark_key: signableResult.data.receiver_stark_key,
1530
+ receiver_vault_id: signableResult.data.receiver_vault_id,
1531
+ asset_id: signableResult.data.asset_id,
1532
+ amount: signableResult.data.amount,
1533
+ nonce: signableResult.data.nonce,
1534
+ expiration_timestamp: signableResult.data.expiration_timestamp,
1535
+ stark_signature: starkSignature,
1536
+ };
1537
+ const response = await transfersApi.createTransferV1({
1538
+ createTransferRequest: transferSigningParams,
1539
+ xImxEthAddress: ethAddress,
1540
+ xImxEthSignature: ethSignature,
1541
+ });
1542
+ return {
1543
+ sent_signature: response?.data.sent_signature,
1544
+ status: response?.data.status?.toString(),
1545
+ time: response?.data.time,
1546
+ transfer_id: response?.data.transfer_id,
1547
+ };
1548
+ }
1549
+ async function batchTransfer({ signers: { ethSigner, starkSigner }, request, config, }) {
1550
+ await validateChain(ethSigner, config.immutableXConfig);
1551
+ const ethAddress = await ethSigner.getAddress();
1552
+ const transfersApi = new coreSdk.TransfersApi(config.immutableXConfig.apiConfiguration);
1553
+ const signableRequests = request.map((nftTransfer) => ({
1554
+ amount: '1',
1555
+ token: convertToSignableToken({
1556
+ type: 'ERC721',
1557
+ tokenId: nftTransfer.tokenId,
1558
+ tokenAddress: nftTransfer.tokenAddress,
1559
+ }),
1560
+ receiver: nftTransfer.receiver,
1561
+ }));
1562
+ const signableResult = await transfersApi.getSignableTransfer({
1563
+ getSignableTransferRequestV2: {
1564
+ sender_ether_key: ethAddress,
1565
+ signable_requests: signableRequests,
1566
+ },
1567
+ });
1568
+ const signableMessage = signableResult.data.signable_message;
1569
+ if (signableMessage === undefined) {
1570
+ throw new Error('Invalid response from Signable registration offchain');
1571
+ }
1572
+ const ethSignature = await signRaw(signableMessage, ethSigner);
1573
+ const requests = [];
1574
+ for (const resp of signableResult.data.signable_responses) {
1575
+ // TODO: remove once fixed
1576
+ // eslint-disable-next-line no-await-in-loop
1577
+ const starkSignature = await starkSigner.signMessage(resp.payload_hash);
1578
+ const req = {
1579
+ sender_vault_id: resp.sender_vault_id,
1580
+ receiver_stark_key: resp.receiver_stark_key,
1581
+ receiver_vault_id: resp.receiver_vault_id,
1582
+ asset_id: resp.asset_id,
1583
+ amount: resp.amount,
1584
+ nonce: resp.nonce,
1585
+ expiration_timestamp: resp.expiration_timestamp,
1586
+ stark_signature: starkSignature,
1587
+ };
1588
+ requests.push(req);
1589
+ }
1590
+ // TODO: throw error on missing payload hash?
1591
+ const transferSigningParams = {
1592
+ sender_stark_key: signableResult.data.sender_stark_key,
1593
+ requests,
1594
+ };
1595
+ const response = await transfersApi.createTransfer({
1596
+ createTransferRequestV2: transferSigningParams,
1597
+ xImxEthAddress: ethAddress,
1598
+ xImxEthSignature: ethSignature,
1599
+ });
1600
+ return {
1601
+ transfer_ids: response?.data.transfer_ids,
1602
+ };
1603
+ }
1604
+
1605
+ async function createOrder({ signers, request, config, }) {
1606
+ await validateChain(signers.ethSigner, config.immutableXConfig);
1607
+ const ethAddress = await signers.ethSigner.getAddress();
1608
+ const ordersApi = new coreSdk.OrdersApi(config.immutableXConfig.apiConfiguration);
1609
+ const amountSell = request.sell.type === 'ERC721' ? '1' : request.sell.amount;
1610
+ const amountBuy = request.buy.type === 'ERC721' ? '1' : request.buy.amount;
1611
+ const getSignableOrderRequest = {
1612
+ user: ethAddress,
1613
+ amount_buy: amountBuy,
1614
+ token_buy: convertToSignableToken(request.buy),
1615
+ amount_sell: amountSell,
1616
+ token_sell: convertToSignableToken(request.sell),
1617
+ fees: request.fees,
1618
+ expiration_timestamp: request.expiration_timestamp,
1619
+ };
1620
+ const getSignableOrderResponse = await ordersApi.getSignableOrder({
1621
+ getSignableOrderRequestV3: getSignableOrderRequest,
1622
+ });
1623
+ const { signable_message: signableMessage, payload_hash: payloadHash } = getSignableOrderResponse.data;
1624
+ const ethSignature = await signRaw(signableMessage, signers.ethSigner);
1625
+ const starkSignature = await signers.starkSigner.signMessage(payloadHash);
1626
+ const resp = getSignableOrderResponse.data;
1627
+ const orderParams = {
1628
+ createOrderRequest: {
1629
+ amount_buy: resp.amount_buy,
1630
+ amount_sell: resp.amount_sell,
1631
+ asset_id_buy: resp.asset_id_buy,
1632
+ asset_id_sell: resp.asset_id_sell,
1633
+ expiration_timestamp: resp.expiration_timestamp,
1634
+ include_fees: true,
1635
+ fees: request.fees,
1636
+ nonce: resp.nonce,
1637
+ stark_key: resp.stark_key,
1638
+ stark_signature: starkSignature,
1639
+ vault_id_buy: resp.vault_id_buy,
1640
+ vault_id_sell: resp.vault_id_sell,
1641
+ },
1642
+ xImxEthAddress: ethAddress,
1643
+ xImxEthSignature: ethSignature,
1644
+ };
1645
+ const createOrderResponse = await ordersApi.createOrder(orderParams);
1646
+ return {
1647
+ ...createOrderResponse.data,
1648
+ };
1649
+ }
1650
+ async function cancelOrder({ signers, request, config, }) {
1651
+ const ordersApi = new coreSdk.OrdersApi(config.immutableXConfig.apiConfiguration);
1652
+ const getSignableCancelOrderResponse = await ordersApi.getSignableCancelOrder({
1653
+ getSignableCancelOrderRequest: {
1654
+ order_id: request.order_id,
1655
+ },
1656
+ });
1657
+ const { signable_message: signableMessage, payload_hash: payloadHash } = getSignableCancelOrderResponse.data;
1658
+ const ethSignature = await signRaw(signableMessage, signers.ethSigner);
1659
+ const starkSignature = await signers.starkSigner.signMessage(payloadHash);
1660
+ const ethAddress = await signers.ethSigner.getAddress();
1661
+ const cancelOrderResponse = await ordersApi.cancelOrder({
1662
+ id: request.order_id.toString(),
1663
+ cancelOrderRequest: {
1664
+ order_id: request.order_id,
1665
+ stark_signature: starkSignature,
1666
+ },
1667
+ xImxEthAddress: ethAddress,
1668
+ xImxEthSignature: ethSignature,
1669
+ });
1670
+ return {
1671
+ order_id: cancelOrderResponse.data.order_id,
1672
+ status: cancelOrderResponse.data.status,
1673
+ };
1674
+ }
1675
+
1676
+ async function registerOffchain(signers, config) {
1677
+ await validateChain(signers.ethSigner, config.immutableXConfig);
1678
+ const usersApi = new coreSdk.UsersApi(config.immutableXConfig.apiConfiguration);
1679
+ const userAddress = await signers.ethSigner.getAddress();
1680
+ const starkPublicKey = await signers.starkSigner.getAddress();
1681
+ const signableResult = await usersApi.getSignableRegistrationOffchain({
1682
+ getSignableRegistrationRequest: {
1683
+ ether_key: userAddress,
1684
+ stark_key: starkPublicKey,
1685
+ },
1686
+ });
1687
+ const { signable_message: signableMessage, payload_hash: payloadHash } = signableResult.data;
1688
+ const ethSignature = await signRaw(signableMessage, signers.ethSigner);
1689
+ const starkSignature = await signers.starkSigner.signMessage(payloadHash);
1690
+ const registeredUser = await usersApi.registerUser({
1691
+ registerUserRequest: {
1692
+ eth_signature: ethSignature,
1693
+ ether_key: userAddress,
1694
+ stark_signature: starkSignature,
1695
+ stark_key: starkPublicKey,
1696
+ },
1697
+ });
1698
+ return registeredUser.data;
1699
+ }
1700
+ async function isRegisteredOnChain(starkPublicKey, ethSigner, config) {
1701
+ await validateChain(ethSigner, config.immutableXConfig);
1702
+ const registrationContract = coreSdk.Contracts.Registration.connect(config.immutableXConfig.ethConfiguration.registrationContractAddress, ethSigner);
1703
+ try {
1704
+ return await registrationContract.isRegistered(starkPublicKey);
1705
+ }
1706
+ catch (ex) {
1707
+ if (ex.reason === 'USER_UNREGISTERED') {
1708
+ return false;
1709
+ }
1710
+ throw ex;
1711
+ }
1712
+ }
1713
+ async function getSignableRegistrationOnchain(etherKey, starkPublicKey, usersApi) {
1714
+ const response = await usersApi.getSignableRegistration({
1715
+ getSignableRegistrationRequest: {
1716
+ ether_key: etherKey,
1717
+ stark_key: starkPublicKey,
1718
+ },
1719
+ });
1720
+ return {
1721
+ operator_signature: response.data.operator_signature,
1722
+ payload_hash: response.data.payload_hash,
1723
+ };
1724
+ }
1725
+
1726
+ const assertIsDefined = (value) => {
1727
+ if (value !== undefined)
1728
+ return value;
1729
+ throw new Error('undefined field exception');
1730
+ };
1731
+ async function prepareWithdrawalAction(params) {
1732
+ const { signers: { ethSigner, starkSigner }, type, config, } = params;
1733
+ await validateChain(ethSigner, params.config);
1734
+ const withdrawalsApi = new coreSdk.WithdrawalsApi(config.apiConfiguration);
1735
+ const withdrawalAmount = type === 'ERC721' ? '1' : params.amount;
1736
+ const signableWithdrawalResult = await withdrawalsApi.getSignableWithdrawal({
1737
+ getSignableWithdrawalRequest: {
1738
+ user: await ethSigner.getAddress(),
1739
+ token: convertToSignableToken(params),
1740
+ amount: withdrawalAmount,
1741
+ },
1742
+ });
1743
+ const { signable_message: signableMessage, payload_hash: payloadHash } = signableWithdrawalResult.data;
1744
+ const starkSignature = await starkSigner.signMessage(payloadHash);
1745
+ const { ethAddress, ethSignature } = await signMessage(signableMessage, ethSigner);
1746
+ const prepareWithdrawalResponse = await withdrawalsApi.createWithdrawal({
1747
+ createWithdrawalRequest: {
1748
+ stark_key: assertIsDefined(signableWithdrawalResult.data.stark_key),
1749
+ amount: withdrawalAmount,
1750
+ asset_id: assertIsDefined(signableWithdrawalResult.data.asset_id),
1751
+ vault_id: assertIsDefined(signableWithdrawalResult.data.vault_id),
1752
+ nonce: assertIsDefined(signableWithdrawalResult.data.nonce),
1753
+ stark_signature: starkSignature,
1754
+ },
1755
+ xImxEthAddress: ethAddress,
1756
+ xImxEthSignature: ethSignature,
1757
+ });
1758
+ return prepareWithdrawalResponse.data;
1759
+ }
1760
+
1761
+ async function getEncodeAssetInfo(assetType, tokenType, config, tokenData) {
1762
+ const encodingApi = new coreSdk.EncodingApi(config.apiConfiguration);
1763
+ const result = await encodingApi.encodeAsset({
1764
+ assetType,
1765
+ encodeAssetRequest: {
1766
+ token: {
1767
+ type: tokenType,
1768
+ ...(tokenData && { data: tokenData }),
1769
+ },
1770
+ },
1771
+ });
1772
+ return result.data;
1773
+ }
1774
+
1775
+ async function executeRegisterAndWithdrawERC20({ ethSigner, assetType, starkPublicKey, config, }) {
1776
+ const etherKey = await ethSigner.getAddress();
1777
+ const imxConfig = config.immutableXConfig;
1778
+ const usersApi = new coreSdk.UsersApi(imxConfig.apiConfiguration);
1779
+ const signableResult = await getSignableRegistrationOnchain(etherKey, starkPublicKey, usersApi);
1780
+ const contract = coreSdk.Contracts.Registration.connect(config.immutableXConfig.ethConfiguration.registrationContractAddress, ethSigner);
1781
+ const populatedTransaction = await contract.populateTransaction.registerAndWithdraw(etherKey, starkPublicKey, signableResult.operator_signature, assetType);
1782
+ return ethSigner.sendTransaction(populatedTransaction);
1783
+ }
1784
+ async function executeWithdrawERC20(ethSigner, assetType, starkPublicKey, config) {
1785
+ const contract = coreSdk.Contracts.Core.connect(config.ethConfiguration.coreContractAddress, ethSigner);
1786
+ const populatedTransaction = await contract.populateTransaction.withdraw(starkPublicKey, assetType);
1787
+ return ethSigner.sendTransaction(populatedTransaction);
1788
+ }
1789
+ async function completeERC20WithdrawalAction({ ethSigner, starkPublicKey, token, config, }) {
1790
+ await validateChain(ethSigner, config.immutableXConfig);
1791
+ const imxConfig = config.immutableXConfig;
1792
+ const assetType = await getEncodeAssetInfo('asset', 'ERC20', imxConfig, {
1793
+ token_address: token.tokenAddress,
1794
+ });
1795
+ const isRegistered = await isRegisteredOnChain(starkPublicKey, ethSigner, config);
1796
+ if (!isRegistered) {
1797
+ return executeRegisterAndWithdrawERC20({
1798
+ ethSigner,
1799
+ assetType: assetType.asset_type,
1800
+ starkPublicKey,
1801
+ config,
1802
+ });
1803
+ }
1804
+ return executeWithdrawERC20(ethSigner, assetType.asset_type, starkPublicKey, imxConfig);
1805
+ }
1806
+
1807
+ async function executeWithdrawMintableERC721(ethSigner, assetType, starkPublicKey, mintingBlob, config) {
1808
+ const contract = coreSdk.Contracts.Core.connect(config.ethConfiguration.coreContractAddress, ethSigner);
1809
+ const populatedTransaction = await contract.populateTransaction.withdrawAndMint(starkPublicKey, assetType, mintingBlob);
1810
+ return ethSigner.sendTransaction(populatedTransaction);
1811
+ }
1812
+ async function executeRegisterAndWithdrawMintableERC721(ethSigner, assetType, starkPublicKey, mintingBlob, config) {
1813
+ const etherKey = await ethSigner.getAddress();
1814
+ const usersApi = new coreSdk.UsersApi(config.apiConfiguration);
1815
+ const signableResult = await getSignableRegistrationOnchain(etherKey, starkPublicKey, usersApi);
1816
+ const contract = coreSdk.Contracts.Registration.connect(config.ethConfiguration.registrationContractAddress, ethSigner);
1817
+ const populatedTransaction = await contract.populateTransaction.regsiterAndWithdrawAndMint(etherKey, starkPublicKey, signableResult.operator_signature, assetType, mintingBlob);
1818
+ return ethSigner.sendTransaction(populatedTransaction);
1819
+ }
1820
+ function getMintingBlob(token) {
1821
+ const { id } = token.data;
1822
+ const blueprint = token.data.blueprint || '';
1823
+ return encUtils__namespace.sanitizeHex(encUtils__namespace.utf8ToHex(`{${id}}:{${blueprint}}`));
1824
+ }
1825
+ async function completeMintableERC721Withdrawal(ethSigner, starkPublicKey, token, config) {
1826
+ const imxConfig = config.immutableXConfig;
1827
+ const assetType = await getEncodeAssetInfo('mintable-asset', 'ERC721', imxConfig, {
1828
+ id: token.data.id,
1829
+ token_address: token.data.tokenAddress,
1830
+ ...(token.data.blueprint && { blueprint: token.data.blueprint }),
1831
+ });
1832
+ const mintingBlob = getMintingBlob(token);
1833
+ const isRegistered = await isRegisteredOnChain(starkPublicKey, ethSigner, config);
1834
+ if (!isRegistered) {
1835
+ return executeRegisterAndWithdrawMintableERC721(ethSigner, assetType.asset_type, starkPublicKey, mintingBlob, imxConfig);
1836
+ }
1837
+ return executeWithdrawMintableERC721(ethSigner, assetType.asset_type, starkPublicKey, mintingBlob, imxConfig);
1838
+ }
1839
+ async function executeRegisterAndWithdrawERC721(ethSigner, assetType, starkPublicKey, tokenId, config) {
1840
+ const etherKey = await ethSigner.getAddress();
1841
+ const usersApi = new coreSdk.UsersApi(config.apiConfiguration);
1842
+ const signableResult = await getSignableRegistrationOnchain(etherKey, starkPublicKey, usersApi);
1843
+ const contract = coreSdk.Contracts.Registration.connect(config.ethConfiguration.registrationContractAddress, ethSigner);
1844
+ const populatedTransaction = await contract.populateTransaction.registerAndWithdrawNft(etherKey, starkPublicKey, signableResult.operator_signature, assetType, tokenId);
1845
+ return ethSigner.sendTransaction(populatedTransaction);
1846
+ }
1847
+ async function executeWithdrawERC721(ethSigner, assetType, starkPublicKey, tokenId, config) {
1848
+ const contract = coreSdk.Contracts.Core.connect(config.ethConfiguration.coreContractAddress, ethSigner);
1849
+ const populatedTransaction = await contract.populateTransaction.withdrawNft(starkPublicKey, assetType, tokenId);
1850
+ return ethSigner.sendTransaction(populatedTransaction);
1851
+ }
1852
+ async function completeERC721Withdrawal(ethSigner, starkPublicKey, token, config) {
1853
+ const imxConfig = config.immutableXConfig;
1854
+ const assetType = await getEncodeAssetInfo('asset', 'ERC721', imxConfig, {
1855
+ token_id: token.tokenId,
1856
+ token_address: token.tokenAddress,
1857
+ });
1858
+ const isRegistered = await isRegisteredOnChain(starkPublicKey, ethSigner, config);
1859
+ if (!isRegistered) {
1860
+ return executeRegisterAndWithdrawERC721(ethSigner, assetType.asset_type, starkPublicKey, token.tokenId, imxConfig);
1861
+ }
1862
+ return executeWithdrawERC721(ethSigner, assetType.asset_type, starkPublicKey, token.tokenId, imxConfig);
1863
+ }
1864
+ async function completeERC721WithdrawalAction({ ethSigner, starkPublicKey, token, config, }) {
1865
+ await validateChain(ethSigner, config.immutableXConfig);
1866
+ const { tokenAddress } = token;
1867
+ const { tokenId } = token;
1868
+ const imxConfig = config.immutableXConfig;
1869
+ const mintsApi = new coreSdk.MintsApi(imxConfig.apiConfiguration);
1870
+ return await mintsApi
1871
+ .getMintableTokenDetailsByClientTokenId({
1872
+ tokenAddress,
1873
+ tokenId,
1874
+ })
1875
+ .then((mintableToken) => completeMintableERC721Withdrawal(ethSigner, starkPublicKey, {
1876
+ type: 'ERC721',
1877
+ data: {
1878
+ id: tokenId,
1879
+ tokenAddress,
1880
+ blueprint: mintableToken.data.blueprint,
1881
+ },
1882
+ }, config))
1883
+ .catch((error) => {
1884
+ if (error.response?.status === 404) {
1885
+ // token is already minted on L1
1886
+ return completeERC721Withdrawal(ethSigner, starkPublicKey, token, config);
1887
+ }
1888
+ throw error; // unable to recover from any other kind of error
1889
+ });
1890
+ }
1891
+
1892
+ async function executeRegisterAndWithdrawEth(ethSigner, assetType, starkPublicKey, config) {
1893
+ const etherKey = await ethSigner.getAddress();
1894
+ const usersApi = new coreSdk.UsersApi(config.apiConfiguration);
1895
+ const signableResult = await getSignableRegistrationOnchain(etherKey, starkPublicKey, usersApi);
1896
+ const contract = coreSdk.Contracts.Registration.connect(config.ethConfiguration.registrationContractAddress, ethSigner);
1897
+ const populatedTransaction = await contract.populateTransaction.registerAndWithdraw(etherKey, starkPublicKey, signableResult.operator_signature, assetType);
1898
+ return ethSigner.sendTransaction(populatedTransaction);
1899
+ }
1900
+ async function executeWithdrawEth(ethSigner, assetType, starkPublicKey, config) {
1901
+ const contract = coreSdk.Contracts.Core.connect(config.ethConfiguration.coreContractAddress, ethSigner);
1902
+ const populatedTransaction = await contract.populateTransaction.withdraw(starkPublicKey, assetType);
1903
+ return ethSigner.sendTransaction(populatedTransaction);
1904
+ }
1905
+ async function completeEthWithdrawalAction({ ethSigner, starkPublicKey, config, }) {
1906
+ await validateChain(ethSigner, config.immutableXConfig);
1907
+ const imxConfig = config.immutableXConfig;
1908
+ const assetType = await getEncodeAssetInfo('asset', 'ETH', imxConfig);
1909
+ const isRegistered = await isRegisteredOnChain(starkPublicKey, ethSigner, config);
1910
+ if (!isRegistered) {
1911
+ return executeRegisterAndWithdrawEth(ethSigner, assetType.asset_type, starkPublicKey, imxConfig);
1912
+ }
1913
+ return executeWithdrawEth(ethSigner, assetType.asset_type, starkPublicKey, imxConfig);
1914
+ }
1915
+
1916
+ async function prepareWithdrawal({ signers, withdrawal, config, }) {
1917
+ return prepareWithdrawalAction({
1918
+ signers,
1919
+ config: config.immutableXConfig,
1920
+ ...withdrawal,
1921
+ });
1922
+ }
1923
+ // TODO: remove once fixed
1924
+ // eslint-disable-next-line consistent-return
1925
+ async function completeWithdrawal({ signers: { ethSigner }, starkPublicKey, token, config, }) {
1926
+ // TODO: please add a reasonable default here
1927
+ // eslint-disable-next-line default-case
1928
+ switch (token.type) {
1929
+ case 'ETH':
1930
+ return completeEthWithdrawalAction({ ethSigner, starkPublicKey, config });
1931
+ case 'ERC20':
1932
+ return completeERC20WithdrawalAction({
1933
+ ethSigner,
1934
+ starkPublicKey,
1935
+ token,
1936
+ config,
1937
+ });
1938
+ case 'ERC721':
1939
+ return completeERC721WithdrawalAction({
1940
+ ethSigner,
1941
+ starkPublicKey,
1942
+ token,
1943
+ config,
1944
+ });
1945
+ }
1946
+ }
1947
+
1948
+ async function createTrade({ signers: { ethSigner, starkSigner }, request, config, }) {
1949
+ await validateChain(ethSigner, config.immutableXConfig);
1950
+ const ethAddress = await ethSigner.getAddress();
1951
+ const tradesApi = new coreSdk.TradesApi(config.immutableXConfig.apiConfiguration);
1952
+ const signableResult = await tradesApi.getSignableTrade({
1953
+ getSignableTradeRequest: {
1954
+ user: ethAddress,
1955
+ order_id: request.order_id,
1956
+ fees: request.fees,
1957
+ },
1958
+ });
1959
+ const { signable_message: signableMessage, payload_hash: payloadHash } = signableResult.data;
1960
+ const ethSignature = await signRaw(signableMessage, ethSigner);
1961
+ const starkSignature = await starkSigner.signMessage(payloadHash);
1962
+ const createTradeResponse = await tradesApi.createTrade({
1963
+ createTradeRequest: {
1964
+ amount_buy: signableResult.data.amount_buy,
1965
+ amount_sell: signableResult.data.amount_sell,
1966
+ asset_id_buy: signableResult.data.asset_id_buy,
1967
+ asset_id_sell: signableResult.data.asset_id_sell,
1968
+ expiration_timestamp: signableResult.data.expiration_timestamp,
1969
+ fee_info: signableResult.data.fee_info,
1970
+ fees: request.fees,
1971
+ include_fees: true,
1972
+ nonce: signableResult.data.nonce,
1973
+ order_id: request.order_id,
1974
+ stark_key: signableResult.data.stark_key,
1975
+ vault_id_buy: signableResult.data.vault_id_buy,
1976
+ vault_id_sell: signableResult.data.vault_id_sell,
1977
+ stark_signature: starkSignature,
1978
+ },
1979
+ xImxEthAddress: ethAddress,
1980
+ xImxEthSignature: ethSignature,
1981
+ });
1982
+ return createTradeResponse.data;
1983
+ }
1984
+
1985
+ async function executeRegisterAndDepositEth(ethSigner, amount, assetType, starkPublicKey, vaultId, config, usersApi) {
1986
+ const etherKey = await ethSigner.getAddress();
1987
+ const coreContract = coreSdk.Contracts.Core.connect(config.ethConfiguration.coreContractAddress, ethSigner);
1988
+ const signableResult = await getSignableRegistrationOnchain(etherKey, starkPublicKey, usersApi);
1989
+ const populatedTransaction = await coreContract.populateTransaction.registerAndDepositEth(etherKey, starkPublicKey, signableResult.operator_signature, assetType, vaultId);
1990
+ return ethSigner.sendTransaction({ ...populatedTransaction, value: amount });
1991
+ }
1992
+ async function executeDepositEth(ethSigner, amount, assetType, starkPublicKey, vaultId, config) {
1993
+ const coreContract = coreSdk.Contracts.Core.connect(config.ethConfiguration.coreContractAddress, ethSigner);
1994
+ const populatedTransaction = await coreContract.populateTransaction['deposit(uint256,uint256,uint256)'](starkPublicKey, assetType, vaultId);
1995
+ return ethSigner.sendTransaction({ ...populatedTransaction, value: amount });
1996
+ }
1997
+ async function depositEth({ signers: { ethSigner }, deposit, config, }) {
1998
+ await validateChain(ethSigner, config.immutableXConfig);
1999
+ const user = await ethSigner.getAddress();
2000
+ const data = {
2001
+ decimals: 18,
2002
+ };
2003
+ const amount = units.parseUnits(deposit.amount, 'wei');
2004
+ const imxConfig = config.immutableXConfig;
2005
+ const depositsApi = new coreSdk.DepositsApi(imxConfig.apiConfiguration);
2006
+ const encodingApi = new coreSdk.EncodingApi(imxConfig.apiConfiguration);
2007
+ const usersApi = new coreSdk.UsersApi(imxConfig.apiConfiguration);
2008
+ const getSignableDepositRequest = {
2009
+ user,
2010
+ token: {
2011
+ type: deposit.type,
2012
+ data,
2013
+ },
2014
+ amount: amount.toString(),
2015
+ };
2016
+ const signableDepositResult = await depositsApi.getSignableDeposit({
2017
+ getSignableDepositRequest,
2018
+ });
2019
+ const encodingResult = await encodingApi.encodeAsset({
2020
+ assetType: 'asset',
2021
+ encodeAssetRequest: {
2022
+ token: {
2023
+ type: deposit.type,
2024
+ },
2025
+ },
2026
+ });
2027
+ const assetType = encodingResult.data.asset_type;
2028
+ const starkPublicKey = signableDepositResult.data.stark_key;
2029
+ const vaultId = signableDepositResult.data.vault_id;
2030
+ const isRegistered = await isRegisteredOnChain(starkPublicKey, ethSigner, config);
2031
+ if (!isRegistered) {
2032
+ return executeRegisterAndDepositEth(ethSigner, amount, assetType, starkPublicKey, vaultId, imxConfig, usersApi);
2033
+ }
2034
+ return executeDepositEth(ethSigner, amount, assetType, starkPublicKey, vaultId, imxConfig);
2035
+ }
2036
+
2037
+ async function executeRegisterAndDepositERC20(ethSigner, quantizedAmount, assetType, starkPublicKey, vaultId, config, usersApi) {
2038
+ const etherKey = await ethSigner.getAddress();
2039
+ const coreContract = coreSdk.Contracts.Core.connect(config.coreContractAddress, ethSigner);
2040
+ const signableResult = await getSignableRegistrationOnchain(etherKey, starkPublicKey, usersApi);
2041
+ const populatedTransaction = await coreContract.populateTransaction.registerAndDepositERC20(etherKey, starkPublicKey, signableResult.operator_signature, assetType, vaultId, quantizedAmount);
2042
+ return ethSigner.sendTransaction(populatedTransaction);
2043
+ }
2044
+ async function executeDepositERC20(ethSigner, quantizedAmount, assetType, starkPublicKey, vaultId, config) {
2045
+ const coreContract = coreSdk.Contracts.Core.connect(config.coreContractAddress, ethSigner);
2046
+ const populatedTransaction = await coreContract.populateTransaction.depositERC20(starkPublicKey, assetType, vaultId, quantizedAmount);
2047
+ return ethSigner.sendTransaction(populatedTransaction);
2048
+ }
2049
+ async function depositERC20({ signers: { ethSigner }, deposit, config, }) {
2050
+ await validateChain(ethSigner, config.immutableXConfig);
2051
+ const { apiConfiguration, ethConfiguration } = config.immutableXConfig;
2052
+ const user = await ethSigner.getAddress();
2053
+ const tokensApi = new coreSdk.TokensApi(apiConfiguration);
2054
+ const depositsApi = new coreSdk.DepositsApi(apiConfiguration);
2055
+ const encodingApi = new coreSdk.EncodingApi(apiConfiguration);
2056
+ const usersApi = new coreSdk.UsersApi(apiConfiguration);
2057
+ // Get decimals for this specific ERC20
2058
+ const token = await tokensApi.getToken({ address: deposit.tokenAddress });
2059
+ // TODO: remove once fixed
2060
+ // eslint-disable-next-line radix
2061
+ const decimals = parseInt(token.data.decimals);
2062
+ const data = {
2063
+ decimals,
2064
+ token_address: deposit.tokenAddress,
2065
+ };
2066
+ const amount = units.parseUnits(deposit.amount, 0); // 0 to always use undecimalized value
2067
+ // Approve whether an amount of token from an account can be spent by a third-party account
2068
+ const tokenContract = coreSdk.Contracts.IERC20.connect(deposit.tokenAddress, ethSigner);
2069
+ const approveTransaction = await tokenContract.populateTransaction.approve(ethConfiguration.coreContractAddress, amount);
2070
+ await ethSigner.sendTransaction(approveTransaction);
2071
+ const getSignableDepositRequest = {
2072
+ user,
2073
+ token: {
2074
+ type: deposit.type,
2075
+ data,
2076
+ },
2077
+ amount: amount.toString(),
2078
+ };
2079
+ const signableDepositResult = await depositsApi.getSignableDeposit({
2080
+ getSignableDepositRequest,
2081
+ });
2082
+ // Perform encoding on asset details to get an assetType (required for stark contract request)
2083
+ const encodingResult = await encodingApi.encodeAsset({
2084
+ assetType: 'asset',
2085
+ encodeAssetRequest: {
2086
+ token: {
2087
+ type: deposit.type,
2088
+ data: {
2089
+ token_address: deposit.tokenAddress,
2090
+ },
2091
+ },
2092
+ },
2093
+ });
2094
+ const assetType = encodingResult.data.asset_type;
2095
+ const starkPublicKey = signableDepositResult.data.stark_key;
2096
+ const vaultId = signableDepositResult.data.vault_id;
2097
+ const quantizedAmount = bignumber.BigNumber.from(signableDepositResult.data.amount);
2098
+ const isRegistered = await isRegisteredOnChain(starkPublicKey, ethSigner, config);
2099
+ if (!isRegistered) {
2100
+ return executeRegisterAndDepositERC20(ethSigner, quantizedAmount, assetType, starkPublicKey, vaultId, ethConfiguration, usersApi);
2101
+ }
2102
+ return executeDepositERC20(ethSigner, quantizedAmount, assetType, starkPublicKey, vaultId, ethConfiguration);
2103
+ }
2104
+
2105
+ async function executeDepositERC721(ethSigner, tokenId, assetType, starkPublicKey, vaultId, config) {
2106
+ const coreContract = coreSdk.Contracts.Core.connect(config.ethConfiguration.coreContractAddress, ethSigner);
2107
+ const populatedTransaction = await coreContract.populateTransaction.depositNft(starkPublicKey, assetType, vaultId, tokenId);
2108
+ return ethSigner.sendTransaction(populatedTransaction);
2109
+ }
2110
+ async function depositERC721({ signers: { ethSigner }, deposit, config, }) {
2111
+ await validateChain(ethSigner, config.immutableXConfig);
2112
+ const user = await ethSigner.getAddress();
2113
+ const { immutableXConfig } = config;
2114
+ const depositsApi = new coreSdk.DepositsApi(immutableXConfig.apiConfiguration);
2115
+ const encodingApi = new coreSdk.EncodingApi(immutableXConfig.apiConfiguration);
2116
+ const usersApi = new coreSdk.UsersApi(immutableXConfig.apiConfiguration);
2117
+ const data = {
2118
+ token_address: deposit.tokenAddress,
2119
+ token_id: deposit.tokenId,
2120
+ };
2121
+ const amount = '1';
2122
+ const getSignableDepositRequest = {
2123
+ user,
2124
+ token: {
2125
+ type: deposit.type,
2126
+ data,
2127
+ },
2128
+ amount: amount.toString(),
2129
+ };
2130
+ const signableDepositResult = await depositsApi.getSignableDeposit({
2131
+ getSignableDepositRequest,
2132
+ });
2133
+ // Perform encoding on asset details to get an assetType (required for stark contract request)
2134
+ const encodingResult = await encodingApi.encodeAsset({
2135
+ assetType: 'asset',
2136
+ encodeAssetRequest: {
2137
+ token: {
2138
+ type: deposit.type,
2139
+ data: {
2140
+ token_address: deposit.tokenAddress,
2141
+ token_id: deposit.tokenId,
2142
+ },
2143
+ },
2144
+ },
2145
+ });
2146
+ const assetType = encodingResult.data.asset_type;
2147
+ const starkPublicKey = signableDepositResult.data.stark_key;
2148
+ const vaultId = signableDepositResult.data.vault_id;
2149
+ const isRegistered = await isRegisteredOnChain(starkPublicKey, ethSigner, config);
2150
+ // Approve whether an amount of token from an account can be spent by a third-party account
2151
+ const tokenContract = coreSdk.Contracts.IERC721.connect(deposit.tokenAddress, ethSigner);
2152
+ const operator = immutableXConfig.ethConfiguration.coreContractAddress;
2153
+ const isApprovedForAll = await tokenContract.isApprovedForAll(user, operator);
2154
+ if (!isApprovedForAll) {
2155
+ await tokenContract.setApprovalForAll(operator, true);
2156
+ }
2157
+ if (!isRegistered) {
2158
+ const signableResult = await getSignableRegistrationOnchain(user, starkPublicKey, usersApi);
2159
+ const coreContract = coreSdk.Contracts.Core.connect(immutableXConfig.ethConfiguration.coreContractAddress, ethSigner);
2160
+ // Note: proxy registration contract registerAndDepositNft method is not used as
2161
+ // it currently fails erc721 transfer ownership check
2162
+ await coreContract.registerUser(user, starkPublicKey, signableResult.operator_signature);
2163
+ }
2164
+ return executeDepositERC721(ethSigner, deposit.tokenId, assetType, starkPublicKey, vaultId, immutableXConfig);
2165
+ }
2166
+
2167
+ // TODO: remove once fixed deposit variable shadowing
2168
+ // eslint-disable-next-line consistent-return, @typescript-eslint/no-shadow
2169
+ async function deposit({ signers, deposit, config }) {
2170
+ // TODO: please add a reasonable default here
2171
+ // eslint-disable-next-line default-case
2172
+ switch (deposit.type) {
2173
+ case 'ETH':
2174
+ return depositEth({ signers, deposit, config });
2175
+ case 'ERC20':
2176
+ return depositERC20({ signers, deposit, config });
2177
+ case 'ERC721':
2178
+ return depositERC721({ signers, deposit, config });
2179
+ }
2180
+ }
2181
+
2182
+ async function exchangeTransfer({ signers, request, config, }) {
2183
+ await validateChain(signers.ethSigner, config.immutableXConfig);
2184
+ const exchangeApi = new coreSdk.ExchangesApi(config.immutableXConfig.apiConfiguration);
2185
+ const ethAddress = await signers.ethSigner.getAddress();
2186
+ const transferAmount = request.amount;
2187
+ const signableResult = await exchangeApi.getExchangeSignableTransfer({
2188
+ id: request.transactionID,
2189
+ getSignableTransferRequest: {
2190
+ sender: ethAddress,
2191
+ token: convertToSignableToken(request),
2192
+ amount: transferAmount,
2193
+ receiver: request.receiver,
2194
+ },
2195
+ });
2196
+ const { signable_message: signableMessage, payload_hash: payloadHash } = signableResult.data;
2197
+ const ethSignature = await signRaw(signableMessage, signers.ethSigner);
2198
+ const starkSignature = await signers.starkSigner.signMessage(payloadHash);
2199
+ const transferSigningParams = {
2200
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
2201
+ sender_stark_key: signableResult.data.sender_stark_key,
2202
+ sender_vault_id: signableResult.data.sender_vault_id,
2203
+ receiver_stark_key: signableResult.data.receiver_stark_key,
2204
+ receiver_vault_id: signableResult.data.receiver_vault_id,
2205
+ asset_id: signableResult.data.asset_id,
2206
+ amount: signableResult.data.amount,
2207
+ nonce: signableResult.data.nonce,
2208
+ expiration_timestamp: signableResult.data.expiration_timestamp,
2209
+ stark_signature: starkSignature,
2210
+ };
2211
+ const response = await exchangeApi.createExchangeTransfer({
2212
+ id: request.transactionID,
2213
+ createTransferRequest: transferSigningParams,
2214
+ xImxEthAddress: ethAddress,
2215
+ xImxEthSignature: ethSignature,
2216
+ });
2217
+ return {
2218
+ sent_signature: response?.data.sent_signature,
2219
+ status: response?.data.status?.toString(),
2220
+ time: response?.data.time,
2221
+ transfer_id: response?.data.transfer_id,
2222
+ };
2223
+ }
2224
+
2225
+ class GenericIMXProvider {
2226
+ config;
2227
+ signers;
2228
+ constructor(config, ethSigner, starkSigner) {
2229
+ this.config = config;
2230
+ this.signers = { ethSigner, starkSigner };
2231
+ }
2232
+ async getAddress() {
2233
+ return await this.signers.ethSigner.getAddress();
2234
+ }
2235
+ registerOffchain() {
2236
+ return registerOffchain(this.signers, this.config);
2237
+ }
2238
+ batchNftTransfer(request) {
2239
+ return batchTransfer({
2240
+ signers: this.signers,
2241
+ request,
2242
+ config: this.config,
2243
+ });
2244
+ }
2245
+ cancelOrder(request) {
2246
+ return cancelOrder({
2247
+ signers: this.signers,
2248
+ request,
2249
+ config: this.config,
2250
+ });
2251
+ }
2252
+ completeWithdrawal(starkPublicKey, token) {
2253
+ return completeWithdrawal({
2254
+ config: this.config,
2255
+ signers: this.signers,
2256
+ token,
2257
+ starkPublicKey,
2258
+ });
2259
+ }
2260
+ createOrder(request) {
2261
+ return createOrder({
2262
+ signers: this.signers,
2263
+ request,
2264
+ config: this.config,
2265
+ });
2266
+ }
2267
+ createTrade(request) {
2268
+ return createTrade({
2269
+ signers: this.signers,
2270
+ request,
2271
+ config: this.config,
2272
+ });
2273
+ }
2274
+ deposit(tokenAmount) {
2275
+ return deposit({
2276
+ signers: this.signers,
2277
+ deposit: tokenAmount,
2278
+ config: this.config,
2279
+ });
2280
+ }
2281
+ exchangeTransfer(request) {
2282
+ return exchangeTransfer({
2283
+ signers: this.signers,
2284
+ request,
2285
+ config: this.config,
2286
+ });
2287
+ }
2288
+ async isRegisteredOnchain() {
2289
+ const starkPublicKey = await this.signers.starkSigner.getAddress();
2290
+ return isRegisteredOnChain(starkPublicKey, this.signers.ethSigner, this.config);
2291
+ }
2292
+ prepareWithdrawal(request) {
2293
+ return prepareWithdrawal({
2294
+ signers: this.signers,
2295
+ withdrawal: request,
2296
+ config: this.config,
2297
+ });
2298
+ }
2299
+ transfer(request) {
2300
+ return transfer({
2301
+ signers: this.signers,
2302
+ request,
2303
+ config: this.config,
2304
+ });
2305
+ }
2306
+ }
2307
+
2308
+ const WALLET_ACTION = {
2309
+ // TODO: remove once fixed - consider using an enum
2310
+ // eslint-disable-next-line @typescript-eslint/naming-convention
2311
+ SWITCH_CHAIN: 'wallet_switchEthereumChain',
2312
+ // eslint-disable-next-line @typescript-eslint/naming-convention
2313
+ CONNECT: 'eth_requestAccounts',
2314
+ };
2315
+ function isRequestableProvider(provider) {
2316
+ return !!provider?.request;
2317
+ }
2318
+ async function connectProvider(provider, chainID) {
2319
+ await provider.request({ method: WALLET_ACTION.CONNECT });
2320
+ if (chainID) {
2321
+ await provider.request({
2322
+ method: WALLET_ACTION.SWITCH_CHAIN,
2323
+ params: [{ chainId: `0x${chainID.toString(16)}` }],
2324
+ });
2325
+ }
2326
+ }
2327
+
2328
+ const ERRORS = {
2329
+ // TODO: remove once fixed - consider using something in line with the naming convention
2330
+ // eslint-disable-next-line @typescript-eslint/naming-convention
2331
+ PROVIDER_NOT_FOUND: 'The Metamask provider was not found',
2332
+ };
2333
+ async function connect$1({ chainID, }) {
2334
+ const provider = (await detectEthereumProvider());
2335
+ if (!isRequestableProvider(provider)) {
2336
+ throw new Error(ERRORS.PROVIDER_NOT_FOUND);
2337
+ }
2338
+ await connectProvider(provider, chainID);
2339
+ // NOTE: if we want to listen to Metamask events in the future, we can add a
2340
+ // listener here.
2341
+ return new ethers.ethers.providers.Web3Provider(provider);
2342
+ }
2343
+
2344
+ const COMMUNICATION_TYPE = 'message';
2345
+ var RequestEventType;
2346
+ (function (RequestEventType) {
2347
+ RequestEventType["GET_CONNECTION_REQUEST"] = "GET_CONNECTION_REQUEST";
2348
+ RequestEventType["CONNECT_WALLET_REQUEST"] = "CONNECT_WALLET_REQUEST";
2349
+ RequestEventType["SIGN_MESSAGE_REQUEST"] = "SIGN_MESSAGE_REQUEST";
2350
+ RequestEventType["DISCONNECT_WALLET_REQUEST"] = "DISCONNECT_WALLET_REQUEST";
2351
+ })(RequestEventType || (RequestEventType = {}));
2352
+ var ResponseEventType;
2353
+ (function (ResponseEventType) {
2354
+ ResponseEventType["CONNECT_WALLET_RESPONSE"] = "CONNECT_WALLET_RESPONSE";
2355
+ ResponseEventType["SIGN_MESSAGE_RESPONSE"] = "SIGN_MESSAGE_RESPONSE";
2356
+ ResponseEventType["GET_CONNECTION_RESPONSE"] = "GET_CONNECTION_RESPONSE";
2357
+ ResponseEventType["DISCONNECT_WALLET_RESPONSE"] = "DISCONNECT_WALLET_RESPONSE";
2358
+ })(ResponseEventType || (ResponseEventType = {}));
2359
+
2360
+ function postRequestMessage(iframe, payload) {
2361
+ if (iframe && iframe.contentWindow) {
2362
+ iframe.contentWindow.postMessage(payload, new URL(iframe.src).origin);
2363
+ }
2364
+ }
2365
+
2366
+ function messageResponseListener(iframe, event, eventType, callback) {
2367
+ if (iframe && event.source !== iframe.contentWindow) {
2368
+ return;
2369
+ }
2370
+ const l2WalletMessage = event.data;
2371
+ if (l2WalletMessage.type !== eventType) {
2372
+ return;
2373
+ }
2374
+ callback(l2WalletMessage.details);
2375
+ }
2376
+
2377
+ class ImxSigner {
2378
+ publicAddress;
2379
+ iframe;
2380
+ constructor(publicAddress, iframe) {
2381
+ this.publicAddress = publicAddress;
2382
+ this.iframe = iframe;
2383
+ }
2384
+ getAddress() {
2385
+ return this.publicAddress;
2386
+ }
2387
+ signMessage(rawMessage) {
2388
+ return new Promise((resolve, reject) => {
2389
+ const listener = (event) => {
2390
+ messageResponseListener(this.iframe, event, ResponseEventType.SIGN_MESSAGE_RESPONSE, (messageDetails) => {
2391
+ window.removeEventListener(COMMUNICATION_TYPE, listener);
2392
+ if (!messageDetails.success) {
2393
+ reject(new Error(messageDetails.error?.message));
2394
+ }
2395
+ resolve(messageDetails.data.signedMessage);
2396
+ });
2397
+ };
2398
+ window.addEventListener(COMMUNICATION_TYPE, listener);
2399
+ postRequestMessage(this.iframe, {
2400
+ type: RequestEventType.SIGN_MESSAGE_REQUEST,
2401
+ details: { starkPublicKey: this.publicAddress, message: rawMessage },
2402
+ });
2403
+ });
2404
+ }
2405
+ getIFrame() {
2406
+ return this.iframe;
2407
+ }
2408
+ }
2409
+
2410
+ const IMX_WALLET_IFRAME_ID = 'imx-wallet-app';
2411
+ const IMX_WALLET_IFRAME_HOSTS = {
2412
+ [exports.Environment.SANDBOX]: 'https://wallets.sandbox.immutable.com',
2413
+ [exports.Environment.PRODUCTION]: 'https://wallets.immutable.com',
2414
+ };
2415
+ const IMX_WALLET_IFRAME_STYLE = 'display: none;';
2416
+ function getIFrame() {
2417
+ return document.querySelector(`iframe#${IMX_WALLET_IFRAME_ID}`);
2418
+ }
2419
+ async function setupIFrame(env) {
2420
+ return new Promise((resolve) => {
2421
+ const iframe = document.createElement('iframe');
2422
+ iframe.setAttribute('id', IMX_WALLET_IFRAME_ID);
2423
+ iframe.setAttribute('src', IMX_WALLET_IFRAME_HOSTS[env]);
2424
+ iframe.setAttribute('style', IMX_WALLET_IFRAME_STYLE);
2425
+ document.body.appendChild(iframe);
2426
+ iframe.onload = () => resolve(iframe);
2427
+ });
2428
+ }
2429
+ async function getOrSetupIFrame(env) {
2430
+ const iframe = getIFrame();
2431
+ if (iframe)
2432
+ return iframe;
2433
+ return await setupIFrame(env);
2434
+ }
2435
+
2436
+ const DEFAULT_CONNECTION_MESSAGE = 'Only sign this request if you’ve initiated an action with Immutable X.';
2437
+ const CONNECTION_FAILED_ERROR = 'The L2 IMX Wallet connection has failed';
2438
+ async function connect(l1Provider, env) {
2439
+ const l1Signer = l1Provider.getSigner();
2440
+ const address = await l1Signer.getAddress();
2441
+ const signature = await l1Signer.signMessage(DEFAULT_CONNECTION_MESSAGE);
2442
+ const iframe = await getOrSetupIFrame(env);
2443
+ return new Promise((resolve, reject) => {
2444
+ const listener = (event) => {
2445
+ messageResponseListener(iframe, event, ResponseEventType.CONNECT_WALLET_RESPONSE, (messageDetails) => {
2446
+ window.removeEventListener(COMMUNICATION_TYPE, listener);
2447
+ if (!messageDetails.success) {
2448
+ reject(new Error(CONNECTION_FAILED_ERROR));
2449
+ }
2450
+ resolve(new ImxSigner(messageDetails.data.starkPublicKey, iframe));
2451
+ });
2452
+ };
2453
+ window.addEventListener(COMMUNICATION_TYPE, listener);
2454
+ postRequestMessage(iframe, {
2455
+ type: RequestEventType.CONNECT_WALLET_REQUEST,
2456
+ details: { ethAddress: address, signature },
2457
+ });
2458
+ });
2459
+ }
2460
+ async function disconnect(imxSigner) {
2461
+ const iframe = imxSigner.getIFrame();
2462
+ return new Promise((resolve, reject) => {
2463
+ const listener = (event) => {
2464
+ messageResponseListener(iframe, event, ResponseEventType.DISCONNECT_WALLET_RESPONSE, (messageDetails) => {
2465
+ window.removeEventListener(COMMUNICATION_TYPE, listener);
2466
+ if (!messageDetails.success && messageDetails.error) {
2467
+ reject(messageDetails.error);
2468
+ }
2469
+ iframe.remove();
2470
+ resolve();
2471
+ });
2472
+ };
2473
+ window.addEventListener(COMMUNICATION_TYPE, listener);
2474
+ postRequestMessage(iframe, {
2475
+ type: RequestEventType.DISCONNECT_WALLET_REQUEST,
2476
+ details: { starkPublicKey: imxSigner.getAddress() },
2477
+ });
2478
+ });
2479
+ }
2480
+
2481
+ var ProviderErrorType;
2482
+ (function (ProviderErrorType) {
2483
+ ProviderErrorType["PROVIDER_CONNECTION_ERROR"] = "PROVIDER_CONNECTION_ERROR";
2484
+ ProviderErrorType["WALLET_CONNECTION_ERROR"] = "WALLET_CONNECTION_ERROR";
2485
+ })(ProviderErrorType || (ProviderErrorType = {}));
2486
+ class ProviderError extends Error {
2487
+ type;
2488
+ constructor(message, type) {
2489
+ super(message);
2490
+ this.type = type;
2491
+ }
2492
+ }
2493
+ const withProviderError = async (fn, customError) => {
2494
+ try {
2495
+ return await fn();
2496
+ }
2497
+ catch (error) {
2498
+ const errorMessage = customError.message || `${error.message}` || 'UnknownError';
2499
+ throw new ProviderError(errorMessage, customError.type);
2500
+ }
2501
+ };
2502
+
2503
+ class MetaMaskIMXProvider extends GenericIMXProvider {
2504
+ static imxSigner;
2505
+ static async connect(config) {
2506
+ return await withProviderError(async () => {
2507
+ const metaMaskProvider = await connect$1({
2508
+ chainID: config.immutableXConfig.ethConfiguration.chainID,
2509
+ });
2510
+ this.imxSigner = await connect(metaMaskProvider, config.baseConfig.environment);
2511
+ return new MetaMaskIMXProvider(config, metaMaskProvider.getSigner(), this.imxSigner);
2512
+ }, { type: ProviderErrorType.WALLET_CONNECTION_ERROR });
2513
+ }
2514
+ static async disconnect() {
2515
+ if (!this.imxSigner) {
2516
+ throw new ProviderError('Attempted to disconnect from the MetaMask IMX provider without an established connection', ProviderErrorType.PROVIDER_CONNECTION_ERROR);
2517
+ }
2518
+ return withProviderError(async () => {
2519
+ await disconnect(this.imxSigner);
2520
+ }, { type: ProviderErrorType.PROVIDER_CONNECTION_ERROR });
2521
+ }
2522
+ static async signMessage(message) {
2523
+ if (!this.imxSigner) {
2524
+ throw new ProviderError('Attempted to sign a message with the MetaMask IMX provider without an established connection', ProviderErrorType.PROVIDER_CONNECTION_ERROR);
2525
+ }
2526
+ return withProviderError(async () => await this.imxSigner.signMessage(message), { type: ProviderErrorType.PROVIDER_CONNECTION_ERROR });
2527
+ }
2528
+ }
2529
+
2530
+ class ProviderConfiguration {
2531
+ immutableXConfig;
2532
+ baseConfig;
2533
+ constructor({ baseConfig, overrides }) {
2534
+ this.baseConfig = baseConfig;
2535
+ if (overrides) {
2536
+ this.immutableXConfig = overrides.immutableXConfig;
2537
+ }
2538
+ else {
2539
+ // TODO: remove once a sensible default is chosen
2540
+ // eslint-disable-next-line default-case
2541
+ switch (baseConfig.environment) {
2542
+ case exports.Environment.SANDBOX: {
2543
+ this.immutableXConfig = coreSdk.Config.SANDBOX;
2544
+ break;
2545
+ }
2546
+ case exports.Environment.PRODUCTION: {
2547
+ this.immutableXConfig = coreSdk.Config.PRODUCTION;
2548
+ break;
2549
+ }
2550
+ }
2551
+ }
2552
+ }
2553
+ }
2554
+
1484
2555
  Object.defineProperty(exports, 'imxClientCreateStarkSigner', {
1485
2556
  enumerable: true,
1486
2557
  get: function () { return coreSdk.createStarkSigner; }
@@ -1489,7 +2560,10 @@ Object.defineProperty(exports, 'imxClientGenerateLegacyStarkPrivateKey', {
1489
2560
  enumerable: true,
1490
2561
  get: function () { return coreSdk.generateLegacyStarkPrivateKey; }
1491
2562
  });
2563
+ exports.GenericIMXProvider = GenericIMXProvider;
1492
2564
  exports.ImmutableConfiguration = ImmutableConfiguration;
1493
2565
  exports.ImmutableXClient = ImmutableXClient;
2566
+ exports.MetaMaskIMXProvider = MetaMaskIMXProvider;
1494
2567
  exports.Passport = Passport;
1495
2568
  exports.PassportError = PassportError;
2569
+ exports.ProviderConfiguration = ProviderConfiguration;