"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
    if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __param = (this && this.__param) || function (paramIndex, decorator) {
    return function (target, key) { decorator(target, key, paramIndex); }
};
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.DolomiteContractPositionTemplatePositionFetcher = void 0;
const common_1 = require("@nestjs/common");
const ethers_1 = require("ethers");
const graphql_request_1 = require("graphql-request");
const lodash_1 = require("lodash");
const app_toolkit_interface_1 = require("../../../app-toolkit/app-toolkit.interface");
const address_1 = require("../../../app-toolkit/constants/address");
const drill_balance_helper_1 = require("../../../app-toolkit/helpers/drill-balance.helper");
const image_present_1 = require("../../../app-toolkit/helpers/presentation/image.present");
const utils_1 = require("../../../apps/dolomite/common/utils");
const contracts_1 = require("../../../apps/dolomite/contracts");
const ethers_2 = require("../../../contract/contracts/ethers");
const position_interface_1 = require("../../../position/position.interface");
const custom_contract_position_template_position_fetcher_1 = require("../../../position/template/custom-contract-position.template.position-fetcher");
let DolomiteContractPositionTemplatePositionFetcher = class DolomiteContractPositionTemplatePositionFetcher extends custom_contract_position_template_position_fetcher_1.CustomContractPositionTemplatePositionFetcher {
    constructor(appToolkit, contractFactory) {
        super(appToolkit);
        this.appToolkit = appToolkit;
        this.contractFactory = contractFactory;
    }
    getContract(address) {
        return this.contractFactory.dolomiteMargin({ address, network: this.network });
    }
    getDefinitions({ multicall }) {
        return __awaiter(this, void 0, void 0, function* () {
            const dolomiteMarginContract = this.contractFactory.dolomiteMargin({
                address: utils_1.DOLOMITE_MARGIN_ADDRESSES[this.network],
                network: this.network,
            });
            const marketsCount = yield multicall.wrap(dolomiteMarginContract).getNumMarkets();
            const underlyingTokenAddresses = yield Promise.all((0, lodash_1.range)(0, marketsCount.toNumber()).map((index) => __awaiter(this, void 0, void 0, function* () {
                const underlyingTokenAddressRaw = yield multicall.wrap(dolomiteMarginContract).getMarketTokenAddress(index);
                return underlyingTokenAddressRaw.toLowerCase();
            })));
            const tokenNameCallChunks = (0, utils_1.chunkArrayForMultiCall)(underlyingTokenAddresses, tokenAddress => ({
                target: tokenAddress,
                callData: ethers_2.Erc20__factory.createInterface().encodeFunctionData('name'),
            }));
            const tokenNames = [];
            for (let i = 0; i < tokenNameCallChunks.length; i++) {
                const { returnData } = yield multicall.contract.callStatic.aggregate(tokenNameCallChunks[i], false);
                returnData.forEach(({ data }) => {
                    const result = ethers_1.ethers.utils.defaultAbiCoder.decode(['string'], data);
                    tokenNames.push(result[0]);
                });
            }
            const wrappedTokenAddresses = [];
            const modes = [];
            for (let i = 0; i < underlyingTokenAddresses.length; i++) {
                wrappedTokenAddresses.push(underlyingTokenAddresses[i]);
                const tokenName = tokenNames[i];
                if (utils_1.SPECIAL_TOKEN_NAME_MATCHERS.some(matcher => tokenName.includes(matcher))) {
                    modes[i] = utils_1.ISOLATION_MODE_MATCHERS.find(matcher => tokenName.includes(matcher))
                        ? utils_1.TokenMode.ISOLATION
                        : utils_1.SILO_MODE_MATCHERS.find(matcher => tokenName.includes(matcher))
                            ? utils_1.TokenMode.SILO
                            : utils_1.TokenMode.NORMAL;
                    const isolationModeTokenContract = this.contractFactory.isolationModeToken({
                        address: underlyingTokenAddresses[i],
                        network: this.network,
                    });
                    if (tokenName.includes('Fee + Staked GLP')) {
                        underlyingTokenAddresses[i] = '0x4277f8f2c384827b5273592ff7cebd9f2c1ac258';
                    }
                    else {
                        underlyingTokenAddresses[i] = (yield isolationModeTokenContract.UNDERLYING_TOKEN()).toLowerCase();
                    }
                }
                else {
                    modes.push(utils_1.TokenMode.NORMAL);
                }
            }
            const marketIdToMarketMap = wrappedTokenAddresses.reduce((memo, wrappedTokenAddress, i) => {
                memo[i] = {
                    underlyingTokenAddress: underlyingTokenAddresses[i],
                    wrappedTokenAddress,
                    mode: modes[i],
                    name: tokenNames[i],
                    marketId: i,
                };
                return memo;
            }, {});
            return [
                {
                    address: dolomiteMarginContract.address,
                    marketsCount: marketsCount.toNumber(),
                    marketIdToMarketMap: marketIdToMarketMap,
                },
            ];
        });
    }
    getTokenDefinitions(params) {
        return __awaiter(this, void 0, void 0, function* () {
            return (0, utils_1.getTokenDefinitionsLib)(params, this.network);
        });
    }
    getDataProps(params) {
        return __awaiter(this, void 0, void 0, function* () {
            const multicall = this.appToolkit.getMulticall(this.network);
            return (0, utils_1.mapTokensToDolomiteDataProps)(params, this.isFetchingDolomiteBalances(), multicall);
        });
    }
    getLabel(params) {
        return __awaiter(this, void 0, void 0, function* () {
            const label = (0, image_present_1.getLabelFromToken)(params.contractPosition.tokens[0]);
            return label.replace('WETH', 'ETH');
        });
    }
    getBalances(address) {
        return __awaiter(this, void 0, void 0, function* () {
            if (address === address_1.ZERO_ADDRESS) {
                return [];
            }
            const multicall = this.appToolkit.getMulticall(this.network);
            const contractPositions = yield this.getPositionsForBalances(address, multicall);
            return yield Promise.all(contractPositions.map((contractPosition) => __awaiter(this, void 0, void 0, function* () {
                const contract = multicall.wrap(this.getContract(contractPosition.address));
                const balanceMap = new Map();
                for (let i = 0; i < contractPosition.accountStructs.length; i++) {
                    const accountStruct = contractPosition.accountStructs[i];
                    const innerBalanceMap = yield this.getTokenBalancesPerAccountStruct({ address: accountStruct.accountOwner, contract, contractPosition, multicall }, accountStruct);
                    innerBalanceMap.forEach((value, key) => {
                        if (!balanceMap.has(key)) {
                            balanceMap.set(key, ethers_1.BigNumber.from(0));
                        }
                        balanceMap.set(key, balanceMap.get(key).add(value));
                    });
                    if (!this.isFetchingDolomiteBalances()) {
                        contractPosition.dataProps.positionKey = `${accountStruct.accountOwner}-${accountStruct.accountNumber}`;
                        contractPosition.key = this.appToolkit.getPositionKey(contractPosition);
                    }
                }
                const allTokens = contractPosition.tokens.map(cp => {
                    var _a, _b;
                    const balance = balanceMap.get(cp.address) || ethers_1.BigNumber.from(0);
                    if (balance.lt(0)) {
                        cp.metaType = position_interface_1.MetaType.BORROWED;
                    }
                    return (0, drill_balance_helper_1.drillBalance)(cp, (_b = (_a = balance.abs()) === null || _a === void 0 ? void 0 : _a.toString()) !== null && _b !== void 0 ? _b : '0', { isDebt: cp.metaType === position_interface_1.MetaType.BORROWED });
                });
                const tokens = allTokens.filter(v => Math.abs(v.balanceUSD) > 0.01);
                const balanceUSD = (0, lodash_1.sumBy)(tokens, t => t.balanceUSD);
                const balance = Object.assign(Object.assign({}, contractPosition), { tokens, balanceUSD });
                return balance;
            })));
        });
    }
    getPositionsForBalances(account, multicall) {
        var _a;
        return __awaiter(this, void 0, void 0, function* () {
            const defaultContractPositions = yield this.appToolkit.getAppContractPositions({
                appId: this.appId,
                network: this.network,
                groupIds: [this.groupId],
            });
            if (this.isFetchingDolomiteBalances()) {
                const isolationModeVaults = yield (0, utils_1.getAllIsolationModeTokensFromContractPositions)(account, defaultContractPositions, multicall);
                const isolationModeAccountStructs = isolationModeVaults.map(vault => ({
                    accountOwner: vault,
                    accountNumber: '0',
                }));
                const tokenLoader = this.appToolkit.getTokenDependencySelector({
                    tags: { network: this.network, context: `${this.appId}__template` },
                });
                const definitions = yield this.getDefinitions({ multicall, tokenLoader });
                const tokenCount = (yield this.getContract(definitions[0].address).getNumMarkets()).toNumber();
                const accounts = [];
                for (let i = 0; i < tokenCount; i += utils_1.CHUNK_SIZE) {
                    accounts.push({ accountOwner: account, accountNumber: ethers_1.BigNumber.from(i).div(utils_1.CHUNK_SIZE).toString() });
                }
                return [
                    Object.assign(Object.assign({}, defaultContractPositions[0]), { accountStructs: [...accounts, ...isolationModeAccountStructs] }),
                ];
            }
            else {
                const client = new graphql_request_1.GraphQLClient(utils_1.DOLOMITE_GRAPH_ENDPOINT, {
                    headers: { 'Content-Type': 'application/json' },
                });
                const isolationModeVaults = yield (0, utils_1.getAllIsolationModeTokensFromContractPositions)(account, defaultContractPositions, multicall);
                const query = (0, graphql_request_1.gql) `
        query getMarginAccounts($walletAddress: String!) {
          marginAccounts(
            where: { user_contains_nocase: $walletAddress, accountNumber_gt: "100", hasSupplyValue: true }
          ) {
            user {
              id
            }
            accountNumber
          }
        }
      `;
                const accountStructs = [];
                const allAccounts = [account, ...isolationModeVaults];
                for (let i = 0; i < allAccounts.length; i++) {
                    const result = yield client.request(query, { walletAddress: allAccounts[i] });
                    (_a = result === null || result === void 0 ? void 0 : result.marginAccounts) === null || _a === void 0 ? void 0 : _a.forEach(account => {
                        accountStructs.push({
                            accountOwner: account.user.id,
                            accountNumber: account.accountNumber,
                        });
                    });
                }
                const positions = [];
                for (let i = 0; i < accountStructs.length; i++) {
                    const contractPositions = yield this.appToolkit.getAppContractPositions({
                        appId: this.appId,
                        network: this.network,
                        groupIds: [this.groupId],
                    });
                    positions[i] = Object.assign(Object.assign({}, contractPositions[0]), { accountStructs: [accountStructs[i]] });
                }
                return positions;
            }
        });
    }
    getTokenBalancesPerPosition(_params) {
        throw new Error('Not implemented');
    }
    getTokenBalancesPerAccountStruct(params, accountStruct) {
        return __awaiter(this, void 0, void 0, function* () {
            return (0, utils_1.getTokenBalancesPerAccountStructLib)(params, accountStruct);
        });
    }
};
DolomiteContractPositionTemplatePositionFetcher = __decorate([
    __param(0, (0, common_1.Inject)(app_toolkit_interface_1.APP_TOOLKIT)),
    __param(1, (0, common_1.Inject)(contracts_1.DolomiteContractFactory)),
    __metadata("design:paramtypes", [Object, contracts_1.DolomiteContractFactory])
], DolomiteContractPositionTemplatePositionFetcher);
exports.DolomiteContractPositionTemplatePositionFetcher = DolomiteContractPositionTemplatePositionFetcher;
//# sourceMappingURL=dolomite.contract-position.template.position-fetcher.js.map