@biconomy/inex
Version:
InstaExit SDK to enable instant and seamless cross-chain deposits & withdrawals
238 lines (237 loc) • 12.8 kB
JavaScript
"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 };