"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.computeNewAssetOutcome = exports.computeNewAllocation = exports.computeNewAllocationWithGuarantee = exports.convertAddressToBytes32 = exports.convertBytes32ToAddress = exports.getDepositedEvent = void 0;
var ethers_1 = require("ethers");
var ethers_utils_1 = require("../ethers-utils");
var AssetHolder_json_1 = __importDefault(require("../../artifacts/contracts/AssetHolder.sol/AssetHolder.json"));
var NitroAdjudicator_json_1 = __importDefault(require("../../artifacts/contracts/NitroAdjudicator.sol/NitroAdjudicator.json"));
var outcome_1 = require("./outcome");
var channel_1 = require("./channel");
function getDepositedEvent(eventResult) {
    var _a = ethers_utils_1.parseEventResult(eventResult), destination = _a.destination, amountDeposited = _a.amountDeposited, destinationHoldings = _a.destinationHoldings;
    return {
        destination: destination,
        amountDeposited: ethers_1.BigNumber.from(amountDeposited),
        destinationHoldings: ethers_1.BigNumber.from(destinationHoldings),
    };
}
exports.getDepositedEvent = getDepositedEvent;
function convertBytes32ToAddress(bytes32) {
    var normalized = ethers_1.utils.hexZeroPad(bytes32, 32);
    return ethers_1.utils.getAddress("0x" + normalized.slice(-40));
}
exports.convertBytes32ToAddress = convertBytes32ToAddress;
function convertAddressToBytes32(address) {
    var normalizedAddress = ethers_1.BigNumber.from(address).toHexString();
    if (!ethers_1.utils.isAddress(address)) {
        throw new Error("Input is not a valid Ethereum address.");
    }
    return ethers_1.utils.hexZeroPad(normalizedAddress, 32);
}
exports.convertAddressToBytes32 = convertAddressToBytes32;
function computeNewAllocationWithGuarantee(initialHoldings, allocation, indices, guarantee) {
    var payouts = Array(indices.length > 0 ? indices.length : allocation.length).fill(ethers_1.BigNumber.from(0).toHexString());
    var totalPayouts = ethers_1.BigNumber.from(0);
    var safeToDelete = true;
    var surplus = ethers_1.BigNumber.from(initialHoldings);
    var k = 0;
    var newAllocation = [];
    for (var i = 0; i < allocation.length; i++) {
        newAllocation.push({
            destination: allocation[i].destination,
            amount: allocation[i].amount,
        });
    }
    for (var j = 0; j < guarantee.destinations.length; j++) {
        if (surplus.isZero())
            break;
        for (var i = 0; i < newAllocation.length; i++) {
            if (surplus.isZero())
                break;
            if (ethers_1.BigNumber.from(guarantee.destinations[j]).eq(ethers_1.BigNumber.from(newAllocation[i].destination))) {
                var affordsForDestination = min(ethers_1.BigNumber.from(newAllocation[i].amount), surplus);
                surplus = surplus.sub(affordsForDestination);
                if (indices.length === 0 || (k < indices.length && indices[k] === i)) {
                    newAllocation[i].amount = ethers_1.BigNumber.from(newAllocation[i].amount)
                        .sub(affordsForDestination)
                        .toHexString();
                    payouts[k] = affordsForDestination.toHexString();
                    totalPayouts = totalPayouts.add(affordsForDestination);
                    ++k;
                }
                break;
            }
        }
    }
    for (var i = 0; i < allocation.length; i++) {
        if (!ethers_1.BigNumber.from(newAllocation[i].amount).isZero()) {
            safeToDelete = false;
            break;
        }
    }
    return {
        newAllocation: newAllocation,
        deleted: safeToDelete,
        payouts: payouts,
        totalPayouts: totalPayouts.toHexString(),
    };
}
exports.computeNewAllocationWithGuarantee = computeNewAllocationWithGuarantee;
function computeNewAllocation(initialHoldings, allocation, indices) {
    var payouts = Array(indices.length > 0 ? indices.length : allocation.length).fill(ethers_1.BigNumber.from(0).toHexString());
    var totalPayouts = ethers_1.BigNumber.from(0);
    var newAllocation = [];
    var safeToDelete = true;
    var surplus = ethers_1.BigNumber.from(initialHoldings);
    var k = 0;
    for (var i = 0; i < allocation.length; i++) {
        newAllocation.push({
            destination: allocation[i].destination,
            amount: ethers_1.BigNumber.from(0).toHexString(),
        });
        var affordsForDestination = min(ethers_1.BigNumber.from(allocation[i].amount), surplus);
        if (indices.length == 0 || (k < indices.length && indices[k] === i)) {
            newAllocation[i].amount = ethers_1.BigNumber.from(allocation[i].amount)
                .sub(affordsForDestination)
                .toHexString();
            payouts[k] = affordsForDestination.toHexString();
            totalPayouts = totalPayouts.add(affordsForDestination);
            ++k;
        }
        else {
            newAllocation[i].amount = allocation[i].amount;
        }
        if (!ethers_1.BigNumber.from(newAllocation[i].amount).isZero())
            safeToDelete = false;
        surplus = surplus.sub(affordsForDestination);
    }
    return {
        newAllocation: newAllocation,
        deleted: safeToDelete,
        payouts: payouts,
        totalPayouts: totalPayouts.toHexString(),
    };
}
exports.computeNewAllocation = computeNewAllocation;
function computeNewAssetOutcome(assetHolderAddress, nitroAdjudicatorAddress, allocationUpdatedEvent, tx) {
    var _a = extractOldAllocationAndIndices(assetHolderAddress, nitroAdjudicatorAddress, tx), oldAllocation = _a.oldAllocation, indices = _a.indices, guarantee = _a.guarantee;
    var _b = guarantee
        ? computeNewAllocationWithGuarantee(allocationUpdatedEvent.initialHoldings, oldAllocation, indices, guarantee)
        : computeNewAllocation(allocationUpdatedEvent.initialHoldings, oldAllocation, indices), newAllocation = _b.newAllocation, deleted = _b.deleted, payouts = _b.payouts, totalPayouts = _b.totalPayouts;
    var newHoldings = ethers_1.BigNumber.from(allocationUpdatedEvent.initialHoldings).sub(totalPayouts);
    var newAssetOutcome = deleted
        ? '0x00'
        : {
            assetHolderAddress: assetHolderAddress,
            allocationItems: newAllocation,
        };
    var longHandIndices = indices.length === 0
        ? Array.from(Array(payouts.length).keys())
        : indices;
    var hydratedPayouts = payouts.map(function (v, i) { return ({
        destination: oldAllocation[longHandIndices[i]].destination,
        amount: v,
    }); });
    var externalPayouts = hydratedPayouts.filter(function (payout) {
        return channel_1.isExternalDestination(payout.destination);
    });
    var internalPayouts = hydratedPayouts.filter(function (payout) { return !channel_1.isExternalDestination(payout.destination); });
    return { newAssetOutcome: newAssetOutcome, newHoldings: newHoldings, externalPayouts: externalPayouts, internalPayouts: internalPayouts };
}
exports.computeNewAssetOutcome = computeNewAssetOutcome;
function extractOldAllocationAndIndices(assetHolderAddress, nitroAdjudicatorAddress, tx) {
    var oldAllocation;
    var indices = [];
    var guarantee = undefined;
    if (tx.to === assetHolderAddress) {
        var txDescription = new ethers_1.ethers.Contract(assetHolderAddress, AssetHolder_json_1.default.abi).interface.parseTransaction(tx);
        oldAllocation = outcome_1.decodeAllocation(txDescription.args.allocationBytes);
        if (txDescription.name === 'claim') {
            guarantee = outcome_1.decodeGuarantee(txDescription.args.guarantee);
        }
        indices =
            txDescription.name === 'transfer' || txDescription.name == 'claim'
                ? txDescription.args.indices
                : [];
    }
    else if (tx.to === nitroAdjudicatorAddress) {
        indices = [];
        var txDescription = new ethers_1.ethers.Contract(nitroAdjudicatorAddress, NitroAdjudicator_json_1.default.abi).interface.parseTransaction(tx);
        var oldOutcome = outcome_1.decodeOutcome(txDescription.args.outcomeBytes);
        var assetOutcome = oldOutcome.find(function (outcome) { return outcome.assetHolderAddress === assetHolderAddress; });
        if (outcome_1.isAllocationOutcome(assetOutcome)) {
            oldAllocation = assetOutcome.allocationItems;
        }
        else
            throw Error('No allocation for this asset holder');
    }
    else
        throw Error('transaction did not originate from either of the supplied contract addresses');
    return { oldAllocation: oldAllocation, indices: indices, guarantee: guarantee };
}
function min(a, b) {
    return a.gt(b) ? b : a;
}
//# sourceMappingURL=asset-holder.js.map