import { beforeEach, describe, expect, test, vi } from 'vitest';
import {
  addrDetails,
  calculateLeverageFromCollateralRatio,
  calculateTotalCollateralForRedemption,
  cdpCollateralRatioPercentage,
  fromSystemParamsAsset,
  getInlineDatumOrThrow,
  leverageCdpWithRob,
  MAX_REDEMPTIONS_WITH_CDP_OPEN,
  MIN_ROB_COLLATERAL_AMT,
  openRob,
  randomRobsSubsetSatisfyingTargetCollateral,
  Rational,
  rationalToFloat,
  robCollateralAmtToSpend,
  RobDatum,
  SystemParams,
} from '../../src';
import {
  addAssets,
  Assets,
  Emulator,
  EmulatorAccount,
  fromHex,
  fromText,
  generateEmulatorAccount,
  Lucid,
  slotToUnixTime,
  toHex,
  UTxO,
} from '@lucid-evolution/lucid';
import { LucidContext, runAndAwaitTx } from '../test-helpers';

import { init } from '../endpoints/initialize';
import {
  DEFAULT_PRICE,
  iusdInitialAssetCfg,
  iusdInitialAssetCfgWithPyth,
  mkBaseCollateralAsset,
} from '../mock/assets-mock';
import { assertValueInRange } from '../utils/asserts';

import {
  adaAssetClass,
  AssetClass,
  assetClassToUnit,
  assetClassValueOf,
  isSameAssetClass,
  lovelacesAmt,
  mkAssetsOf,
  mkLovelacesOf,
} from '@3rd-eye-labs/cardano-offchain-common';
import {
  findAllNecessaryOrefs,
  findCdp,
  findPriceOracleFromCollateralAsset,
} from '../cdp/cdp-queries';
import { parsePriceOracleDatum } from '../../src/contracts/price-oracle/types-new';
import { parseInterestOracleDatum } from '../../src/contracts/interest-oracle/types-new';
import { benchmarkAndAwaitTx } from '../utils/benchmark-utils';
import { findAllRobs } from './rob-queries';
import { MAINNET_PROTOCOL_PARAMETERS } from '../indigo-test-helpers';
import { match, P } from 'ts-pattern';
import { ParsedFeedPayload } from '@pythnetwork/pyth-lazer-sdk';
import { createPythMessage } from '../pyth/helpers';
import { retrieveAdjustedPrice } from '../../src/utils/oracle-helpers';

type MyContext = LucidContext<{
  admin: EmulatorAccount;
  user: EmulatorAccount;
}>;

const collateralAssetA: AssetClass = {
  currencySymbol: fromHex(
    // random generated
    'cc072059ae741791b7b9c23d9baea6a0b0d764dec617ce7e027a8dea',
  ),
  tokenName: fromHex(fromText('A')),
};

async function openBuyRobs(
  context: MyContext,
  sysParams: SystemParams,
  iasset: string,
  collateralAsset: AssetClass,
  amountsToSpend: bigint[],
  maxPrice: Rational,
): Promise<void> {
  for (const amt of amountsToSpend) {
    await runAndAwaitTx(
      context.lucid,
      openRob(
        iasset,
        amt,
        { BuyIAssetOrder: { collateralAsset: collateralAsset, maxPrice } },
        context.lucid,
        sysParams,
      ),
    );
  }
}

function hadRobRedemption(
  lrp: { utxo: UTxO; datum: RobDatum },
  sysParams: SystemParams,
): boolean {
  return (
    assetClassValueOf(lrp.utxo.assets, {
      currencySymbol: fromHex(
        sysParams.cdpParams.cdpAssetSymbol.unCurrencySymbol,
      ),
      tokenName: lrp.datum.iasset,
    }) > 0
  );
}

