import { __assign, __awaiter, __generator } from "tslib";
import { encodeAbiParameters, keccak256 } from "viem";
import { fixedPointAdjustment as fixedPoint } from "./common";
import { MAX_SAFE_DEPOSIT_ID, ZERO_BYTES } from "../constants";
import { FillType, } from "../interfaces";
import { svm } from "../arch";
import { BigNumber } from "./BigNumberUtils";
import { isMessageEmpty, validateFillForDeposit } from "./DepositUtils";
import { chainIsSvm, getNetworkName } from "./NetworkUtils";
import { toAddressType } from "./AddressUtils";
export function isSlowFill(fill) {
    return fill.relayExecutionInfo.fillType === FillType.SlowFill;
}
export function getSlowFillLeafLpFeePct(leaf) {
    var relayData = leaf.relayData, updatedOutputAmount = leaf.updatedOutputAmount;
    return relayData.inputAmount.sub(updatedOutputAmount).mul(fixedPoint).div(relayData.inputAmount);
}
/**
 * Given a SortableEvent type, unpack the available FundsDeposited fields.
 * @note Some fields cannot be evaluated without additional context - i.e. quoteBlockNumber, {from,to}LiteChain.
 * @param rawEvent emitted by FundsDeposited event.
 * @param originChainId Deposit originChainId
 * @returns A mostly-populated DepositWithBlock event.
 */
export function unpackDepositEvent(rawEvent, originChainId) {
    var event = rawEvent;
    return __assign(__assign({}, event), { originChainId: originChainId, depositor: toAddressType(event.depositor, originChainId), recipient: toAddressType(event.recipient, event.destinationChainId), inputToken: toAddressType(event.inputToken, originChainId), outputToken: toAddressType(event.outputToken, event.destinationChainId), exclusiveRelayer: toAddressType(event.exclusiveRelayer, event.destinationChainId), messageHash: getMessageHash(event.message) });
}
/**
 * Given a SortableEvent type, unpack the complete set of FilledRelay fields.
 * @param rawEvent emitted by FundsDeposited event.
 * @param originChainId Deposit originChainId
 * @returns A mostly-populated DepositWithBlock event.
 */
export function unpackFillEvent(rawEvent, destinationChainId) {
    var event = rawEvent;
    return __assign(__assign({}, event), { destinationChainId: destinationChainId, depositor: toAddressType(event.depositor, event.originChainId), recipient: toAddressType(event.recipient, destinationChainId), inputToken: toAddressType(event.inputToken, event.originChainId), outputToken: toAddressType(event.outputToken, destinationChainId), exclusiveRelayer: toAddressType(event.exclusiveRelayer, destinationChainId), relayer: toAddressType(event.relayer, event.repaymentChainId), relayExecutionInfo: __assign(__assign({}, event.relayExecutionInfo), { updatedRecipient: toAddressType(event.relayExecutionInfo.updatedRecipient, destinationChainId) }) });
}
/**
 * Compute the RelayData hash for a fill. This can be used to determine the fill status.
 * @param relayData RelayData information that is used to complete a fill.
 * @param destinationChainId Supplementary destination chain ID required by V3 hashes.
 * @returns The corresponding RelayData hash.
 */
