UNPKG

@biconomy/inex

Version:

InstaExit SDK to enable instant and seamless cross-chain deposits & withdrawals

238 lines (237 loc) 12.8 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); const ethers_1 = require("ethers"); const config_1 = require("./config"); const { config, RESPONSE_CODES } = require('./config'); class InstaExit { constructor(provider, options) { this.init = () => __awaiter(this, void 0, void 0, function* () { const networkIds = config.supportedNetworkIds; for (let index = 0; index < networkIds.length; index++) { const networkId = networkIds[index]; const supportedTokens = yield this._getSupportedTokensFromServer(networkId); this.supportedTokens.set(networkId, supportedTokens); } }); this._validate = (options) => { if (!options) { throw new Error(`Options object needs to be passed to InstaExit Object`); } }; this.getERC20TokenDecimals = (address) => { const tokenContract = new ethers_1.ethers.Contract(address, config.erc20TokenABI, this.provider); if (tokenContract) { return tokenContract.decimals(); } else { throw new Error("Unable to create token contract object. Please check your network and token address"); } }; this.getFetchOptions = (method) => { return { method, headers: { 'Content-Type': 'application/json;charset=utf-8' } }; }; this.getSupportedTokens = (networkId) => { return this.supportedTokens.get(networkId); }; this.preDepositStatus = (checkStatusRequest) => { const self = this; return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () { const fetchOptions = this.getFetchOptions('POST'); const body = { tokenAddress: checkStatusRequest.tokenAddress, amount: checkStatusRequest.amount, fromChainId: checkStatusRequest.fromChainId, toChainId: checkStatusRequest.toChainId, userAddress: checkStatusRequest.userAddress }; fetchOptions.body = JSON.stringify(body); fetch(`${self._getInstaExitBaseURL()}${config.checkRequestStatusPath}`, fetchOptions) .then(response => response.json()) .then((response) => { self._logMessage(response); resolve(response); }) .catch((error) => { self._logMessage(error); reject(error); }); })); }; this._getSupportedTokensFromServer = (networkId) => { const self = this; return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () { const fetchOptions = this.getFetchOptions('GET'); fetch(`${self._getInstaExitBaseURL()}${config.getSupportedTokensPath}?networkId=${networkId}`, fetchOptions) .then(response => response.json()) .then((response) => { if (response && response.SupportedPairList) { self._logMessage(response.SupportedPairList); resolve(response.SupportedPairList); } else { const error = self.formatMessage(RESPONSE_CODES.ERROR_RESPONSE, `Unable to get supported tokens`); self._logMessage(error); self._logMessage("Returning default list from config"); resolve(config.defaultSupportedTokens.get(networkId)); } }) .catch((error) => { self._logMessage(error); self._logMessage("Returning default list from config"); resolve(config.defaultSupportedTokens.get(networkId)); }); })); }; this.deposit = (request) => __awaiter(this, void 0, void 0, function* () { const tokenContract = new ethers_1.ethers.Contract(request.tokenAddress, config.erc20TokenABI, this.provider.getUncheckedSigner()); const allowance = yield tokenContract.allowance(request.sender, request.depositContractAddress); this._logMessage(`Allowance given to LiquidityPoolManager is ${allowance}`); if (ethers_1.BigNumber.from(request.amount).lte(allowance)) { const depositTransaction = yield this._depositTokensToLiquidityPoolManager(request); this.listenForExitTransaction(depositTransaction, parseInt(request.fromChainId, 10)); return depositTransaction; } else { return Promise.reject(this.formatMessage(RESPONSE_CODES.ALLOWANCE_NOT_GIVEN, `Not enough allowance given to Liquidity Pool Manager contract`)); } }); this.listenForExitTransaction = (transaction, fromChainId) => __awaiter(this, void 0, void 0, function* () { if (this.options.onFundsTransfered) { const interval = this.options.exitCheckInterval || config.defaultExitCheckInterval; yield transaction.wait(1); this._logMessage(`Deposit transaction Confirmed. Listening for exit transaction now`); let invocationCount = 0; const intervalId = setInterval(() => __awaiter(this, void 0, void 0, function* () { const depositHash = transaction.hash; const response = yield this.checkDepositStatus({ depositHash, fromChainId }); invocationCount++; if (response && response.code === RESPONSE_CODES.SUCCESS) { if (response.statusCode === config_1.EXIT_STATUS.PROCESSED && response.exitHash) { this.options.onFundsTransfered(response); clearInterval(this.depositTransactionListenerMap.get(depositHash)); this.depositTransactionListenerMap.delete(depositHash); } } if (invocationCount >= config.maxDepositCheckCallbackCount) { this._logMessage(`Max callback count reached ${config.maxDepositCheckCallbackCount}. Clearing interval now`); clearInterval(this.depositTransactionListenerMap.get(depositHash)); this.depositTransactionListenerMap.delete(depositHash); } }), interval); this.depositTransactionListenerMap.set(transaction.hash, intervalId); } else { this._logMessage(`onFundsTransfered method is missing from options so not listening for exit transaction`); } }); this.checkDepositStatus = (depositRequest) => { const self = this; return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () { if (depositRequest && depositRequest.depositHash && depositRequest.fromChainId) { const fetchOptions = this.getFetchOptions('GET'); const getURL = `${self._getInstaExitBaseURL()}${config.checkTransferStatusPath}?depositHash=${depositRequest.depositHash}&fromChainId=${depositRequest.fromChainId}`; fetch(getURL, fetchOptions) .then(response => response.json()) .then((response) => { self._logMessage(response); resolve(response); }) .catch((error) => { self._logMessage(error); reject(error); }); } else { reject(this.formatMessage(RESPONSE_CODES.BAD_REQUEST, "Bad input params. depositHash and fromChainId are mandatory parameters")); } })); }; this.getPoolInformation = (tokenAddress, fromChainId, toChainId) => { const self = this; return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () { if (tokenAddress && fromChainId !== undefined && toChainId !== undefined) { const fetchOptions = this.getFetchOptions('GET'); const getURL = `${self._getInstaExitBaseURL()}${config.getPoolInfoPath}?tokenAddress=${tokenAddress}&fromChainId=${fromChainId}&toChainId=${toChainId}`; fetch(getURL, fetchOptions) .then(response => response.json()) .then((response) => { self._logMessage(response); resolve(response); }) .catch((error) => { self._logMessage(error); reject(error); }); } else { reject(this.formatMessage(RESPONSE_CODES.BAD_REQUEST, "Bad input params. fromChainId, toChainId and tokenAddress are mandatory parameters")); } })); }; this.approveERC20 = (tokenAddress, spender, amount) => __awaiter(this, void 0, void 0, function* () { const tokenContract = new ethers_1.ethers.Contract(tokenAddress, config.erc20TokenABI, this.provider.getUncheckedSigner()); if (tokenContract) { if (this.options.infiniteApproval) { amount = ethers_1.ethers.constants.MaxUint256.toString(); this._logMessage(`Infinite approval flag is true, so overwriting the amount with value ${amount}`); } if (spender && amount) { return yield tokenContract.approve(spender, amount); } else { this._logMessage(`One of the inputs is not valid => spender: ${spender}, amount: ${amount}`); } } else { this._logMessage("Token contract is not defined"); throw new Error("Token contract is not defined. Please check if token address is present on the current chain"); } }); this._depositTokensToLiquidityPoolManager = (request) => __awaiter(this, void 0, void 0, function* () { const liquidityPoolManager = new ethers_1.ethers.Contract(request.depositContractAddress, config.liquidityPoolManagerABI, this.provider.getUncheckedSigner()); const transaction = yield liquidityPoolManager.depositErc20(request.tokenAddress, request.receiver, request.amount, request.toChainId); return transaction; }); this._getInstaExitBaseURL = () => { const environment = this.options.environment || "prod"; return config.instaBaseUrl[environment]; }; this.formatMessage = (code, message) => { return { code, message }; }; this._logMessage = (message) => { if (this.options && this.options.debug) console.log(message); }; this._validate(options); this.options = options; if (ethers_1.ethers.providers.Provider.isProvider(provider)) { this._logMessage(`Ethers provider detected`); this.provider = provider; } else { this._logMessage(`Non-Ethers provider detected`); this.provider = new ethers_1.ethers.providers.Web3Provider(provider); } this.supportedTokens = new Map(); this.depositTransactionListenerMap = new Map(); } } module.exports = { InstaExit, RESPONSE_CODES };