describe('randomRobsSubsetSatisfyingTargetCollateral', () => {
  const mockUtxo = (ada: bigint, otherAssets: Assets = {}): UTxO => ({
    address: '',
    assets: addAssets(mkLovelacesOf(ada), otherAssets),
    outputIndex: 0,
    txHash: '',
  });

  test('1', () => {
    const lrps: [UTxO, RobDatum][] = [
      [
        mockUtxo(100_000_000n),
        {
          iasset: fromHex(fromText('iUSD')),
          orderType: {
            BuyIAssetOrder: {
              collateralAsset: adaAssetClass,
              maxPrice: { numerator: 1n, denominator: 1n },
            },
          },
          owner: fromHex(''),
          robRefInput: { outputIndex: 0n, txHash: fromHex('') },
        },
      ],
      [
        mockUtxo(100_000_000n),
        {
          iasset: fromHex(fromText('iUSD')),
          orderType: {
            BuyIAssetOrder: {
              collateralAsset: adaAssetClass,
              maxPrice: { numerator: 1n, denominator: 1n },
            },
          },
          owner: fromHex(''),
          robRefInput: { outputIndex: 0n, txHash: fromHex('') },
        },
      ],
    ];

    expect(
      randomRobsSubsetSatisfyingTargetCollateral(
        fromHex(fromText('iUSD')),
        adaAssetClass,
        120_000_000n,
        { numerator: 1n, denominator: 1n },
        lrps,
        MAX_REDEMPTIONS_WITH_CDP_OPEN,
      ),
    ).toEqual(expect.arrayContaining(lrps));
  });

  test('2', () => {
    const lrps: [UTxO, RobDatum][] = [
      [
        mockUtxo(150_000_000n),
        {
          iasset: fromHex(fromText('iUSD')),
          orderType: {
            BuyIAssetOrder: {
              collateralAsset: adaAssetClass,
              maxPrice: { numerator: 1n, denominator: 1n },
            },
          },
          owner: fromHex(''),
          robRefInput: { outputIndex: 0n, txHash: fromHex('') },
        },
      ],
    ];

    expect(
      randomRobsSubsetSatisfyingTargetCollateral(
        fromHex(fromText('iUSD')),
        adaAssetClass,
        100_000_000n,
        { numerator: 1n, denominator: 1n },
        lrps,
        MAX_REDEMPTIONS_WITH_CDP_OPEN,
      ),
    ).toEqual(lrps);
  });

  test('3', () => {
    const lrps: [UTxO, RobDatum][] = [
      [
        mockUtxo(100_000_000n),
        {
          iasset: fromHex(fromText('iUSD')),
          orderType: {
            BuyIAssetOrder: {
              collateralAsset: adaAssetClass,
              maxPrice: { numerator: 1n, denominator: 1n },
            },
          },
          owner: fromHex(''),
          robRefInput: { outputIndex: 0n, txHash: fromHex('') },
        },
      ],
      [
        mockUtxo(10_000_000n),
        {
          iasset: fromHex(fromText('iUSD')),
          orderType: {
            BuyIAssetOrder: {
              collateralAsset: adaAssetClass,
              maxPrice: { numerator: 1n, denominator: 1n },
            },
          },
          owner: fromHex(''),
          robRefInput: { outputIndex: 0n, txHash: fromHex('') },
        },
      ],
      [
        mockUtxo(100_000_000n),
        {
          iasset: fromHex(fromText('iUSD')),
          orderType: {
            BuyIAssetOrder: {
              collateralAsset: adaAssetClass,
              maxPrice: { numerator: 1n, denominator: 1n },
            },
          },
          owner: fromHex(''),
          robRefInput: { outputIndex: 0n, txHash: fromHex('') },
        },
      ],
    ];

    const mockedShuffle = vi.fn().mockImplementation(() => lrps);

    expect(
      randomRobsSubsetSatisfyingTargetCollateral(
        fromHex(fromText('iUSD')),
        adaAssetClass,
        120_000_000n,
        { numerator: 1n, denominator: 1n },
        lrps,
        MAX_REDEMPTIONS_WITH_CDP_OPEN,
        mockedShuffle,
      ),
    ).toEqual(expect.arrayContaining(lrps));
  });

  test('4 (min rob collateral causes less redeemable)', () => {
    const lrps: [UTxO, RobDatum][] = [
      [
        mockUtxo(100_000_000n),
        {
          iasset: fromHex(fromText('iUSD')),
          orderType: {
            BuyIAssetOrder: {
              collateralAsset: adaAssetClass,
              maxPrice: { numerator: 1n, denominator: 1n },
            },
          },
          owner: fromHex(''),
          robRefInput: { outputIndex: 0n, txHash: fromHex('') },
        },
      ],
    ];

    const mockedShuffle = vi.fn().mockImplementation(() => lrps);

    expect(() =>
      randomRobsSubsetSatisfyingTargetCollateral(
        fromHex(fromText('iUSD')),
        adaAssetClass,
        100_000_000n,
        { numerator: 1n, denominator: 1n },
        lrps,
        MAX_REDEMPTIONS_WITH_CDP_OPEN,
        mockedShuffle,
      ),
    ).toThrow(new Error("Couldn't achieve target lovelaces"));
  });

  test('5 (too small amount to redeem - payout 0)', () => {
    const lrps: [UTxO, RobDatum][] = [
      [
        mockUtxo(100_000_000n),
        {
          iasset: fromHex(fromText('iUSD')),
          orderType: {
            BuyIAssetOrder: {
              collateralAsset: adaAssetClass,
              maxPrice: { numerator: 1n, denominator: 1n },
            },
          },
          owner: fromHex(''),
          robRefInput: { outputIndex: 0n, txHash: fromHex('') },
        },
      ],
    ];

    expect(() =>
      randomRobsSubsetSatisfyingTargetCollateral(
        fromHex(fromText('iUSD')),
        adaAssetClass,
        5n,
        { numerator: 10n, denominator: 1n },
        lrps,
        MAX_REDEMPTIONS_WITH_CDP_OPEN,
      ),
    ).toThrow('Must redeem and payout more than 0.');
  });

  test('6 (too small amount to redeem - redeemable 0)', () => {
    const lrps: [UTxO, RobDatum][] = [
      [
        mockUtxo(100_000_000n),
        {
          iasset: fromHex(fromText('iUSD')),
          orderType: {
            BuyIAssetOrder: {
              collateralAsset: adaAssetClass,
              maxPrice: { numerator: 1n, denominator: 1n },
            },
          },
          owner: fromHex(''),
          robRefInput: { outputIndex: 0n, txHash: fromHex('') },
        },
      ],
    ];

    expect(() =>
      randomRobsSubsetSatisfyingTargetCollateral(
        fromHex(fromText('iUSD')),
        adaAssetClass,
        0n,
        { numerator: 1n, denominator: 1n },
        lrps,
        MAX_REDEMPTIONS_WITH_CDP_OPEN,
      ),
    ).toThrow('Must redeem and payout more than 0.');
  });

  test('7 (dont pick fully redeemed rob)', () => {
    const lrps: [UTxO, RobDatum][] = [
      [
        mockUtxo(5n + MIN_ROB_COLLATERAL_AMT),
        {
          iasset: fromHex(fromText('iUSD')),
          orderType: {
            BuyIAssetOrder: {
              collateralAsset: adaAssetClass,
              maxPrice: { numerator: 20n, denominator: 1n },
            },
          },
          owner: fromHex(''),
          robRefInput: { outputIndex: 0n, txHash: fromHex('') },
        },
      ],
      [
        mockUtxo(100_000_000n),
        {
          iasset: fromHex(fromText('iUSD')),
          orderType: {
            BuyIAssetOrder: {
              collateralAsset: adaAssetClass,
              maxPrice: { numerator: 1n, denominator: 1n },
            },
          },
          owner: fromHex(''),
          robRefInput: { outputIndex: 0n, txHash: fromHex('') },
        },
      ],
    ];

    const mockedShuffle = vi.fn().mockImplementation(() => lrps);

    expect(
      randomRobsSubsetSatisfyingTargetCollateral(
        fromHex(fromText('iUSD')),
        adaAssetClass,
        100_000n,
        { numerator: 10n, denominator: 1n },
        lrps,
        MAX_REDEMPTIONS_WITH_CDP_OPEN,
        mockedShuffle,
      ),
    ).toEqual(expect.arrayContaining([lrps[1]]));
  });

  test('8 (prevent redeeming 0 or payout 0 to any ROB)', () => {
    const lrps: [UTxO, RobDatum][] = [
      [
        mockUtxo(100n + MIN_ROB_COLLATERAL_AMT),
        {
          iasset: fromHex(fromText('iUSD')),
          orderType: {
            BuyIAssetOrder: {
              collateralAsset: adaAssetClass,
              maxPrice: { numerator: 20n, denominator: 1n },
            },
          },
          owner: fromHex(''),
          robRefInput: { outputIndex: 0n, txHash: fromHex('') },
        },
      ],
      [
        mockUtxo(10n + MIN_ROB_COLLATERAL_AMT),
        {
          iasset: fromHex(fromText('iUSD')),
          orderType: {
            BuyIAssetOrder: {
              collateralAsset: adaAssetClass,
              maxPrice: { numerator: 20n, denominator: 1n },
            },
          },
          owner: fromHex(''),
          robRefInput: { outputIndex: 0n, txHash: fromHex('') },
        },
      ],
      [
        mockUtxo(30n + MIN_ROB_COLLATERAL_AMT),
        {
          iasset: fromHex(fromText('iUSD')),
          orderType: {
            BuyIAssetOrder: {
              collateralAsset: adaAssetClass,
              maxPrice: { numerator: 20n, denominator: 1n },
            },
          },
          owner: fromHex(''),
          robRefInput: { outputIndex: 0n, txHash: fromHex('') },
        },
      ],
    ];

    const mockedShuffle = vi.fn().mockImplementation(() => lrps);

    // Since the second ROB would cause the target redeemable unachievable because otherwise,
    // the remaining ROB would payout 0.
    // Since there's a larger ROB, replace the second one with the last one achieving the target in full.
    expect(
      randomRobsSubsetSatisfyingTargetCollateral(
        fromHex(fromText('iUSD')),
        adaAssetClass,
        115n,
        { numerator: 10n, denominator: 1n },
        lrps,
        MAX_REDEMPTIONS_WITH_CDP_OPEN,
        mockedShuffle,
      ),
    ).toEqual(expect.arrayContaining([lrps[0], lrps[2]]));
  });

  test('9 (cant redeem more to achieve target because it would cause 0 payout)', () => {
    const lrps: [UTxO, RobDatum][] = [
      [
        mockUtxo(100n + MIN_ROB_COLLATERAL_AMT),
        {
          iasset: fromHex(fromText('iUSD')),
          orderType: {
            BuyIAssetOrder: {
              collateralAsset: adaAssetClass,
              maxPrice: { numerator: 20n, denominator: 1n },
            },
          },
          owner: fromHex(''),
          robRefInput: { outputIndex: 0n, txHash: fromHex('') },
        },
      ],
      [
        mockUtxo(100n + MIN_ROB_COLLATERAL_AMT),
        {
          iasset: fromHex(fromText('iUSD')),
          orderType: {
            BuyIAssetOrder: {
              collateralAsset: adaAssetClass,
              maxPrice: { numerator: 20n, denominator: 1n },
            },
          },
          owner: fromHex(''),
          robRefInput: { outputIndex: 0n, txHash: fromHex('') },
        },
      ],
    ];

    const mockedShuffle = vi.fn().mockImplementation(() => lrps);

    expect(
      randomRobsSubsetSatisfyingTargetCollateral(
        fromHex(fromText('iUSD')),
        adaAssetClass,
        105n,
        { numerator: 10n, denominator: 1n },
        lrps,
        MAX_REDEMPTIONS_WITH_CDP_OPEN,
        mockedShuffle,
      ),
    ).toEqual(expect.arrayContaining([lrps[0]]));
  });

  test('filtering by iasset 1', () => {
    const lrps: [UTxO, RobDatum][] = [
      [
        mockUtxo(100_000_000n),
        {
          iasset: fromHex(fromText('iUSD')),
          orderType: {
            BuyIAssetOrder: {
              collateralAsset: adaAssetClass,
              maxPrice: { numerator: 1n, denominator: 1n },
            },
          },
          owner: fromHex(''),
          robRefInput: { outputIndex: 0n, txHash: fromHex('') },
        },
      ],
      [
        mockUtxo(100_000_000n),
        {
          iasset: fromHex(fromText('iBTC')),
          orderType: {
            BuyIAssetOrder: {
              collateralAsset: adaAssetClass,
              maxPrice: { numerator: 1n, denominator: 1n },
            },
          },
          owner: fromHex(''),
          robRefInput: { outputIndex: 0n, txHash: fromHex('') },
        },
      ],
      [
        mockUtxo(100_000_000n),
        {
          iasset: fromHex(fromText('iUSD')),
          orderType: {
            BuyIAssetOrder: {
              collateralAsset: adaAssetClass,
              maxPrice: { numerator: 1n, denominator: 1n },
            },
          },
          owner: fromHex(''),
          robRefInput: { outputIndex: 0n, txHash: fromHex('') },
        },
      ],
    ];

    expect(
      randomRobsSubsetSatisfyingTargetCollateral(
        fromHex(fromText('iUSD')),
        adaAssetClass,
        110_000_000n,
        { numerator: 1n, denominator: 1n },
        lrps,
        MAX_REDEMPTIONS_WITH_CDP_OPEN,
      ),
    ).toEqual(expect.arrayContaining([lrps[0], lrps[2]]));
  });

  test('filtering by iasset 2', () => {
    const lrps: [UTxO, RobDatum][] = [
      [
        mockUtxo(100_000_000n),
        {
          iasset: fromHex(fromText('iUSD')),
          orderType: {
            BuyIAssetOrder: {
              collateralAsset: adaAssetClass,
              maxPrice: { numerator: 1n, denominator: 1n },
            },
          },
          owner: fromHex(''),
          robRefInput: { outputIndex: 0n, txHash: fromHex('') },
        },
      ],
      [
        mockUtxo(100_000_000n),
        {
          iasset: fromHex(fromText('iBTC')),
          orderType: {
            BuyIAssetOrder: {
              collateralAsset: adaAssetClass,
              maxPrice: { numerator: 1n, denominator: 1n },
            },
          },
          owner: fromHex(''),
          robRefInput: { outputIndex: 0n, txHash: fromHex('') },
        },
      ],
    ];

    expect(() =>
      randomRobsSubsetSatisfyingTargetCollateral(
        fromHex(fromText('iUSD')),
        adaAssetClass,
        110_000_000n,
        { numerator: 1n, denominator: 1n },
        lrps,
        MAX_REDEMPTIONS_WITH_CDP_OPEN,
      ),
    ).toThrow("Couldn't achieve target lovelaces");
  });

  test('filtering by collateral asset 1', () => {
    const lrps: [UTxO, RobDatum][] = [
      [
        mockUtxo(100_000_000n, mkAssetsOf(collateralAssetA, 100n)),
        {
          iasset: fromHex(fromText('iUSD')),
          orderType: {
            BuyIAssetOrder: {
              collateralAsset: collateralAssetA,
              maxPrice: { numerator: 1n, denominator: 1n },
            },
          },
          owner: fromHex(''),
          robRefInput: { outputIndex: 0n, txHash: fromHex('') },
        },
      ],
      [
        mockUtxo(100_000_000n),
        {
          iasset: fromHex(fromText('iUSD')),
          orderType: {
            BuyIAssetOrder: {
              collateralAsset: adaAssetClass,
              maxPrice: { numerator: 1n, denominator: 1n },
            },
          },
          owner: fromHex(''),
          robRefInput: { outputIndex: 0n, txHash: fromHex('') },
        },
      ],
      [
        mockUtxo(100_000_000n),
        {
          iasset: fromHex(fromText('iBTC')),
          orderType: {
            BuyIAssetOrder: {
              collateralAsset: adaAssetClass,
              maxPrice: { numerator: 1n, denominator: 1n },
            },
          },
          owner: fromHex(''),
          robRefInput: { outputIndex: 0n, txHash: fromHex('') },
        },
      ],
      [
        mockUtxo(100_000_000n, mkAssetsOf(collateralAssetA, 100n)),
        {
          iasset: fromHex(fromText('iUSD')),
          orderType: {
            BuyIAssetOrder: {
              collateralAsset: collateralAssetA,
              maxPrice: { numerator: 1n, denominator: 1n },
            },
          },
          owner: fromHex(''),
          robRefInput: { outputIndex: 0n, txHash: fromHex('') },
        },
      ],
    ];

    expect(
      randomRobsSubsetSatisfyingTargetCollateral(
        fromHex(fromText('iUSD')),
        collateralAssetA,
        110n,
        { numerator: 1n, denominator: 1n },
        lrps,
        MAX_REDEMPTIONS_WITH_CDP_OPEN,
      ),
    ).toEqual(expect.arrayContaining([lrps[0], lrps[3]]));
  });

  test('filtering by collateral asset 2', () => {
    const lrps: [UTxO, RobDatum][] = [
      [
        mockUtxo(100n),
        {
          iasset: fromHex(fromText('iUSD')),
          orderType: {
            BuyIAssetOrder: {
              collateralAsset: adaAssetClass,
              maxPrice: { numerator: 1n, denominator: 1n },
            },
          },
          owner: fromHex(''),
          robRefInput: { outputIndex: 0n, txHash: fromHex('') },
        },
      ],
      [
        mockUtxo(100n),
        {
          iasset: fromHex(fromText('iUSD')),
          orderType: {
            BuyIAssetOrder: {
              collateralAsset: adaAssetClass,
              maxPrice: { numerator: 1n, denominator: 1n },
            },
          },
          owner: fromHex(''),
          robRefInput: { outputIndex: 0n, txHash: fromHex('') },
        },
      ],
      [
        mockUtxo(100n),
        {
          iasset: fromHex(fromText('iBTC')),
          orderType: {
            BuyIAssetOrder: {
              collateralAsset: adaAssetClass,
              maxPrice: { numerator: 1n, denominator: 1n },
            },
          },
          owner: fromHex(''),
          robRefInput: { outputIndex: 0n, txHash: fromHex('') },
        },
      ],
      [
        mockUtxo(100n, mkAssetsOf(collateralAssetA, 100n)),
        {
          iasset: fromHex(fromText('iUSD')),
          orderType: {
            BuyIAssetOrder: {
              collateralAsset: collateralAssetA,
              maxPrice: { numerator: 1n, denominator: 1n },
            },
          },
          owner: fromHex(''),
          robRefInput: { outputIndex: 0n, txHash: fromHex('') },
        },
      ],
    ];

    expect(() =>
      randomRobsSubsetSatisfyingTargetCollateral(
        fromHex(fromText('iUSD')),
        collateralAssetA,
        110n,
        { numerator: 1n, denominator: 1n },
        lrps,
        MAX_REDEMPTIONS_WITH_CDP_OPEN,
      ),
    ).toThrow("Couldn't achieve target lovelaces");
  });

  test('filtering by price 1', () => {
    const lrps: [UTxO, RobDatum][] = [
      [
        mockUtxo(100n),
        {
          iasset: fromHex(fromText('iUSD')),
          orderType: {
            BuyIAssetOrder: {
              collateralAsset: collateralAssetA,
              maxPrice: { numerator: 15n, denominator: 10n },
            },
          },
          owner: fromHex(''),
          robRefInput: { outputIndex: 0n, txHash: fromHex('') },
        },
      ],
      [
        mockUtxo(100n),
        {
          iasset: fromHex(fromText('iUSD')),
          orderType: {
            BuyIAssetOrder: {
              collateralAsset: collateralAssetA,
              maxPrice: { numerator: 1n, denominator: 1n },
            },
          },
          owner: fromHex(''),
          robRefInput: { outputIndex: 0n, txHash: fromHex('') },
        },
      ],
    ];

    expect(() =>
      randomRobsSubsetSatisfyingTargetCollateral(
        fromHex(fromText('iUSD')),
        adaAssetClass,
        120n,
        { numerator: 1n, denominator: 1n },
        lrps,
        MAX_REDEMPTIONS_WITH_CDP_OPEN,
      ),
    ).toThrow("Couldn't achieve target lovelaces");
  });

  test('filtering by price 2', () => {
    const lrps: [UTxO, RobDatum][] = [
      [
        mockUtxo(10n, mkAssetsOf(collateralAssetA, 100n)),
        {
          iasset: fromHex(fromText('iUSD')),
          orderType: {
            BuyIAssetOrder: {
              collateralAsset: collateralAssetA,
              maxPrice: { numerator: 15n, denominator: 10n },
            },
          },
          owner: fromHex(''),
          robRefInput: { outputIndex: 0n, txHash: fromHex('') },
        },
      ],
      [
        mockUtxo(10n, mkAssetsOf(collateralAssetA, 100n)),
        {
          iasset: fromHex(fromText('iUSD')),
          orderType: {
            BuyIAssetOrder: {
              collateralAsset: collateralAssetA,
              maxPrice: { numerator: 1n, denominator: 1n },
            },
          },
          owner: fromHex(''),
          robRefInput: { outputIndex: 0n, txHash: fromHex('') },
        },
      ],
      [
        mockUtxo(10n, mkAssetsOf(collateralAssetA, 100n)),
        {
          iasset: fromHex(fromText('iUSD')),
          orderType: {
            BuyIAssetOrder: {
              collateralAsset: collateralAssetA,
              maxPrice: { numerator: 13n, denominator: 10n },
            },
          },
          owner: fromHex(''),
          robRefInput: { outputIndex: 0n, txHash: fromHex('') },
        },
      ],
    ];

    expect(
      randomRobsSubsetSatisfyingTargetCollateral(
        fromHex(fromText('iUSD')),
        collateralAssetA,
        120n,
        { numerator: 11n, denominator: 10n },
        lrps,
        MAX_REDEMPTIONS_WITH_CDP_OPEN,
      ),
    ).toEqual(expect.arrayContaining([lrps[0], lrps[2]]));
  });

  test('max redemptions check 1', () => {
    const lrps: [UTxO, RobDatum][] = [
      [
        mockUtxo(10n, mkAssetsOf(collateralAssetA, 100n)),
        {
          iasset: fromHex(fromText('iUSD')),
          orderType: {
            BuyIAssetOrder: {
              collateralAsset: collateralAssetA,
              maxPrice: { numerator: 13n, denominator: 10n },
            },
          },
          owner: fromHex(''),
          robRefInput: { outputIndex: 0n, txHash: fromHex('') },
        },
      ],
      [
        mockUtxo(10n, mkAssetsOf(collateralAssetA, 90n)),
        {
          iasset: fromHex(fromText('iUSD')),
          orderType: {
            BuyIAssetOrder: {
              collateralAsset: collateralAssetA,
              maxPrice: { numerator: 13n, denominator: 10n },
            },
          },
          owner: fromHex(''),
          robRefInput: { outputIndex: 0n, txHash: fromHex('') },
        },
      ],
      [
        mockUtxo(10n, mkAssetsOf(collateralAssetA, 80n)),
        {
          iasset: fromHex(fromText('iUSD')),
          orderType: {
            BuyIAssetOrder: {
              collateralAsset: collateralAssetA,
              maxPrice: { numerator: 13n, denominator: 10n },
            },
          },
          owner: fromHex(''),
          robRefInput: { outputIndex: 0n, txHash: fromHex('') },
        },
      ],
      [
        mockUtxo(10n, mkAssetsOf(collateralAssetA, 70n)),
        {
          iasset: fromHex(fromText('iUSD')),
          orderType: {
            BuyIAssetOrder: {
              collateralAsset: collateralAssetA,
              maxPrice: { numerator: 13n, denominator: 10n },
            },
          },
          owner: fromHex(''),
          robRefInput: { outputIndex: 0n, txHash: fromHex('') },
        },
      ],
      [
        mockUtxo(10n, mkAssetsOf(collateralAssetA, 100n)),
        {
          iasset: fromHex(fromText('iUSD')),
          orderType: {
            BuyIAssetOrder: {
              collateralAsset: collateralAssetA,
              maxPrice: { numerator: 13n, denominator: 10n },
            },
          },
          owner: fromHex(''),
          robRefInput: { outputIndex: 0n, txHash: fromHex('') },
        },
      ],
    ];

    const mockedShuffle = vi.fn().mockImplementation(() => lrps);

    expect(MAX_REDEMPTIONS_WITH_CDP_OPEN).toBe(4);

    // replaces the lrps[3] with lrps[4] since it's larger and already hitting max redemptions
    expect(
      randomRobsSubsetSatisfyingTargetCollateral(
        fromHex(fromText('iUSD')),
        collateralAssetA,
        360n,
        { numerator: 1n, denominator: 1n },
        lrps,
        MAX_REDEMPTIONS_WITH_CDP_OPEN,
        mockedShuffle,
      ),
    ).toEqual(expect.arrayContaining([lrps[0], lrps[1], lrps[2], lrps[4]]));
  });
});

