"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());
    });
};
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.withLeverage = void 0;
const types_1 = require("@ionicprotocol/types");
const ethers_1 = require("ethers");
const EIP20Interface_1 = __importDefault(require("../../abis/EIP20Interface"));
const utils_1 = require("../IonicSdk/utils");
const Pools_1 = require("./Pools");
function withLeverage(Base) {
    return class Leverage extends Base {
        getAllLeveredPositions(account) {
            return __awaiter(this, void 0, void 0, function* () {
                if (this.chainId === types_1.SupportedChains.chapel) {
                    try {
                        const openPositions = [];
                        const newPositions = [];
                        const leveredPositionLens = this.createLeveredPositionLens();
                        const ionicFlywheelLensRouter = this.createIonicFlywheelLensRouter();
                        const [{ markets: collateralCTokens, underlyings: collateralUnderlyings, decimals: collateralDecimals, totalUnderlyingSupplied: collateralTotalSupplys, symbols: collateralsymbols, ratesPerBlock: supplyRatePerBlock, underlyingPrices: collateralUnderlyingPrices, poolOfMarket }, positions] = yield Promise.all([
                            leveredPositionLens.callStatic.getCollateralMarkets(),
                            this.getPositionsByAccount(account)
                        ]);
                        const rewards = yield ionicFlywheelLensRouter.callStatic.getMarketRewardsInfo(collateralCTokens);
                        yield Promise.all(collateralCTokens.map((collateralCToken, index) => __awaiter(this, void 0, void 0, function* () {
                            const collateralAsset = Pools_1.ChainSupportedAssets[this.chainId].find((asset) => asset.underlying === collateralUnderlyings[index]);
                            const { markets: borrowableMarkets, underlyings: borrowableUnderlyings, symbols: borrowableSymbols, rates: borrowableRates, decimals: borrowableDecimals, underlyingsPrices: borrowableUnderlyingPrices } = yield leveredPositionLens.callStatic.getBorrowableMarketsAndRates(collateralCToken);
                            // get rewards
                            const reward = rewards.find((rw) => rw.market === collateralCToken);
                            //get borrowable asset
                            const leveredBorrowable = [];
                            borrowableMarkets.map((borrowableMarket, i) => {
                                const borrowableAsset = Pools_1.ChainSupportedAssets[this.chainId].find((asset) => asset.underlying === borrowableUnderlyings[i]);
                                const position = positions.find((pos) => pos.collateralMarket === collateralCToken && pos.borrowMarket === borrowableMarket);
                                const borrowable = {
                                    underlyingDecimals: borrowableDecimals[i],
                                    cToken: borrowableMarket,
                                    underlyingToken: borrowableUnderlyings[i],
                                    underlyingPrice: borrowableUnderlyingPrices[i],
                                    symbol: borrowableAsset
                                        ? borrowableAsset.originalSymbol
                                            ? borrowableAsset.originalSymbol
                                            : borrowableAsset.symbol
                                        : borrowableSymbols[i],
                                    rate: borrowableRates[i]
                                };
                                leveredBorrowable.push(Object.assign({}, borrowable));
                                if (position) {
                                    openPositions.push({
                                        chainId: this.chainId,
                                        collateral: {
                                            cToken: collateralCToken,
                                            underlyingToken: collateralUnderlyings[index],
                                            underlyingDecimals: collateralAsset
                                                ? ethers_1.BigNumber.from(collateralAsset.decimals)
                                                : ethers_1.BigNumber.from(collateralDecimals[index]),
                                            totalSupplied: collateralTotalSupplys[index],
                                            symbol: collateralAsset
                                                ? collateralAsset.originalSymbol
                                                    ? collateralAsset.originalSymbol
                                                    : collateralAsset.symbol
                                                : collateralsymbols[index],
                                            supplyRatePerBlock: supplyRatePerBlock[index],
                                            reward,
                                            pool: poolOfMarket[index],
                                            plugin: this.marketToPlugin[collateralCToken],
                                            underlyingPrice: collateralUnderlyingPrices[index]
                                        },
                                        borrowable,
                                        address: position.position,
                                        isClosed: position.isClosed
                                    });
                                }
                            });
                            newPositions.push({
                                chainId: this.chainId,
                                collateral: {
                                    cToken: collateralCToken,
                                    underlyingToken: collateralUnderlyings[index],
                                    underlyingDecimals: collateralAsset
                                        ? ethers_1.BigNumber.from(collateralAsset.decimals)
                                        : ethers_1.BigNumber.from(collateralDecimals[index]),
                                    totalSupplied: collateralTotalSupplys[index],
                                    symbol: collateralAsset
                                        ? collateralAsset.originalSymbol
                                            ? collateralAsset.originalSymbol
                                            : collateralAsset.symbol
                                        : collateralsymbols[index],
                                    supplyRatePerBlock: supplyRatePerBlock[index],
                                    reward,
                                    pool: poolOfMarket[index],
                                    plugin: this.marketToPlugin[collateralCToken],
                                    underlyingPrice: collateralUnderlyingPrices[index]
                                },
                                borrowable: leveredBorrowable
                            });
                        })));
                        return { newPositions, openPositions };
                    }
                    catch (error) {
                        this.logger.error(`get levered positions error in chain ${this.chainId}:  ${error}`);
                        throw Error(`Getting levered position failed in chain ${this.chainId}: ` +
                            (error instanceof Error ? error.message : error));
                    }
                }
                else {
                    return { newPositions: [], openPositions: [] };
                }
            });
        }
        getPositionsByAccount(account) {
            return __awaiter(this, void 0, void 0, function* () {
                const leveredPositionFactory = this.createLeveredPositionFactory();
                const [positions, states] = yield leveredPositionFactory.callStatic.getPositionsByAccount(account);
                return yield Promise.all(positions.map((position, index) => __awaiter(this, void 0, void 0, function* () {
                    const positionContract = this.createLeveredPosition(position);
                    const state = states[index];
                    const [collateralMarket, borrowMarket] = yield Promise.all([
                        positionContract.callStatic.collateralMarket(),
                        positionContract.callStatic.stableMarket()
                    ]);
                    return { collateralMarket, borrowMarket, position, isClosed: state };
                })));
            });
        }
        getPositionSupplyApy(cTokenAddress, amount) {
            return __awaiter(this, void 0, void 0, function* () {
                const cToken = this.createICErc20(cTokenAddress);
                // @ts-ignore
                return yield cToken.callStatic.supplyRatePerBlockAfterDeposit(amount);
            });
        }
        getPositionBorrowApr(collateralMarket, borrowMarket, leverageRatio, amount) {
            return __awaiter(this, void 0, void 0, function* () {
                const leveredPositionLens = this.createLeveredPositionLens();
                return yield leveredPositionLens.callStatic.getBorrowRateAtRatio(collateralMarket, borrowMarket, amount, leverageRatio);
            });
        }
        leveredFactoryApprove(collateralUnderlying) {
            return __awaiter(this, void 0, void 0, function* () {
                const token = (0, utils_1.getContract)(collateralUnderlying, EIP20Interface_1.default, this.signer);
                const tx = yield token.approve(this.chainDeployment.LeveredPositionFactory.address, ethers_1.constants.MaxUint256);
                return tx;
            });
        }
        leveredPositionApprove(positionAddress, collateralUnderlying) {
            return __awaiter(this, void 0, void 0, function* () {
                const token = (0, utils_1.getContract)(collateralUnderlying, EIP20Interface_1.default, this.signer);
                const tx = yield token.approve(positionAddress, ethers_1.constants.MaxUint256);
                return tx;
            });
        }
        createAndFundPosition(collateralMarket, borrowMarket, fundingAsset, fundingAmount) {
            return __awaiter(this, void 0, void 0, function* () {
                const leveredPositionFactory = this.createLeveredPositionFactory(this.signer);
                return yield leveredPositionFactory.createAndFundPosition(collateralMarket, borrowMarket, fundingAsset, fundingAmount);
            });
        }
        createAndFundPositionAtRatio(collateralMarket, borrowMarket, fundingAsset, fundingAmount, leverageRatio) {
            return __awaiter(this, void 0, void 0, function* () {
                const leveredPositionFactory = this.createLeveredPositionFactory(this.signer);
                return yield leveredPositionFactory.createAndFundPositionAtRatio(collateralMarket, borrowMarket, fundingAsset, fundingAmount, leverageRatio);
            });
        }
        getRangeOfLeverageRatio(address) {
            return __awaiter(this, void 0, void 0, function* () {
                const leveredPosition = this.createLeveredPosition(address);
                return yield Promise.all([
                    leveredPosition.callStatic.getMinLeverageRatio(),
                    leveredPosition.callStatic.getMaxLeverageRatio()
                ]);
            });
        }
        isPositionClosed(address) {
            return __awaiter(this, void 0, void 0, function* () {
                const leveredPosition = this.createLeveredPosition(address);
                return yield leveredPosition.callStatic.isPositionClosed();
            });
        }
        closeLeveredPosition(address, withdrawTo) {
            return __awaiter(this, void 0, void 0, function* () {
                const isPositionClosed = yield this.isPositionClosed(address);
                const leveredPosition = this.createLeveredPosition(address, this.signer);
                if (!isPositionClosed) {
                    let tx;
                    if (withdrawTo) {
                        tx = yield leveredPosition["closePosition(address)"](withdrawTo);
                    }
                    else {
                        tx = yield leveredPosition["closePosition()"]();
                    }
                    return tx;
                }
                else {
                    return null;
                }
            });
        }
        adjustLeverageRatio(address, ratio) {
            return __awaiter(this, void 0, void 0, function* () {
                const leveredPosition = this.createLeveredPosition(address, this.signer);
                const tx = yield leveredPosition.adjustLeverageRatio(ethers_1.utils.parseUnits(ratio.toString()));
                return tx;
            });
        }
        fundPosition(positionAddress, underlyingToken, amount) {
            return __awaiter(this, void 0, void 0, function* () {
                const leveredPosition = this.createLeveredPosition(positionAddress, this.signer);
                const tx = yield leveredPosition.fundPosition(underlyingToken, amount);
                return tx;
            });
        }
        getNetAPY(supplyApy, supplyAmount, collateralMarket, borrowableMarket, leverageRatio) {
            return __awaiter(this, void 0, void 0, function* () {
                const leveredPositionLens = this.createLeveredPositionLens();
                return yield leveredPositionLens.callStatic.getNetAPY(supplyApy, supplyAmount, collateralMarket, borrowableMarket, leverageRatio);
            });
        }
        getCurrentLeverageRatio(positionAddress) {
            return __awaiter(this, void 0, void 0, function* () {
                const leveredPosition = this.createLeveredPosition(positionAddress);
                return yield leveredPosition.callStatic.getCurrentLeverageRatio();
            });
        }
        getEquityAmount(positionAddress) {
            return __awaiter(this, void 0, void 0, function* () {
                const leveredPosition = this.createLeveredPosition(positionAddress);
                return yield leveredPosition.callStatic.getEquityAmount();
            });
        }
        removeClosedPosition(positionAddress) {
            return __awaiter(this, void 0, void 0, function* () {
                const leveredPositionFactory = this.createLeveredPositionFactory(this.signer);
                const tx = yield leveredPositionFactory.removeClosedPosition(positionAddress);
                return tx;
            });
        }
        getPositionInfo(positionAddress, supplyApy) {
            return __awaiter(this, void 0, void 0, function* () {
                const leveredPositionLens = this.createLeveredPositionLens();
                return yield leveredPositionLens.getPositionInfo(positionAddress, supplyApy);
            });
        }
        getNetApyForPositionAfterFunding(positionAddress, supplyApy, newFunding) {
            return __awaiter(this, void 0, void 0, function* () {
                const leveredPositionLens = this.createLeveredPositionLens();
                return yield leveredPositionLens.getNetApyForPositionAfterFunding(positionAddress, supplyApy, newFunding);
            });
        }
        getLeverageRatioAfterFunding(positionAddress, newFunding) {
            return __awaiter(this, void 0, void 0, function* () {
                const leveredPositionLens = this.createLeveredPositionLens();
                return yield leveredPositionLens.getLeverageRatioAfterFunding(positionAddress, newFunding);
            });
        }
    };
}
exports.withLeverage = withLeverage;
