"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getLiquiditySatBalance = exports.getSettingsFromScript = exports.market2JSON = exports.isValidMarketInitEntry = exports.isValidMarketInit = exports.getMinMarketSatBalance = exports.validateEntries = exports.isValidMarketInfo = exports.isValidMarketBalance = exports.isValidMarketDetails = exports.isValidMarketStatus = exports.getMarketDetailsFromHex = exports.getMarketDetailsHex = exports.getMarketStatusfromHex = exports.getMarketStatusHex = exports.getMarketBalanceHex = exports.getMerklePath = exports.getBalanceMerkleRoot = exports.getMarketBalance = exports.getBalanceHexLength = exports.getSharesFromHex = exports.getSharesHex = exports.getBalanceFromHex = exports.getBalanceHex = exports.getEntryFromHex = exports.getEntryHex = exports.getVotesHex = exports.getNewIndexScript = exports.getToken = exports.getScryptTokenParams = exports.getOpReturnData = exports.getNewMarket = exports.getMarketVersion = exports.liquidityByteLength = exports.sharesByteLength = exports.entrySharePos = exports.entryLiqudityPos = exports.marketStatusHexLength = exports.voteCountByteLen = exports.balanceTableByteLength = void 0;
const scryptlib_1 = require("scryptlib");
const merkleTree_1 = require("./merkleTree");
const hex_1 = require("./hex");
const sha_1 = require("./sha");
const oracle_1 = require("./oracle");
const lmsr_1 = require("./lmsr");
const contracts_1 = require("./contracts");
const lt_1 = __importDefault(require("semver/functions/lt"));
const gte_1 = __importDefault(require("semver/functions/gte"));
const valaIndexContract = "2af7dfaa7e799e28c7c31fc303dc915c";
exports.balanceTableByteLength = 32;
exports.voteCountByteLen = 2;
exports.marketStatusHexLength = 4;
exports.entryLiqudityPos = 33;
exports.entrySharePos = 34;
exports.sharesByteLength = 4;
exports.liquidityByteLength = 4;
// export function getCompiledPM(): void {
//   const contractPath = require.resolve("scrypt_boilerplate/contracts/predictionMarket.scrypt")
//   compile({ path: contractPath }, { desc: true })
// }
// export function getContractDescription(): ContractDescription {
//   return require("../predictionMarket.json") as ContractDescription
// }
// export function getLockingScriptASMTemplate(): string[] {
//   const currentVersion = versions[0]
//   const compiled = require(`../scripts/${currentVersion.identifier}`) as ContractDescription
//   return compiled.asm.split(" ")
// }
// export function getLockingScriptASM(oracleDetails: oracleDetail[]): string[] {
//   const asmTemplate = getLockingScriptASMTemplate()
//   const oracleKeyPos = asmTemplate.findIndex(op => op === "$oracleKeys")
//   asmTemplate[oracleKeyPos] = getOracleDetailsHex(oracleDetails)
//   return asmTemplate
// }
function getMarketVersion(identifier) {
    const v = contracts_1.marketContracts[identifier];
    if (v)
        return v;
    throw new Error(`Market version ${identifier} not supported`);
}
exports.getMarketVersion = getMarketVersion;
// export function getToken(oracles: oracleDetail): Token {
//   const Token = buildContractClass(require("../predictionMarket.json"))
//   return new Token(getOracleDetailsHex(oracles))
// }
function getNewMarket(details, oracles, creator, creatorFee, liquidityFee, requiredVotes, version = contracts_1.currentMarketContract) {
    const votes = "0"
        .repeat(details.options.length)
        .split("")
        .map(n => parseInt(n));
    const status = { decided: false, decision: 0, votes, liquidityFeePool: 0, accLiquidityFeePool: 0, liquidityPoints: 0 };
    const firstEntryHash = (0, lt_1.default)(version.version, "0.6.5") ? (0, sha_1.sha256)("00") : (0, sha_1.sha256d)("00");
    return {
        version: contracts_1.currentMarketContract.identifier,
        details,
        status,
        oracles,
        balance: {
            liquidity: 0,
            shares: new Array(details.options.length).fill(0)
        },
        balanceMerkleRoot: (0, merkleTree_1.getMerkleRoot)([firstEntryHash]),
        creator,
        creatorFee,
        liquidityFee,
        requiredVotes,
        settingsHash: (0, sha_1.sha256)("00")
    };
}
exports.getNewMarket = getNewMarket;
function getOpReturnData(market) {
    const version = getMarketVersion(market.version);
    const marketDetailsHex = getMarketDetailsHex(market.details);
    const oracleStatesHex = (0, oracle_1.getOracleStatesHex)(market.oracles);
    const marketStatusHex = getMarketStatusHex(market.status);
    const marketBalanceHex = getBalanceHex(market.balance, version);
    const marketBalanceMerkleRoot = String(market.balanceMerkleRoot);
    const marketVotesHex = getVotesHex(market.status.votes);
    const liquidityFeePoolHex = (0, hex_1.int2Hex)(market.status.liquidityFeePool, 5);
    const accLiquidityFeePoolHex = (0, hex_1.int2Hex)(market.status.accLiquidityFeePool, 5);
    const liquidityPointsHex = (0, hex_1.int2Hex)(market.status.liquidityPoints, 8);
    // console.log({
    //   marketStatusHex,
    //   oracleStatesHex,
    //   marketVotesHex,
    //   liquidityFeePoolHex,
    //   accLiquidityFeePoolHex,
    //   liquidityPointsHex,
    //   marketBalanceHex,
    //   marketBalanceMerkleRoot
    // })
    const marketDataHex = marketStatusHex +
        oracleStatesHex +
        marketVotesHex +
        liquidityFeePoolHex +
        accLiquidityFeePoolHex +
        liquidityPointsHex +
        marketBalanceHex +
        marketBalanceMerkleRoot;
    if ((0, lt_1.default)(version.version, "0.4.0")) {
        return `${market.version} ${marketDetailsHex} ${marketDataHex}`;
    }
    return `${market.version} ${marketDetailsHex} ${market.settingsHash} ${marketDataHex}`;
}
exports.getOpReturnData = getOpReturnData;
function getScryptTokenParams(market) {
    return [
        new scryptlib_1.Bytes((0, oracle_1.getOracleDetailsHex)(market.oracles)).toLiteral(),
        market.details.options.length,
        market.requiredVotes,
        new scryptlib_1.PubKey(market.creator.pubKey.toHex()).toLiteral(),
        new scryptlib_1.Ripemd160(market.creator.payoutAddress.hashBuffer.toString("hex")).toLiteral(),
        market.creatorFee * 100,
        market.liquidityFee * 100
    ];
}
exports.getScryptTokenParams = getScryptTokenParams;
function getToken(market) {
    const Token = (0, scryptlib_1.buildContractClass)(require(`../scripts/${market.version}.json`)); // eslint-disable-line
    const token = new Token(// eslint-disable-line
    new scryptlib_1.Bytes((0, oracle_1.getOracleDetailsHex)(market.oracles)), // oracleKeys
    market.details.options.length, // globalOptionCount
    market.requiredVotes, // requiredVotes,
    new scryptlib_1.PubKey(market.creator.pubKey.toHex()), new scryptlib_1.Ripemd160(market.creator.payoutAddress.hashBuffer.toString("hex")), market.creatorFee * 100, market.liquidityFee * 100);
    token.setDataPartInASM(getOpReturnData(market));
    // console.log(`${market.version} ${marketDetailsHex} ${marketDataHex}`)
    return token;
}
exports.getToken = getToken;
function getNewIndexScript() {
    const contract = require(`../scripts/${valaIndexContract}.json`);
    return scryptlib_1.bsv.Script.fromASM(contract.asm);
}
exports.getNewIndexScript = getNewIndexScript;
function getVotesHex(votes) {
    return votes.map(vote => (0, hex_1.int2Hex)(vote, 2)).join("");
}
exports.getVotesHex = getVotesHex;
function getEntryHex(entry, version) {
    return (entry.publicKey.toString() +
        getBalanceHex(entry.balance, version) +
        (0, hex_1.int2Hex)(entry.globalLiqidityFeePoolSave, 5) +
        (0, hex_1.int2Hex)(entry.liquidityPoints, 8));
}
exports.getEntryHex = getEntryHex;
function getEntryFromHex(bytes, version) {
    const feePoolSavePos = bytes.length - 10 - 16;
    const liquidityPointsPos = bytes.length - 16;
    const publicKey = scryptlib_1.bsv.PublicKey.fromString(bytes.slice(0, 66));
    const balanceHex = bytes.slice(66, feePoolSavePos);
    const feePoolHex = bytes.slice(feePoolSavePos, liquidityPointsPos);
    const liquidityPointsHex = bytes.slice(liquidityPointsPos);
    return {
        publicKey,
        balance: getBalanceFromHex(balanceHex, version),
        globalLiqidityFeePoolSave: (0, hex_1.hex2Int)(feePoolHex),
        liquidityPoints: (0, hex_1.hex2Int)(liquidityPointsHex)
    };
}
exports.getEntryFromHex = getEntryFromHex;
function getBalanceHex(balance, version) {
    let bytes = exports.liquidityByteLength;
    if ((0, lt_1.default)(version.version, "0.3.15")) {
        bytes = 1;
    }
    return (0, hex_1.int2Hex)(balance.liquidity, bytes) + getSharesHex(balance.shares, version);
}
exports.getBalanceHex = getBalanceHex;
function getBalanceFromHex(bytes, version) {
    let byteLen = exports.liquidityByteLength;
    if ((0, lt_1.default)(version.version, "0.3.15")) {
        byteLen = 1;
    }
    const sharesBytePos = byteLen * 2;
    return {
        liquidity: (0, hex_1.hex2Int)(bytes.slice(0, sharesBytePos)),
        shares: getSharesFromHex(bytes.slice(sharesBytePos), version)
    };
}
exports.getBalanceFromHex = getBalanceFromHex;
function getSharesHex(shares, version) {
    let byteLen = exports.sharesByteLength;
    if ((0, lt_1.default)(version.version, "0.3.15")) {
        byteLen = 1;
    }
    return shares.reduce((bytes, n) => bytes + (0, hex_1.int2Hex)(n, byteLen), "");
}
exports.getSharesHex = getSharesHex;
function getSharesFromHex(bytes, version) {
    let byteLen = exports.sharesByteLength;
    if ((0, lt_1.default)(version.version, "0.3.15")) {
        byteLen = 1;
    }
    return (0, hex_1.hex2IntArray)(bytes, byteLen);
}
exports.getSharesFromHex = getSharesFromHex;
function getBalanceHexLength(version, script) {
    const optionCountPos = version.args.findIndex(arg => arg === "globalOptionCount") + version.argPos;
    return (0, hex_1.hex2Int)(script.toASM()[optionCountPos]) * 2;
}
exports.getBalanceHexLength = getBalanceHexLength;
function getMarketBalance(entries, optionCount) {
    return entries.reduce((balance, entry) => {
        return {
            liquidity: balance.liquidity + entry.balance.liquidity,
            shares: balance.shares.map((n, i) => n + entry.balance.shares[i])
        };
    }, { liquidity: 0, shares: Array.from({ length: optionCount }, () => 0) });
}
exports.getMarketBalance = getMarketBalance;
function getBalanceMerkleRoot(entries, version) {
    const sha = (0, lt_1.default)(version.version, "0.6.5") ? sha_1.sha256 : sha_1.sha256d;
    return (0, merkleTree_1.getMerkleRoot)([sha("00"), ...entries.map(entry => sha(getEntryHex(entry, version)))]);
}
exports.getBalanceMerkleRoot = getBalanceMerkleRoot;
function getMerklePath(entries, position, version) {
    const sha = (0, lt_1.default)(version.version, "0.6.5") ? sha_1.sha256 : sha_1.sha256d;
    return (0, merkleTree_1.getMerklePath)(position + 1, [sha("00"), ...entries.map(entry => sha(getEntryHex(entry, version)))]);
}
exports.getMerklePath = getMerklePath;
function getMarketBalanceHex(entries, optionCount, version) {
    const marketBalance = getBalanceHex(getMarketBalance(entries, optionCount), version);
    const balanceTableRoot = getBalanceMerkleRoot(entries, version);
    return marketBalance + String(balanceTableRoot);
}
exports.getMarketBalanceHex = getMarketBalanceHex;
function getMarketStatusHex(status) {
    const isDecidedHex = status.decided ? "01" : "00";
    const resultHex = (0, hex_1.int2Hex)(status.decision, 1);
    return isDecidedHex + resultHex;
}
exports.getMarketStatusHex = getMarketStatusHex;
function getMarketStatusfromHex(decisionHex, votesHex, liquidityFeePoolHex, accLiquidityFeePoolHex, liquidityPointsHex) {
    // console.log(
    //   votesHex,
    //   "->",
    //   splitHexByNumber(votesHex, voteCountByteLen * 2),
    //   "->",
    //   splitHexByNumber(votesHex, voteCountByteLen * 2).map(n => parseInt(reverseHex(n), 16))
    // )
    return {
        decided: Boolean((0, hex_1.hex2Int)(decisionHex.slice(0, 2))),
        decision: (0, hex_1.hex2Int)(decisionHex.slice(2, 4)),
        votes: (0, hex_1.splitHexByNumber)(votesHex, exports.voteCountByteLen * 2).map(n => (0, hex_1.hex2Int)(n)),
        liquidityFeePool: (0, hex_1.hex2Int)(liquidityFeePoolHex),
        accLiquidityFeePool: (0, hex_1.hex2Int)(accLiquidityFeePoolHex),
        liquidityPoints: (0, hex_1.hex2Int)(liquidityPointsHex)
    };
}
exports.getMarketStatusfromHex = getMarketStatusfromHex;
function getMarketDetailsHex(marketDetails) {
    return (0, hex_1.toHex)(JSON.stringify(marketDetails));
}
exports.getMarketDetailsHex = getMarketDetailsHex;
function getMarketDetailsFromHex(hex) {
    return JSON.parse((0, hex_1.fromHex)(hex));
}
exports.getMarketDetailsFromHex = getMarketDetailsFromHex;
function isValidMarketStatus(status, optionCount) {
    // console.log([
    //   status.decided === false || status.decided === true,
    //   status.decision >= 0 || status.decision < optionCount,
    //   status.liquidityFeePool >= 0,
    //   status.accLiquidityFeePool >= status.liquidityFeePool,
    //   status.liquidityPoints >= status.liquidityFeePool
    // ])
    return ((status.decided === false || status.decided === true) &&
        (status.decision >= 0 || status.decision < optionCount) &&
        status.liquidityFeePool >= 0 &&
        status.accLiquidityFeePool >= status.liquidityFeePool &&
        status.liquidityFeePool >= 0 &&
        status.liquidityPoints >= 0);
}
exports.isValidMarketStatus = isValidMarketStatus;
function isValidMarketDetails(details) {
    return Boolean(details.resolve);
}
exports.isValidMarketDetails = isValidMarketDetails;
function isValidMarketBalance(balance) {
    return balance.liquidity >= 0 && balance.shares.every(share => share >= 0);
}
exports.isValidMarketBalance = isValidMarketBalance;
function isValidMarketInfo(market) {
    // console.log([
    //   isValidMarketStatus(market.status, market.details.options.length),
    //   isValidMarketDetails(market.details),
    //   isValidMarketBalance(market.balance),
    //   isValidOracleDetails(market.oracles),
    //   isHash(market.balanceMerkleRoot),
    //   market.status.votes.length === market.details.options.length
    // ])
    return (isValidMarketStatus(market.status, market.details.options.length) &&
        isValidMarketDetails(market.details) &&
        isValidMarketBalance(market.balance) &&
        (0, oracle_1.isValidOracleDetails)(market.oracles) &&
        (0, sha_1.isHash)(market.balanceMerkleRoot) &&
        market.status.votes.length === market.details.options.length);
}
exports.isValidMarketInfo = isValidMarketInfo;
function validateEntries(market, entries) {
    const version = getMarketVersion(market.version);
    const optionCount = market.details.options.length;
    const calculatedBalance = getMarketBalance(entries, optionCount);
    const hasCorrectLiquidity = market.balance.liquidity === calculatedBalance.liquidity;
    const hasCorrectShares = market.status.decided
        ? true
        : market.balance.shares.every((n, i) => n === calculatedBalance.shares[i]);
    // console.log({ entries: JSON.stringify(entries.map(e => e.balance)) })
    const hasCorrectBalanceMerkleRoot = market.balanceMerkleRoot === getBalanceMerkleRoot(entries, version);
    // console.log({
    //   hasCorrectLiquidity,
    //   hasCorrectShares,
    //   hasCorrectBalanceMerkleRoot
    // })
    return hasCorrectLiquidity && hasCorrectShares && hasCorrectBalanceMerkleRoot;
}
exports.validateEntries = validateEntries;
function getMinMarketSatBalance(market, entries) {
    // Calculates minimal market sat balance from entries
    const isDecided = market.status.decided;
    const optionCount = market.details.options.length;
    const balance = getMarketBalance(entries, optionCount);
    if (isDecided) {
        const shares = balance.shares[market.status.decision];
        return shares * lmsr_1.SatScaling + market.status.liquidityFeePool;
    }
    else {
        const version = getMarketVersion(market.version);
        return (0, lmsr_1.getLmsrSatsFixed)(balance, version) + market.status.liquidityFeePool;
    }
}
exports.getMinMarketSatBalance = getMinMarketSatBalance;
function isValidMarketInit(market) {
    const version = getMarketVersion(market.version);
    const firstEntryHash = (0, lt_1.default)(version.version, "0.6.5") ? (0, sha_1.sha256)("00") : (0, sha_1.sha256d)("00");
    return (!market.status.decided &&
        market.status.votes.reduce((a, b) => a + b, 0) === 0 &&
        market.oracles.every(oracle => !oracle.committed && !oracle.voted) &&
        market.balance.liquidity === 0 &&
        market.balance.shares.reduce((a, b) => a + b, 0) === 0 &&
        market.balanceMerkleRoot === (0, merkleTree_1.getMerkleRoot)([firstEntryHash]));
}
exports.isValidMarketInit = isValidMarketInit;
function isValidMarketInitEntry(entry) {
    return entry.globalLiqidityFeePoolSave === 0 && entry.liquidityPoints === 0;
}
exports.isValidMarketInitEntry = isValidMarketInitEntry;
function market2JSON(market) {
    return JSON.parse(JSON.stringify(market, (key, value) => (typeof value === "bigint" ? value.toString() : value) // return everything else unchanged
    ));
}
exports.market2JSON = market2JSON;
function getSettingsFromScript(inputScript) {
    const settingsHex = inputScript.toASM().split(" ")[21];
    let settings;
    try {
        settings = JSON.parse((0, hex_1.fromHex)(settingsHex));
    }
    catch (e) {
        throw new Error("Failed to parse new settings hex");
    }
    return settings;
}
exports.getSettingsFromScript = getSettingsFromScript;
/**
 * Returns number of satoshis that can be extracted when redeeming liquidity
 */
function getLiquiditySatBalance(marketBalance, marketStatus, contractSats, liquidity, marketVersion) {
    if (marketStatus.decided && (0, gte_1.default)(marketVersion.version, "0.6.0")) {
        if (marketBalance.liquidity === 0)
            return 0;
        const winningShares = marketBalance.shares[marketStatus.decision];
        const marketSatBalance = winningShares * lmsr_1.SatScaling + marketStatus.liquidityFeePool;
        const liquiditySatBalance = contractSats - marketSatBalance;
        const liquidityShare = liquidity / marketBalance.liquidity; // contract uses 32bit unint
        return liquidityShare * liquiditySatBalance;
    }
    else {
        return ((0, lmsr_1.getLmsrSats)(marketBalance) -
            (0, lmsr_1.getLmsrSats)({
                shares: marketBalance.shares,
                liquidity: marketBalance.liquidity - liquidity
            }));
    }
}
exports.getLiquiditySatBalance = getLiquiditySatBalance;
//# sourceMappingURL=pm.js.map