"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 });
exports.withPools = exports.ChainSupportedAssets = void 0;
const chains_1 = require("@ionicprotocol/chains");
const types_1 = require("@ionicprotocol/types");
const ethers_1 = require("ethers");
const utils_1 = require("../IonicSdk/utils");
exports.ChainSupportedAssets = {
    [types_1.SupportedChains.bsc]: chains_1.bsc.assets,
    [types_1.SupportedChains.polygon]: chains_1.polygon.assets,
    [types_1.SupportedChains.ganache]: chains_1.ganache.assets,
    [types_1.SupportedChains.chapel]: chains_1.chapel.assets,
    [types_1.SupportedChains.neon]: chains_1.neon.assets,
    [types_1.SupportedChains.arbitrum]: chains_1.arbitrum.assets,
    [types_1.SupportedChains.linea]: chains_1.linea.assets,
    [types_1.SupportedChains.ethereum]: chains_1.ethereum.assets,
    [types_1.SupportedChains.zkevm]: chains_1.zkevm.assets
};
function withPools(Base) {
    return class IonicPools extends Base {
        fetchPoolData(poolId, overrides = {}) {
            var _a;
            return __awaiter(this, void 0, void 0, function* () {
                const { comptroller, name: _unfiliteredName, creator, blockPosted, timestampPosted } = yield this.contracts.PoolDirectory.callStatic.pools(Number(poolId), overrides);
                if (comptroller === ethers_1.constants.AddressZero) {
                    return null;
                }
                const name = (0, utils_1.filterPoolName)(_unfiliteredName);
                const assets = (yield this.contracts.PoolLens.callStatic.getPoolAssetsWithData(comptroller, overrides)).map(utils_1.filterOnlyObjectProperties);
                let totalLiquidityNative = 0;
                let totalAvailableLiquidityNative = 0;
                let totalSupplyBalanceNative = 0;
                let totalCollateralSupplyBalanceNative = 0;
                let totalBorrowBalanceNative = 0;
                let totalSuppliedNative = 0;
                let totalBorrowedNative = 0;
                let suppliedForUtilization = 0;
                let borrowedForUtilization = 0;
                let utilization = 0;
                const promises = [];
                for (let i = 0; i < assets.length; i++) {
                    const asset = assets[i];
                    asset.isBorrowPaused = asset.borrowGuardianPaused;
                    asset.isSupplyPaused = asset.mintGuardianPaused;
                    asset.plugin = this.marketToPlugin[asset.cToken];
                    const _asset = exports.ChainSupportedAssets[this.chainId].find((ass) => ass.underlying === asset.underlyingToken);
                    if (_asset) {
                        asset.underlyingSymbol = (_a = _asset.symbol) !== null && _a !== void 0 ? _a : "";
                        asset.logoUrl = _asset.symbol
                            ? "https://d1912tcoux65lj.cloudfront.net/token/96x96/" + _asset.symbol.toLowerCase() + ".png"
                            : "";
                        asset.originalSymbol = _asset.originalSymbol ? _asset.originalSymbol : undefined;
                    }
                    asset.netSupplyBalance = asset.supplyBalance.gt(asset.borrowBalance)
                        ? asset.supplyBalance.sub(asset.borrowBalance)
                        : ethers_1.constants.Zero;
                    asset.netSupplyBalanceNative =
                        Number(ethers_1.utils.formatUnits(asset.netSupplyBalance, asset.underlyingDecimals)) *
                            Number(ethers_1.utils.formatUnits(asset.underlyingPrice, 18));
                    asset.supplyBalanceNative =
                        Number(ethers_1.utils.formatUnits(asset.supplyBalance, asset.underlyingDecimals)) *
                            Number(ethers_1.utils.formatUnits(asset.underlyingPrice, 18));
                    asset.borrowBalanceNative =
                        Number(ethers_1.utils.formatUnits(asset.borrowBalance, asset.underlyingDecimals)) *
                            Number(ethers_1.utils.formatUnits(asset.underlyingPrice, 18));
                    if (asset.membership) {
                        totalCollateralSupplyBalanceNative += asset.supplyBalanceNative;
                    }
                    totalSupplyBalanceNative += asset.supplyBalanceNative;
                    totalBorrowBalanceNative += asset.borrowBalanceNative;
                    asset.totalSupplyNative =
                        Number(ethers_1.utils.formatUnits(asset.totalSupply, asset.underlyingDecimals)) *
                            Number(ethers_1.utils.formatUnits(asset.underlyingPrice, 18));
                    asset.totalBorrowNative =
                        Number(ethers_1.utils.formatUnits(asset.totalBorrow, asset.underlyingDecimals)) *
                            Number(ethers_1.utils.formatUnits(asset.underlyingPrice, 18));
                    if (asset.totalSupplyNative === 0) {
                        asset.utilization = 0;
                    }
                    else {
                        asset.utilization = (asset.totalBorrowNative / asset.totalSupplyNative) * 100;
                    }
                    totalSuppliedNative += asset.totalSupplyNative;
                    totalBorrowedNative += asset.totalBorrowNative;
                    const assetLiquidityNative = Number(ethers_1.utils.formatUnits(asset.liquidity, asset.underlyingDecimals)) *
                        Number(ethers_1.utils.formatUnits(asset.underlyingPrice, 18));
                    asset.liquidityNative = assetLiquidityNative;
                    totalAvailableLiquidityNative += asset.isBorrowPaused ? 0 : assetLiquidityNative;
                    totalLiquidityNative += asset.liquidityNative;
                    if (!asset.isBorrowPaused) {
                        suppliedForUtilization += asset.totalSupplyNative;
                        borrowedForUtilization += asset.totalBorrowNative;
                    }
                    const supportedAsset = this.supportedAssets.find((_asset) => _asset.underlying === asset.underlyingToken);
                    asset.extraDocs = supportedAsset ? supportedAsset.extraDocs : "";
                }
                if (suppliedForUtilization !== 0) {
                    utilization = (borrowedForUtilization / suppliedForUtilization) * 100;
                }
                yield Promise.all(promises);
                // Sort array by liquidity, array is mutated in place with .sort()
                assets.sort((a, b) => b.liquidityNative - a.liquidityNative);
                return {
                    id: Number(poolId),
                    chainId: this.chainId,
                    assets,
                    creator,
                    comptroller,
                    name,
                    totalLiquidityNative,
                    totalAvailableLiquidityNative,
                    totalSuppliedNative,
                    totalBorrowedNative,
                    totalSupplyBalanceNative,
                    totalCollateralSupplyBalanceNative,
                    totalBorrowBalanceNative,
                    blockPosted,
                    timestampPosted,
                    underlyingTokens: assets.map((a) => a.underlyingToken),
                    underlyingSymbols: assets.map((a) => a.underlyingSymbol),
                    utilization
                };
            });
        }
        fetchPoolsManual(overrides = {}) {
            return __awaiter(this, void 0, void 0, function* () {
                const [poolIndexes, pools] = yield this.contracts.PoolDirectory.callStatic.getActivePools(overrides);
                if (!pools.length || !poolIndexes.length) {
                    return undefined;
                }
                const poolData = yield Promise.all(poolIndexes.map((poolId) => {
                    return this.fetchPoolData(poolId.toString(), overrides).catch((error) => {
                        this.logger.error(`Pool ID ${poolId} wasn't able to be fetched from PoolLens without error.`, error);
                        return null;
                    });
                }));
                return poolData.filter((p) => !!p);
            });
        }
        fetchPools({ filter, options }) {
            return __awaiter(this, void 0, void 0, function* () {
                const isCreatedPools = filter === "created-pools";
                const isVerifiedPools = filter === "verified-pools";
                const isUnverifiedPools = filter === "unverified-pools";
                const req = isCreatedPools
                    ? this.contracts.PoolLens.callStatic.getPoolsByAccountWithData(options.from)
                    : isVerifiedPools
                        ? this.contracts.PoolDirectory.callStatic.getPublicPoolsByVerification(true)
                        : isUnverifiedPools
                            ? this.contracts.PoolDirectory.callStatic.getPublicPoolsByVerification(false)
                            : this.contracts.PoolLens.callStatic.getPublicPoolsWithData();
                const whitelistedPoolsRequest = this.contracts.PoolLens.callStatic.getWhitelistedPoolsByAccountWithData(options.from);
                const responses = yield Promise.all([req, whitelistedPoolsRequest]);
                const [pools, whitelistedPools] = yield Promise.all(responses.map((poolData) => __awaiter(this, void 0, void 0, function* () {
                    return yield Promise.all(poolData[0].map((_id) => {
                        return this.fetchPoolData(_id.toString());
                    }));
                })));
                const whitelistedIds = whitelistedPools.map((pool) => pool === null || pool === void 0 ? void 0 : pool.id);
                const filteredPools = pools.filter((pool) => !whitelistedIds.includes(pool === null || pool === void 0 ? void 0 : pool.id));
                return [...filteredPools, ...whitelistedPools].filter((p) => !!p);
            });
        }
        isAuth(pool, market, role, user) {
            return __awaiter(this, void 0, void 0, function* () {
                if (this.chainId === types_1.SupportedChains.neon) {
                    return true;
                }
                const authRegistry = this.createAuthoritiesRegistry();
                const poolAuthAddress = yield authRegistry.callStatic.poolsAuthorities(pool);
                if (poolAuthAddress === ethers_1.constants.AddressZero) {
                    console.log(`Pool authority for pool ${pool} does not exist`);
                    return false;
                }
                const poolAuth = this.createPoolRolesAuthority(poolAuthAddress);
                if (role === types_1.Roles.SUPPLIER_ROLE) {
                    // let's check if it's public
                    const cToken = this.createICErc20(market);
                    const func = cToken.interface.getFunction("mint");
                    const selectorHash = cToken.interface.getSighash(func);
                    const isPublic = yield poolAuth.callStatic.isCapabilityPublic(market, selectorHash);
                    if (isPublic) {
                        return true;
                    }
                }
                return yield poolAuth.callStatic.doesUserHaveRole(user, role);
            });
        }
    };
}
exports.withPools = withPools;
