import { __assign, __awaiter, __generator } from "tslib";
import _ from "lodash";
import assert from "assert";
import { providers } from "ethers";
import { isSlowFill, isValidEvmAddress, isDefined, chainIsEvm, toAddressType } from "../../../utils";
/**
 * @notice Return repayment chain and repayment token for a fill, but does not verify if the returned
 * repayment information is valid for the desired repayment address.
 * @dev The passed in fill should be verified first via verifyFillRepayment().
 * @param fill The fill to get the repayment information for.
 * @return The chain to send the refund to and the token to use for the refund.
 */
export function getRefundInformationFromFill(relayData, hubPoolClient, bundleEndBlockForMainnet) {
    var chainToSendRefundTo = _getRepaymentChainId(relayData, hubPoolClient, bundleEndBlockForMainnet);
    if (chainToSendRefundTo === relayData.originChainId) {
        return {
            chainToSendRefundTo: chainToSendRefundTo,
            repaymentToken: relayData.inputToken,
        };
    }
    // Now figure out the equivalent L2 token for the repayment token. If the inputToken doesn't have a
    // PoolRebalanceRoute, then the repayment chain would have been the originChainId after the getRepaymentChainId()
    // call and we would have returned already, so the following call should always succeed.
    var l1TokenCounterpart = hubPoolClient.getL1TokenForL2TokenAtBlock(relayData.inputToken, relayData.originChainId, bundleEndBlockForMainnet);
    var repaymentToken = hubPoolClient.getL2TokenForL1TokenAtBlock(l1TokenCounterpart, chainToSendRefundTo, bundleEndBlockForMainnet);
    return {
        chainToSendRefundTo: chainToSendRefundTo,
        repaymentToken: repaymentToken,
    };
}
/**
 * @notice Verifies that the fill can be repaid. If the repayment address is not
 * valid for the requested repayment chain, then this function will attempt to change the fill's repayment chain
 * to the destination chain and its repayment address to the  msg.sender and if this is possible,
 * return the fill. Otherwise, return undefined.
 * @param _fill Fill with a requested repayment chain and address
 * @return Fill with the applied repayment chain (depends on the validity of the requested repayment address)
 * and applied repayment address, or undefined if the applied repayment address is not valid for the
 * applied repayment chain.
 */
export function verifyFillRepayment(_fill, destinationChainProvider, matchedDeposit, hubPoolClient, bundleEndBlockForMainnet) {
    return __awaiter(this, void 0, void 0, function () {
        var fill, repaymentChainId, fillTransaction, destinationRelayer;
        return __generator(this, function (_a) {
            switch (_a.label) {
                case 0:
                    fill = _.cloneDeep(_fill);
                    // Slow fills don't result in repayments so they're always valid.
                    if (isSlowFill(fill)) {
                        return [2 /*return*/, fill];
                    }
                    repaymentChainId = _getRepaymentChainId(__assign(__assign({}, fill), { fromLiteChain: matchedDeposit.fromLiteChain }), hubPoolClient, bundleEndBlockForMainnet);
                    if (!_repaymentAddressNeedsToBeOverwritten(fill)) return [3 /*break*/, 3];
                    if (!chainIsEvm(fill.destinationChainId)) return [3 /*break*/, 2];
                    assert(destinationChainProvider instanceof providers.Provider, "BundleDataClient#verifyFillRepayment: unexpected destination chain provider for chain ID ".concat(fill.destinationChainId));
                    return [4 /*yield*/, destinationChainProvider.getTransaction(fill.txnRef)];
                case 1:
                    fillTransaction = _a.sent();
                    destinationRelayer = fillTransaction === null || fillTransaction === void 0 ? void 0 : fillTransaction.from;
                    // Repayment chain is still an EVM chain, but the msg.sender is a bytes32 address, so the fill is invalid.
                    if (!isDefined(destinationRelayer) || !isValidEvmAddress(destinationRelayer)) {
                        return [2 /*return*/, undefined];
                    }
                    // If we can switch the repayment chain to the destination chain, then do so. We should only switch if the
                    // destination chain has a valid repayment token that is equivalent to the deposited input token. This would
                    // also be the same mapping as the repayment token on the repayment chain.
                    if (!matchedDeposit.fromLiteChain &&
                        hubPoolClient.areTokensEquivalent(fill.inputToken, fill.originChainId, fill.outputToken, fill.destinationChainId)) {
                        repaymentChainId = fill.destinationChainId;
                    }
                    // If we can't switch the chain, then we need to verify that the msg.sender is a valid address on the repayment chain.
                    // Because we already checked that the `destinationRelayer` was a valid EVM address above, we only need to check
                    // that the repayment chain is an EVM chain.
                    else {
                        if (!chainIsEvm(repaymentChainId)) {
                            return [2 /*return*/, undefined];
                        }
                    }
                    fill.relayer = toAddressType(destinationRelayer, fill.destinationChainId);
                    assert(fill.relayer.isValidOn(fill.destinationChainId), "Cannot re-assign fill to msg.sender: ".concat(destinationRelayer));
                    return [3 /*break*/, 3];
                case 2: return [2 /*return*/, undefined];
                case 3:
                    // Repayment address is now valid and repayment chain is either origin chain for lite chain or the destination
                    // chain for cases where the repayment address was invalid. Fill should be valid now.
                    fill.repaymentChainId = repaymentChainId;
                    return [2 /*return*/, fill];
            }
        });
    });
}
function _getRepaymentChainId(relayData, hubPoolClient, bundleEndBlockForMainnet) {
    if (relayData.fromLiteChain) {
        assert(!isSlowFill(relayData), "getRepaymentChainId: fromLiteChain and slow fill are mutually exclusive");
        return relayData.originChainId;
    }
    // Handle slow relay where FilledRelay.repaymentChainId = 0, as hardcoded in the SpokePool contract.
    // Slow relays always pay recipient on destination chain.
    if (isSlowFill(relayData)) {
        return relayData.destinationChainId;
    }
    // Repayment chain is valid if the input token and repayment chain are mapped to the same PoolRebalanceRoute.
    var repaymentTokenIsValid = _repaymentChainTokenIsValid(relayData, hubPoolClient, bundleEndBlockForMainnet);
    if (repaymentTokenIsValid) {
        return relayData.repaymentChainId;
    }
    // If repayment chain is not valid, default to origin chain since we always know the input token can be refunded.
    return relayData.originChainId;
}
function _repaymentChainTokenIsValid(relayData, hubPoolClient, bundleEndBlockForMainnet) {
    if (!hubPoolClient.l2TokenHasPoolRebalanceRoute(relayData.inputToken, relayData.originChainId, bundleEndBlockForMainnet)) {
        return false;
    }
    var l1TokenCounterpart = hubPoolClient.getL1TokenForL2TokenAtBlock(relayData.inputToken, relayData.originChainId, bundleEndBlockForMainnet);
    if (!hubPoolClient.l2TokenEnabledForL1TokenAtBlock(l1TokenCounterpart, relayData.repaymentChainId, bundleEndBlockForMainnet)) {
        return false;
    }
    return true;
}
function _repaymentAddressNeedsToBeOverwritten(fill) {
    // Slow fills don't result in repayments so they're always valid.
    if (isSlowFill(fill)) {
        return false;
    }
    // @dev If the repayment chain is EVM, the repayment address is valid if the address is 20 bytes.
    // If the repayment chain is SVM, the repayment address is valid if the address is strictly greater than
    // 20 bytes.
    return !fill.relayer.isValidOn(fill.repaymentChainId);
}
//# sourceMappingURL=FillUtils.js.map