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