describe('robAmountToSpend', () => {
  const mockUtxo = (ada: bigint, otherAssets: Assets = {}): UTxO => ({
    address: '',
    assets: addAssets(mkLovelacesOf(ada), otherAssets),
    outputIndex: 0,
    txHash: '',
  });

  test('1', () => {
    expect(
      robCollateralAmtToSpend(
        mockUtxo(100n, mkAssetsOf(collateralAssetA, 30n)).assets,
        {
          BuyIAssetOrder: {
            collateralAsset: collateralAssetA,
            maxPrice: { numerator: 13n, denominator: 10n },
          },
        },
      ),
    ).toEqual<bigint>(30n);
  });

  test('2', () => {
    expect(
      robCollateralAmtToSpend(mockUtxo(20_000_000n).assets, {
        BuyIAssetOrder: {
          collateralAsset: adaAssetClass,
          maxPrice: { numerator: 13n, denominator: 10n },
        },
      }),
    ).toEqual<bigint>(20_000_000n - MIN_ROB_COLLATERAL_AMT);
  });

  test('Less than min rob collateral', () => {
    expect(
      robCollateralAmtToSpend(mockUtxo(2_000_000n).assets, {
        BuyIAssetOrder: {
          collateralAsset: adaAssetClass,
          maxPrice: { numerator: 13n, denominator: 10n },
        },
      }),
    ).toEqual<bigint>(0n);
  });
});