export function getRelayDataHash(relayData, destinationChainId) {
    var abi = [
        {
            type: "tuple",
            components: [
                { type: "bytes32", name: "depositor" },
                { type: "bytes32", name: "recipient" },
                { type: "bytes32", name: "exclusiveRelayer" },
                { type: "bytes32", name: "inputToken" },
                { type: "bytes32", name: "outputToken" },
                { type: "uint256", name: "inputAmount" },
                { type: "uint256", name: "outputAmount" },
                { type: "uint256", name: "originChainId" },
                { type: "uint256", name: "depositId" },
                { type: "uint32", name: "fillDeadline" },
                { type: "uint32", name: "exclusivityDeadline" },
                { type: "bytes", name: "message" },
            ],
        },
        { type: "uint256", name: "destinationChainId" },
    ];
    var _relayData = __assign(__assign({}, relayData), { depositor: relayData.depositor.toBytes32(), recipient: relayData.recipient.toBytes32(), inputToken: relayData.inputToken.toBytes32(), outputToken: relayData.outputToken.toBytes32(), exclusiveRelayer: relayData.exclusiveRelayer.toBytes32() });
    if (chainIsSvm(destinationChainId)) {
        var messageHash = getMessageHash(relayData.message);
        return svm.getRelayDataHash(__assign(__assign({}, relayData), { messageHash: messageHash }), destinationChainId);
    }
    return keccak256(encodeAbiParameters(abi, [_relayData, destinationChainId]));
}
export function getRelayHashFromEvent(e) {
    return getRelayDataHash(e, e.destinationChainId);
}
export function isUnsafeDepositId(depositId) {
    // SpokePool.unsafeDepositV3() produces a uint256 depositId by hashing the msg.sender, depositor and input
    // uint256 depositNonce. There is a possibility that this resultant uint256 is less than the maxSafeDepositId (i.e.
    // the maximum uint32 value) which makes it possible that an unsafeDepositV3's depositId can collide with a safe
    // depositV3's depositId, but the chances of a collision are 1 in 2^(256 - 32), so we'll ignore this
    // possibility.
    var maxSafeDepositId = BigNumber.from(MAX_SAFE_DEPOSIT_ID);
    return maxSafeDepositId.lt(depositId);
}
export function getMessageHash(message) {
    return isMessageEmpty(message) ? ZERO_BYTES : keccak256(message);
}
export function findInvalidFills(spokePoolClients) {
    return __awaiter(this, void 0, void 0, function () {
        var invalidFills, _i, _a, spokePoolClient, fills, fillDepositPairs, _b, fillDepositPairs_1, pair;
        var _this = this;
        return __generator(this, function (_c) {
            switch (_c.label) {
                case 0:
                    invalidFills = [];
                    _i = 0, _a = Object.values(spokePoolClients);
                    _c.label = 1;
                case 1:
                    if (!(_i < _a.length)) return [3 /*break*/, 4];
                    spokePoolClient = _a[_i];
                    fills = spokePoolClient.getFills();
                    return [4 /*yield*/, Promise.all(fills.map(function (fill) { return __awaiter(_this, void 0, void 0, function () {
                            var depositResult, validationResult;
                            var _a;
                            return __generator(this, function (_b) {
                                switch (_b.label) {
                                    case 0:
                                        // Skip fills with unsafe deposit IDs
                                        if (isUnsafeDepositId(fill.depositId)) {
                                            return [2 /*return*/, null]; // Return null for unsafe deposits
                                        }
                                        return [4 /*yield*/, ((_a = spokePoolClients[fill.originChainId]) === null || _a === void 0 ? void 0 : _a.findDeposit(fill.depositId))];
                                    case 1:
                                        depositResult = _b.sent();
                                        // If no deposits found at all
                                        if (!(depositResult === null || depositResult === void 0 ? void 0 : depositResult.found)) {
                                            return [2 /*return*/, {
                                                    fill: fill,
                                                    deposit: null,
                                                    reason: "No ".concat(getNetworkName(fill.originChainId), " deposit with depositId ").concat(fill.depositId, " found"),
                                                }];
                                        }
                                        validationResult = validateFillForDeposit(fill, depositResult.deposit);
                                        if (!validationResult.valid) {
                                            return [2 /*return*/, {
                                                    fill: fill,
                                                    deposit: depositResult.deposit,
                                                    reason: validationResult.reason,
                                                }];
                                        }
                                        // Valid fill with deposit - return null to filter out
                                        return [2 /*return*/, null];
                                }
                            });
                        }); }))];
                case 2:
                    fillDepositPairs = _c.sent();
                    for (_b = 0, fillDepositPairs_1 = fillDepositPairs; _b < fillDepositPairs_1.length; _b++) {
                        pair = fillDepositPairs_1[_b];
                        if (pair) {
                            invalidFills.push({
                                fill: pair.fill,
                                reason: pair.reason,
                                deposit: pair.deposit || undefined,
                            });
                        }
                    }
                    _c.label = 3;
                case 3:
                    _i++;
                    return [3 /*break*/, 1];
                case 4: return [2 /*return*/, invalidFills];
            }
        });
    });
}
//# sourceMappingURL=SpokeUtils.js.map