"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());
    });
};
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.UniswapV3LiquidityContractPositionBuilder = void 0;
const common_1 = require("@nestjs/common");
const bignumber_js_1 = __importDefault(require("bignumber.js"));
const lodash_1 = require("lodash");
const app_toolkit_interface_1 = require("../../../app-toolkit/app-toolkit.interface");
const drill_balance_helper_1 = require("../../../app-toolkit/helpers/drill-balance.helper");
const display_item_present_1 = require("../../../app-toolkit/helpers/presentation/display-item.present");
const image_present_1 = require("../../../app-toolkit/helpers/presentation/image.present");
const contract_interface_1 = require("../../../position/contract.interface");
const position_interface_1 = require("../../../position/position.interface");
const position_utils_1 = require("../../../position/position.utils");
const contracts_1 = require("../contracts");
const uniswap_v3_liquidity_utils_1 = require("./uniswap-v3.liquidity.utils");
let UniswapV3LiquidityContractPositionBuilder = class UniswapV3LiquidityContractPositionBuilder {
    constructor(appToolkit, contractFactory) {
        this.appToolkit = appToolkit;
        this.contractFactory = contractFactory;
        this.managerAddress = '0xc36442b4a4522e871399cd717abdd847ab11fe88';
        this.factoryAddress = '0x1f98431c8ad98523631ae4a59f267346ea31f984';
    }
    getTokensForPosition({ multicall, positionId, tokenLoader, network, }) {
        return __awaiter(this, void 0, void 0, function* () {
            const positionManager = this.contractFactory.uniswapV3PositionManager({ address: this.managerAddress, network });
            const position = yield multicall.wrap(positionManager).positions(positionId);
            const token0Address = position.token0.toLowerCase();
            const token1Address = position.token1.toLowerCase();
            const queries = [token0Address, token1Address].map(t => ({ address: t, network }));
            const [token0, token1] = yield tokenLoader.getMany(queries);
            return [token0, token1];
        });
    }
    buildPosition({ multicall, positionId, tokenLoader, network, collapseClaimable, }) {
        return __awaiter(this, void 0, void 0, function* () {
            const positionManager = this.contractFactory.uniswapV3PositionManager({ address: this.managerAddress, network });
            const factoryContract = this.contractFactory.uniswapV3Factory({ address: this.factoryAddress, network });
            const position = yield multicall.wrap(positionManager).positions(positionId);
            const [token0, token1] = yield this.getTokensForPosition({ multicall, positionId, tokenLoader, network });
            if (!token0 || !token1)
                return null;
            const fee = position.fee;
            const poolAddr = yield multicall.wrap(factoryContract).getPool(token0.address, token1.address, fee);
            const poolAddress = poolAddr.toLowerCase();
            const poolContract = this.contractFactory.uniswapV3Pool({ address: poolAddress, network });
            const token0Contract = this.contractFactory.erc20(token0);
            const token1Contract = this.contractFactory.erc20(token1);
            const [slot, liquidity, feeGrowth0, feeGrowth1, ticksLower, ticksUpper, reserveRaw0, reserveRaw1] = yield Promise.all([
                multicall.wrap(poolContract).slot0(),
                multicall.wrap(poolContract).liquidity(),
                multicall.wrap(poolContract).feeGrowthGlobal0X128(),
                multicall.wrap(poolContract).feeGrowthGlobal1X128(),
                multicall.wrap(poolContract).ticks(Number(position.tickLower)),
                multicall.wrap(poolContract).ticks(Number(position.tickUpper)),
                multicall.wrap(token0Contract).balanceOf(poolAddress),
                multicall.wrap(token1Contract).balanceOf(poolAddress),
            ]);
            const range = (0, uniswap_v3_liquidity_utils_1.getRange)({ position, slot, token0, token1, network, liquidity });
            const suppliedBalances = (0, uniswap_v3_liquidity_utils_1.getSupplied)({ position, slot, token0, token1, network, liquidity });
            const suppliedTokens = [token0, token1].map(v => (0, position_utils_1.supplied)(v));
            const suppliedTokenBalances = suppliedTokens.map((t, i) => (0, drill_balance_helper_1.drillBalance)(t, suppliedBalances[i]));
            const claimableBalances = (0, uniswap_v3_liquidity_utils_1.getClaimable)({ position, slot, ticksLower, ticksUpper, feeGrowth0, feeGrowth1 });
            const claimableTokens = [token0, token1].map(v => (0, position_utils_1.claimable)(v));
            const claimableTokenBalances = claimableTokens.map((t, i) => (0, drill_balance_helper_1.drillBalance)(t, claimableBalances[i]));
            let tokens = [...suppliedTokenBalances, ...claimableTokenBalances].filter(t => t.balanceUSD > 0.01);
            const balanceUSD = (0, lodash_1.sumBy)(tokens, v => v.balanceUSD);
            const reservesRaw = [reserveRaw0, reserveRaw1];
            const reserves = reservesRaw.map((r, i) => Number(r) / Math.pow(10, suppliedTokens[i].decimals));
            const totalLiquidity = reserves[0] * suppliedTokens[0].price + reserves[1] * suppliedTokens[1].price;
            if (collapseClaimable) {
                tokens = [token0, token1].map((t, i) => (0, drill_balance_helper_1.drillBalance)(t, new bignumber_js_1.default(suppliedBalances[i]).plus(claimableBalances[i]).toFixed(0)));
            }
            const feeTier = Number(fee) / Math.pow(10, 4);
            const dataProps = {
                feeTier,
                rangeStart: range[0],
                rangeEnd: range[1],
                liquidity: totalLiquidity,
                reserves: reserves,
                poolAddress: poolAddr.toLowerCase(),
                assetStandard: position_interface_1.Standard.ERC_721,
                positionKey: `${feeTier}`,
            };
            const displayProps = {
                label: `${token0.symbol} / ${token1.symbol} (${range[0]} to ${range[1]})`,
                images: [...(0, image_present_1.getImagesFromToken)(token0), ...(0, image_present_1.getImagesFromToken)(token1)],
                statsItems: [{ label: 'Liquidity', value: (0, display_item_present_1.buildDollarDisplayItem)(Number(liquidity)) }],
            };
            const balance = {
                type: contract_interface_1.ContractType.POSITION,
                address: this.managerAddress,
                appId: 'uniswap-v3',
                groupId: 'liquidity',
                network,
                tokens,
                dataProps,
                displayProps,
                balanceUSD,
            };
            return balance;
        });
    }
};
UniswapV3LiquidityContractPositionBuilder = __decorate([
    __param(0, (0, common_1.Inject)(app_toolkit_interface_1.APP_TOOLKIT)),
    __param(1, (0, common_1.Inject)(contracts_1.UniswapV3ContractFactory)),
    __metadata("design:paramtypes", [Object, contracts_1.UniswapV3ContractFactory])
], UniswapV3LiquidityContractPositionBuilder);
exports.UniswapV3LiquidityContractPositionBuilder = UniswapV3LiquidityContractPositionBuilder;
//# sourceMappingURL=uniswap-v3.liquidity.contract-position-builder.js.map