describe('calculateTotalCollateralForRedemption', () => {
  const mockUtxo = (ada: bigint, otherAssets: Assets = {}): UTxO => ({
    address: '',
    assets: addAssets(mkLovelacesOf(ada), otherAssets),
    outputIndex: 0,
    txHash: '',
  });

  test('1', () => {
    const lrps: [UTxO, RobDatum][] = [
      [
        mockUtxo(100_000_000n),
        {
          iasset: fromHex(fromText('iUSD')),
          orderType: {
            BuyIAssetOrder: {
              collateralAsset: adaAssetClass,
              maxPrice: { numerator: 13n, denominator: 10n },
            },
          },
          owner: fromHex(''),
          robRefInput: { outputIndex: 0n, txHash: fromHex('') },
        },
      ],
      [
        mockUtxo(100_000_000n),
        {
          iasset: fromHex(fromText('iUSD')),
          orderType: {
            BuyIAssetOrder: {
              collateralAsset: adaAssetClass,
              maxPrice: { numerator: 13n, denominator: 10n },
            },
          },
          owner: fromHex(''),
          robRefInput: { outputIndex: 0n, txHash: fromHex('') },
        },
      ],
    ];

    expect(
      calculateTotalCollateralForRedemption(
        fromHex(fromText('iUSD')),
        adaAssetClass,
        { numerator: 1n, denominator: 1n },
        lrps,
        MAX_REDEMPTIONS_WITH_CDP_OPEN,
      ),
    ).toEqual<bigint>(200_000_000n - MIN_ROB_COLLATERAL_AMT * 2n);
  });

  test('2', () => {
    const lrps: [UTxO, RobDatum][] = [
      [
        mockUtxo(100_000_000n),
        {
          iasset: fromHex(fromText('iUSD')),
          orderType: {
            BuyIAssetOrder: {
              collateralAsset: adaAssetClass,
              maxPrice: { numerator: 13n, denominator: 10n },
            },
          },
          owner: fromHex(''),
          robRefInput: { outputIndex: 0n, txHash: fromHex('') },
        },
      ],
      [
        mockUtxo(1000n),
        {
          iasset: fromHex(fromText('iUSD')),
          orderType: {
            BuyIAssetOrder: {
              collateralAsset: collateralAssetA,
              maxPrice: { numerator: 13n, denominator: 10n },
            },
          },
          owner: fromHex(''),
          robRefInput: { outputIndex: 0n, txHash: fromHex('') },
        },
      ],
    ];

    expect(
      calculateTotalCollateralForRedemption(
        fromHex(fromText('iUSD')),
        adaAssetClass,
        { numerator: 1n, denominator: 1n },
        lrps,
        MAX_REDEMPTIONS_WITH_CDP_OPEN,
      ),
    ).toEqual<bigint>(100_000_000n - MIN_ROB_COLLATERAL_AMT);
  });

  test('filtering by assets 1', () => {
    const lrps: [UTxO, RobDatum][] = [
      [
        mockUtxo(100_000_000n),
        {
          iasset: fromHex(fromText('iUSD')),
          orderType: {
            BuyIAssetOrder: {
              collateralAsset: adaAssetClass,
              maxPrice: { numerator: 13n, denominator: 10n },
            },
          },
          owner: fromHex(''),
          robRefInput: { outputIndex: 0n, txHash: fromHex('') },
        },
      ],
      [
        mockUtxo(100_000_000n),
        {
          iasset: fromHex(fromText('iBTC')),
          orderType: {
            BuyIAssetOrder: {
              collateralAsset: adaAssetClass,
              maxPrice: { numerator: 13n, denominator: 10n },
            },
          },
          owner: fromHex(''),
          robRefInput: { outputIndex: 0n, txHash: fromHex('') },
        },
      ],
      [
        mockUtxo(100_000_000n),
        {
          iasset: fromHex(fromText('iETH')),
          orderType: {
            BuyIAssetOrder: {
              collateralAsset: adaAssetClass,
              maxPrice: { numerator: 13n, denominator: 10n },
            },
          },
          owner: fromHex(''),
          robRefInput: { outputIndex: 0n, txHash: fromHex('') },
        },
      ],
    ];

    expect(
      calculateTotalCollateralForRedemption(
        fromHex(fromText('iUSD')),
        adaAssetClass,
        { numerator: 1n, denominator: 1n },
        lrps,
        MAX_REDEMPTIONS_WITH_CDP_OPEN,
      ),
    ).toEqual<bigint>(100_000_000n - MIN_ROB_COLLATERAL_AMT);
  });

  test('filtering by price 1', () => {
    const lrps: [UTxO, RobDatum][] = [
      [
        mockUtxo(1000_000_000n),
        {
          iasset: fromHex(fromText('iUSD')),
          orderType: {
            BuyIAssetOrder: {
              collateralAsset: adaAssetClass,
              maxPrice: { numerator: 1n, denominator: 1n },
            },
          },
          owner: fromHex(''),
          robRefInput: { outputIndex: 0n, txHash: fromHex('') },
        },
      ],
      [
        mockUtxo(1000_000_000n),
        {
          iasset: fromHex(fromText('iUSD')),
          orderType: {
            BuyIAssetOrder: {
              collateralAsset: adaAssetClass,
              maxPrice: { numerator: 15n, denominator: 10n },
            },
          },
          owner: fromHex(''),
          robRefInput: { outputIndex: 0n, txHash: fromHex('') },
        },
      ],
      [
        mockUtxo(1000_000_000n),
        {
          iasset: fromHex(fromText('iUSD')),
          orderType: {
            BuyIAssetOrder: {
              collateralAsset: adaAssetClass,
              maxPrice: { numerator: 8n, denominator: 10n },
            },
          },
          owner: fromHex(''),
          robRefInput: { outputIndex: 0n, txHash: fromHex('') },
        },
      ],
    ];

    expect(
      calculateTotalCollateralForRedemption(
        fromHex(fromText('iUSD')),
        adaAssetClass,
        { numerator: 11n, denominator: 10n },
        lrps,
        MAX_REDEMPTIONS_WITH_CDP_OPEN,
      ),
    ).toEqual<bigint>(1000_000_000n - MIN_ROB_COLLATERAL_AMT);
  });

  test('capping by max redemptions 1', () => {
    const lrps: [UTxO, RobDatum][] = [
      [
        mockUtxo(100_000_000n),
        {
          iasset: fromHex(fromText('iUSD')),
          orderType: {
            BuyIAssetOrder: {
              collateralAsset: adaAssetClass,
              maxPrice: { numerator: 1n, denominator: 1n },
            },
          },
          owner: fromHex(''),
          robRefInput: { outputIndex: 0n, txHash: fromHex('') },
        },
      ],
      [
        mockUtxo(140_000_000n),
        {
          iasset: fromHex(fromText('iUSD')),
          orderType: {
            BuyIAssetOrder: {
              collateralAsset: adaAssetClass,
              maxPrice: { numerator: 1n, denominator: 1n },
            },
          },
          owner: fromHex(''),
          robRefInput: { outputIndex: 0n, txHash: fromHex('') },
        },
      ],
      [
        mockUtxo(160_000_000n),
        {
          iasset: fromHex(fromText('iUSD')),
          orderType: {
            BuyIAssetOrder: {
              collateralAsset: adaAssetClass,
              maxPrice: { numerator: 1n, denominator: 1n },
            },
          },
          owner: fromHex(''),
          robRefInput: { outputIndex: 0n, txHash: fromHex('') },
        },
      ],
      [
        mockUtxo(180_000_000n),
        {
          iasset: fromHex(fromText('iUSD')),
          orderType: {
            BuyIAssetOrder: {
              collateralAsset: adaAssetClass,
              maxPrice: { numerator: 1n, denominator: 1n },
            },
          },
          owner: fromHex(''),
          robRefInput: { outputIndex: 0n, txHash: fromHex('') },
        },
      ],
      [
        mockUtxo(200_000_000n),
        {
          iasset: fromHex(fromText('iUSD')),
          orderType: {
            BuyIAssetOrder: {
              collateralAsset: adaAssetClass,
              maxPrice: { numerator: 1n, denominator: 1n },
            },
          },
          owner: fromHex(''),
          robRefInput: { outputIndex: 0n, txHash: fromHex('') },
        },
      ],
    ];

    expect(MAX_REDEMPTIONS_WITH_CDP_OPEN).toBe(4);

    expect(
      calculateTotalCollateralForRedemption(
        fromHex(fromText('iUSD')),
        adaAssetClass,
        { numerator: 1n, denominator: 1n },
        lrps,
        MAX_REDEMPTIONS_WITH_CDP_OPEN,
      ),
      // I.e. the one with 1000n lovelaces is dropped
    ).toEqual<bigint>(680_000_000n - MIN_ROB_COLLATERAL_AMT * 4n);
  });
});

describe('LRP leverage', () => {
  beforeEach<MyContext>(async (context: MyContext) => {
    context.users = {
      admin: generateEmulatorAccount(
        addAssets(
          mkLovelacesOf(100_000_000_000_000n),
          mkAssetsOf(collateralAssetA, 100_000_000_000n),
        ),
      ),
      user: generateEmulatorAccount(addAssets(mkLovelacesOf(150_000_000n))),
    };

    context.emulator = new Emulator(
      [context.users.admin, context.users.user],
      MAINNET_PROTOCOL_PARAMETERS,
    );
    context.lucid = await Lucid(context.emulator, 'Custom');
  });

  test<MyContext>('Open 2x leveraged CDP; 1 LRP; price ~1.1; f_r=.01; f_m=.005', async (context: MyContext) => {
    context.lucid.selectWallet.fromSeed(context.users.admin.seedPhrase);

    const [sysParams, [iusdAssetInfo]] = await init(
      context.lucid,
      [
        {
          ...iusdInitialAssetCfg(),
          collateralAssets: [
            {
              ...mkBaseCollateralAsset(adaAssetClass, 0n, {
                numerator: 1_104_093n,
                denominator: 1_000_000n,
              }),
              maintenanceRatio: { numerator: 15n, denominator: 10n },
            },
          ],
          debtMintingFeeRatio: { numerator: 1n, denominator: 200n },
          redemptionReimbursementRatio: { numerator: 1n, denominator: 100n },
        },
      ],
      context.emulator.slot,
    );

    await openBuyRobs(
      context,
      sysParams,
      iusdAssetInfo.iassetTokenNameAscii,
      adaAssetClass,
      [100_000_000n],
      { numerator: 15n, denominator: 10n },
    );

    const allRobs = await findAllRobs(
      context.lucid,
      sysParams,
      iusdAssetInfo.iassetTokenNameAscii,
    );

    const orefs = await findAllNecessaryOrefs(
      context.lucid,
      sysParams,
      iusdAssetInfo.iassetTokenNameAscii,
      adaAssetClass,
    );

    const priceOracleUtxo = await findPriceOracleFromCollateralAsset(
      context.lucid,
      orefs.collateralAsset,
    );

    if (priceOracleUtxo == null) {
      throw new Error('Expected oracle UTXO');
    }

    const baseCollateral = 20_000_000n;

    await benchmarkAndAwaitTx(
      'Leverage - CDP open with 1 LRP',
      await leverageCdpWithRob(
        2,
        baseCollateral,
        priceOracleUtxo,
        orefs.iasset.utxo,
        orefs.collateralAsset.utxo,
        orefs.cdpCreatorUtxo,
        orefs.interestOracleUtxo,
        orefs.treasuryUtxo,
        sysParams,
        context.lucid,
        allRobs.map((lrps) => [lrps.utxo, lrps.datum]),
        context.emulator.slot,
      ),
      context.lucid,
      context.emulator,
    );

    const [pkh, skh] = await addrDetails(context.lucid);

    const res = await findCdp(
      context.lucid,
      sysParams.validatorHashes.cdpHash,
      fromSystemParamsAsset(sysParams.cdpParams.cdpAuthToken),
      pkh.hash,
      skh,
    );

    // Assert leverage
    assertValueInRange(
      Number(lovelacesAmt(res.utxo.assets)) / Number(baseCollateral),
      {
        min: 1.999,
        max: 2.0,
      },
    );

    // Assert collateral ratio
    assertValueInRange(
      cdpCollateralRatioPercentage(
        context.emulator.slot,
        parsePriceOracleDatum(getInlineDatumOrThrow(priceOracleUtxo)).price,
        res.utxo,
        res.datum,
        parseInterestOracleDatum(
          getInlineDatumOrThrow(orefs.interestOracleUtxo),
        ),
        context.lucid.config().network!,
      ),
      {
        min: 197,
        max: 197.001,
      },
    );
  });

  test<MyContext>('Pyth oracle - Open 2x leveraged CDP; 1 LRP; price ~1.1; f_r=.01; f_m=.005', async (context: MyContext) => {
    context.lucid.selectWallet.fromSeed(context.users.admin.seedPhrase);

    const [sysParams, [iusdAssetInfo]] = await init(
      context.lucid,
      [],
      context.emulator.slot,
      (pythStateNft: AssetClass) => [
        iusdInitialAssetCfgWithPyth(pythStateNft, (pythStatePolicyId) => [
          mkBaseCollateralAsset(
            adaAssetClass,
            0n,
            DEFAULT_PRICE,
            0n,
            pythStatePolicyId,
            {
              tag: 'value',
              val: { priceFeedId: 1 },
            },
          ),
        ]),
      ],
    );

    await openBuyRobs(
      context,
      sysParams,
      iusdAssetInfo.iassetTokenNameAscii,
      adaAssetClass,
      [100_000_000n],
      { numerator: 15n, denominator: 10n },
    );

    const allRobs = await findAllRobs(
      context.lucid,
      sysParams,
      iusdAssetInfo.iassetTokenNameAscii,
    );

    const orefs = await findAllNecessaryOrefs(
      context.lucid,
      sysParams,
      iusdAssetInfo.iassetTokenNameAscii,
      adaAssetClass,
    );

    const priceOracleUtxo = await findPriceOracleFromCollateralAsset(
      context.lucid,
      orefs.collateralAsset,
    );

    const currentTime = BigInt(
      slotToUnixTime(context.lucid.config().network!, context.emulator.slot),
    );

    const iUsdFeed: ParsedFeedPayload = {
      priceFeedId: 1,
      price: '1104093',
      exponent: -6,
    };
    const message = toHex(
      await createPythMessage([iUsdFeed], currentTime * 1_000n),
    );

    const pythStateOref = await context.lucid.utxoByUnit(
      assetClassToUnit(
        fromSystemParamsAsset(sysParams.pythConfig.pythStateAssetClass),
      ),
    );

    const [price, _] = await retrieveAdjustedPrice(
      orefs.iasset.datum.assetName,
      orefs.collateralAsset.datum.collateralAsset,
      orefs.collateralAsset.datum.priceInfo,
      orefs.collateralAsset.datum.extraDecimals,
      priceOracleUtxo,
      message,
      sysParams.pythConfig,
      context.lucid,
    );

    const baseCollateral = 20_000_000n;

    await benchmarkAndAwaitTx(
      'Leverage - CDP open with 1 LRP using Pyth oracle',
      await leverageCdpWithRob(
        2,
        baseCollateral,
        priceOracleUtxo,
        orefs.iasset.utxo,
        orefs.collateralAsset.utxo,
        orefs.cdpCreatorUtxo,
        orefs.interestOracleUtxo,
        orefs.treasuryUtxo,
        sysParams,
        context.lucid,
        allRobs.map((lrps) => [lrps.utxo, lrps.datum]),
        context.emulator.slot,
        message,
        pythStateOref,
      ),
      context.lucid,
      context.emulator,
    );

    const [pkh, skh] = await addrDetails(context.lucid);

    const res = await findCdp(
      context.lucid,
      sysParams.validatorHashes.cdpHash,
      fromSystemParamsAsset(sysParams.cdpParams.cdpAuthToken),
      pkh.hash,
      skh,
    );

    // Assert leverage
    assertValueInRange(
      Number(lovelacesAmt(res.utxo.assets)) / Number(baseCollateral),
      {
        min: 1.999,
        max: 2.0,
      },
    );

    // Assert collateral ratio
    assertValueInRange(
      cdpCollateralRatioPercentage(
        context.emulator.slot,
        price,
        res.utxo,
        res.datum,
        parseInterestOracleDatum(
          getInlineDatumOrThrow(orefs.interestOracleUtxo),
        ),
        context.lucid.config().network!,
      ),
      {
        min: 197,
        max: 197.001,
      },
    );
  });

  test<MyContext>('Open 2x leveraged CDP; 4 LRPs; price ~0.9; f_r=.01; f_m=.005', async (context: MyContext) => {
    context.lucid.selectWallet.fromSeed(context.users.admin.seedPhrase);

    const [sysParams, [iusdAssetInfo]] = await init(
      context.lucid,
      [
        {
          ...iusdInitialAssetCfg(),
          collateralAssets: [
            {
              ...mkBaseCollateralAsset(adaAssetClass, 0n, {
                numerator: 904_093n,
                denominator: 1_000_000n,
              }),
              maintenanceRatio: { numerator: 15n, denominator: 10n },
            },
          ],
          debtMintingFeeRatio: { numerator: 1n, denominator: 200n },
          redemptionReimbursementRatio: { numerator: 1n, denominator: 100n },
        },
      ],
      context.emulator.slot,
    );

    await openBuyRobs(
      context,
      sysParams,
      iusdAssetInfo.iassetTokenNameAscii,
      adaAssetClass,
      [26_250_000n, 26_250_000n, 26_250_000n, 26_250_000n],
      { numerator: 15n, denominator: 10n },
    );

    const allRobs = await findAllRobs(
      context.lucid,
      sysParams,
      iusdAssetInfo.iassetTokenNameAscii,
    );

    const orefs = await findAllNecessaryOrefs(
      context.lucid,
      sysParams,
      iusdAssetInfo.iassetTokenNameAscii,
      adaAssetClass,
    );

    const priceOracleUtxo = await findPriceOracleFromCollateralAsset(
      context.lucid,
      orefs.collateralAsset,
    );

    if (priceOracleUtxo == null) {
      throw new Error('Expected oracle UTXO');
    }

    const baseCollateral = 100_000_000n;
    await runAndAwaitTx(
      context.lucid,
      leverageCdpWithRob(
        2,
        baseCollateral,
        priceOracleUtxo,
        orefs.iasset.utxo,
        orefs.collateralAsset.utxo,
        orefs.cdpCreatorUtxo,
        orefs.interestOracleUtxo,
        orefs.treasuryUtxo,
        sysParams,
        context.lucid,
        allRobs.map((lrps) => [lrps.utxo, lrps.datum]),
        context.emulator.slot,
      ),
    );

    const [pkh, skh] = await addrDetails(context.lucid);

    const res = await findCdp(
      context.lucid,
      sysParams.validatorHashes.cdpHash,
      fromSystemParamsAsset(sysParams.cdpParams.cdpAuthToken),
      pkh.hash,
      skh,
    );

    // Assert leverage
    assertValueInRange(
      Number(lovelacesAmt(res.utxo.assets)) / Number(baseCollateral),
      {
        min: 1.999,
        max: 2.0,
      },
    );

    // Assert collateral ratio
    assertValueInRange(
      cdpCollateralRatioPercentage(
        context.emulator.slot,
        parsePriceOracleDatum(getInlineDatumOrThrow(priceOracleUtxo)).price,
        res.utxo,
        res.datum,
        parseInterestOracleDatum(
          getInlineDatumOrThrow(orefs.interestOracleUtxo),
        ),
        context.lucid.config().network!,
      ),
      {
        min: 197,
        max: 197.001,
      },
    );

    {
      const lrps = await findAllRobs(
        context.lucid,
        sysParams,
        iusdAssetInfo.iassetTokenNameAscii,
      );
      expect(
        lrps.every((lrp) => hadRobRedemption(lrp, sysParams)),
      ).toBeTruthy();
    }
  });

  test<MyContext>('Open 2.3x leveraged CDP; 4 LRPs; price ~1.03; f_r=.01; f_m=.013', async (context: MyContext) => {
    context.lucid.selectWallet.fromSeed(context.users.admin.seedPhrase);

    const [sysParams, [iusdAssetInfo]] = await init(
      context.lucid,
      [
        {
          ...iusdInitialAssetCfg(),
          collateralAssets: [
            {
              ...mkBaseCollateralAsset(adaAssetClass, 0n, {
                numerator: 1_037_093n,
                denominator: 1_000_000n,
              }),
              maintenanceRatio: { numerator: 15n, denominator: 10n },
            },
          ],
          debtMintingFeeRatio: { numerator: 13n, denominator: 1_000n },
          redemptionReimbursementRatio: { numerator: 1n, denominator: 100n },
        },
      ],
      context.emulator.slot,
    );

    await openBuyRobs(
      context,
      sysParams,
      iusdAssetInfo.iassetTokenNameAscii,
      adaAssetClass,
      [35_139_729n, 35_000_397n, 35_001_079n, 35_107_049n],
      { numerator: 15n, denominator: 10n },
    );

    const allLrps = await findAllRobs(
      context.lucid,
      sysParams,
      iusdAssetInfo.iassetTokenNameAscii,
    );

    const orefs = await findAllNecessaryOrefs(
      context.lucid,
      sysParams,
      iusdAssetInfo.iassetTokenNameAscii,
      adaAssetClass,
    );

    const priceOracleUtxo = await findPriceOracleFromCollateralAsset(
      context.lucid,
      orefs.collateralAsset,
    );

    if (priceOracleUtxo == null) {
      throw new Error('Expected oracle UTXO');
    }

    const baseCollateral = 100_000_000n;
    await runAndAwaitTx(
      context.lucid,
      leverageCdpWithRob(
        2.3,
        baseCollateral,
        priceOracleUtxo,
        orefs.iasset.utxo,
        orefs.collateralAsset.utxo,
        orefs.cdpCreatorUtxo,
        orefs.interestOracleUtxo,
        orefs.treasuryUtxo,
        sysParams,
        context.lucid,
        allLrps.map((lrps) => [lrps.utxo, lrps.datum]),
        context.emulator.slot,
      ),
    );

    const [pkh, skh] = await addrDetails(context.lucid);

    const res = await findCdp(
      context.lucid,
      sysParams.validatorHashes.cdpHash,
      fromSystemParamsAsset(sysParams.cdpParams.cdpAuthToken),
      pkh.hash,
      skh,
    );

    // Assert leverage
    assertValueInRange(
      Number(lovelacesAmt(res.utxo.assets)) / Number(baseCollateral),
      {
        min: 2.29999,
        max: 2.3,
      },
    );

    // Assert collateral ratio
    assertValueInRange(
      cdpCollateralRatioPercentage(
        context.emulator.slot,
        parsePriceOracleDatum(getInlineDatumOrThrow(priceOracleUtxo)).price,
        res.utxo,
        res.datum,
        parseInterestOracleDatum(
          getInlineDatumOrThrow(orefs.interestOracleUtxo),
        ),
        context.lucid.config().network!,
      ),
      {
        min: 172,
        max: 172.1,
      },
    );

    {
      const lrps = await findAllRobs(
        context.lucid,
        sysParams,
        iusdAssetInfo.iassetTokenNameAscii,
      );
      expect(
        lrps.every((lrp) => hadRobRedemption(lrp, sysParams)),
      ).toBeTruthy();
    }
  });

  test<MyContext>('Open 1.2x leveraged CDP 3 LRPs price ~1.46; f_r=.02; f_m=.007', async (context: MyContext) => {
    context.lucid.selectWallet.fromSeed(context.users.admin.seedPhrase);

    const [sysParams, [iusdAssetInfo]] = await init(
      context.lucid,
      [
        {
          ...iusdInitialAssetCfg(),
          collateralAssets: [
            {
              ...mkBaseCollateralAsset(adaAssetClass, 0n, {
                numerator: 1_461_093n,
                denominator: 1_000_000n,
              }),
              maintenanceRatio: { numerator: 15n, denominator: 10n },
            },
          ],
          debtMintingFeeRatio: { numerator: 7n, denominator: 1_000n },
          redemptionReimbursementRatio: { numerator: 2n, denominator: 100n },
        },
      ],
      context.emulator.slot,
    );

    await openBuyRobs(
      context,
      sysParams,
      iusdAssetInfo.iassetTokenNameAscii,
      adaAssetClass,
      [75_000_000n, 75_000_000n, 75_000_000n],
      { numerator: 15n, denominator: 10n },
    );

    const allLrps = await findAllRobs(
      context.lucid,
      sysParams,
      iusdAssetInfo.iassetTokenNameAscii,
    );

    const orefs = await findAllNecessaryOrefs(
      context.lucid,
      sysParams,
      iusdAssetInfo.iassetTokenNameAscii,
      adaAssetClass,
    );

    const priceOracleUtxo = await findPriceOracleFromCollateralAsset(
      context.lucid,
      orefs.collateralAsset,
    );

    if (priceOracleUtxo == null) {
      throw new Error('Expected oracle UTXO');
    }

    const baseCollateral = 1_000_000_000n;
    await runAndAwaitTx(
      context.lucid,
      leverageCdpWithRob(
        1.2,
        baseCollateral,
        priceOracleUtxo,
        orefs.iasset.utxo,
        orefs.collateralAsset.utxo,
        orefs.cdpCreatorUtxo,
        orefs.interestOracleUtxo,
        orefs.treasuryUtxo,
        sysParams,
        context.lucid,
        allLrps.map((lrps) => [lrps.utxo, lrps.datum]),
        context.emulator.slot,
      ),
    );

    const [pkh, skh] = await addrDetails(context.lucid);

    const res = await findCdp(
      context.lucid,
      sysParams.validatorHashes.cdpHash,
      fromSystemParamsAsset(sysParams.cdpParams.cdpAuthToken),
      pkh.hash,
      skh,
    );

    // Assert leverage
    assertValueInRange(
      Number(lovelacesAmt(res.utxo.assets)) / Number(baseCollateral),
      {
        min: 1.19999,
        max: 1.2,
      },
    );

    // Assert collateral ratio
    assertValueInRange(
      cdpCollateralRatioPercentage(
        context.emulator.slot,
        parsePriceOracleDatum(getInlineDatumOrThrow(priceOracleUtxo)).price,
        res.utxo,
        res.datum,
        parseInterestOracleDatum(
          getInlineDatumOrThrow(orefs.interestOracleUtxo),
        ),
        context.lucid.config().network!,
      ),
      {
        min: 583,
        max: 583.1,
      },
    );

    {
      const lrps = await findAllRobs(
        context.lucid,
        sysParams,
        iusdAssetInfo.iassetTokenNameAscii,
      );
      expect(
        lrps.every((lrp) => hadRobRedemption(lrp, sysParams)),
      ).toBeTruthy();
    }
  });

  test<MyContext>('Open 2.1x leveraged CDP 3 LRPs price ~0.0589; f_r=.02; f_m=.007', async (context: MyContext) => {
    context.lucid.selectWallet.fromSeed(context.users.admin.seedPhrase);

    const [sysParams, [iusdAssetInfo]] = await init(
      context.lucid,
      [
        {
          ...iusdInitialAssetCfg(),
          collateralAssets: [
            {
              ...mkBaseCollateralAsset(adaAssetClass, 0n, {
                numerator: 58_900n,
                denominator: 1_000_000n,
              }),
              maintenanceRatio: { numerator: 15n, denominator: 10n },
            },
          ],
          debtMintingFeeRatio: { numerator: 7n, denominator: 1_000n },
          redemptionReimbursementRatio: { numerator: 2n, denominator: 100n },
        },
      ],
      context.emulator.slot,
    );

    await openBuyRobs(
      context,
      sysParams,
      iusdAssetInfo.iassetTokenNameAscii,
      adaAssetClass,
      [400_000_000n, 400_000_000n, 400_000_000n],
      { numerator: 15n, denominator: 10n },
    );

    const allLrps = await findAllRobs(
      context.lucid,
      sysParams,
      iusdAssetInfo.iassetTokenNameAscii,
    );

    const orefs = await findAllNecessaryOrefs(
      context.lucid,
      sysParams,
      iusdAssetInfo.iassetTokenNameAscii,
      adaAssetClass,
    );

    const priceOracleUtxo = await findPriceOracleFromCollateralAsset(
      context.lucid,
      orefs.collateralAsset,
    );

    if (priceOracleUtxo == null) {
      throw new Error('Expected oracle UTXO');
    }

    const baseCollateral = 1_000_000_000n;
    await runAndAwaitTx(
      context.lucid,
      leverageCdpWithRob(
        2.1,
        baseCollateral,
        priceOracleUtxo,
        orefs.iasset.utxo,
        orefs.collateralAsset.utxo,
        orefs.cdpCreatorUtxo,
        orefs.interestOracleUtxo,
        orefs.treasuryUtxo,
        sysParams,
        context.lucid,
        allLrps.map((lrps) => [lrps.utxo, lrps.datum]),
        context.emulator.slot,
      ),
    );

    const [pkh, skh] = await addrDetails(context.lucid);

    const res = await findCdp(
      context.lucid,
      sysParams.validatorHashes.cdpHash,
      fromSystemParamsAsset(sysParams.cdpParams.cdpAuthToken),
      pkh.hash,
      skh,
    );

    // Assert leverage
    assertValueInRange(
      Number(lovelacesAmt(res.utxo.assets)) / Number(baseCollateral),
      {
        min: 2.099999,
        max: 2.1,
      },
    );

    // Assert collateral ratio
    assertValueInRange(
      cdpCollateralRatioPercentage(
        context.emulator.slot,
        parsePriceOracleDatum(getInlineDatumOrThrow(priceOracleUtxo)).price,
        res.utxo,
        res.datum,
        parseInterestOracleDatum(
          getInlineDatumOrThrow(orefs.interestOracleUtxo),
        ),
        context.lucid.config().network!,
      ),
      {
        min: 185,
        max: 185.1,
      },
    );

    {
      const lrps = await findAllRobs(
        context.lucid,
        sysParams,
        iusdAssetInfo.iassetTokenNameAscii,
      );
      expect(
        lrps.every((lrp) => hadRobRedemption(lrp, sysParams)),
      ).toBeTruthy();
    }
  });

  test<MyContext>('Open 2.5x leveraged CDP 4 LRPs price ~103.973621; f_r=.01; f_m=.03', async (context: MyContext) => {
    context.lucid.selectWallet.fromSeed(context.users.admin.seedPhrase);

    const [sysParams, [iusdAssetInfo]] = await init(
      context.lucid,
      [
        {
          ...iusdInitialAssetCfg(),
          collateralAssets: [
            {
              ...mkBaseCollateralAsset(adaAssetClass, 0n, {
                numerator: 103_973_621n,
                denominator: 1_000_000n,
              }),
              maintenanceRatio: { numerator: 15n, denominator: 10n },
            },
          ],
          debtMintingFeeRatio: { numerator: 3n, denominator: 100n },
          redemptionReimbursementRatio: { numerator: 1n, denominator: 100n },
        },
      ],
      context.emulator.slot,
    );

    await openBuyRobs(
      context,
      sysParams,
      iusdAssetInfo.iassetTokenNameAscii,
      adaAssetClass,
      [400_000_000n, 400_000_000n, 400_000_000n, 400_000_000n],
      { numerator: 150n, denominator: 1n },
    );

    const allLrps = await findAllRobs(
      context.lucid,
      sysParams,
      iusdAssetInfo.iassetTokenNameAscii,
    );

    const orefs = await findAllNecessaryOrefs(
      context.lucid,
      sysParams,
      iusdAssetInfo.iassetTokenNameAscii,
      adaAssetClass,
    );

    const priceOracleUtxo = await findPriceOracleFromCollateralAsset(
      context.lucid,
      orefs.collateralAsset,
    );

    if (priceOracleUtxo == null) {
      throw new Error('Expected oracle UTXO');
    }

    const baseCollateral = 1_000_000_000n;
    await runAndAwaitTx(
      context.lucid,
      leverageCdpWithRob(
        2.5,
        baseCollateral,
        priceOracleUtxo,
        orefs.iasset.utxo,
        orefs.collateralAsset.utxo,
        orefs.cdpCreatorUtxo,
        orefs.interestOracleUtxo,
        orefs.treasuryUtxo,
        sysParams,
        context.lucid,
        allLrps.map((lrps) => [lrps.utxo, lrps.datum]),
        context.emulator.slot,
      ),
    );

    const [pkh, skh] = await addrDetails(context.lucid);

    const res = await findCdp(
      context.lucid,
      sysParams.validatorHashes.cdpHash,
      fromSystemParamsAsset(sysParams.cdpParams.cdpAuthToken),
      pkh.hash,
      skh,
    );

    // Assert leverage
    assertValueInRange(
      Number(lovelacesAmt(res.utxo.assets)) / Number(baseCollateral),
      {
        min: 2.4999,
        max: 2.5,
      },
    );

    // Assert collateral ratio
    assertValueInRange(
      cdpCollateralRatioPercentage(
        context.emulator.slot,
        parsePriceOracleDatum(getInlineDatumOrThrow(priceOracleUtxo)).price,
        res.utxo,
        res.datum,
        parseInterestOracleDatum(
          getInlineDatumOrThrow(orefs.interestOracleUtxo),
        ),
        context.lucid.config().network!,
      ),
      {
        min: 160,
        max: 160.1,
      },
    );

    {
      const lrps = await findAllRobs(
        context.lucid,
        sysParams,
        iusdAssetInfo.iassetTokenNameAscii,
      );
      expect(
        lrps.every((lrp) => hadRobRedemption(lrp, sysParams)),
      ).toBeTruthy();
    }
  });

  test<MyContext>('(non ADA collateral) Open 1.75x leveraged CDP 3 LRPs price ~2.397; f_r=.017; f_m=.0391', async (context: MyContext) => {
    context.lucid.selectWallet.fromSeed(context.users.admin.seedPhrase);

    const [sysParams, [iusdAssetInfo]] = await init(
      context.lucid,
      [
        {
          ...iusdInitialAssetCfg(),
          collateralAssets: [
            {
              ...mkBaseCollateralAsset(adaAssetClass, 0n, {
                numerator: 1_000_000n,
                denominator: 1_000_000n,
              }),
              maintenanceRatio: { numerator: 15n, denominator: 10n },
            },
            {
              ...mkBaseCollateralAsset(collateralAssetA, 0n, {
                numerator: 2_397_000n,
                denominator: 1_000_000n,
              }),
              maintenanceRatio: { numerator: 15n, denominator: 10n },
            },
          ],
          debtMintingFeeRatio: { numerator: 391n, denominator: 10_000n },
          redemptionReimbursementRatio: { numerator: 17n, denominator: 1000n },
        },
      ],
      context.emulator.slot,
    );

    await openBuyRobs(
      context,
      sysParams,
      iusdAssetInfo.iassetTokenNameAscii,
      adaAssetClass,
      [400_000_000n, 400_000_000n],
      { numerator: 5n, denominator: 1n },
    );
    await openBuyRobs(
      context,
      sysParams,
      iusdAssetInfo.iassetTokenNameAscii,
      collateralAssetA,
      [450_000_000n, 450_000_000n],
      { numerator: 5n, denominator: 1n },
    );

    const allLrps = await findAllRobs(
      context.lucid,
      sysParams,
      iusdAssetInfo.iassetTokenNameAscii,
    );

    const orefs = await findAllNecessaryOrefs(
      context.lucid,
      sysParams,
      iusdAssetInfo.iassetTokenNameAscii,
      collateralAssetA,
    );

    const priceOracleUtxo = await findPriceOracleFromCollateralAsset(
      context.lucid,
      orefs.collateralAsset,
    );

    if (priceOracleUtxo == null) {
      throw new Error('Expected oracle UTXO');
    }

    const baseCollateral = 1_000_000_000n;
    await runAndAwaitTx(
      context.lucid,
      leverageCdpWithRob(
        1.75,
        baseCollateral,
        priceOracleUtxo,
        orefs.iasset.utxo,
        orefs.collateralAsset.utxo,
        orefs.cdpCreatorUtxo,
        orefs.interestOracleUtxo,
        orefs.treasuryUtxo,
        sysParams,
        context.lucid,
        allLrps.map((lrps) => [lrps.utxo, lrps.datum]),
        context.emulator.slot,
      ),
    );

    const [pkh, skh] = await addrDetails(context.lucid);

    const res = await findCdp(
      context.lucid,
      sysParams.validatorHashes.cdpHash,
      fromSystemParamsAsset(sysParams.cdpParams.cdpAuthToken),
      pkh.hash,
      skh,
    );

    // Assert leverage
    assertValueInRange(
      Number(assetClassValueOf(res.utxo.assets, collateralAssetA)) /
        Number(baseCollateral),
      {
        min: 1.749999,
        max: 1.75,
      },
    );

    // Assert collateral ratio
    assertValueInRange(
      cdpCollateralRatioPercentage(
        context.emulator.slot,
        parsePriceOracleDatum(getInlineDatumOrThrow(priceOracleUtxo)).price,
        res.utxo,
        res.datum,
        parseInterestOracleDatum(
          getInlineDatumOrThrow(orefs.interestOracleUtxo),
        ),
        context.lucid.config().network!,
      ),
      {
        min: 220,
        max: 220.1,
      },
    );

    {
      const lrps = await findAllRobs(
        context.lucid,
        sysParams,
        iusdAssetInfo.iassetTokenNameAscii,
      );
      // All with collateral asset A had redemptions
      expect(
        lrps
          .filter((lrp) =>
            match(lrp.datum.orderType)
              .returnType<boolean>()
              .with({ BuyIAssetOrder: P.select() }, (content) =>
                isSameAssetClass(content.collateralAsset, collateralAssetA),
              )
              .otherwise(() => false),
          )
          .every((lrp) => hadRobRedemption(lrp, sysParams)),
      ).toBeTruthy();
    }
  });

  test<MyContext>('Open max leverage leveraged CDP; 4 CDPs; price 1; f_r=.01; f_m=.005', async (context: MyContext) => {
    context.lucid.selectWallet.fromSeed(context.users.admin.seedPhrase);

    const [sysParams, [iusdAssetInfo]] = await init(
      context.lucid,
      [
        {
          ...iusdInitialAssetCfg(),
          collateralAssets: [
            {
              ...mkBaseCollateralAsset(adaAssetClass, 0n, {
                numerator: 1n,
                denominator: 1n,
              }),
              maintenanceRatio: { numerator: 15n, denominator: 10n },
            },
          ],
          debtMintingFeeRatio: { numerator: 5n, denominator: 1_000n },
          redemptionReimbursementRatio: { numerator: 1n, denominator: 100n },
        },
      ],
      context.emulator.slot,
    );

    await openBuyRobs(
      context,
      sysParams,
      iusdAssetInfo.iassetTokenNameAscii,
      adaAssetClass,
      [500_000_000n, 500_000_000n, 500_000_000n, 500_000_000n],
      { numerator: 15n, denominator: 10n },
    );

    const allLrps = await findAllRobs(
      context.lucid,
      sysParams,
      iusdAssetInfo.iassetTokenNameAscii,
    );

    const orefs = await findAllNecessaryOrefs(
      context.lucid,
      sysParams,
      iusdAssetInfo.iassetTokenNameAscii,
      adaAssetClass,
    );

    const priceOracleUtxo = await findPriceOracleFromCollateralAsset(
      context.lucid,
      orefs.collateralAsset,
    );

    if (priceOracleUtxo == null) {
      throw new Error('Expected oracle UTXO');
    }

    const baseCollateral = 1_000_000_000n;
    const maxLeverage = calculateLeverageFromCollateralRatio(
      fromHex(fromText(iusdAssetInfo.iassetTokenNameAscii)),
      adaAssetClass,
      orefs.collateralAsset.datum.maintenanceRatio,
      baseCollateral,
      parsePriceOracleDatum(getInlineDatumOrThrow(priceOracleUtxo)).price,
      orefs.iasset.datum.debtMintingFeeRatio,
      orefs.iasset.datum.redemptionProcessingFeeRatio,
      allLrps.map((lrps) => [lrps.utxo, lrps.datum]),
    )!;

    await benchmarkAndAwaitTx(
      'Leverage - CDP open with 4 LRP',
      await leverageCdpWithRob(
        maxLeverage,
        baseCollateral,
        priceOracleUtxo,
        orefs.iasset.utxo,
        orefs.collateralAsset.utxo,
        orefs.cdpCreatorUtxo,
        orefs.interestOracleUtxo,
        orefs.treasuryUtxo,
        sysParams,
        context.lucid,
        allLrps.map((lrps) => [lrps.utxo, lrps.datum]),
        context.emulator.slot,
      ),
      context.lucid,
      context.emulator,
    );

    const [pkh, skh] = await addrDetails(context.lucid);

    const res = await findCdp(
      context.lucid,
      sysParams.validatorHashes.cdpHash,
      fromSystemParamsAsset(sysParams.cdpParams.cdpAuthToken),
      pkh.hash,
      skh,
    );

    // Assert leverage
    assertValueInRange(
      Number(lovelacesAmt(res.utxo.assets)) / Number(baseCollateral),
      {
        min: 2.9126,
        max: 2.9127,
      },
    );

    // Assert collateral ratio
    assertValueInRange(
      cdpCollateralRatioPercentage(
        context.emulator.slot,
        parsePriceOracleDatum(getInlineDatumOrThrow(priceOracleUtxo)).price,
        res.utxo,
        res.datum,
        parseInterestOracleDatum(
          getInlineDatumOrThrow(orefs.interestOracleUtxo),
        ),
        context.lucid.config().network!,
      ),
      {
        min:
          rationalToFloat(orefs.collateralAsset.datum.maintenanceRatio) * 100,
        max:
          rationalToFloat(orefs.collateralAsset.datum.maintenanceRatio) * 100 +
          1,
      },
    );

    {
      const lrps = await findAllRobs(
        context.lucid,
        sysParams,
        iusdAssetInfo.iassetTokenNameAscii,
      );
      expect(
        lrps.every((lrp) => hadRobRedemption(lrp, sysParams)),
      ).toBeTruthy();
    }
  });

  test<MyContext>('Open max leverage leveraged CDP; 2 CDPs; price 2.5; f_r=.014; f_m=.006', async (context: MyContext) => {
    context.lucid.selectWallet.fromSeed(context.users.admin.seedPhrase);

    const [sysParams, [iusdAssetInfo]] = await init(
      context.lucid,
      [
        {
          ...iusdInitialAssetCfg(),
          collateralAssets: [
            {
              ...mkBaseCollateralAsset(adaAssetClass, 0n, {
                numerator: 25n,
                denominator: 10n,
              }),
              maintenanceRatio: { numerator: 13n, denominator: 10n },
            },
          ],
          debtMintingFeeRatio: { numerator: 6n, denominator: 1_000n },
          redemptionReimbursementRatio: { numerator: 14n, denominator: 1000n },
        },
      ],
      context.emulator.slot,
    );

    await openBuyRobs(
      context,
      sysParams,
      iusdAssetInfo.iassetTokenNameAscii,
      adaAssetClass,
      [325_000_000n, 325_000_000n],
      { numerator: 3n, denominator: 1n },
    );

    const allLrps = await findAllRobs(
      context.lucid,
      sysParams,
      iusdAssetInfo.iassetTokenNameAscii,
    );

    const orefs = await findAllNecessaryOrefs(
      context.lucid,
      sysParams,
      iusdAssetInfo.iassetTokenNameAscii,
      adaAssetClass,
    );

    const priceOracleUtxo = await findPriceOracleFromCollateralAsset(
      context.lucid,
      orefs.collateralAsset,
    );

    if (priceOracleUtxo == null) {
      throw new Error('Expected oracle UTXO');
    }

    const baseCollateral = 200_000_000n;
    const maxLeverage = calculateLeverageFromCollateralRatio(
      fromHex(fromText(iusdAssetInfo.iassetTokenNameAscii)),
      adaAssetClass,
      orefs.collateralAsset.datum.maintenanceRatio,
      baseCollateral,
      parsePriceOracleDatum(getInlineDatumOrThrow(priceOracleUtxo)).price,
      orefs.iasset.datum.debtMintingFeeRatio,
      orefs.iasset.datum.redemptionReimbursementRatio,
      allLrps.map((lrps) => [lrps.utxo, lrps.datum]),
    )!;

    await runAndAwaitTx(
      context.lucid,
      leverageCdpWithRob(
        maxLeverage,
        baseCollateral,
        priceOracleUtxo,
        orefs.iasset.utxo,
        orefs.collateralAsset.utxo,
        orefs.cdpCreatorUtxo,
        orefs.interestOracleUtxo,
        orefs.treasuryUtxo,
        sysParams,
        context.lucid,
        allLrps.map((lrps) => [lrps.utxo, lrps.datum]),
        context.emulator.slot,
      ),
    );

    const [pkh, skh] = await addrDetails(context.lucid);

    const res = await findCdp(
      context.lucid,
      sysParams.validatorHashes.cdpHash,
      fromSystemParamsAsset(sysParams.cdpParams.cdpAuthToken),
      pkh.hash,
      skh,
    );

    // Assert leverage
    assertValueInRange(
      Number(lovelacesAmt(res.utxo.assets)) / Number(baseCollateral),
      {
        min: 4.0625,
        max: 4.06251,
      },
    );

    // Assert collateral ratio
    assertValueInRange(
      cdpCollateralRatioPercentage(
        context.emulator.slot,
        parsePriceOracleDatum(getInlineDatumOrThrow(priceOracleUtxo)).price,
        res.utxo,
        res.datum,
        parseInterestOracleDatum(
          getInlineDatumOrThrow(orefs.interestOracleUtxo),
        ),
        context.lucid.config().network!,
      ),
      {
        min:
          rationalToFloat(orefs.collateralAsset.datum.maintenanceRatio) * 100,
        max:
          rationalToFloat(orefs.collateralAsset.datum.maintenanceRatio) * 100 +
          1,
      },
    );

    {
      const lrps = await findAllRobs(
        context.lucid,
        sysParams,
        iusdAssetInfo.iassetTokenNameAscii,
      );
      expect(
        lrps.every((lrp) => hadRobRedemption(lrp, sysParams)),
      ).toBeTruthy();
    }
  });
});
