Repository: input-output-hk/cardano-js-sdk
Pull request #1621: feat(input-selection): add large first input selection strategy
Author: login: AngelCastilloB
Created: 2025-05-12T08:17:43Z | Merged: 2025-05-14T08:05:27Z

# Context

Some users with complex wallets that have hundreds (or even thousand) of UTxOs with small amounts can make our current input selection strategy fail due to selecting more inputs than max allowed, we need alternative input selection strategies for such wallets

# Proposed Solution

This PR introduces large first input selection, which reduces the number of outputs needed per transaction.




Diff:
diff --git a/packages/input-selection/src/GreedySelection/GreedyInputSelector.ts b/packages/input-selection/src/GreedySelection/GreedyInputSelector.ts
index cfefca50779..373d3f735b3 100644
--- a/packages/input-selection/src/GreedySelection/GreedyInputSelector.ts
+++ b/packages/input-selection/src/GreedySelection/GreedyInputSelector.ts
@@ -1,17 +1,16 @@
 /* eslint-disable max-params */
 import { Cardano, coalesceValueQuantities } from '@cardano-sdk/core';
 import { InputSelectionError, InputSelectionFailure } from '../InputSelectionError';
-import { InputSelectionParameters, InputSelector, SelectionConstraints, SelectionResult } from '../types';
+import { InputSelectionParameters, InputSelector, SelectionResult } from '../types';
 import {
   addTokenMaps,
   getCoinQuantity,
   hasNegativeAssetValue,
-  sortByCoins,
-  stubMaxSizeAddress,
+  sortUtxoByTxIn,
   subtractTokenMaps,
   toValues
 } from '../util';
-import { sortUtxoByTxIn, splitChange } from './util';
+import { splitChangeAndComputeFee } from './util';
 
 /** Greedy selection initialization properties. */
 export interface GreedySelectorProps {
@@ -30,146 +29,6 @@ export interface GreedySelectorProps {
   getChangeAddresses: () => Promise<Map<Cardano.PaymentAddress, number>>;
 }
 
-/**
- * Given a set of input and outputs, compute the fee. Then extract the fee from the change output
- * with the highest value.
- *
- * @param changeLovelace The available amount of lovelace to be used as change.
- * @param constraints The selection constraints.
- * @param inputs The inputs of the transaction.
- * @param outputs The outputs of the transaction.
- * @param changeOutputs The list of change outputs.
- * @param currentFee The current computed fee for this selection.
- */
-const adjustOutputsForFee = async (
-  changeLovelace: bigint,
-  constraints: SelectionConstraints,
-  inputs: Set<Cardano.Utxo>,
-  outputs: Set<Cardano.TxOut>,
-  changeOutputs: Array<Cardano.TxOut>,
-  currentFee: bigint
-): Promise<{
-  fee: bigint;
-  change: Array<Cardano.TxOut>;
-  feeAccountedFor: boolean;
-  redeemers?: Array<Cardano.Redeemer>;
-}> => {
-  const totalOutputs = new Set([...outputs, ...changeOutputs]);
-  const { fee, redeemers } = await constraints.computeMinimumCost({
-    change: [],
-    fee: currentFee,
-    inputs,
-    outputs: totalOutputs
-  });
-
-  if (fee === changeLovelace) return { change: [], fee, feeAccountedFor: true, redeemers };
-
-  if (changeLovelace < fee) throw new InputSelectionError(InputSelectionFailure.UtxoBalanceInsufficient);
-
-  const updatedOutputs = [...changeOutputs];
-
-  updatedOutputs.sort(sortByCoins);
-
-  let feeAccountedFor = false;
-  for (const output of updatedOutputs) {
-    const adjustedCoins = output.value.coins - fee;
-
-    if (adjustedCoins >= constraints.computeMinimumCoinQuantity(output)) {
-      output.value.coins = adjustedCoins;
-      feeAccountedFor = true;
-      break;
-    }
-  }
-
-  return { change: [...updatedOutputs], fee, feeAccountedFor, redeemers };
-};
-
-/**
- * Recursively compute the fee and compute change outputs until it finds a set of change outputs that satisfies the fee.
- *
- * @param inputs The inputs of the transaction.
- * @param outputs The outputs of the transaction.
- * @param changeLovelace The total amount of lovelace in the change.
- * @param changeAssets The total assets to be distributed as change.
- * @param constraints The selection constraints.
- * @param getChangeAddresses A callback that returns a list of addresses and their proportions.
- * @param fee The current computed fee for this selection.
- */
-const splitChangeAndComputeFee = async (
-  inputs: Set<Cardano.Utxo>,
-  outputs: Set<Cardano.TxOut>,
-  changeLovelace: bigint,
-  changeAssets: Cardano.TokenMap | undefined,
-  constraints: SelectionConstraints,
-  getChangeAddresses: () => Promise<Map<Cardano.PaymentAddress, number>>,
-  fee: bigint
-): Promise<{ fee: bigint; change: Array<Cardano.TxOut>; feeAccountedFor: boolean }> => {
-  const changeOutputs = await splitChange(
-    getChangeAddresses,
-    changeLovelace,
-    changeAssets,
-    constraints.computeMinimumCoinQuantity,
-    constraints.tokenBundleSizeExceedsLimit,
-    fee
-  );
-
-  let adjustedChangeOutputs = await adjustOutputsForFee(
-    changeLovelace,
-    constraints,
-    inputs,
-    outputs,
-    changeOutputs,
-    fee
-  );
-
-  // If the newly computed fee is higher than tha available balance for change,
-  // but there are unallocated native assets, return the assets as change with 0n coins.
-  if (adjustedChangeOutputs.fee >= changeLovelace) {
-    const result = {
-      change: [
-        {
-          address: stubMaxSizeAddress,
-          value: {
-            assets: changeAssets,
-            coins: 0n
-          }
-        }
-      ],
-      fee: adjustedChangeOutputs.fee,
-      feeAccountedFor: true
-    };
-
-    if (result.change[0].value.coins < constraints.computeMinimumCoinQuantity(result.change[0]))
-      throw new InputSelectionError(InputSelectionFailure.UtxoFullyDepleted);
-
-    return result;
-  }
-
-  if (fee < adjustedChangeOutputs.fee) {
-    adjustedChangeOutputs = await splitChangeAndComputeFee(
-      inputs,
-      outputs,
-      changeLovelace,
-      changeAssets,
-      constraints,
-      getChangeAddresses,
-      adjustedChangeOutputs.fee
-    );
-
-    if (adjustedChangeOutputs.change.length === 0)
-      throw new InputSelectionError(InputSelectionFailure.UtxoFullyDepleted);
-  }
-
-  for (const out of adjustedChangeOutputs.change) {
-    if (out.value.coins < constraints.computeMinimumCoinQuantity(out))
-      throw new InputSelectionError(InputSelectionFailure.UtxoFullyDepleted);
-  }
-
-  if (!adjustedChangeOutputs.feeAccountedFor) throw new InputSelectionError(InputSelectionFailure.UtxoFullyDepleted);
-
-  return adjustedChangeOutputs;
-};
-
 /** Selects all UTXOs to fulfill the amount required for the given outputs and return the remaining balance as change. */
 export class GreedyInputSelector implements InputSelector {
   #props: GreedySelectorProps;
diff --git a/packages/input-selection/src/GreedySelection/util.ts b/packages/input-selection/src/GreedySelection/util.ts
index 191c23ab2e9..e7bb03cc904 100644
--- a/packages/input-selection/src/GreedySelection/util.ts
+++ b/packages/input-selection/src/GreedySelection/util.ts
@@ -1,9 +1,9 @@
 /* eslint-disable func-style, max-params */
 import { BigNumber } from 'bignumber.js';
 import { Cardano, coalesceValueQuantities } from '@cardano-sdk/core';
-import { ComputeMinimumCoinQuantity, TokenBundleSizeExceedsLimit } from '../types';
+import { ComputeMinimumCoinQuantity, SelectionConstraints, TokenBundleSizeExceedsLimit } from '../types';
 import { InputSelectionError, InputSelectionFailure } from '../InputSelectionError';
-import { addTokenMaps, isValidValue, sortByCoins } from '../util';
+import { addTokenMaps, isValidValue, sortByCoins, stubMaxSizeAddress } from '../util';
 
 const PERCENTAGE_TOLERANCE = 0.05;
 
@@ -227,22 +227,141 @@ export const splitChange = async (
 };
 
 /**
- * Sorts the given TxIn set first by txId and then by index.
+ * Given a set of input and outputs, compute the fee. Then extract the fee from the change output
+ * with the highest value.
  *
- * @param lhs The left-hand side of the comparison operation.
- * @param rhs The left-hand side of the comparison operation.
+ * @param changeLovelace The available amount of lovelace to be used as change.
+ * @param constraints The selection constraints.
+ * @param inputs The inputs of the transaction.
+ * @param outputs The outputs of the transaction.
+ * @param changeOutputs The list of change outputs.
+ * @param currentFee The current computed fee for this selection.
  */
-export const sortTxIn = (lhs: Cardano.TxIn, rhs: Cardano.TxIn) => {
-  const txIdComparison = lhs.txId.localeCompare(rhs.txId);
-  if (txIdComparison !== 0) return txIdComparison;
+export const adjustOutputsForFee = async (
+  changeLovelace: bigint,
+  constraints: SelectionConstraints,
+  inputs: Set<Cardano.Utxo>,
+  outputs: Set<Cardano.TxOut>,
+  changeOutputs: Array<Cardano.TxOut>,
+  currentFee: bigint
+): Promise<{
+  fee: bigint;
+  change: Array<Cardano.TxOut>;
+  feeAccountedFor: boolean;
+  redeemers?: Array<Cardano.Redeemer>;
+}> => {
+  const totalOutputs = new Set([...outputs, ...changeOutputs]);
+  const { fee, redeemers } = await constraints.computeMinimumCost({
+    change: [],
+    fee: currentFee,
+    inputs,
+    outputs: totalOutputs
+  });
+
+  if (fee === changeLovelace) return { change: [], fee, feeAccountedFor: true, redeemers };
+
+  if (changeLovelace < fee) throw new InputSelectionError(InputSelectionFailure.UtxoBalanceInsufficient);
+
+  const updatedOutputs = [...changeOutputs];
+
+  updatedOutputs.sort(sortByCoins);
+
+  let feeAccountedFor = false;
+  for (const output of updatedOutputs) {
+    const adjustedCoins = output.value.coins - fee;
+
+    if (adjustedCoins >= constraints.computeMinimumCoinQuantity(output)) {
+      output.value.coins = adjustedCoins;
+      feeAccountedFor = true;
+      break;
+    }
+  }
 
-  return lhs.index - rhs.index;
+  return { change: [...updatedOutputs], fee, feeAccountedFor, redeemers };
 };
 
 /**
- * Sorts the given Utxo set first by TxIn.
+ * Recursively compute the fee and compute change outputs until it finds a set of change outputs that satisfies the fee.
  *
- * @param lhs The left-hand side of the comparison operation.
- * @param rhs The left-hand side of the comparison operation.
+ * @param inputs The inputs of the transaction.
+ * @param outputs The outputs of the transaction.
+ * @param changeLovelace The total amount of lovelace in the change.
+ * @param changeAssets The total assets to be distributed as change.
+ * @param constraints The selection constraints.
+ * @param getChangeAddresses A callback that returns a list of addresses and their proportions.
+ * @param fee The current computed fee for this selection.
  */
-export const sortUtxoByTxIn = (lhs: Cardano.Utxo, rhs: Cardano.Utxo) => sortTxIn(lhs[0], rhs[0]);
+export const splitChangeAndComputeFee = async (
+  inputs: Set<Cardano.Utxo>,
+  outputs: Set<Cardano.TxOut>,
+  changeLovelace: bigint,
+  changeAssets: Cardano.TokenMap | undefined,
+  constraints: SelectionConstraints,
+  getChangeAddresses: () => Promise<Map<Cardano.PaymentAddress, number>>,
+  fee: bigint
+): Promise<{ fee: bigint; change: Array<Cardano.TxOut>; feeAccountedFor: boolean }> => {
+  const changeOutputs = await splitChange(
+    getChangeAddresses,
+    changeLovelace,
+    changeAssets,
+    constraints.computeMinimumCoinQuantity,
+    constraints.tokenBundleSizeExceedsLimit,
+    fee
+  );
+
+  let adjustedChangeOutputs = await adjustOutputsForFee(
+    changeLovelace,
+    constraints,
+    inputs,
+    outputs,
+    changeOutputs,
+    fee
+  );
+
+  // If the newly computed fee is higher than the available balance for change,
+  // but there are unallocated native assets, return the assets as change with 0n coins.
+  if (adjustedChangeOutputs.fee >= changeLovelace) {
+    const result = {
+      change: [
+        {
+          address: stubMaxSizeAddress,
+          value: {
+            assets: changeAssets,
+            coins: 0n
+          }
+        }
+      ],
+      fee: adjustedChangeOutputs.fee,
+      feeAccountedFor: true
+    };
+
+    if (result.change[0].value.coins < constraints.computeMinimumCoinQuantity(result.change[0]))
+      throw new InputSelectionError(InputSelectionFailure.UtxoFullyDepleted);
+
+    return result;
+  }
+
+  if (fee < adjustedChangeOutputs.fee) {
+    adjustedChangeOutputs = await splitChangeAndComputeFee(
+      inputs,
+      outputs,
+      changeLovelace,
+      changeAssets,
+      constraints,
+      getChangeAddresses,
+      adjustedChangeOutputs.fee
+    );
+
+    if (adjustedChangeOutputs.change.length === 0)
+      throw new InputSelectionError(InputSelectionFailure.UtxoFullyDepleted);
+  }
+
+  for (const out of adjustedChangeOutputs.change) {
+    if (out.value.coins < constraints.computeMinimumCoinQuantity(out))
+      throw new InputSelectionError(InputSelectionFailure.UtxoFullyDepleted);
+  }
+
+  if (!adjustedChangeOutputs.feeAccountedFor) throw new InputSelectionError(InputSelectionFailure.UtxoFullyDepleted);
+
+  return adjustedChangeOutputs;
+};
diff --git a/packages/input-selection/src/LargeFirstSelection/LargeFirstInputSelector.ts b/packages/input-selection/src/LargeFirstSelection/LargeFirstInputSelector.ts
new file mode 100644
index 00000000000..45492a06d4c
--- /dev/null
+++ b/packages/input-selection/src/LargeFirstSelection/LargeFirstInputSelector.ts
@@ -0,0 +1,429 @@
+/* eslint-disable max-params */
+import { Cardano, coalesceValueQuantities } from '@cardano-sdk/core';
+import { ChangeAddressResolver } from '../ChangeAddress';
+import {
+  ImplicitTokens,
+  MAX_U64,
+  UtxoSelection,
+  addTokenMaps,
+  getCoinQuantity,
+  hasNegativeAssetValue,
+  mintToImplicitTokens,
+  sortByAssetQuantity,
+  sortByCoins,
+  sortUtxoByTxIn,
+  stubMaxSizeAddress,
+  subtractTokenMaps,
+  toValues
+} from '../util';
+import {
+  ImplicitValue,
+  InputSelectionParameters,
+  InputSelector,
+  SelectionConstraints,
+  SelectionResult
+} from '../types';
+import { InputSelectionError, InputSelectionFailure } from '../InputSelectionError';
+
+import { computeChangeAndAdjustForFee } from '../change';
+
+import uniq from 'lodash/uniq.js';
+
+/**
+ * A `PickAdditionalUtxo` callback for the large-first strategy.
+ *
+ * The callback simply:
+ * 1. Sorts the remaining UTxOs by coin quantity (largest first).
+ * 2. Picks the first UTxO from the sorted list.
+ * 3. Returns the updated `UtxoSelection`.
+ */
+const pickAdditionalLargestUtxo = ({ utxoRemaining, utxoSelected }: UtxoSelection): UtxoSelection => {
+  if (utxoRemaining.length === 0) {
+    return { utxoRemaining, utxoSelected };
+  }
+
+  const sorted = utxoRemaining.sort(([, a], [, b]) => sortByCoins(a, b));
+  const [picked, ...newRemaining] = sorted;
+
+  return {
+    utxoRemaining: newRemaining,
+    utxoSelected: [...utxoSelected, picked]
+  };
+};
+
+/** LargeFirst selection initialization properties. */
+export interface LargeFirstSelectorProps {
+  changeAddressResolver: ChangeAddressResolver;
+}
+
+/**
+ * Input selector that implements a "large-first" strategy.
+ *
+ * This strategy selects the largest UTxOs per asset, one asset at a time, until the requirements
+ * of all assets in the outputs + fees and implicit values are satisfied.
+ */
+export class LargeFirstSelector implements InputSelector {
+  #props: LargeFirstSelectorProps;
+
+  /** Creates a new instance of the LargeFirstSelector. */
+  constructor(props: LargeFirstSelectorProps) {
+    this.#props = props;
+  }
+
+  /**
+   * Selects inputs using a large-first strategy:
+   * Selects largest UTxOs per asset until target is met
+   * Then selects largest UTxOs by Ada
+   * Then iteratively adds more UTxOs if needed to cover fees
+   *
+   * @param params Input selection parameters (available UTxOs, outputs, constraints, etc.)
+   * @returns A complete selection including inputs, outputs, change, and fee.
+   * @throws {InputSelectionError} If the selection cannot satisfy the outputs and fees.
+   */
+  async select(params: InputSelectionParameters): Promise<SelectionResult> {
+    const { utxo, preSelectedUtxo, outputs, constraints, implicitValue } = params;
+
+    const preSelected = [...preSelectedUtxo];
+    const available = [...utxo].filter(
+      ([txIn]) => !preSelected.some(([preTxIn]) => preTxIn.txId === txIn.txId && preTxIn.index === txIn.index)
+    );
+    const allAvailable = [...preSelected, ...available];
+
+    const { totalLovelaceOutput, outputAssets } = this.#computeNetImplicitSelectionValues(
+      new Set<Cardano.Utxo>(allAvailable),
+      outputs,
+      implicitValue
+    );
+
+    let workingUtxo = new Set<Cardano.Utxo>(preSelected);
+    workingUtxo = this.#selectAssets(outputAssets, allAvailable, [...workingUtxo]);
+    workingUtxo = this.#selectLovelace(
+      totalLovelaceOutput,
+      allAvailable,
+      workingUtxo,
+      implicitValue?.coin?.input ?? 0n
+    );
+
+    const { finalSelection, fee, change } = await this.#expandUtxosUntilFeeCovered(
+      workingUtxo,
+      outputs,
+      allAvailable,
+      outputAssets,
+      constraints,
+      this.#props.changeAddressResolver,
+      implicitValue
+    );
+
+    const limit = await constraints.computeSelectionLimit({ change, fee, inputs: finalSelection, outputs });
+    if (finalSelection.size > limit) throw new InputSelectionError(InputSelectionFailure.MaximumInputCountExceeded);
+
+    return {
+      remainingUTxO: this.#computeRemainingUtxo([...utxo], finalSelection),
+      selection: {
+        change,
+        fee,
+        inputs: new Set([...finalSelection].sort(sortUtxoByTxIn)),
+        outputs
+      }
+    };
+  }
+
+  /**
+   * Aggregate Lovelace that enters and leaves the transaction,
+   * taking implicit withdrawals / deposits into account.
+   *
+   * @param inputs   Array of `Value`s held by the selected UTxOs.
+   * @param outputs  Array of explicit transaction outputs.
+   * @param implicit Optional implicit values.
+   * @returns An object with
+   * `totalIn`  — Lovelace provided by UTxOs + withdrawals
+   * `totalOut` — Lovelace required for explicit outputs + deposits
+   */
+  #aggregateLovelace(
+    inputs: Cardano.Value[],
+    outputs: Cardano.Value[],
+    implicit?: ImplicitValue
+  ): { totalIn: bigint; totalOut: bigint } {
+    const utxoAda = getCoinQuantity(inputs);
+    const outputAda = getCoinQuantity(outputs);
+    const withdrawAda = implicit?.coin?.input ?? 0n;
+    const depositAda = implicit?.coin?.deposit ?? 0n;
+
+    return {
+      totalIn: utxoAda + withdrawAda,
+      totalOut: outputAda + depositAda
+    };
+  }
+
+  /**
+   * Compute two token maps:
+   * - required: the exact quantity of every asset that must be provided by inputs in order to satisfy explicit outputs plus any burns.
+   * - available: the quantity of every asset that is actually provided by inputs + positive mint.
+   *
+   * @param inputs   Values contained in the UTxOs selected so far.
+   * @param outputs  Values required by the user’s explicit transaction outputs.
+   * @param implicit Optional `mint` map (positive = forge, negative = burn).
+   * @returns `{ required, available }`
+   */
+  #aggregateAssets(
+    inputs: Cardano.Value[],
+    outputs: Cardano.Value[],
+    implicit?: ImplicitValue
+  ): {
+    required: Cardano.TokenMap;
+    available: Cardano.TokenMap;
+  } {
+    const outputsMap = coalesceValueQuantities(outputs).assets ?? new Map<Cardano.AssetId, bigint>();
+    const utxoMap = coalesceValueQuantities(inputs).assets;
+    const mint = implicit?.mint ?? new Map<Cardano.AssetId, bigint>();
+
+    const posMint = new Map<Cardano.AssetId, bigint>();
+    const negMint = new Map<Cardano.AssetId, bigint>();
+
+    for (const [id, q] of mint) (q > 0n ? posMint : negMint).set(id, q > 0n ? q : -q);
+
+    let required = addTokenMaps(outputsMap, negMint);
+    required = subtractTokenMaps(required, posMint) ?? new Map<Cardano.AssetId, bigint>();
+
+    const available = addTokenMaps(utxoMap, posMint) ?? new Map<Cardano.AssetId, bigint>();
+
+    return { available, required };
+  }
+
+  /**
+   * Computes the total Lovelace and asset output requirements, including the effects
+   * of implicit values such as key deposits, withdrawals, and minting/burning.
+   *
+   * Minting and burning are treated as negative or positive contributions to input balance,
+   * and are subtracted from the output requirements.
+   *
+   * @param inputs The full set of selected UTxOs (including pre-selected).
+   * @param outputs The transaction outputs.
+   * @param implicitValue Optional implicit values including deposits, withdrawals, and minting.
+   * @returns An object with:
+   * `totalLovelaceInput`: Sum of all input Lovelace, including withdrawals.
+   * `totalLovelaceOutput`: Sum of all output Lovelace, including deposits.
+   * `outputAssets`: Asset requirements after accounting for minting/burning.
+   * @throws {InputSelectionError} If balance is insufficient to satisfy the target.
+   */
+  #computeNetImplicitSelectionValues(
+    inputs: Set<Cardano.Utxo>,
+    outputs: Set<Cardano.TxOut>,
+    implicitValue?: ImplicitValue
+  ): {
+    totalLovelaceInput: bigint;
+    totalLovelaceOutput: bigint;
+    outputAssets: Cardano.TokenMap;
+  } {
+    const inputVals = toValues([...inputs]);
+    const outputVals = toValues([...outputs]);
+
+    const { totalIn, totalOut } = this.#aggregateLovelace(inputVals, outputVals, implicitValue);
+    const { required, available } = this.#aggregateAssets(inputVals, outputVals, implicitValue);
+
+    const changeAda = totalIn - totalOut;
+    const changeAssets = subtractTokenMaps(available, required);
+
+    if (inputs.size === 0 || changeAda < 0n || hasNegativeAssetValue(changeAssets))
+      throw new InputSelectionError(InputSelectionFailure.UtxoBalanceInsufficient);
+
+    return {
+      outputAssets: required,
+      totalLovelaceInput: totalIn,
+      totalLovelaceOutput: totalOut
+    };
+  }
+
+  /**
+   * Selects the largest UTxOs per required asset until each target amount is fulfilled.
+   *
+   * @param requiredAssets The asset quantities required by the transaction outputs.
+   * @param allAvailable All available UTxOs (including preselected ones).
+   * @param preSelected The UTxOs already selected for the transaction.
+   * @returns A set of selected UTxOs covering the asset requirements.
+   * @throws {InputSelectionError} If any asset cannot be sufficiently fulfilled.
+   */
+  #selectAssets(
+    requiredAssets: Map<Cardano.AssetId, bigint>,
+    allAvailable: Cardano.Utxo[],
+    preSelected: Cardano.Utxo[]
+  ): Set<Cardano.Utxo> {
+    const selected = new Set(preSelected);
+
+    for (const [assetId, requiredQuantity] of requiredAssets) {
+      const candidates = allAvailable
+        .filter(([_, out]) => (out.value.assets?.get(assetId) ?? 0n) > 0n)
+        .sort(([, a], [, b]) => sortByAssetQuantity(assetId)(a, b));
+
+      let accumulated = 0n;
+
+      for (const candidate of candidates) {
+        selected.add(candidate);
+        accumulated += candidate[1].value.assets?.get(assetId) ?? 0n;
+        if (accumulated >= requiredQuantity) break;
+      }
+
+      if (accumulated < requiredQuantity) throw new InputSelectionError(InputSelectionFailure.UtxoBalanceInsufficient);
+    }
+
+    return selected;
+  }
+
+  /**
+   * Selects UTxOs (largest Ada first) until the total Lovelace covers the target amount.
+   *
+   * @param target The required amount of Lovelace.
+   * @param allAvailable All available UTxOs.
+   * @param selected The current set of already selected UTxOs.
+   * @param implicitCoinInput The implicit coin input amount.
+   * @returns A new set including the original selected UTxOs plus any added for Ada coverage.
+   * @throws {InputSelectionError} If the Lovelace requirement cannot be fulfilled.
+   */
+  #selectLovelace(
+    target: bigint,
+    allAvailable: Cardano.Utxo[],
+    selected: Set<Cardano.Utxo>,
+    implicitCoinInput: bigint
+  ): Set<Cardano.Utxo> {
+    const result = new Set(selected);
+    const selectedTxIns = new Set([...selected].map(([txIn]) => txIn));
+
+    const adaCandidates = allAvailable
+      .filter(([txIn]) => !selectedTxIns.has(txIn))
+      .sort(([, a], [, b]) => sortByCoins(a, b));
+
+    let adaAccumulated = getCoinQuantity(toValues([...result])) + implicitCoinInput;
+
+    for (const candidate of adaCandidates) {
+      if (adaAccumulated >= target) break;
+      result.add(candidate);
+      adaAccumulated += candidate[1].value.coins;
+    }
+
+    if (result.size === 0) {
+      if (adaCandidates.length === 0) throw new InputSelectionError(InputSelectionFailure.UtxoBalanceInsufficient);
+      result.add(adaCandidates[0]);
+    }
+
+    if (adaAccumulated < target) throw new InputSelectionError(InputSelectionFailure.UtxoBalanceInsufficient);
+
+    return result;
+  }
+
+  /**
+   * Computes the UTxOs that were not selected, given the original list and selected set.
+   *
+   * @param original The original list of available UTxOs (excluding preselected).
+   * @param used The set of selected UTxOs.
+   * @returns A new set of UTxOs that were not consumed in the transaction.
+   */
+  #computeRemainingUtxo(original: Cardano.Utxo[], used: Set<Cardano.Utxo>): Set<Cardano.Utxo> {
+    const usedTxIns = new Set([...used].map(([txIn]) => txIn));
+    return new Set(original.filter(([txIn]) => !usedTxIns.has(txIn)));
+  }
+
+  /**
+   * Select additional UTxOs until the fee and min-Ada requirements are
+   * satisfied, then build the final change outputs.
+   *
+   * @param initialInputs The UTxOs already chosen for assets and/or pre-selected by the wallet.
+   * @param outputs The explicit transaction outputs.
+   * @param allAvailable Every UTxO the wallet can spend (pre-selected ∪ utxo).
+   * @param requiredAssets Aggregate asset requirements computed from `outputs` + burn.  Used for fee/Ada expansion.
+   * @param constraints Network / wallet selection constraints (min-Ada, fee estimator, bundle size, limit…).
+   * @param changeAddressResolver Callback that assigns addresses (or further splits) for the provisional change bundles returned by the fee engine.
+   * @param implicitValue Optional implicit components (deposits, withdrawals, mint or burn)
+   * @returns An object containing Set of all inputs that will appear in the tx body, minimum fee returned by the cost model and an Array of change outputs.
+   */
+  async #expandUtxosUntilFeeCovered(
+    initialInputs: Set<Cardano.Utxo>,
+    outputs: Set<Cardano.TxOut>,
+    allAvailable: Cardano.Utxo[],
+    requiredAssets: Cardano.TokenMap,
+    constraints: SelectionConstraints,
+    changeAddressResolver: ChangeAddressResolver,
+    implicitValue?: ImplicitValue
+  ): Promise<{ finalSelection: Set<Cardano.Utxo>; fee: bigint; change: Cardano.TxOut[] }> {
+    const utxoSelectedArr = [...initialInputs];
+    const utxoRemainingArr = allAvailable
+      .filter((u) => !initialInputs.has(u))
+      .sort(([, a], [, b]) => sortByCoins(a, b)); // Ada-descending
+
+    const outputValues = toValues([...outputs]);
+
+    const changeAddress = stubMaxSizeAddress;
+
+    const implicitCoin: Required<Cardano.util.ImplicitCoin> = {
+      deposit: implicitValue?.coin?.deposit || 0n,
+      input: implicitValue?.coin?.input || 0n,
+      reclaimDeposit: implicitValue?.coin?.reclaimDeposit || 0n,
+      withdrawals: implicitValue?.coin?.withdrawals || 0n
+    };
+    const mintMap: Cardano.TokenMap = implicitValue?.mint || new Map();
+    const uniqueTxAssetIDs = uniq([...requiredAssets.keys(), ...mintMap.keys()]);
+
+    const { implicitTokensInput, implicitTokensSpend } = mintToImplicitTokens(mintMap);
+    const implicitTokens: ImplicitTokens = {
+      input: (assetId) => implicitTokensInput.get(assetId) || 0n,
+      spend: (assetId) => implicitTokensSpend.get(assetId) || 0n
+    };
+    const {
+      change,
+      fee,
+      inputs: finalInputs
+    } = await computeChangeAndAdjustForFee({
+      computeMinimumCoinQuantity: constraints.computeMinimumCoinQuantity,
+      estimateTxCosts: (utxos, changeValues) =>
+        constraints.computeMinimumCost({
+          change: changeValues.map(
+            (value) =>
+              ({
+                address: changeAddress,
+                value
+              } as Cardano.TxOut)
+          ),
+          fee: MAX_U64,
+          inputs: new Set(utxos),
+          outputs
+        }),
+      implicitValue: {
+        implicitCoin,
+        implicitTokens
+      },
+      outputValues,
+      pickAdditionalUtxo: pickAdditionalLargestUtxo,
+      tokenBundleSizeExceedsLimit: constraints.tokenBundleSizeExceedsLimit,
+      uniqueTxAssetIDs,
+      utxoSelection: {
+        utxoRemaining: utxoRemainingArr,
+        utxoSelected: utxoSelectedArr
+      }
+    });
+
+    if (change.length === 0) {
+      return {
+        change: [],
+        fee,
+        finalSelection: new Set(finalInputs)
+      };
+    }
+
+    const changeTxOuts: Cardano.TxOut[] = change.map((val) => ({
+      address: changeAddress,
+      value: val
+    }));
+
+    const resolvedChange = await changeAddressResolver.resolve({
+      change: changeTxOuts,
+      fee,
+      inputs: new Set(finalInputs),
+      outputs
+    });
+
+    return {
+      change: resolvedChange,
+      fee,
+      finalSelection: new Set(finalInputs)
+    };
+  }
+}
diff --git a/packages/input-selection/src/LargeFirstSelection/index.ts b/packages/input-selection/src/LargeFirstSelection/index.ts
new file mode 100644
index 00000000000..2fb18d2e788
--- /dev/null
+++ b/packages/input-selection/src/LargeFirstSelection/index.ts
@@ -0,0 +1 @@
+export * from './LargeFirstInputSelector';
diff --git a/packages/input-selection/src/RoundRobinRandomImprove/index.ts b/packages/input-selection/src/RoundRobinRandomImprove/index.ts
index 466d182a80d..c36f58022e1 100644
--- a/packages/input-selection/src/RoundRobinRandomImprove/index.ts
+++ b/packages/input-selection/src/RoundRobinRandomImprove/index.ts
@@ -2,18 +2,36 @@ import { Cardano } from '@cardano-sdk/core';
 import { ChangeAddressResolver } from '../ChangeAddress';
 import { InputSelectionError, InputSelectionFailure } from '../InputSelectionError';
 import { InputSelectionParameters, InputSelector, SelectionResult } from '../types';
-import { assertIsBalanceSufficient, preProcessArgs, stubMaxSizeAddress, toValues } from '../util';
-import { computeChangeAndAdjustForFee } from './change';
+import {
+  MAX_U64,
+  UtxoSelection,
+  assertIsBalanceSufficient,
+  preProcessArgs,
+  sortUtxoByTxIn,
+  stubMaxSizeAddress,
+  toValues
+} from '../util';
+import { PickAdditionalUtxo, computeChangeAndAdjustForFee } from '../change';
 import { roundRobinSelection } from './roundRobin';
-import { sortUtxoByTxIn } from '../GreedySelection';
-
-export const MAX_U64 = 18_446_744_073_709_551_615n;
 
 interface RoundRobinRandomImproveOptions {
   changeAddressResolver: ChangeAddressResolver;
   random?: typeof Math.random;
 }
 
+/** Picks one UTxO from remaining set and puts it to the selected set. Precondition: utxoRemaining.length > 0 */
+export const createPickAdditionalRandomUtxo =
+  (random: typeof Math.random): PickAdditionalUtxo =>
+  ({ utxoRemaining, utxoSelected }: UtxoSelection): UtxoSelection => {
+    const remainingUtxoOfOnlyCoin = utxoRemaining.filter(([_, { value }]) => !value.assets);
+    const pickFrom = remainingUtxoOfOnlyCoin.length > 0 ? remainingUtxoOfOnlyCoin : utxoRemaining;
+    const pickIdx = Math.floor(random() * pickFrom.length);
+    const newUtxoSelected = [...utxoSelected, pickFrom[pickIdx]];
+    const originalIdx = utxoRemaining.indexOf(pickFrom[pickIdx]);
+    const newUtxoRemaining = [...utxoRemaining.slice(0, originalIdx), ...utxoRemaining.slice(originalIdx + 1)];
+    return { utxoRemaining: newUtxoRemaining, utxoSelected: newUtxoSelected };
+  };
+
 export const roundRobinRandomImprove = ({
   changeAddressResolver,
   random = Math.random
@@ -63,7 +81,7 @@ export const roundRobinRandomImprove = ({
         }),
       implicitValue,
       outputValues: toValues(outputs),
-      random,
+      pickAdditionalUtxo: createPickAdditionalRandomUtxo(random),
       tokenBundleSizeExceedsLimit,
       uniqueTxAssetIDs,
       utxoSelection: roundRobinSelectionResult
diff --git a/packages/input-selection/src/RoundRobinRandomImprove/change.ts b/packages/input-selection/src/change.ts
similarity index 94%
rename from packages/input-selection/src/RoundRobinRandomImprove/change.ts
rename to packages/input-selection/src/change.ts
index 42b78358b4b..bdffbaf97ab 100644
--- a/packages/input-selection/src/RoundRobinRandomImprove/change.ts
+++ b/packages/input-selection/src/change.ts
@@ -1,6 +1,6 @@
 import { Cardano, coalesceValueQuantities } from '@cardano-sdk/core';
-import { ComputeMinimumCoinQuantity, TokenBundleSizeExceedsLimit, TxCosts } from '../types';
-import { InputSelectionError, InputSelectionFailure } from '../InputSelectionError';
+import { ComputeMinimumCoinQuantity, TokenBundleSizeExceedsLimit, TxCosts } from './types';
+import { InputSelectionError, InputSelectionFailure } from './InputSelectionError';
 import {
   RequiredImplicitValue,
   UtxoSelection,
@@ -8,13 +8,21 @@ import {
   getCoinQuantity,
   stubMaxSizeAddress,
   toValues
-} from '../util';
+} from './util';
 import minBy from 'lodash/minBy.js';
 import orderBy from 'lodash/orderBy.js';
 import pick from 'lodash/pick.js';
 
 type EstimateTxCostsWithOriginalOutputs = (utxo: Cardano.Utxo[], change: Cardano.Value[]) => Promise<TxCosts>;
 
+/**
+ * Callback used by the change-selection whenever it needs to pull one additional UTxO from `utxoRemaining` into `utxoSelected`.
+ *
+ * @param selection Object that holds the current state of the selection.
+ * @returns A new `UtxoSelection` object in which exactly one entry has been moved from `utxoRemaining` to `utxoSelected`.
+ */
+export type PickAdditionalUtxo = (selection: UtxoSelection) => UtxoSelection;
+
 interface ChangeComputationArgs {
   utxoSelection: UtxoSelection;
   outputValues: Cardano.Value[];
@@ -23,7 +31,7 @@ interface ChangeComputationArgs {
   estimateTxCosts: EstimateTxCostsWithOriginalOutputs;
   computeMinimumCoinQuantity: ComputeMinimumCoinQuantity;
   tokenBundleSizeExceedsLimit: TokenBundleSizeExceedsLimit;
-  random: typeof Math.random;
+  pickAdditionalUtxo: PickAdditionalUtxo;
 }
 
 interface ChangeComputationResult {
@@ -194,20 +202,6 @@ const computeRequestedAssetChangeBundles = (
   return bundles;
 };
 
-/** Picks one UTxO from remaining set and puts it to the selected set. Precondition: utxoRemaining.length > 0 */
-const pickExtraRandomUtxo = (
-  { utxoRemaining, utxoSelected }: UtxoSelection,
-  random: typeof Math.random
-): UtxoSelection => {
-  const remainingUtxoOfOnlyCoin = utxoRemaining.filter(([_, { value }]) => !value.assets);
-  const pickFrom = remainingUtxoOfOnlyCoin.length > 0 ? remainingUtxoOfOnlyCoin : utxoRemaining;
-  const pickIdx = Math.floor(random() * pickFrom.length);
-  const newUtxoSelected = [...utxoSelected, pickFrom[pickIdx]];
-  const originalIdx = utxoRemaining.indexOf(pickFrom[pickIdx]);
-  const newUtxoRemaining = [...utxoRemaining.slice(0, originalIdx), ...utxoRemaining.slice(originalIdx + 1)];
-  return { utxoRemaining: newUtxoRemaining, utxoSelected: newUtxoSelected };
-};
-
 const mergeWithSmallestBundle = (values: Cardano.Value[], index: number): Cardano.Value[] => {
   let result = [...values];
   const toBeMerged = result.splice(index, 1)[0];
@@ -408,7 +402,7 @@ export const computeChangeAndAdjustForFee = async ({
   outputValues,
   uniqueTxAssetIDs,
   implicitValue,
-  random,
+  pickAdditionalUtxo,
   utxoSelection
 }: ChangeComputationArgs): Promise<ChangeComputationResult> => {
   const recomputeChangeAndAdjustForFeeWithExtraUtxo = (currentUtxoSelection: UtxoSelection) => {
@@ -418,10 +412,10 @@ export const computeChangeAndAdjustForFee = async ({
         estimateTxCosts,
         implicitValue,
         outputValues,
-        random,
+        pickAdditionalUtxo,
         tokenBundleSizeExceedsLimit,
         uniqueTxAssetIDs,
-        utxoSelection: pickExtraRandomUtxo(currentUtxoSelection, random)
+        utxoSelection: pickAdditionalUtxo(currentUtxoSelection)
       });
     }
     // This is not a great error type for this, because the spec says
diff --git a/packages/input-selection/src/index.ts b/packages/input-selection/src/index.ts
index cf8b263d3a4..6d18b7aa34e 100644
--- a/packages/input-selection/src/index.ts
+++ b/packages/input-selection/src/index.ts
@@ -1,5 +1,7 @@
 export * from './RoundRobinRandomImprove';
 export * from './GreedySelection';
+export * from './LargeFirstSelection';
 export * from './types';
 export * from './InputSelectionError';
 export * from './ChangeAddress';
+export { sortTxIn, sortUtxoByTxIn } from './util';
diff --git a/packages/input-selection/src/util.ts b/packages/input-selection/src/util.ts
index 6a46653fc2a..4404e146aee 100644
--- a/packages/input-selection/src/util.ts
+++ b/packages/input-selection/src/util.ts
@@ -6,10 +6,33 @@ import { ComputeMinimumCoinQuantity, ImplicitValue, TokenBundleSizeExceedsLimit
 import { InputSelectionError, InputSelectionFailure } from './InputSelectionError';
 import uniq from 'lodash/uniq.js';
 
+export const MAX_U64 = 18_446_744_073_709_551_615n;
+
 export const stubMaxSizeAddress = Cardano.PaymentAddress(
   'addr_test1qqydn46r6mhge0kfpqmt36m6q43knzsd9ga32n96m89px3nuzcjqw982pcftgx53fu5527z2cj2tkx2h8ux2vxsg475qypp3m9'
 );
 
+/**
+ * Sorts the given TxIn set first by txId and then by index.
+ *
+ * @param lhs The left-hand side of the comparison operation.
+ * @param rhs The left-hand side of the comparison operation.
+ */
+export const sortTxIn = (lhs: Cardano.TxIn, rhs: Cardano.TxIn) => {
+  const txIdComparison = lhs.txId.localeCompare(rhs.txId);
+  if (txIdComparison !== 0) return txIdComparison;
+
+  return lhs.index - rhs.index;
+};
+
+/**
+ * Sorts the given Utxo set first by TxIn.
+ *
+ * @param lhs The left-hand side of the comparison operation.
+ * @param rhs The left-hand side of the comparison operation.
+ */
+export const sortUtxoByTxIn = (lhs: Cardano.Utxo, rhs: Cardano.Utxo) => sortTxIn(lhs[0], rhs[0]);
+
 export interface ImplicitTokens {
   spend(assetId: Cardano.AssetId): bigint;
   input(assetId: Cardano.AssetId): bigint;
@@ -307,3 +330,21 @@ export const isValidValue = (
 
   return isValid;
 };
+
+/**
+ * Creates a comparator function that sorts `TxOut`s in descending order based on
+ * the quantity of a specified asset.
+ *
+ * @param assetId - The asset ID to compare quantities of.
+ * @returns A comparator function that can be used with `.sort()`.
+ */
+export const sortByAssetQuantity =
+  (assetId: Cardano.AssetId) =>
+  (lhs: Cardano.TxOut, rhs: Cardano.TxOut): number => {
+    const lhsQty = lhs.value.assets?.get(assetId) ?? 0n;
+    const rhsQty = rhs.value.assets?.get(assetId) ?? 0n;
+
+    if (lhsQty > rhsQty) return -1;
+    if (lhsQty < rhsQty) return 1;
+    return 0;
+  };
diff --git a/packages/input-selection/test/InputSelectionPropertyTesting.test.ts b/packages/input-selection/test/InputSelectionPropertyTesting.test.ts
index a6622dc9752..81d8995199b 100644
--- a/packages/input-selection/test/InputSelectionPropertyTesting.test.ts
+++ b/packages/input-selection/test/InputSelectionPropertyTesting.test.ts
@@ -1,6 +1,13 @@
 import { AssetId, TxTestUtil } from '@cardano-sdk/util-dev';
 import { Cardano, coalesceValueQuantities } from '@cardano-sdk/core';
-import { ChangeAddressResolver, GreedyInputSelector, InputSelectionError, InputSelector, Selection } from '../src';
+import {
+  ChangeAddressResolver,
+  GreedyInputSelector,
+  InputSelectionError,
+  InputSelector,
+  LargeFirstSelector,
+  Selection
+} from '../src';
 import { InputSelectionFailure } from '../src/InputSelectionError';
 import {
   SelectionConstraints,
@@ -36,6 +43,11 @@ const createGreedySelector = () =>
     getChangeAddresses: async () => new Map([[asPaymentAddress('A'), 1]])
   });
 
+const createLargeFirstSelector = () =>
+  new LargeFirstSelector({
+    changeAddressResolver: new MockChangeAddressResolver()
+  });
+
 const testInputSelection = (name: string, getAlgorithm: () => InputSelector) => {
   describe(name, () => {
     describe('Properties', () => {
@@ -343,3 +355,4 @@ const testInputSelection = (name: string, getAlgorithm: () => InputSelector) =>
 
 testInputSelection('RoundRobinRandomImprove', createRoundRobinRandomImprove);
 testInputSelection('GreedySelector', createGreedySelector);
+testInputSelection('LargeFirstSelector', createLargeFirstSelector);
diff --git a/packages/input-selection/test/LargeFirstSelection/LargeFirstSelection.test.ts b/packages/input-selection/test/LargeFirstSelection/LargeFirstSelection.test.ts
new file mode 100644
index 00000000000..4ddbb2b4ab0
--- /dev/null
+++ b/packages/input-selection/test/LargeFirstSelection/LargeFirstSelection.test.ts
@@ -0,0 +1,342 @@
+import { Cardano } from '@cardano-sdk/core';
+import { LargeFirstSelector } from '../../src';
+import { MOCK_NO_CONSTRAINTS, mockConstraintsToConstraints } from '../util/selectionConstraints';
+import {
+  MockChangeAddressResolver,
+  asAssetId,
+  asTokenMap,
+  assertInputSelectionProperties,
+  getCoinValueForAddress,
+  mockChangeAddress
+} from '../util';
+import { TxTestUtil } from '@cardano-sdk/util-dev';
+
+describe('LargeFirstSelection', () => {
+  it('picks the largest ADA UTxOs first', async () => {
+    const selector = new LargeFirstSelector({
+      changeAddressResolver: new MockChangeAddressResolver()
+    });
+
+    const utxo = new Set([
+      TxTestUtil.createUnspentTxOutput({ coins: 3_000_000n }),
+      TxTestUtil.createUnspentTxOutput({ coins: 2_000_000n }),
+      TxTestUtil.createUnspentTxOutput({ coins: 5_000_000n }),
+      TxTestUtil.createUnspentTxOutput({ coins: 4_000_000n }),
+      TxTestUtil.createUnspentTxOutput({ coins: 1_000_000n })
+    ]);
+
+    const outputs = new Set([TxTestUtil.createOutput({ coins: 6_000_000n })]);
+
+    const constraints = mockConstraintsToConstraints({
+      ...MOCK_NO_CONSTRAINTS,
+      minimumCostCoefficient: 100n
+    });
+
+    const results = await selector.select({
+      constraints,
+      implicitValue: {},
+      outputs,
+      preSelectedUtxo: new Set(),
+      utxo
+    });
+
+    const { selection, remainingUTxO } = results;
+
+    expect(selection.inputs.size).toBe(2);
+    expect(remainingUTxO.size).toBe(3);
+
+    const inputValues = new Set([...selection.inputs.entries()].map(([[_, output]]) => output.value.coins));
+    expect(inputValues.has(5_000_000n)).toBe(true);
+    expect(inputValues.has(4_000_000n)).toBe(true);
+
+    const expectedFee = BigInt(selection.inputs.size) * 100n;
+    expect(selection.fee).toBe(expectedFee);
+
+    expect(getCoinValueForAddress(mockChangeAddress, selection.change)).toBe(2_999_800n);
+
+    assertInputSelectionProperties({
+      constraints: {
+        ...MOCK_NO_CONSTRAINTS,
+        minimumCostCoefficient: 100n
+      },
+      implicitValue: {},
+      outputs,
+      results,
+      utxo
+    });
+  });
+
+  it('picks the largest ADA UTxOs first as change', async () => {
+    const selector = new LargeFirstSelector({
+      changeAddressResolver: new MockChangeAddressResolver()
+    });
+
+    const utxo = new Set([
+      TxTestUtil.createUnspentTxOutput({ coins: 2_000_000n }),
+      TxTestUtil.createUnspentTxOutput({ coins: 5_000_000n }),
+      TxTestUtil.createUnspentTxOutput({ coins: 4_000_000n }),
+      TxTestUtil.createUnspentTxOutput({ coins: 1_000_000n }),
+      TxTestUtil.createUnspentTxOutput({ coins: 3_000_000n })
+    ]);
+
+    const outputs = new Set([TxTestUtil.createOutput({ coins: 9_000_000n })]);
+
+    const constraints = mockConstraintsToConstraints({
+      ...MOCK_NO_CONSTRAINTS,
+      minimumCostCoefficient: 1n
+    });
+
+    const results = await selector.select({
+      constraints,
+      implicitValue: {},
+      outputs,
+      preSelectedUtxo: new Set(),
+      utxo
+    });
+
+    const { selection, remainingUTxO } = results;
+
+    expect(selection.inputs.size).toBe(3);
+    expect(remainingUTxO.size).toBe(2);
+
+    const inputValues = new Set([...selection.inputs.entries()].map(([[_, output]]) => output.value.coins));
+    expect(inputValues.has(5_000_000n)).toBe(true);
+    expect(inputValues.has(4_000_000n)).toBe(true);
+
+    const expectedFee = BigInt(selection.inputs.size);
+    expect(selection.fee).toBe(expectedFee);
+
+    expect(getCoinValueForAddress(mockChangeAddress, selection.change)).toBe(2_999_997n);
+
+    assertInputSelectionProperties({
+      constraints: {
+        ...MOCK_NO_CONSTRAINTS,
+        minimumCostCoefficient: 100n
+      },
+      implicitValue: {},
+      outputs,
+      results,
+      utxo
+    });
+  });
+
+  it('picks the largest native asset UTxOs first', async () => {
+    const selector = new LargeFirstSelector({
+      changeAddressResolver: new MockChangeAddressResolver()
+    });
+
+    const assetX = asAssetId('X');
+
+    const utxo = new Set([
+      TxTestUtil.createUnspentTxOutput({
+        assets: asTokenMap([[assetX, 20n]]),
+        coins: 3_000_000n
+      }),
+      TxTestUtil.createUnspentTxOutput({
+        assets: asTokenMap([[assetX, 80n]]),
+        coins: 3_000_000n
+      }),
+      TxTestUtil.createUnspentTxOutput({
+        assets: asTokenMap([[assetX, 50n]]),
+        coins: 3_000_000n
+      }),
+      TxTestUtil.createUnspentTxOutput({
+        assets: asTokenMap([[assetX, 100n]]),
+        coins: 3_000_000n
+      }),
+      TxTestUtil.createUnspentTxOutput({ coins: 3_000_000n })
+    ]);
+
+    const outputs = new Set([
+      TxTestUtil.createOutput({
+        assets: asTokenMap([[assetX, 130n]]),
+        coins: 2_000_000n
+      })
+    ]);
+
+    const constraints = mockConstraintsToConstraints({
+      ...MOCK_NO_CONSTRAINTS,
+      minimumCostCoefficient: 100n
+    });
+
+    const results = await selector.select({
+      constraints,
+      implicitValue: {},
+      outputs,
+      preSelectedUtxo: new Set(),
+      utxo
+    });
+
+    const { selection, remainingUTxO } = results;
+
+    expect(selection.inputs.size).toBe(2);
+    const coinsPicked = new Set([...selection.inputs].map(([, o]) => o.value.assets?.get(assetX) ?? 0n));
+    expect(coinsPicked.has(100n)).toBe(true);
+    expect(coinsPicked.has(80n)).toBe(true);
+    expect(remainingUTxO.size).toBe(3);
+
+    const expectedFee = BigInt(selection.inputs.size) * 100n;
+    expect(selection.fee).toBe(expectedFee);
+
+    expect(selection.change.some((txOut) => txOut.value.assets?.get(assetX) === 50n)).toBe(true);
+
+    assertInputSelectionProperties({
+      constraints: {
+        ...MOCK_NO_CONSTRAINTS,
+        minimumCostCoefficient: 100n
+      },
+      implicitValue: {},
+      outputs,
+      results,
+      utxo
+    });
+  });
+
+  it('consumes just enough ADA UTxOs needed and leaves the remainder', async () => {
+    const selector = new LargeFirstSelector({
+      changeAddressResolver: new MockChangeAddressResolver()
+    });
+
+    const utxo = new Set([
+      TxTestUtil.createUnspentTxOutput({ coins: 3_000_000n }),
+      TxTestUtil.createUnspentTxOutput({ coins: 3_000_000n }),
+      TxTestUtil.createUnspentTxOutput({ coins: 3_000_000n }),
+      TxTestUtil.createUnspentTxOutput({ coins: 3_000_000n }),
+      TxTestUtil.createUnspentTxOutput({ coins: 3_000_000n })
+    ]);
+    const outputs = new Set([TxTestUtil.createOutput({ coins: 5_000_000n })]);
+
+    const constraints = mockConstraintsToConstraints({
+      ...MOCK_NO_CONSTRAINTS,
+      minimumCostCoefficient: 100n
+    });
+
+    const results = await selector.select({
+      constraints,
+      implicitValue: {},
+      outputs,
+      preSelectedUtxo: new Set(),
+      utxo
+    });
+
+    const { selection, remainingUTxO } = results;
+
+    expect(selection.inputs.size).toBe(2);
+    expect(remainingUTxO.size).toBe(3);
+
+    const expectedFee = BigInt(selection.inputs.size) * 100n;
+    expect(selection.fee).toBe(expectedFee);
+
+    expect(getCoinValueForAddress(mockChangeAddress, selection.change)).toBe(999_800n);
+
+    assertInputSelectionProperties({
+      constraints: {
+        ...MOCK_NO_CONSTRAINTS,
+        minimumCostCoefficient: 100n
+      },
+      implicitValue: {},
+      outputs,
+      results,
+      utxo
+    });
+  });
+
+  it('picks the single largest UTxO for each required asset', async () => {
+    const selector = new LargeFirstSelector({
+      changeAddressResolver: new MockChangeAddressResolver()
+    });
+
+    const utxo = new Set([
+      TxTestUtil.createUnspentTxOutput({
+        assets: asTokenMap([[asAssetId('0'), 100n]]),
+        coins: 3_000_000n
+      }),
+      TxTestUtil.createUnspentTxOutput({
+        assets: asTokenMap([[asAssetId('0'), 50n]]),
+        coins: 3_000_000n
+      }),
+      TxTestUtil.createUnspentTxOutput({
+        assets: asTokenMap([[asAssetId('1'), 8n]]),
+        coins: 4_000_000n
+      }),
+      TxTestUtil.createUnspentTxOutput({ coins: 4_000_000n })
+    ]);
+
+    // Need 90 of asset-0 and 5 of asset-1, plus 2000000 Ada
+    const outputs = new Set([
+      TxTestUtil.createOutput({
+        assets: asTokenMap([
+          [asAssetId('0'), 90n],
+          [asAssetId('1'), 5n]
+        ]),
+        coins: 2_000_000n
+      })
+    ]);
+
+    const constraints = mockConstraintsToConstraints({
+      ...MOCK_NO_CONSTRAINTS,
+      minimumCostCoefficient: 100n
+    });
+
+    const results = await selector.select({
+      constraints,
+      implicitValue: {},
+      outputs,
+      preSelectedUtxo: new Set(),
+      utxo
+    });
+
+    expect(results.selection.inputs.has([...utxo][0])).toBe(true); // 100-token-0
+    expect(results.selection.inputs.has([...utxo][2])).toBe(true); //   8-token-1
+    expect(results.remainingUTxO.has([...utxo][1])).toBe(true);
+
+    assertInputSelectionProperties({
+      constraints: {
+        ...MOCK_NO_CONSTRAINTS,
+        minimumCostCoefficient: 100n
+      },
+      implicitValue: {},
+      outputs,
+      results,
+      utxo
+    });
+  });
+
+  it('accounts for implicit deposits, withdrawals, and mint', async () => {
+    const selector = new LargeFirstSelector({
+      changeAddressResolver: new MockChangeAddressResolver()
+    });
+
+    const utxo = new Set([
+      TxTestUtil.createUnspentTxOutput({ coins: 3_000_000n }),
+      TxTestUtil.createUnspentTxOutput({ coins: 3_000_000n })
+    ]);
+
+    const outputs = new Set<Cardano.TxOut>();
+
+    const implicitValue = {
+      coin: { deposit: 1_000_000n, input: 5_000_000n },
+      mint: asTokenMap([[asAssetId('XYZ'), 2n]])
+    };
+
+    const constraints = mockConstraintsToConstraints({
+      ...MOCK_NO_CONSTRAINTS,
+      minimumCostCoefficient: 100n
+    });
+
+    const { selection, remainingUTxO } = await selector.select({
+      constraints,
+      implicitValue,
+      outputs,
+      preSelectedUtxo: new Set(),
+      utxo
+    });
+
+    const expectedFee = BigInt(selection.inputs.size) * 100n;
+    expect(selection.fee).toBe(expectedFee);
+    expect(getCoinValueForAddress(mockChangeAddress, selection.change)).toBe(6_999_900n);
+    expect(selection.change[0].value.assets?.get(asAssetId('XYZ'))).toBe(2n);
+
+    expect(remainingUTxO.size).toBe(1);
+  });
+});
diff --git a/packages/input-selection/test/change.test.ts b/packages/input-selection/test/change.test.ts
index b4f0d266fdb..734eb1812cf 100644
--- a/packages/input-selection/test/change.test.ts
+++ b/packages/input-selection/test/change.test.ts
@@ -1,5 +1,5 @@
 import { Cardano } from '@cardano-sdk/core';
-import { coalesceChangeBundlesForMinCoinRequirement } from '../src/RoundRobinRandomImprove/change';
+import { coalesceChangeBundlesForMinCoinRequirement } from '../src/change';
 
 const TOKEN1_ASSET_ID = Cardano.AssetId('5c677ba4dd295d9286e0e22786fea9ed735a6ae9c07e7a45ae4d95c84249530000');
 const TOKEN2_ASSET_ID = Cardano.AssetId('5c677ba4dd295d9286e0e22786fea9ed735a6ae9c07e7a45ae4d95c84249530001');
diff --git a/packages/input-selection/test/util/index.ts b/packages/input-selection/test/util/index.ts
index 6bd24d1a6a7..7735e557c6e 100644
--- a/packages/input-selection/test/util/index.ts
+++ b/packages/input-selection/test/util/index.ts
@@ -9,12 +9,14 @@ export const asAssetId = (x: string): Cardano.AssetId => x as unknown as Cardano
 export const asPaymentAddress = (x: string): Cardano.PaymentAddress => x as unknown as Cardano.PaymentAddress;
 export const asTokenMap = (elements: Iterable<[Cardano.AssetId, bigint]>) => new Map<Cardano.AssetId, bigint>(elements);
 
+export const mockChangeAddress =
+  'addr_test1qqydn46r6mhge0kfpqmt36m6q43knzsd9ga32n96m89px3nuzcjqw982pcftgx53fu5527z2cj2tkx2h8ux2vxsg475qypp3m9' as Cardano.PaymentAddress;
+
 export class MockChangeAddressResolver implements ChangeAddressResolver {
   async resolve(selection: Selection) {
     return selection.change.map((txOut) => ({
       ...txOut,
-      address:
-        'addr_test1qqydn46r6mhge0kfpqmt36m6q43knzsd9ga32n96m89px3nuzcjqw982pcftgx53fu5527z2cj2tkx2h8ux2vxsg475qypp3m9' as Cardano.PaymentAddress
+      address: mockChangeAddress
     }));
   }
 }


Reviews:
user: login: AngelCastilloB
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-12T08:22:25Z
user: login: mkazlauskas
state: CHANGES_REQUESTED
author_association: MEMBER
submitted_at: 2025-05-12T08:38:20Z
user: login: AngelCastilloB
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-13T02:53:52Z
user: login: AngelCastilloB
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-13T03:01:59Z
user: login: AngelCastilloB
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-13T03:25:34Z
user: login: AngelCastilloB
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-13T04:01:17Z
user: login: mkazlauskas
body: Great work! 🚀 There are some build errors to resolve (CI fail)
state: DISMISSED
author_association: MEMBER
submitted_at: 2025-05-13T05:24:06Z
user: login: mkazlauskas
state: DISMISSED
author_association: MEMBER
submitted_at: 2025-05-13T07:24:12Z
user: login: rhyslbw
body: Looks good. Just a few minor formatting issues , I’ve highlighted some examples but do a find/replace to correct. Also was there any opportunity to commit an initial change with just simple code refactoring , then introduce the new algorithm? I found it hard to differentiate and see the most important code to review, so keep in mind for next time, if so
state: CHANGES_REQUESTED
author_association: MEMBER
submitted_at: 2025-05-13T07:42:21Z
user: login: AngelCastilloB
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-13T09:50:03Z
user: login: AngelCastilloB
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-13T09:50:06Z
user: login: AngelCastilloB
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-13T09:50:09Z
user: login: rhyslbw
state: APPROVED
author_association: MEMBER
submitted_at: 2025-05-13T10:10:47Z
user: login: mkazlauskas
state: APPROVED
author_association: MEMBER
submitted_at: 2025-05-14T05:20:21Z
user: login: iccicci
body: LGTM!
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-05-14T07:11:35Z

Review comments:
diff_hunk: @@ -343,3 +355,4 @@ const testInputSelection = (name: string, getAlgorithm: () => InputSelector) =>
 
 testInputSelection('RoundRobinRandomImprove', createRoundRobinRandomImprove);
 testInputSelection('GreedySelector', createGreedySelector);
+testInputSelection('LargeFirstSelector', createLargeFirstSelector);
path: packages/input-selection/test/InputSelectionPropertyTesting.test.ts
user: login: AngelCastilloB
body: Successfully ran the property tests for several hours
created_at: 2025-05-12T08:22:25Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,162 @@
+import { Cardano } from '@cardano-sdk/core';
+import { LargeFirstSelector } from '../../src';
+import { MOCK_NO_CONSTRAINTS, mockConstraintsToConstraints } from '../util/selectionConstraints';
+import {
+  MockChangeAddressResolver,
+  asAssetId,
+  asTokenMap,
+  assertInputSelectionProperties,
+  getCoinValueForAddress,
+  mockChangeAddress
+} from '../util';
+import { TxTestUtil } from '@cardano-sdk/util-dev';
+
+describe('LargeFirstSelection', () => {
+  it('consumes only the largest ADA UTXOs needed and leaves the remainder', async () => {
path: packages/input-selection/test/LargeFirstSelection/LargeFirstSelection.test.ts
user: login: mkazlauskas
body: all utxos in the test have the same value (`3_000_000n`) so the test name is not accurate for what's being asserted
```suggestion
  it('consumes just enough ADA UTXOs needed and leaves the remainder', async () => {
```
created_at: 2025-05-12T08:30:20Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,409 @@
+/* eslint-disable max-params */
+import { Cardano, coalesceValueQuantities } from '@cardano-sdk/core';
+import { ChangeAddressResolver } from '../ChangeAddress';
+import {
+  ImplicitTokens,
+  addTokenMaps,
+  getCoinQuantity,
+  hasNegativeAssetValue,
+  mintToImplicitTokens,
+  sortByAssetQuantity,
+  sortByCoins,
+  stubMaxSizeAddress,
+  subtractTokenMaps,
+  toValues
+} from '../util';
+import {
+  ImplicitValue,
+  InputSelectionParameters,
+  InputSelector,
+  SelectionConstraints,
+  SelectionResult
+} from '../types';
+import { InputSelectionError, InputSelectionFailure } from '../InputSelectionError';
+import { MAX_U64 } from '../RoundRobinRandomImprove';
+import { computeChangeAndAdjustForFee } from '../RoundRobinRandomImprove/change';
path: packages/input-selection/src/LargeFirstSelection/LargeFirstInputSelector.ts
user: login: mkazlauskas
body: I think it's a little bit odd to import utils from another algorithm. Maybe we could hoist those to `util.ts`
created_at: 2025-05-12T08:36:07Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,409 @@
+/* eslint-disable max-params */
+import { Cardano, coalesceValueQuantities } from '@cardano-sdk/core';
+import { ChangeAddressResolver } from '../ChangeAddress';
+import {
+  ImplicitTokens,
+  addTokenMaps,
+  getCoinQuantity,
+  hasNegativeAssetValue,
+  mintToImplicitTokens,
+  sortByAssetQuantity,
+  sortByCoins,
+  stubMaxSizeAddress,
+  subtractTokenMaps,
+  toValues
+} from '../util';
+import {
+  ImplicitValue,
+  InputSelectionParameters,
+  InputSelector,
+  SelectionConstraints,
+  SelectionResult
+} from '../types';
+import { InputSelectionError, InputSelectionFailure } from '../InputSelectionError';
+import { MAX_U64 } from '../RoundRobinRandomImprove';
+import { computeChangeAndAdjustForFee } from '../RoundRobinRandomImprove/change';
+import { sortUtxoByTxIn } from '../GreedySelection';
+import uniq from 'lodash/uniq.js';
+
+const DETERMINISTIC_RANDOM: typeof Math.random = () => 0;
+
+/** LargeFirst selection initialization properties. */
+export interface LargeFirstSelectorProps {
+  changeAddressResolver: ChangeAddressResolver;
+}
+
+/**
+ * Input selector that implements a "large-first" strategy.
+ *
+ * This strategy selects the largest UTXOs per asset, one asset at a time, until the requirements
+ * of all assets in the outputs + fees and implicit values are satisfied.
+ */
+export class LargeFirstSelector implements InputSelector {
+  #props: LargeFirstSelectorProps;
+
+  /** Creates a new instance of the LargeFirstSelector. */
+  constructor(props: LargeFirstSelectorProps) {
+    this.#props = props;
+  }
+
+  /**
+   * Selects inputs using a large-first strategy:
+   * Selects largest UTXOs per asset until target is met
+   * Then selects largest UTxOs by ADA
+   * Then iteratively adds more UTxOs if needed to cover fees
+   *
+   * @param params Input selection parameters (available UTXOs, outputs, constraints, etc.)
+   * @returns A complete selection including inputs, outputs, change, and fee.
+   * @throws {InputSelectionError} If the selection cannot satisfy the outputs and fees.
+   */
+  async select(params: InputSelectionParameters): Promise<SelectionResult> {
+    const { utxo, preSelectedUtxo, outputs, constraints, implicitValue } = params;
+
+    const preSelected = [...preSelectedUtxo];
+    const available = [...utxo].filter(
+      ([txIn]) => !preSelected.some(([preTxIn]) => preTxIn.txId === txIn.txId && preTxIn.index === txIn.index)
+    );
+    const allAvailable = [...preSelected, ...available];
+
+    const { totalLovelaceOutput, outputAssets } = this.#computeNetImplicitSelectionValues(
+      new Set<Cardano.Utxo>(allAvailable),
+      outputs,
+      implicitValue
+    );
+
+    let workingUtxo = new Set<Cardano.Utxo>(preSelected);
+    workingUtxo = this.#selectAssets(outputAssets, allAvailable, [...workingUtxo]);
+    workingUtxo = this.#selectLovelace(
+      totalLovelaceOutput,
+      allAvailable,
+      workingUtxo,
+      implicitValue?.coin?.input ?? 0n
+    );
+
+    const { finalSelection, fee, change } = await this.#expandUtxosUntilFeeCovered(
+      workingUtxo,
+      outputs,
+      allAvailable,
+      outputAssets,
+      constraints,
+      this.#props.changeAddressResolver,
+      implicitValue
+    );
+
+    const limit = await constraints.computeSelectionLimit({ change, fee, inputs: finalSelection, outputs });
+    if (finalSelection.size > limit) throw new InputSelectionError(InputSelectionFailure.MaximumInputCountExceeded);
+
+    return {
+      remainingUTxO: this.#computeRemainingUtxo([...utxo], finalSelection),
+      selection: {
+        change,
+        fee,
+        inputs: new Set([...finalSelection].sort(sortUtxoByTxIn)),
+        outputs
+      }
+    };
+  }
+
+  /**
+   * Aggregate Lovelace that enters and leaves the transaction,
+   * taking implicit withdrawals / deposits into account.
+   *
+   * @param inputs   Array of `Value`s held by the selected UTxOs.
+   * @param outputs  Array of explicit transaction outputs.
+   * @param implicit Optional implicit values.
+   * @returns An object with
+   * `totalIn`  — Lovelace provided by UTxOs + withdrawals
+   * `totalOut` — Lovelace required for explicit outputs + deposits
+   */
+  #aggregateLovelace(
+    inputs: Cardano.Value[],
+    outputs: Cardano.Value[],
+    implicit?: ImplicitValue
+  ): { totalIn: bigint; totalOut: bigint } {
+    const utxoAda = getCoinQuantity(inputs);
+    const outputAda = getCoinQuantity(outputs);
+    const withdrawAda = implicit?.coin?.input ?? 0n;
+    const depositAda = implicit?.coin?.deposit ?? 0n;
+
+    return {
+      totalIn: utxoAda + withdrawAda,
+      totalOut: outputAda + depositAda
+    };
+  }
+
+  /**
+   * Compute two token maps:
+   * - required: the exact quantity of every asset that must be provided by inputs in order to satisfy explicit outputs plus any burns.
+   * - available: the quantity of every asset that is actually provided by inputs + positive mint.
+   *
+   * @param inputs   Values contained in the UTxOs selected so far.
+   * @param outputs  Values required by the user’s explicit transaction outputs.
+   * @param implicit Optional `mint` map (positive = forge, negative = burn).
+   * @returns `{ required, available }`
+   */
+  #aggregateAssets(
+    inputs: Cardano.Value[],
+    outputs: Cardano.Value[],
+    implicit?: ImplicitValue
+  ): {
+    required: Cardano.TokenMap;
+    available: Cardano.TokenMap;
+  } {
+    const outputsMap = coalesceValueQuantities(outputs).assets ?? new Map<Cardano.AssetId, bigint>();
+    const utxoMap = coalesceValueQuantities(inputs).assets;
+    const mint = implicit?.mint ?? new Map<Cardano.AssetId, bigint>();
+
+    const posMint = new Map<Cardano.AssetId, bigint>();
+    const negMint = new Map<Cardano.AssetId, bigint>();
+
+    for (const [id, q] of mint) (q > 0n ? posMint : negMint).set(id, q > 0n ? q : -q);
+
+    let required = addTokenMaps(outputsMap, negMint);
+    required = subtractTokenMaps(required, posMint) ?? new Map<Cardano.AssetId, bigint>();
+
+    const available = addTokenMaps(utxoMap, posMint) ?? new Map<Cardano.AssetId, bigint>();
+
+    return { available, required };
+  }
+
+  /**
+   * Computes the total Lovelace and asset output requirements, including the effects
+   * of implicit values such as key deposits, withdrawals, and minting/burning.
+   *
+   * Minting and burning are treated as negative or positive contributions to input balance,
+   * and are subtracted from the output requirements.
+   *
+   * @param inputs The full set of selected UTXOs (including pre-selected).
+   * @param outputs The transaction outputs.
+   * @param implicitValue Optional implicit values including deposits, withdrawals, and minting.
+   * @returns An object with:
+   * `totalLovelaceInput`: Sum of all input Lovelace, including withdrawals.
+   * `totalLovelaceOutput`: Sum of all output Lovelace, including deposits.
+   * `outputAssets`: Asset requirements after accounting for minting/burning.
+   * @throws {InputSelectionError} If balance is insufficient to satisfy the target.
+   */
+  #computeNetImplicitSelectionValues(
+    inputs: Set<Cardano.Utxo>,
+    outputs: Set<Cardano.TxOut>,
+    implicitValue?: ImplicitValue
+  ): {
+    totalLovelaceInput: bigint;
+    totalLovelaceOutput: bigint;
+    outputAssets: Cardano.TokenMap;
+  } {
+    const inputVals = toValues([...inputs]);
+    const outputVals = toValues([...outputs]);
+
+    const { totalIn, totalOut } = this.#aggregateLovelace(inputVals, outputVals, implicitValue);
+    const { required, available } = this.#aggregateAssets(inputVals, outputVals, implicitValue);
+
+    const changeAda = totalIn - totalOut;
+    const changeAssets = subtractTokenMaps(available, required);
+
+    if (inputs.size === 0 || changeAda < 0n || hasNegativeAssetValue(changeAssets))
+      throw new InputSelectionError(InputSelectionFailure.UtxoBalanceInsufficient);
+
+    return {
+      outputAssets: required,
+      totalLovelaceInput: totalIn,
+      totalLovelaceOutput: totalOut
+    };
+  }
+
+  /**
+   * Selects the largest UTXOs per required asset until each target amount is fulfilled.
+   *
+   * @param requiredAssets The asset quantities required by the transaction outputs.
+   * @param allAvailable All available UTXOs (including preselected ones).
+   * @param preSelected The UTXOs already selected for the transaction.
+   * @returns A set of selected UTXOs covering the asset requirements.
+   * @throws {InputSelectionError} If any asset cannot be sufficiently fulfilled.
+   */
+  #selectAssets(
+    requiredAssets: Map<Cardano.AssetId, bigint>,
+    allAvailable: Cardano.Utxo[],
+    preSelected: Cardano.Utxo[]
+  ): Set<Cardano.Utxo> {
+    const selected = new Set(preSelected);
+
+    for (const [assetId, requiredQuantity] of requiredAssets) {
+      const candidates = allAvailable
+        .filter(([_, out]) => (out.value.assets?.get(assetId) ?? 0n) > 0n)
+        .sort(([, a], [, b]) => sortByAssetQuantity(assetId)(a, b));
+
+      let accumulated = 0n;
+
+      for (const candidate of candidates) {
+        selected.add(candidate);
+        accumulated += candidate[1].value.assets?.get(assetId) ?? 0n;
+        if (accumulated >= requiredQuantity) break;
+      }
+
+      if (accumulated < requiredQuantity) throw new InputSelectionError(InputSelectionFailure.UtxoBalanceInsufficient);
+    }
+
+    return selected;
+  }
+
+  /**
+   * Selects UTXOs (largest ADA first) until the total Lovelace covers the target amount.
+   *
+   * @param target The required amount of Lovelace.
+   * @param allAvailable All available UTXOs.
+   * @param selected The current set of already selected UTXOs.
+   * @param implicitCoinInput The implicit coin input amount.
+   * @returns A new set including the original selected UTXOs plus any added for ADA coverage.
+   * @throws {InputSelectionError} If the Lovelace requirement cannot be fulfilled.
+   */
+  #selectLovelace(
+    target: bigint,
+    allAvailable: Cardano.Utxo[],
+    selected: Set<Cardano.Utxo>,
+    implicitCoinInput: bigint
+  ): Set<Cardano.Utxo> {
+    const result = new Set(selected);
+    const selectedTxIns = new Set([...selected].map(([txIn]) => txIn));
+
+    const adaCandidates = allAvailable
+      .filter(([txIn]) => !selectedTxIns.has(txIn))
+      .sort(([, a], [, b]) => sortByCoins(a, b));
+
+    let adaAccumulated = getCoinQuantity(toValues([...result])) + implicitCoinInput;
+
+    for (const candidate of adaCandidates) {
+      if (adaAccumulated >= target) break;
+      result.add(candidate);
+      adaAccumulated += candidate[1].value.coins;
+    }
+
+    if (result.size === 0) {
+      if (adaCandidates.length === 0) throw new InputSelectionError(InputSelectionFailure.UtxoBalanceInsufficient);
+      result.add(adaCandidates[0]);
+    }
+
+    if (adaAccumulated < target) throw new InputSelectionError(InputSelectionFailure.UtxoBalanceInsufficient);
+
+    return result;
+  }
+
+  /**
+   * Computes the UTXOs that were not selected, given the original list and selected set.
+   *
+   * @param original The original list of available UTXOs (excluding preselected).
+   * @param used The set of selected UTXOs.
+   * @returns A new set of UTXOs that were not consumed in the transaction.
+   */
+  #computeRemainingUtxo(original: Cardano.Utxo[], used: Set<Cardano.Utxo>): Set<Cardano.Utxo> {
+    const usedTxIns = new Set([...used].map(([txIn]) => txIn));
+    return new Set(original.filter(([txIn]) => !usedTxIns.has(txIn)));
+  }
+
+  /**
+   * Select additional UTXOs until the fee and min-ADA requirements are
+   * satisfied, then build the final change outputs.
+   *
+   * @param initialInputs The UTXOs already chosen for assets and/or pre-selected by the wallet.
+   * @param outputs The explicit transaction outputs.
+   * @param allAvailable Every UTXO the wallet can spend (pre-selected ∪ utxo).
+   * @param requiredAssets Aggregate asset requirements computed from `outputs` + burn.  Used for fee/ADA expansion.
+   * @param constraints Network / wallet selection constraints (min-ADA, fee estimator, bundle size, limit…).
+   * @param changeAddressResolver Callback that assigns addresses (or further splits) for the provisional change bundles returned by the fee engine.
+   * @param implicitValue Optional implicit components (deposits, withdrawals, mint or burn)
+   * @returns An object containing Set of all inputs that will appear in the tx body, minimum fee returned by the cost model and an Array of change outputs.
+   */
+  async #expandUtxosUntilFeeCovered(
+    initialInputs: Set<Cardano.Utxo>,
+    outputs: Set<Cardano.TxOut>,
+    allAvailable: Cardano.Utxo[],
+    requiredAssets: Cardano.TokenMap,
+    constraints: SelectionConstraints,
+    changeAddressResolver: ChangeAddressResolver,
+    implicitValue?: ImplicitValue
+  ): Promise<{ finalSelection: Set<Cardano.Utxo>; fee: bigint; change: Cardano.TxOut[] }> {
+    const utxoSelectedArr = [...initialInputs];
+    const utxoRemainingArr = allAvailable
+      .filter((u) => !initialInputs.has(u))
+      .sort(([, a], [, b]) => sortByCoins(a, b)); // ADA-descending
+
+    const outputValues = toValues([...outputs]);
+
+    const changeAddress = stubMaxSizeAddress;
+
+    const implicitCoin: Required<Cardano.util.ImplicitCoin> = {
+      deposit: implicitValue?.coin?.deposit || 0n,
+      input: implicitValue?.coin?.input || 0n,
+      reclaimDeposit: implicitValue?.coin?.reclaimDeposit || 0n,
+      withdrawals: implicitValue?.coin?.withdrawals || 0n
+    };
+    const mintMap: Cardano.TokenMap = implicitValue?.mint || new Map();
+    const uniqueTxAssetIDs = uniq([...requiredAssets.keys(), ...mintMap.keys()]);
+
+    const { implicitTokensInput, implicitTokensSpend } = mintToImplicitTokens(mintMap);
+    const implicitTokens: ImplicitTokens = {
+      input: (assetId) => implicitTokensInput.get(assetId) || 0n,
+      spend: (assetId) => implicitTokensSpend.get(assetId) || 0n
+    };
+    const {
+      change,
+      fee,
+      inputs: finalInputs
+    } = await computeChangeAndAdjustForFee({
+      computeMinimumCoinQuantity: constraints.computeMinimumCoinQuantity,
+      estimateTxCosts: (utxos, changeValues) =>
+        constraints.computeMinimumCost({
+          change: changeValues.map(
+            (value) =>
+              ({
+                address: changeAddress,
+                value
+              } as Cardano.TxOut)
+          ),
+          fee: MAX_U64,
+          inputs: new Set(utxos),
+          outputs
+        }),
+      implicitValue: {
+        implicitCoin,
+        implicitTokens
+      },
+      outputValues,
+      // `DETERMINISTIC_RANDOM` (`() => 0`) is passed so that the computeChangeAndAdjustForFee
+      // always picks the *first* entry from the ADA-descending `utxoRemaining`
+      // list.
+      random: DETERMINISTIC_RANDOM,
path: packages/input-selection/src/LargeFirstSelection/LargeFirstInputSelector.ts
user: login: mkazlauskas
body: This feels a little hacky, perhaps we could refactor it slightly to decouple from random-improve internals
```suggestion
      pickAdditionalUtxo: (utxos) => utxos[0],
```
created_at: 2025-05-12T08:37:49Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,162 @@
+import { Cardano } from '@cardano-sdk/core';
+import { LargeFirstSelector } from '../../src';
+import { MOCK_NO_CONSTRAINTS, mockConstraintsToConstraints } from '../util/selectionConstraints';
+import {
+  MockChangeAddressResolver,
+  asAssetId,
+  asTokenMap,
+  assertInputSelectionProperties,
+  getCoinValueForAddress,
+  mockChangeAddress
+} from '../util';
+import { TxTestUtil } from '@cardano-sdk/util-dev';
+
+describe('LargeFirstSelection', () => {
+  it('consumes only the largest ADA UTXOs needed and leaves the remainder', async () => {
path: packages/input-selection/test/LargeFirstSelection/LargeFirstSelection.test.ts
user: login: AngelCastilloB
body: done here https://github.com/input-output-hk/cardano-js-sdk/pull/1621/commits/c64fabc330187e9a108841491ba761d093df096f
created_at: 2025-05-13T02:53:52Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,409 @@
+/* eslint-disable max-params */
+import { Cardano, coalesceValueQuantities } from '@cardano-sdk/core';
+import { ChangeAddressResolver } from '../ChangeAddress';
+import {
+  ImplicitTokens,
+  addTokenMaps,
+  getCoinQuantity,
+  hasNegativeAssetValue,
+  mintToImplicitTokens,
+  sortByAssetQuantity,
+  sortByCoins,
+  stubMaxSizeAddress,
+  subtractTokenMaps,
+  toValues
+} from '../util';
+import {
+  ImplicitValue,
+  InputSelectionParameters,
+  InputSelector,
+  SelectionConstraints,
+  SelectionResult
+} from '../types';
+import { InputSelectionError, InputSelectionFailure } from '../InputSelectionError';
+import { MAX_U64 } from '../RoundRobinRandomImprove';
+import { computeChangeAndAdjustForFee } from '../RoundRobinRandomImprove/change';
path: packages/input-selection/src/LargeFirstSelection/LargeFirstInputSelector.ts
user: login: AngelCastilloB
body: done here https://github.com/input-output-hk/cardano-js-sdk/pull/1621/commits/2e97aa84591000e3c8b25cf0ad999cef94500d60
created_at: 2025-05-13T03:01:59Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,409 @@
+/* eslint-disable max-params */
+import { Cardano, coalesceValueQuantities } from '@cardano-sdk/core';
+import { ChangeAddressResolver } from '../ChangeAddress';
+import {
+  ImplicitTokens,
+  addTokenMaps,
+  getCoinQuantity,
+  hasNegativeAssetValue,
+  mintToImplicitTokens,
+  sortByAssetQuantity,
+  sortByCoins,
+  stubMaxSizeAddress,
+  subtractTokenMaps,
+  toValues
+} from '../util';
+import {
+  ImplicitValue,
+  InputSelectionParameters,
+  InputSelector,
+  SelectionConstraints,
+  SelectionResult
+} from '../types';
+import { InputSelectionError, InputSelectionFailure } from '../InputSelectionError';
+import { MAX_U64 } from '../RoundRobinRandomImprove';
+import { computeChangeAndAdjustForFee } from '../RoundRobinRandomImprove/change';
+import { sortUtxoByTxIn } from '../GreedySelection';
+import uniq from 'lodash/uniq.js';
+
+const DETERMINISTIC_RANDOM: typeof Math.random = () => 0;
+
+/** LargeFirst selection initialization properties. */
+export interface LargeFirstSelectorProps {
+  changeAddressResolver: ChangeAddressResolver;
+}
+
+/**
+ * Input selector that implements a "large-first" strategy.
+ *
+ * This strategy selects the largest UTXOs per asset, one asset at a time, until the requirements
+ * of all assets in the outputs + fees and implicit values are satisfied.
+ */
+export class LargeFirstSelector implements InputSelector {
+  #props: LargeFirstSelectorProps;
+
+  /** Creates a new instance of the LargeFirstSelector. */
+  constructor(props: LargeFirstSelectorProps) {
+    this.#props = props;
+  }
+
+  /**
+   * Selects inputs using a large-first strategy:
+   * Selects largest UTXOs per asset until target is met
+   * Then selects largest UTxOs by ADA
+   * Then iteratively adds more UTxOs if needed to cover fees
+   *
+   * @param params Input selection parameters (available UTXOs, outputs, constraints, etc.)
+   * @returns A complete selection including inputs, outputs, change, and fee.
+   * @throws {InputSelectionError} If the selection cannot satisfy the outputs and fees.
+   */
+  async select(params: InputSelectionParameters): Promise<SelectionResult> {
+    const { utxo, preSelectedUtxo, outputs, constraints, implicitValue } = params;
+
+    const preSelected = [...preSelectedUtxo];
+    const available = [...utxo].filter(
+      ([txIn]) => !preSelected.some(([preTxIn]) => preTxIn.txId === txIn.txId && preTxIn.index === txIn.index)
+    );
+    const allAvailable = [...preSelected, ...available];
+
+    const { totalLovelaceOutput, outputAssets } = this.#computeNetImplicitSelectionValues(
+      new Set<Cardano.Utxo>(allAvailable),
+      outputs,
+      implicitValue
+    );
+
+    let workingUtxo = new Set<Cardano.Utxo>(preSelected);
+    workingUtxo = this.#selectAssets(outputAssets, allAvailable, [...workingUtxo]);
+    workingUtxo = this.#selectLovelace(
+      totalLovelaceOutput,
+      allAvailable,
+      workingUtxo,
+      implicitValue?.coin?.input ?? 0n
+    );
+
+    const { finalSelection, fee, change } = await this.#expandUtxosUntilFeeCovered(
+      workingUtxo,
+      outputs,
+      allAvailable,
+      outputAssets,
+      constraints,
+      this.#props.changeAddressResolver,
+      implicitValue
+    );
+
+    const limit = await constraints.computeSelectionLimit({ change, fee, inputs: finalSelection, outputs });
+    if (finalSelection.size > limit) throw new InputSelectionError(InputSelectionFailure.MaximumInputCountExceeded);
+
+    return {
+      remainingUTxO: this.#computeRemainingUtxo([...utxo], finalSelection),
+      selection: {
+        change,
+        fee,
+        inputs: new Set([...finalSelection].sort(sortUtxoByTxIn)),
+        outputs
+      }
+    };
+  }
+
+  /**
+   * Aggregate Lovelace that enters and leaves the transaction,
+   * taking implicit withdrawals / deposits into account.
+   *
+   * @param inputs   Array of `Value`s held by the selected UTxOs.
+   * @param outputs  Array of explicit transaction outputs.
+   * @param implicit Optional implicit values.
+   * @returns An object with
+   * `totalIn`  — Lovelace provided by UTxOs + withdrawals
+   * `totalOut` — Lovelace required for explicit outputs + deposits
+   */
+  #aggregateLovelace(
+    inputs: Cardano.Value[],
+    outputs: Cardano.Value[],
+    implicit?: ImplicitValue
+  ): { totalIn: bigint; totalOut: bigint } {
+    const utxoAda = getCoinQuantity(inputs);
+    const outputAda = getCoinQuantity(outputs);
+    const withdrawAda = implicit?.coin?.input ?? 0n;
+    const depositAda = implicit?.coin?.deposit ?? 0n;
+
+    return {
+      totalIn: utxoAda + withdrawAda,
+      totalOut: outputAda + depositAda
+    };
+  }
+
+  /**
+   * Compute two token maps:
+   * - required: the exact quantity of every asset that must be provided by inputs in order to satisfy explicit outputs plus any burns.
+   * - available: the quantity of every asset that is actually provided by inputs + positive mint.
+   *
+   * @param inputs   Values contained in the UTxOs selected so far.
+   * @param outputs  Values required by the user’s explicit transaction outputs.
+   * @param implicit Optional `mint` map (positive = forge, negative = burn).
+   * @returns `{ required, available }`
+   */
+  #aggregateAssets(
+    inputs: Cardano.Value[],
+    outputs: Cardano.Value[],
+    implicit?: ImplicitValue
+  ): {
+    required: Cardano.TokenMap;
+    available: Cardano.TokenMap;
+  } {
+    const outputsMap = coalesceValueQuantities(outputs).assets ?? new Map<Cardano.AssetId, bigint>();
+    const utxoMap = coalesceValueQuantities(inputs).assets;
+    const mint = implicit?.mint ?? new Map<Cardano.AssetId, bigint>();
+
+    const posMint = new Map<Cardano.AssetId, bigint>();
+    const negMint = new Map<Cardano.AssetId, bigint>();
+
+    for (const [id, q] of mint) (q > 0n ? posMint : negMint).set(id, q > 0n ? q : -q);
+
+    let required = addTokenMaps(outputsMap, negMint);
+    required = subtractTokenMaps(required, posMint) ?? new Map<Cardano.AssetId, bigint>();
+
+    const available = addTokenMaps(utxoMap, posMint) ?? new Map<Cardano.AssetId, bigint>();
+
+    return { available, required };
+  }
+
+  /**
+   * Computes the total Lovelace and asset output requirements, including the effects
+   * of implicit values such as key deposits, withdrawals, and minting/burning.
+   *
+   * Minting and burning are treated as negative or positive contributions to input balance,
+   * and are subtracted from the output requirements.
+   *
+   * @param inputs The full set of selected UTXOs (including pre-selected).
+   * @param outputs The transaction outputs.
+   * @param implicitValue Optional implicit values including deposits, withdrawals, and minting.
+   * @returns An object with:
+   * `totalLovelaceInput`: Sum of all input Lovelace, including withdrawals.
+   * `totalLovelaceOutput`: Sum of all output Lovelace, including deposits.
+   * `outputAssets`: Asset requirements after accounting for minting/burning.
+   * @throws {InputSelectionError} If balance is insufficient to satisfy the target.
+   */
+  #computeNetImplicitSelectionValues(
+    inputs: Set<Cardano.Utxo>,
+    outputs: Set<Cardano.TxOut>,
+    implicitValue?: ImplicitValue
+  ): {
+    totalLovelaceInput: bigint;
+    totalLovelaceOutput: bigint;
+    outputAssets: Cardano.TokenMap;
+  } {
+    const inputVals = toValues([...inputs]);
+    const outputVals = toValues([...outputs]);
+
+    const { totalIn, totalOut } = this.#aggregateLovelace(inputVals, outputVals, implicitValue);
+    const { required, available } = this.#aggregateAssets(inputVals, outputVals, implicitValue);
+
+    const changeAda = totalIn - totalOut;
+    const changeAssets = subtractTokenMaps(available, required);
+
+    if (inputs.size === 0 || changeAda < 0n || hasNegativeAssetValue(changeAssets))
+      throw new InputSelectionError(InputSelectionFailure.UtxoBalanceInsufficient);
+
+    return {
+      outputAssets: required,
+      totalLovelaceInput: totalIn,
+      totalLovelaceOutput: totalOut
+    };
+  }
+
+  /**
+   * Selects the largest UTXOs per required asset until each target amount is fulfilled.
+   *
+   * @param requiredAssets The asset quantities required by the transaction outputs.
+   * @param allAvailable All available UTXOs (including preselected ones).
+   * @param preSelected The UTXOs already selected for the transaction.
+   * @returns A set of selected UTXOs covering the asset requirements.
+   * @throws {InputSelectionError} If any asset cannot be sufficiently fulfilled.
+   */
+  #selectAssets(
+    requiredAssets: Map<Cardano.AssetId, bigint>,
+    allAvailable: Cardano.Utxo[],
+    preSelected: Cardano.Utxo[]
+  ): Set<Cardano.Utxo> {
+    const selected = new Set(preSelected);
+
+    for (const [assetId, requiredQuantity] of requiredAssets) {
+      const candidates = allAvailable
+        .filter(([_, out]) => (out.value.assets?.get(assetId) ?? 0n) > 0n)
+        .sort(([, a], [, b]) => sortByAssetQuantity(assetId)(a, b));
+
+      let accumulated = 0n;
+
+      for (const candidate of candidates) {
+        selected.add(candidate);
+        accumulated += candidate[1].value.assets?.get(assetId) ?? 0n;
+        if (accumulated >= requiredQuantity) break;
+      }
+
+      if (accumulated < requiredQuantity) throw new InputSelectionError(InputSelectionFailure.UtxoBalanceInsufficient);
+    }
+
+    return selected;
+  }
+
+  /**
+   * Selects UTXOs (largest ADA first) until the total Lovelace covers the target amount.
+   *
+   * @param target The required amount of Lovelace.
+   * @param allAvailable All available UTXOs.
+   * @param selected The current set of already selected UTXOs.
+   * @param implicitCoinInput The implicit coin input amount.
+   * @returns A new set including the original selected UTXOs plus any added for ADA coverage.
+   * @throws {InputSelectionError} If the Lovelace requirement cannot be fulfilled.
+   */
+  #selectLovelace(
+    target: bigint,
+    allAvailable: Cardano.Utxo[],
+    selected: Set<Cardano.Utxo>,
+    implicitCoinInput: bigint
+  ): Set<Cardano.Utxo> {
+    const result = new Set(selected);
+    const selectedTxIns = new Set([...selected].map(([txIn]) => txIn));
+
+    const adaCandidates = allAvailable
+      .filter(([txIn]) => !selectedTxIns.has(txIn))
+      .sort(([, a], [, b]) => sortByCoins(a, b));
+
+    let adaAccumulated = getCoinQuantity(toValues([...result])) + implicitCoinInput;
+
+    for (const candidate of adaCandidates) {
+      if (adaAccumulated >= target) break;
+      result.add(candidate);
+      adaAccumulated += candidate[1].value.coins;
+    }
+
+    if (result.size === 0) {
+      if (adaCandidates.length === 0) throw new InputSelectionError(InputSelectionFailure.UtxoBalanceInsufficient);
+      result.add(adaCandidates[0]);
+    }
+
+    if (adaAccumulated < target) throw new InputSelectionError(InputSelectionFailure.UtxoBalanceInsufficient);
+
+    return result;
+  }
+
+  /**
+   * Computes the UTXOs that were not selected, given the original list and selected set.
+   *
+   * @param original The original list of available UTXOs (excluding preselected).
+   * @param used The set of selected UTXOs.
+   * @returns A new set of UTXOs that were not consumed in the transaction.
+   */
+  #computeRemainingUtxo(original: Cardano.Utxo[], used: Set<Cardano.Utxo>): Set<Cardano.Utxo> {
+    const usedTxIns = new Set([...used].map(([txIn]) => txIn));
+    return new Set(original.filter(([txIn]) => !usedTxIns.has(txIn)));
+  }
+
+  /**
+   * Select additional UTXOs until the fee and min-ADA requirements are
+   * satisfied, then build the final change outputs.
+   *
+   * @param initialInputs The UTXOs already chosen for assets and/or pre-selected by the wallet.
+   * @param outputs The explicit transaction outputs.
+   * @param allAvailable Every UTXO the wallet can spend (pre-selected ∪ utxo).
+   * @param requiredAssets Aggregate asset requirements computed from `outputs` + burn.  Used for fee/ADA expansion.
+   * @param constraints Network / wallet selection constraints (min-ADA, fee estimator, bundle size, limit…).
+   * @param changeAddressResolver Callback that assigns addresses (or further splits) for the provisional change bundles returned by the fee engine.
+   * @param implicitValue Optional implicit components (deposits, withdrawals, mint or burn)
+   * @returns An object containing Set of all inputs that will appear in the tx body, minimum fee returned by the cost model and an Array of change outputs.
+   */
+  async #expandUtxosUntilFeeCovered(
+    initialInputs: Set<Cardano.Utxo>,
+    outputs: Set<Cardano.TxOut>,
+    allAvailable: Cardano.Utxo[],
+    requiredAssets: Cardano.TokenMap,
+    constraints: SelectionConstraints,
+    changeAddressResolver: ChangeAddressResolver,
+    implicitValue?: ImplicitValue
+  ): Promise<{ finalSelection: Set<Cardano.Utxo>; fee: bigint; change: Cardano.TxOut[] }> {
+    const utxoSelectedArr = [...initialInputs];
+    const utxoRemainingArr = allAvailable
+      .filter((u) => !initialInputs.has(u))
+      .sort(([, a], [, b]) => sortByCoins(a, b)); // ADA-descending
+
+    const outputValues = toValues([...outputs]);
+
+    const changeAddress = stubMaxSizeAddress;
+
+    const implicitCoin: Required<Cardano.util.ImplicitCoin> = {
+      deposit: implicitValue?.coin?.deposit || 0n,
+      input: implicitValue?.coin?.input || 0n,
+      reclaimDeposit: implicitValue?.coin?.reclaimDeposit || 0n,
+      withdrawals: implicitValue?.coin?.withdrawals || 0n
+    };
+    const mintMap: Cardano.TokenMap = implicitValue?.mint || new Map();
+    const uniqueTxAssetIDs = uniq([...requiredAssets.keys(), ...mintMap.keys()]);
+
+    const { implicitTokensInput, implicitTokensSpend } = mintToImplicitTokens(mintMap);
+    const implicitTokens: ImplicitTokens = {
+      input: (assetId) => implicitTokensInput.get(assetId) || 0n,
+      spend: (assetId) => implicitTokensSpend.get(assetId) || 0n
+    };
+    const {
+      change,
+      fee,
+      inputs: finalInputs
+    } = await computeChangeAndAdjustForFee({
+      computeMinimumCoinQuantity: constraints.computeMinimumCoinQuantity,
+      estimateTxCosts: (utxos, changeValues) =>
+        constraints.computeMinimumCost({
+          change: changeValues.map(
+            (value) =>
+              ({
+                address: changeAddress,
+                value
+              } as Cardano.TxOut)
+          ),
+          fee: MAX_U64,
+          inputs: new Set(utxos),
+          outputs
+        }),
+      implicitValue: {
+        implicitCoin,
+        implicitTokens
+      },
+      outputValues,
+      // `DETERMINISTIC_RANDOM` (`() => 0`) is passed so that the computeChangeAndAdjustForFee
+      // always picks the *first* entry from the ADA-descending `utxoRemaining`
+      // list.
+      random: DETERMINISTIC_RANDOM,
path: packages/input-selection/src/LargeFirstSelection/LargeFirstInputSelector.ts
user: login: AngelCastilloB
body: done here https://github.com/input-output-hk/cardano-js-sdk/pull/1621/commits/15976cecb629369018a43d23d93310a99ce64c12
created_at: 2025-05-13T03:25:34Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,162 @@
+import { Cardano } from '@cardano-sdk/core';
+import { LargeFirstSelector } from '../../src';
+import { MOCK_NO_CONSTRAINTS, mockConstraintsToConstraints } from '../util/selectionConstraints';
+import {
+  MockChangeAddressResolver,
+  asAssetId,
+  asTokenMap,
+  assertInputSelectionProperties,
+  getCoinValueForAddress,
+  mockChangeAddress
+} from '../util';
+import { TxTestUtil } from '@cardano-sdk/util-dev';
+
+describe('LargeFirstSelection', () => {
+  it('consumes only the largest ADA UTXOs needed and leaves the remainder', async () => {
path: packages/input-selection/test/LargeFirstSelection/LargeFirstSelection.test.ts
user: login: AngelCastilloB
body: also added a few more tests to better assert the large first behaviour https://github.com/input-output-hk/cardano-js-sdk/pull/1621/commits/4f3f9f261d409e1b5c3185e86c3a1c5f641328ec
created_at: 2025-05-13T04:01:17Z
author_association: MEMBER
diff_hunk: @@ -227,22 +227,141 @@ export const splitChange = async (
 };
 
 /**
- * Sorts the given TxIn set first by txId and then by index.
+ * Given a set of input and outputs, compute the fee. Then extract the fee from the change output
+ * with the highest value.
  *
- * @param lhs The left-hand side of the comparison operation.
- * @param rhs The left-hand side of the comparison operation.
+ * @param changeLovelace The available amount of lovelace to be used as change.
+ * @param constraints The selection constraints.
+ * @param inputs The inputs of the transaction.
+ * @param outputs The outputs of the transaction.
+ * @param changeOutputs The list of change outputs.
+ * @param currentFee The current computed fee for this selection.
  */
-export const sortTxIn = (lhs: Cardano.TxIn, rhs: Cardano.TxIn) => {
-  const txIdComparison = lhs.txId.localeCompare(rhs.txId);
-  if (txIdComparison !== 0) return txIdComparison;
+export const adjustOutputsForFee = async (
+  changeLovelace: bigint,
+  constraints: SelectionConstraints,
+  inputs: Set<Cardano.Utxo>,
+  outputs: Set<Cardano.TxOut>,
+  changeOutputs: Array<Cardano.TxOut>,
+  currentFee: bigint
+): Promise<{
+  fee: bigint;
+  change: Array<Cardano.TxOut>;
+  feeAccountedFor: boolean;
+  redeemers?: Array<Cardano.Redeemer>;
+}> => {
+  const totalOutputs = new Set([...outputs, ...changeOutputs]);
+  const { fee, redeemers } = await constraints.computeMinimumCost({
+    change: [],
+    fee: currentFee,
+    inputs,
+    outputs: totalOutputs
+  });
+
+  if (fee === changeLovelace) return { change: [], fee, feeAccountedFor: true, redeemers };
+
+  if (changeLovelace < fee) throw new InputSelectionError(InputSelectionFailure.UtxoBalanceInsufficient);
+
+  const updatedOutputs = [...changeOutputs];
+
+  updatedOutputs.sort(sortByCoins);
+
+  let feeAccountedFor = false;
+  for (const output of updatedOutputs) {
+    const adjustedCoins = output.value.coins - fee;
+
+    if (adjustedCoins >= constraints.computeMinimumCoinQuantity(output)) {
+      output.value.coins = adjustedCoins;
+      feeAccountedFor = true;
+      break;
+    }
+  }
 
-  return lhs.index - rhs.index;
+  return { change: [...updatedOutputs], fee, feeAccountedFor, redeemers };
 };
 
 /**
- * Sorts the given Utxo set first by TxIn.
+ * Recursively compute the fee and compute change outputs until it finds a set of change outputs that satisfies the fee.
  *
- * @param lhs The left-hand side of the comparison operation.
- * @param rhs The left-hand side of the comparison operation.
+ * @param inputs The inputs of the transaction.
+ * @param outputs The outputs of the transaction.
+ * @param changeLovelace The total amount of lovelace in the change.
+ * @param changeAssets The total assets to be distributed as change.
+ * @param constraints The selection constraints.
+ * @param getChangeAddresses A callback that returns a list of addresses and their proportions.
+ * @param fee The current computed fee for this selection.
  */
-export const sortUtxoByTxIn = (lhs: Cardano.Utxo, rhs: Cardano.Utxo) => sortTxIn(lhs[0], rhs[0]);
+export const splitChangeAndComputeFee = async (
+  inputs: Set<Cardano.Utxo>,
+  outputs: Set<Cardano.TxOut>,
+  changeLovelace: bigint,
+  changeAssets: Cardano.TokenMap | undefined,
+  constraints: SelectionConstraints,
+  getChangeAddresses: () => Promise<Map<Cardano.PaymentAddress, number>>,
+  fee: bigint
+): Promise<{ fee: bigint; change: Array<Cardano.TxOut>; feeAccountedFor: boolean }> => {
+  const changeOutputs = await splitChange(
+    getChangeAddresses,
+    changeLovelace,
+    changeAssets,
+    constraints.computeMinimumCoinQuantity,
+    constraints.tokenBundleSizeExceedsLimit,
+    fee
+  );
+
+  let adjustedChangeOutputs = await adjustOutputsForFee(
+    changeLovelace,
+    constraints,
+    inputs,
+    outputs,
+    changeOutputs,
+    fee
+  );
+
+  // If the newly computed fee is higher than tha available balance for change,
path: packages/input-selection/src/GreedySelection/util.ts
user: login: rhyslbw
body: ```suggestion
  // If the newly computed fee is higher than the available balance for change,
```
created_at: 2025-05-13T07:30:09Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,429 @@
+/* eslint-disable max-params */
+import { Cardano, coalesceValueQuantities } from '@cardano-sdk/core';
+import { ChangeAddressResolver } from '../ChangeAddress';
+import {
+  ImplicitTokens,
+  MAX_U64,
+  UtxoSelection,
+  addTokenMaps,
+  getCoinQuantity,
+  hasNegativeAssetValue,
+  mintToImplicitTokens,
+  sortByAssetQuantity,
+  sortByCoins,
+  sortUtxoByTxIn,
+  stubMaxSizeAddress,
+  subtractTokenMaps,
+  toValues
+} from '../util';
+import {
+  ImplicitValue,
+  InputSelectionParameters,
+  InputSelector,
+  SelectionConstraints,
+  SelectionResult
+} from '../types';
+import { InputSelectionError, InputSelectionFailure } from '../InputSelectionError';
+
+import { computeChangeAndAdjustForFee } from '../change';
+
+import uniq from 'lodash/uniq.js';
+
+/**
+ * A `PickAdditionalUtxo` callback for the large-first strategy.
+ *
+ * The callback simply:
+ * 1. Sorts the remaining UTxOs by coin quantity (largest first).
+ * 2. Picks the first UTxO from the sorted list.
+ * 3. Returns the updated `UtxoSelection`.
+ */
+const pickAdditionalLargestUtxo = ({ utxoRemaining, utxoSelected }: UtxoSelection): UtxoSelection => {
+  if (utxoRemaining.length === 0) {
+    return { utxoRemaining, utxoSelected };
+  }
+
+  const sorted = utxoRemaining.sort(([, a], [, b]) => sortByCoins(a, b));
+  const [picked, ...newRemaining] = sorted;
+
+  return {
+    utxoRemaining: newRemaining,
+    utxoSelected: [...utxoSelected, picked]
+  };
+};
+
+/** LargeFirst selection initialization properties. */
+export interface LargeFirstSelectorProps {
+  changeAddressResolver: ChangeAddressResolver;
+}
+
+/**
+ * Input selector that implements a "large-first" strategy.
+ *
+ * This strategy selects the largest UTXOs per asset, one asset at a time, until the requirements
+ * of all assets in the outputs + fees and implicit values are satisfied.
+ */
+export class LargeFirstSelector implements InputSelector {
+  #props: LargeFirstSelectorProps;
+
+  /** Creates a new instance of the LargeFirstSelector. */
+  constructor(props: LargeFirstSelectorProps) {
+    this.#props = props;
+  }
+
+  /**
+   * Selects inputs using a large-first strategy:
+   * Selects largest UTXOs per asset until target is met
+   * Then selects largest UTxOs by ADA
+   * Then iteratively adds more UTxOs if needed to cover fees
+   *
+   * @param params Input selection parameters (available UTXOs, outputs, constraints, etc.)
+   * @returns A complete selection including inputs, outputs, change, and fee.
+   * @throws {InputSelectionError} If the selection cannot satisfy the outputs and fees.
+   */
+  async select(params: InputSelectionParameters): Promise<SelectionResult> {
+    const { utxo, preSelectedUtxo, outputs, constraints, implicitValue } = params;
+
+    const preSelected = [...preSelectedUtxo];
+    const available = [...utxo].filter(
+      ([txIn]) => !preSelected.some(([preTxIn]) => preTxIn.txId === txIn.txId && preTxIn.index === txIn.index)
+    );
+    const allAvailable = [...preSelected, ...available];
+
+    const { totalLovelaceOutput, outputAssets } = this.#computeNetImplicitSelectionValues(
+      new Set<Cardano.Utxo>(allAvailable),
+      outputs,
+      implicitValue
+    );
+
+    let workingUtxo = new Set<Cardano.Utxo>(preSelected);
+    workingUtxo = this.#selectAssets(outputAssets, allAvailable, [...workingUtxo]);
+    workingUtxo = this.#selectLovelace(
+      totalLovelaceOutput,
+      allAvailable,
+      workingUtxo,
+      implicitValue?.coin?.input ?? 0n
+    );
+
+    const { finalSelection, fee, change } = await this.#expandUtxosUntilFeeCovered(
+      workingUtxo,
+      outputs,
+      allAvailable,
+      outputAssets,
+      constraints,
+      this.#props.changeAddressResolver,
+      implicitValue
+    );
+
+    const limit = await constraints.computeSelectionLimit({ change, fee, inputs: finalSelection, outputs });
+    if (finalSelection.size > limit) throw new InputSelectionError(InputSelectionFailure.MaximumInputCountExceeded);
+
+    return {
+      remainingUTxO: this.#computeRemainingUtxo([...utxo], finalSelection),
+      selection: {
+        change,
+        fee,
+        inputs: new Set([...finalSelection].sort(sortUtxoByTxIn)),
+        outputs
+      }
+    };
+  }
+
+  /**
+   * Aggregate Lovelace that enters and leaves the transaction,
+   * taking implicit withdrawals / deposits into account.
+   *
+   * @param inputs   Array of `Value`s held by the selected UTxOs.
+   * @param outputs  Array of explicit transaction outputs.
+   * @param implicit Optional implicit values.
+   * @returns An object with
+   * `totalIn`  — Lovelace provided by UTxOs + withdrawals
+   * `totalOut` — Lovelace required for explicit outputs + deposits
+   */
+  #aggregateLovelace(
+    inputs: Cardano.Value[],
+    outputs: Cardano.Value[],
+    implicit?: ImplicitValue
+  ): { totalIn: bigint; totalOut: bigint } {
+    const utxoAda = getCoinQuantity(inputs);
+    const outputAda = getCoinQuantity(outputs);
+    const withdrawAda = implicit?.coin?.input ?? 0n;
+    const depositAda = implicit?.coin?.deposit ?? 0n;
+
+    return {
+      totalIn: utxoAda + withdrawAda,
+      totalOut: outputAda + depositAda
+    };
+  }
+
+  /**
+   * Compute two token maps:
+   * - required: the exact quantity of every asset that must be provided by inputs in order to satisfy explicit outputs plus any burns.
+   * - available: the quantity of every asset that is actually provided by inputs + positive mint.
+   *
+   * @param inputs   Values contained in the UTxOs selected so far.
+   * @param outputs  Values required by the user’s explicit transaction outputs.
+   * @param implicit Optional `mint` map (positive = forge, negative = burn).
+   * @returns `{ required, available }`
+   */
+  #aggregateAssets(
+    inputs: Cardano.Value[],
+    outputs: Cardano.Value[],
+    implicit?: ImplicitValue
+  ): {
+    required: Cardano.TokenMap;
+    available: Cardano.TokenMap;
+  } {
+    const outputsMap = coalesceValueQuantities(outputs).assets ?? new Map<Cardano.AssetId, bigint>();
+    const utxoMap = coalesceValueQuantities(inputs).assets;
+    const mint = implicit?.mint ?? new Map<Cardano.AssetId, bigint>();
+
+    const posMint = new Map<Cardano.AssetId, bigint>();
+    const negMint = new Map<Cardano.AssetId, bigint>();
+
+    for (const [id, q] of mint) (q > 0n ? posMint : negMint).set(id, q > 0n ? q : -q);
+
+    let required = addTokenMaps(outputsMap, negMint);
+    required = subtractTokenMaps(required, posMint) ?? new Map<Cardano.AssetId, bigint>();
+
+    const available = addTokenMaps(utxoMap, posMint) ?? new Map<Cardano.AssetId, bigint>();
+
+    return { available, required };
+  }
+
+  /**
+   * Computes the total Lovelace and asset output requirements, including the effects
+   * of implicit values such as key deposits, withdrawals, and minting/burning.
+   *
+   * Minting and burning are treated as negative or positive contributions to input balance,
+   * and are subtracted from the output requirements.
+   *
+   * @param inputs The full set of selected UTXOs (including pre-selected).
+   * @param outputs The transaction outputs.
+   * @param implicitValue Optional implicit values including deposits, withdrawals, and minting.
+   * @returns An object with:
+   * `totalLovelaceInput`: Sum of all input Lovelace, including withdrawals.
+   * `totalLovelaceOutput`: Sum of all output Lovelace, including deposits.
+   * `outputAssets`: Asset requirements after accounting for minting/burning.
+   * @throws {InputSelectionError} If balance is insufficient to satisfy the target.
+   */
+  #computeNetImplicitSelectionValues(
+    inputs: Set<Cardano.Utxo>,
+    outputs: Set<Cardano.TxOut>,
+    implicitValue?: ImplicitValue
+  ): {
+    totalLovelaceInput: bigint;
+    totalLovelaceOutput: bigint;
+    outputAssets: Cardano.TokenMap;
+  } {
+    const inputVals = toValues([...inputs]);
+    const outputVals = toValues([...outputs]);
+
+    const { totalIn, totalOut } = this.#aggregateLovelace(inputVals, outputVals, implicitValue);
+    const { required, available } = this.#aggregateAssets(inputVals, outputVals, implicitValue);
+
+    const changeAda = totalIn - totalOut;
+    const changeAssets = subtractTokenMaps(available, required);
+
+    if (inputs.size === 0 || changeAda < 0n || hasNegativeAssetValue(changeAssets))
+      throw new InputSelectionError(InputSelectionFailure.UtxoBalanceInsufficient);
+
+    return {
+      outputAssets: required,
+      totalLovelaceInput: totalIn,
+      totalLovelaceOutput: totalOut
+    };
+  }
+
+  /**
+   * Selects the largest UTXOs per required asset until each target amount is fulfilled.
+   *
+   * @param requiredAssets The asset quantities required by the transaction outputs.
+   * @param allAvailable All available UTXOs (including preselected ones).
+   * @param preSelected The UTXOs already selected for the transaction.
+   * @returns A set of selected UTXOs covering the asset requirements.
+   * @throws {InputSelectionError} If any asset cannot be sufficiently fulfilled.
+   */
+  #selectAssets(
+    requiredAssets: Map<Cardano.AssetId, bigint>,
+    allAvailable: Cardano.Utxo[],
+    preSelected: Cardano.Utxo[]
+  ): Set<Cardano.Utxo> {
+    const selected = new Set(preSelected);
+
+    for (const [assetId, requiredQuantity] of requiredAssets) {
+      const candidates = allAvailable
+        .filter(([_, out]) => (out.value.assets?.get(assetId) ?? 0n) > 0n)
+        .sort(([, a], [, b]) => sortByAssetQuantity(assetId)(a, b));
+
+      let accumulated = 0n;
+
+      for (const candidate of candidates) {
+        selected.add(candidate);
+        accumulated += candidate[1].value.assets?.get(assetId) ?? 0n;
+        if (accumulated >= requiredQuantity) break;
+      }
+
+      if (accumulated < requiredQuantity) throw new InputSelectionError(InputSelectionFailure.UtxoBalanceInsufficient);
+    }
+
+    return selected;
+  }
+
+  /**
+   * Selects UTXOs (largest ADA first) until the total Lovelace covers the target amount.
+   *
+   * @param target The required amount of Lovelace.
+   * @param allAvailable All available UTXOs.
+   * @param selected The current set of already selected UTXOs.
+   * @param implicitCoinInput The implicit coin input amount.
+   * @returns A new set including the original selected UTXOs plus any added for ADA coverage.
+   * @throws {InputSelectionError} If the Lovelace requirement cannot be fulfilled.
+   */
+  #selectLovelace(
+    target: bigint,
+    allAvailable: Cardano.Utxo[],
+    selected: Set<Cardano.Utxo>,
+    implicitCoinInput: bigint
+  ): Set<Cardano.Utxo> {
+    const result = new Set(selected);
+    const selectedTxIns = new Set([...selected].map(([txIn]) => txIn));
+
+    const adaCandidates = allAvailable
+      .filter(([txIn]) => !selectedTxIns.has(txIn))
+      .sort(([, a], [, b]) => sortByCoins(a, b));
+
+    let adaAccumulated = getCoinQuantity(toValues([...result])) + implicitCoinInput;
+
+    for (const candidate of adaCandidates) {
+      if (adaAccumulated >= target) break;
+      result.add(candidate);
+      adaAccumulated += candidate[1].value.coins;
+    }
+
+    if (result.size === 0) {
+      if (adaCandidates.length === 0) throw new InputSelectionError(InputSelectionFailure.UtxoBalanceInsufficient);
+      result.add(adaCandidates[0]);
+    }
+
+    if (adaAccumulated < target) throw new InputSelectionError(InputSelectionFailure.UtxoBalanceInsufficient);
+
+    return result;
+  }
+
+  /**
+   * Computes the UTXOs that were not selected, given the original list and selected set.
+   *
+   * @param original The original list of available UTXOs (excluding preselected).
+   * @param used The set of selected UTXOs.
+   * @returns A new set of UTXOs that were not consumed in the transaction.
+   */
+  #computeRemainingUtxo(original: Cardano.Utxo[], used: Set<Cardano.Utxo>): Set<Cardano.Utxo> {
+    const usedTxIns = new Set([...used].map(([txIn]) => txIn));
+    return new Set(original.filter(([txIn]) => !usedTxIns.has(txIn)));
+  }
+
+  /**
+   * Select additional UTXOs until the fee and min-ADA requirements are
+   * satisfied, then build the final change outputs.
+   *
+   * @param initialInputs The UTXOs already chosen for assets and/or pre-selected by the wallet.
path: packages/input-selection/src/LargeFirstSelection/LargeFirstInputSelector.ts
user: login: rhyslbw
body: ```suggestion
   * @param initialInputs The UTxOs already chosen for assets and/or pre-selected by the wallet.
```
created_at: 2025-05-13T07:38:11Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,429 @@
+/* eslint-disable max-params */
+import { Cardano, coalesceValueQuantities } from '@cardano-sdk/core';
+import { ChangeAddressResolver } from '../ChangeAddress';
+import {
+  ImplicitTokens,
+  MAX_U64,
+  UtxoSelection,
+  addTokenMaps,
+  getCoinQuantity,
+  hasNegativeAssetValue,
+  mintToImplicitTokens,
+  sortByAssetQuantity,
+  sortByCoins,
+  sortUtxoByTxIn,
+  stubMaxSizeAddress,
+  subtractTokenMaps,
+  toValues
+} from '../util';
+import {
+  ImplicitValue,
+  InputSelectionParameters,
+  InputSelector,
+  SelectionConstraints,
+  SelectionResult
+} from '../types';
+import { InputSelectionError, InputSelectionFailure } from '../InputSelectionError';
+
+import { computeChangeAndAdjustForFee } from '../change';
+
+import uniq from 'lodash/uniq.js';
+
+/**
+ * A `PickAdditionalUtxo` callback for the large-first strategy.
+ *
+ * The callback simply:
+ * 1. Sorts the remaining UTxOs by coin quantity (largest first).
+ * 2. Picks the first UTxO from the sorted list.
+ * 3. Returns the updated `UtxoSelection`.
+ */
+const pickAdditionalLargestUtxo = ({ utxoRemaining, utxoSelected }: UtxoSelection): UtxoSelection => {
+  if (utxoRemaining.length === 0) {
+    return { utxoRemaining, utxoSelected };
+  }
+
+  const sorted = utxoRemaining.sort(([, a], [, b]) => sortByCoins(a, b));
+  const [picked, ...newRemaining] = sorted;
+
+  return {
+    utxoRemaining: newRemaining,
+    utxoSelected: [...utxoSelected, picked]
+  };
+};
+
+/** LargeFirst selection initialization properties. */
+export interface LargeFirstSelectorProps {
+  changeAddressResolver: ChangeAddressResolver;
+}
+
+/**
+ * Input selector that implements a "large-first" strategy.
+ *
+ * This strategy selects the largest UTXOs per asset, one asset at a time, until the requirements
+ * of all assets in the outputs + fees and implicit values are satisfied.
+ */
+export class LargeFirstSelector implements InputSelector {
+  #props: LargeFirstSelectorProps;
+
+  /** Creates a new instance of the LargeFirstSelector. */
+  constructor(props: LargeFirstSelectorProps) {
+    this.#props = props;
+  }
+
+  /**
+   * Selects inputs using a large-first strategy:
+   * Selects largest UTXOs per asset until target is met
+   * Then selects largest UTxOs by ADA
+   * Then iteratively adds more UTxOs if needed to cover fees
+   *
+   * @param params Input selection parameters (available UTXOs, outputs, constraints, etc.)
+   * @returns A complete selection including inputs, outputs, change, and fee.
+   * @throws {InputSelectionError} If the selection cannot satisfy the outputs and fees.
+   */
+  async select(params: InputSelectionParameters): Promise<SelectionResult> {
+    const { utxo, preSelectedUtxo, outputs, constraints, implicitValue } = params;
+
+    const preSelected = [...preSelectedUtxo];
+    const available = [...utxo].filter(
+      ([txIn]) => !preSelected.some(([preTxIn]) => preTxIn.txId === txIn.txId && preTxIn.index === txIn.index)
+    );
+    const allAvailable = [...preSelected, ...available];
+
+    const { totalLovelaceOutput, outputAssets } = this.#computeNetImplicitSelectionValues(
+      new Set<Cardano.Utxo>(allAvailable),
+      outputs,
+      implicitValue
+    );
+
+    let workingUtxo = new Set<Cardano.Utxo>(preSelected);
+    workingUtxo = this.#selectAssets(outputAssets, allAvailable, [...workingUtxo]);
+    workingUtxo = this.#selectLovelace(
+      totalLovelaceOutput,
+      allAvailable,
+      workingUtxo,
+      implicitValue?.coin?.input ?? 0n
+    );
+
+    const { finalSelection, fee, change } = await this.#expandUtxosUntilFeeCovered(
+      workingUtxo,
+      outputs,
+      allAvailable,
+      outputAssets,
+      constraints,
+      this.#props.changeAddressResolver,
+      implicitValue
+    );
+
+    const limit = await constraints.computeSelectionLimit({ change, fee, inputs: finalSelection, outputs });
+    if (finalSelection.size > limit) throw new InputSelectionError(InputSelectionFailure.MaximumInputCountExceeded);
+
+    return {
+      remainingUTxO: this.#computeRemainingUtxo([...utxo], finalSelection),
+      selection: {
+        change,
+        fee,
+        inputs: new Set([...finalSelection].sort(sortUtxoByTxIn)),
+        outputs
+      }
+    };
+  }
+
+  /**
+   * Aggregate Lovelace that enters and leaves the transaction,
+   * taking implicit withdrawals / deposits into account.
+   *
+   * @param inputs   Array of `Value`s held by the selected UTxOs.
+   * @param outputs  Array of explicit transaction outputs.
+   * @param implicit Optional implicit values.
+   * @returns An object with
+   * `totalIn`  — Lovelace provided by UTxOs + withdrawals
+   * `totalOut` — Lovelace required for explicit outputs + deposits
+   */
+  #aggregateLovelace(
+    inputs: Cardano.Value[],
+    outputs: Cardano.Value[],
+    implicit?: ImplicitValue
+  ): { totalIn: bigint; totalOut: bigint } {
+    const utxoAda = getCoinQuantity(inputs);
+    const outputAda = getCoinQuantity(outputs);
+    const withdrawAda = implicit?.coin?.input ?? 0n;
+    const depositAda = implicit?.coin?.deposit ?? 0n;
+
+    return {
+      totalIn: utxoAda + withdrawAda,
+      totalOut: outputAda + depositAda
+    };
+  }
+
+  /**
+   * Compute two token maps:
+   * - required: the exact quantity of every asset that must be provided by inputs in order to satisfy explicit outputs plus any burns.
+   * - available: the quantity of every asset that is actually provided by inputs + positive mint.
+   *
+   * @param inputs   Values contained in the UTxOs selected so far.
+   * @param outputs  Values required by the user’s explicit transaction outputs.
+   * @param implicit Optional `mint` map (positive = forge, negative = burn).
+   * @returns `{ required, available }`
+   */
+  #aggregateAssets(
+    inputs: Cardano.Value[],
+    outputs: Cardano.Value[],
+    implicit?: ImplicitValue
+  ): {
+    required: Cardano.TokenMap;
+    available: Cardano.TokenMap;
+  } {
+    const outputsMap = coalesceValueQuantities(outputs).assets ?? new Map<Cardano.AssetId, bigint>();
+    const utxoMap = coalesceValueQuantities(inputs).assets;
+    const mint = implicit?.mint ?? new Map<Cardano.AssetId, bigint>();
+
+    const posMint = new Map<Cardano.AssetId, bigint>();
+    const negMint = new Map<Cardano.AssetId, bigint>();
+
+    for (const [id, q] of mint) (q > 0n ? posMint : negMint).set(id, q > 0n ? q : -q);
+
+    let required = addTokenMaps(outputsMap, negMint);
+    required = subtractTokenMaps(required, posMint) ?? new Map<Cardano.AssetId, bigint>();
+
+    const available = addTokenMaps(utxoMap, posMint) ?? new Map<Cardano.AssetId, bigint>();
+
+    return { available, required };
+  }
+
+  /**
+   * Computes the total Lovelace and asset output requirements, including the effects
+   * of implicit values such as key deposits, withdrawals, and minting/burning.
+   *
+   * Minting and burning are treated as negative or positive contributions to input balance,
+   * and are subtracted from the output requirements.
+   *
+   * @param inputs The full set of selected UTXOs (including pre-selected).
+   * @param outputs The transaction outputs.
+   * @param implicitValue Optional implicit values including deposits, withdrawals, and minting.
+   * @returns An object with:
+   * `totalLovelaceInput`: Sum of all input Lovelace, including withdrawals.
+   * `totalLovelaceOutput`: Sum of all output Lovelace, including deposits.
+   * `outputAssets`: Asset requirements after accounting for minting/burning.
+   * @throws {InputSelectionError} If balance is insufficient to satisfy the target.
+   */
+  #computeNetImplicitSelectionValues(
+    inputs: Set<Cardano.Utxo>,
+    outputs: Set<Cardano.TxOut>,
+    implicitValue?: ImplicitValue
+  ): {
+    totalLovelaceInput: bigint;
+    totalLovelaceOutput: bigint;
+    outputAssets: Cardano.TokenMap;
+  } {
+    const inputVals = toValues([...inputs]);
+    const outputVals = toValues([...outputs]);
+
+    const { totalIn, totalOut } = this.#aggregateLovelace(inputVals, outputVals, implicitValue);
+    const { required, available } = this.#aggregateAssets(inputVals, outputVals, implicitValue);
+
+    const changeAda = totalIn - totalOut;
+    const changeAssets = subtractTokenMaps(available, required);
+
+    if (inputs.size === 0 || changeAda < 0n || hasNegativeAssetValue(changeAssets))
+      throw new InputSelectionError(InputSelectionFailure.UtxoBalanceInsufficient);
+
+    return {
+      outputAssets: required,
+      totalLovelaceInput: totalIn,
+      totalLovelaceOutput: totalOut
+    };
+  }
+
+  /**
+   * Selects the largest UTXOs per required asset until each target amount is fulfilled.
+   *
+   * @param requiredAssets The asset quantities required by the transaction outputs.
+   * @param allAvailable All available UTXOs (including preselected ones).
+   * @param preSelected The UTXOs already selected for the transaction.
+   * @returns A set of selected UTXOs covering the asset requirements.
+   * @throws {InputSelectionError} If any asset cannot be sufficiently fulfilled.
+   */
+  #selectAssets(
+    requiredAssets: Map<Cardano.AssetId, bigint>,
+    allAvailable: Cardano.Utxo[],
+    preSelected: Cardano.Utxo[]
+  ): Set<Cardano.Utxo> {
+    const selected = new Set(preSelected);
+
+    for (const [assetId, requiredQuantity] of requiredAssets) {
+      const candidates = allAvailable
+        .filter(([_, out]) => (out.value.assets?.get(assetId) ?? 0n) > 0n)
+        .sort(([, a], [, b]) => sortByAssetQuantity(assetId)(a, b));
+
+      let accumulated = 0n;
+
+      for (const candidate of candidates) {
+        selected.add(candidate);
+        accumulated += candidate[1].value.assets?.get(assetId) ?? 0n;
+        if (accumulated >= requiredQuantity) break;
+      }
+
+      if (accumulated < requiredQuantity) throw new InputSelectionError(InputSelectionFailure.UtxoBalanceInsufficient);
+    }
+
+    return selected;
+  }
+
+  /**
+   * Selects UTXOs (largest ADA first) until the total Lovelace covers the target amount.
path: packages/input-selection/src/LargeFirstSelection/LargeFirstInputSelector.ts
user: login: rhyslbw
body: ```suggestion
   * Selects UTxOs (largest Ada first) until the total Lovelace covers the target amount.
```
created_at: 2025-05-13T07:38:51Z
author_association: MEMBER
diff_hunk: @@ -227,22 +227,141 @@ export const splitChange = async (
 };
 
 /**
- * Sorts the given TxIn set first by txId and then by index.
+ * Given a set of input and outputs, compute the fee. Then extract the fee from the change output
+ * with the highest value.
  *
- * @param lhs The left-hand side of the comparison operation.
- * @param rhs The left-hand side of the comparison operation.
+ * @param changeLovelace The available amount of lovelace to be used as change.
+ * @param constraints The selection constraints.
+ * @param inputs The inputs of the transaction.
+ * @param outputs The outputs of the transaction.
+ * @param changeOutputs The list of change outputs.
+ * @param currentFee The current computed fee for this selection.
  */
-export const sortTxIn = (lhs: Cardano.TxIn, rhs: Cardano.TxIn) => {
-  const txIdComparison = lhs.txId.localeCompare(rhs.txId);
-  if (txIdComparison !== 0) return txIdComparison;
+export const adjustOutputsForFee = async (
+  changeLovelace: bigint,
+  constraints: SelectionConstraints,
+  inputs: Set<Cardano.Utxo>,
+  outputs: Set<Cardano.TxOut>,
+  changeOutputs: Array<Cardano.TxOut>,
+  currentFee: bigint
+): Promise<{
+  fee: bigint;
+  change: Array<Cardano.TxOut>;
+  feeAccountedFor: boolean;
+  redeemers?: Array<Cardano.Redeemer>;
+}> => {
+  const totalOutputs = new Set([...outputs, ...changeOutputs]);
+  const { fee, redeemers } = await constraints.computeMinimumCost({
+    change: [],
+    fee: currentFee,
+    inputs,
+    outputs: totalOutputs
+  });
+
+  if (fee === changeLovelace) return { change: [], fee, feeAccountedFor: true, redeemers };
+
+  if (changeLovelace < fee) throw new InputSelectionError(InputSelectionFailure.UtxoBalanceInsufficient);
+
+  const updatedOutputs = [...changeOutputs];
+
+  updatedOutputs.sort(sortByCoins);
+
+  let feeAccountedFor = false;
+  for (const output of updatedOutputs) {
+    const adjustedCoins = output.value.coins - fee;
+
+    if (adjustedCoins >= constraints.computeMinimumCoinQuantity(output)) {
+      output.value.coins = adjustedCoins;
+      feeAccountedFor = true;
+      break;
+    }
+  }
 
-  return lhs.index - rhs.index;
+  return { change: [...updatedOutputs], fee, feeAccountedFor, redeemers };
 };
 
 /**
- * Sorts the given Utxo set first by TxIn.
+ * Recursively compute the fee and compute change outputs until it finds a set of change outputs that satisfies the fee.
  *
- * @param lhs The left-hand side of the comparison operation.
- * @param rhs The left-hand side of the comparison operation.
+ * @param inputs The inputs of the transaction.
+ * @param outputs The outputs of the transaction.
+ * @param changeLovelace The total amount of lovelace in the change.
+ * @param changeAssets The total assets to be distributed as change.
+ * @param constraints The selection constraints.
+ * @param getChangeAddresses A callback that returns a list of addresses and their proportions.
+ * @param fee The current computed fee for this selection.
  */
-export const sortUtxoByTxIn = (lhs: Cardano.Utxo, rhs: Cardano.Utxo) => sortTxIn(lhs[0], rhs[0]);
+export const splitChangeAndComputeFee = async (
+  inputs: Set<Cardano.Utxo>,
+  outputs: Set<Cardano.TxOut>,
+  changeLovelace: bigint,
+  changeAssets: Cardano.TokenMap | undefined,
+  constraints: SelectionConstraints,
+  getChangeAddresses: () => Promise<Map<Cardano.PaymentAddress, number>>,
+  fee: bigint
+): Promise<{ fee: bigint; change: Array<Cardano.TxOut>; feeAccountedFor: boolean }> => {
+  const changeOutputs = await splitChange(
+    getChangeAddresses,
+    changeLovelace,
+    changeAssets,
+    constraints.computeMinimumCoinQuantity,
+    constraints.tokenBundleSizeExceedsLimit,
+    fee
+  );
+
+  let adjustedChangeOutputs = await adjustOutputsForFee(
+    changeLovelace,
+    constraints,
+    inputs,
+    outputs,
+    changeOutputs,
+    fee
+  );
+
+  // If the newly computed fee is higher than tha available balance for change,
path: packages/input-selection/src/GreedySelection/util.ts
user: login: AngelCastilloB
body: done here https://github.com/input-output-hk/cardano-js-sdk/pull/1621/commits/307e16b37bb64858c46e4d714100a9f814ef5af6
created_at: 2025-05-13T09:50:03Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,429 @@
+/* eslint-disable max-params */
+import { Cardano, coalesceValueQuantities } from '@cardano-sdk/core';
+import { ChangeAddressResolver } from '../ChangeAddress';
+import {
+  ImplicitTokens,
+  MAX_U64,
+  UtxoSelection,
+  addTokenMaps,
+  getCoinQuantity,
+  hasNegativeAssetValue,
+  mintToImplicitTokens,
+  sortByAssetQuantity,
+  sortByCoins,
+  sortUtxoByTxIn,
+  stubMaxSizeAddress,
+  subtractTokenMaps,
+  toValues
+} from '../util';
+import {
+  ImplicitValue,
+  InputSelectionParameters,
+  InputSelector,
+  SelectionConstraints,
+  SelectionResult
+} from '../types';
+import { InputSelectionError, InputSelectionFailure } from '../InputSelectionError';
+
+import { computeChangeAndAdjustForFee } from '../change';
+
+import uniq from 'lodash/uniq.js';
+
+/**
+ * A `PickAdditionalUtxo` callback for the large-first strategy.
+ *
+ * The callback simply:
+ * 1. Sorts the remaining UTxOs by coin quantity (largest first).
+ * 2. Picks the first UTxO from the sorted list.
+ * 3. Returns the updated `UtxoSelection`.
+ */
+const pickAdditionalLargestUtxo = ({ utxoRemaining, utxoSelected }: UtxoSelection): UtxoSelection => {
+  if (utxoRemaining.length === 0) {
+    return { utxoRemaining, utxoSelected };
+  }
+
+  const sorted = utxoRemaining.sort(([, a], [, b]) => sortByCoins(a, b));
+  const [picked, ...newRemaining] = sorted;
+
+  return {
+    utxoRemaining: newRemaining,
+    utxoSelected: [...utxoSelected, picked]
+  };
+};
+
+/** LargeFirst selection initialization properties. */
+export interface LargeFirstSelectorProps {
+  changeAddressResolver: ChangeAddressResolver;
+}
+
+/**
+ * Input selector that implements a "large-first" strategy.
+ *
+ * This strategy selects the largest UTXOs per asset, one asset at a time, until the requirements
+ * of all assets in the outputs + fees and implicit values are satisfied.
+ */
+export class LargeFirstSelector implements InputSelector {
+  #props: LargeFirstSelectorProps;
+
+  /** Creates a new instance of the LargeFirstSelector. */
+  constructor(props: LargeFirstSelectorProps) {
+    this.#props = props;
+  }
+
+  /**
+   * Selects inputs using a large-first strategy:
+   * Selects largest UTXOs per asset until target is met
+   * Then selects largest UTxOs by ADA
+   * Then iteratively adds more UTxOs if needed to cover fees
+   *
+   * @param params Input selection parameters (available UTXOs, outputs, constraints, etc.)
+   * @returns A complete selection including inputs, outputs, change, and fee.
+   * @throws {InputSelectionError} If the selection cannot satisfy the outputs and fees.
+   */
+  async select(params: InputSelectionParameters): Promise<SelectionResult> {
+    const { utxo, preSelectedUtxo, outputs, constraints, implicitValue } = params;
+
+    const preSelected = [...preSelectedUtxo];
+    const available = [...utxo].filter(
+      ([txIn]) => !preSelected.some(([preTxIn]) => preTxIn.txId === txIn.txId && preTxIn.index === txIn.index)
+    );
+    const allAvailable = [...preSelected, ...available];
+
+    const { totalLovelaceOutput, outputAssets } = this.#computeNetImplicitSelectionValues(
+      new Set<Cardano.Utxo>(allAvailable),
+      outputs,
+      implicitValue
+    );
+
+    let workingUtxo = new Set<Cardano.Utxo>(preSelected);
+    workingUtxo = this.#selectAssets(outputAssets, allAvailable, [...workingUtxo]);
+    workingUtxo = this.#selectLovelace(
+      totalLovelaceOutput,
+      allAvailable,
+      workingUtxo,
+      implicitValue?.coin?.input ?? 0n
+    );
+
+    const { finalSelection, fee, change } = await this.#expandUtxosUntilFeeCovered(
+      workingUtxo,
+      outputs,
+      allAvailable,
+      outputAssets,
+      constraints,
+      this.#props.changeAddressResolver,
+      implicitValue
+    );
+
+    const limit = await constraints.computeSelectionLimit({ change, fee, inputs: finalSelection, outputs });
+    if (finalSelection.size > limit) throw new InputSelectionError(InputSelectionFailure.MaximumInputCountExceeded);
+
+    return {
+      remainingUTxO: this.#computeRemainingUtxo([...utxo], finalSelection),
+      selection: {
+        change,
+        fee,
+        inputs: new Set([...finalSelection].sort(sortUtxoByTxIn)),
+        outputs
+      }
+    };
+  }
+
+  /**
+   * Aggregate Lovelace that enters and leaves the transaction,
+   * taking implicit withdrawals / deposits into account.
+   *
+   * @param inputs   Array of `Value`s held by the selected UTxOs.
+   * @param outputs  Array of explicit transaction outputs.
+   * @param implicit Optional implicit values.
+   * @returns An object with
+   * `totalIn`  — Lovelace provided by UTxOs + withdrawals
+   * `totalOut` — Lovelace required for explicit outputs + deposits
+   */
+  #aggregateLovelace(
+    inputs: Cardano.Value[],
+    outputs: Cardano.Value[],
+    implicit?: ImplicitValue
+  ): { totalIn: bigint; totalOut: bigint } {
+    const utxoAda = getCoinQuantity(inputs);
+    const outputAda = getCoinQuantity(outputs);
+    const withdrawAda = implicit?.coin?.input ?? 0n;
+    const depositAda = implicit?.coin?.deposit ?? 0n;
+
+    return {
+      totalIn: utxoAda + withdrawAda,
+      totalOut: outputAda + depositAda
+    };
+  }
+
+  /**
+   * Compute two token maps:
+   * - required: the exact quantity of every asset that must be provided by inputs in order to satisfy explicit outputs plus any burns.
+   * - available: the quantity of every asset that is actually provided by inputs + positive mint.
+   *
+   * @param inputs   Values contained in the UTxOs selected so far.
+   * @param outputs  Values required by the user’s explicit transaction outputs.
+   * @param implicit Optional `mint` map (positive = forge, negative = burn).
+   * @returns `{ required, available }`
+   */
+  #aggregateAssets(
+    inputs: Cardano.Value[],
+    outputs: Cardano.Value[],
+    implicit?: ImplicitValue
+  ): {
+    required: Cardano.TokenMap;
+    available: Cardano.TokenMap;
+  } {
+    const outputsMap = coalesceValueQuantities(outputs).assets ?? new Map<Cardano.AssetId, bigint>();
+    const utxoMap = coalesceValueQuantities(inputs).assets;
+    const mint = implicit?.mint ?? new Map<Cardano.AssetId, bigint>();
+
+    const posMint = new Map<Cardano.AssetId, bigint>();
+    const negMint = new Map<Cardano.AssetId, bigint>();
+
+    for (const [id, q] of mint) (q > 0n ? posMint : negMint).set(id, q > 0n ? q : -q);
+
+    let required = addTokenMaps(outputsMap, negMint);
+    required = subtractTokenMaps(required, posMint) ?? new Map<Cardano.AssetId, bigint>();
+
+    const available = addTokenMaps(utxoMap, posMint) ?? new Map<Cardano.AssetId, bigint>();
+
+    return { available, required };
+  }
+
+  /**
+   * Computes the total Lovelace and asset output requirements, including the effects
+   * of implicit values such as key deposits, withdrawals, and minting/burning.
+   *
+   * Minting and burning are treated as negative or positive contributions to input balance,
+   * and are subtracted from the output requirements.
+   *
+   * @param inputs The full set of selected UTXOs (including pre-selected).
+   * @param outputs The transaction outputs.
+   * @param implicitValue Optional implicit values including deposits, withdrawals, and minting.
+   * @returns An object with:
+   * `totalLovelaceInput`: Sum of all input Lovelace, including withdrawals.
+   * `totalLovelaceOutput`: Sum of all output Lovelace, including deposits.
+   * `outputAssets`: Asset requirements after accounting for minting/burning.
+   * @throws {InputSelectionError} If balance is insufficient to satisfy the target.
+   */
+  #computeNetImplicitSelectionValues(
+    inputs: Set<Cardano.Utxo>,
+    outputs: Set<Cardano.TxOut>,
+    implicitValue?: ImplicitValue
+  ): {
+    totalLovelaceInput: bigint;
+    totalLovelaceOutput: bigint;
+    outputAssets: Cardano.TokenMap;
+  } {
+    const inputVals = toValues([...inputs]);
+    const outputVals = toValues([...outputs]);
+
+    const { totalIn, totalOut } = this.#aggregateLovelace(inputVals, outputVals, implicitValue);
+    const { required, available } = this.#aggregateAssets(inputVals, outputVals, implicitValue);
+
+    const changeAda = totalIn - totalOut;
+    const changeAssets = subtractTokenMaps(available, required);
+
+    if (inputs.size === 0 || changeAda < 0n || hasNegativeAssetValue(changeAssets))
+      throw new InputSelectionError(InputSelectionFailure.UtxoBalanceInsufficient);
+
+    return {
+      outputAssets: required,
+      totalLovelaceInput: totalIn,
+      totalLovelaceOutput: totalOut
+    };
+  }
+
+  /**
+   * Selects the largest UTXOs per required asset until each target amount is fulfilled.
+   *
+   * @param requiredAssets The asset quantities required by the transaction outputs.
+   * @param allAvailable All available UTXOs (including preselected ones).
+   * @param preSelected The UTXOs already selected for the transaction.
+   * @returns A set of selected UTXOs covering the asset requirements.
+   * @throws {InputSelectionError} If any asset cannot be sufficiently fulfilled.
+   */
+  #selectAssets(
+    requiredAssets: Map<Cardano.AssetId, bigint>,
+    allAvailable: Cardano.Utxo[],
+    preSelected: Cardano.Utxo[]
+  ): Set<Cardano.Utxo> {
+    const selected = new Set(preSelected);
+
+    for (const [assetId, requiredQuantity] of requiredAssets) {
+      const candidates = allAvailable
+        .filter(([_, out]) => (out.value.assets?.get(assetId) ?? 0n) > 0n)
+        .sort(([, a], [, b]) => sortByAssetQuantity(assetId)(a, b));
+
+      let accumulated = 0n;
+
+      for (const candidate of candidates) {
+        selected.add(candidate);
+        accumulated += candidate[1].value.assets?.get(assetId) ?? 0n;
+        if (accumulated >= requiredQuantity) break;
+      }
+
+      if (accumulated < requiredQuantity) throw new InputSelectionError(InputSelectionFailure.UtxoBalanceInsufficient);
+    }
+
+    return selected;
+  }
+
+  /**
+   * Selects UTXOs (largest ADA first) until the total Lovelace covers the target amount.
+   *
+   * @param target The required amount of Lovelace.
+   * @param allAvailable All available UTXOs.
+   * @param selected The current set of already selected UTXOs.
+   * @param implicitCoinInput The implicit coin input amount.
+   * @returns A new set including the original selected UTXOs plus any added for ADA coverage.
+   * @throws {InputSelectionError} If the Lovelace requirement cannot be fulfilled.
+   */
+  #selectLovelace(
+    target: bigint,
+    allAvailable: Cardano.Utxo[],
+    selected: Set<Cardano.Utxo>,
+    implicitCoinInput: bigint
+  ): Set<Cardano.Utxo> {
+    const result = new Set(selected);
+    const selectedTxIns = new Set([...selected].map(([txIn]) => txIn));
+
+    const adaCandidates = allAvailable
+      .filter(([txIn]) => !selectedTxIns.has(txIn))
+      .sort(([, a], [, b]) => sortByCoins(a, b));
+
+    let adaAccumulated = getCoinQuantity(toValues([...result])) + implicitCoinInput;
+
+    for (const candidate of adaCandidates) {
+      if (adaAccumulated >= target) break;
+      result.add(candidate);
+      adaAccumulated += candidate[1].value.coins;
+    }
+
+    if (result.size === 0) {
+      if (adaCandidates.length === 0) throw new InputSelectionError(InputSelectionFailure.UtxoBalanceInsufficient);
+      result.add(adaCandidates[0]);
+    }
+
+    if (adaAccumulated < target) throw new InputSelectionError(InputSelectionFailure.UtxoBalanceInsufficient);
+
+    return result;
+  }
+
+  /**
+   * Computes the UTXOs that were not selected, given the original list and selected set.
+   *
+   * @param original The original list of available UTXOs (excluding preselected).
+   * @param used The set of selected UTXOs.
+   * @returns A new set of UTXOs that were not consumed in the transaction.
+   */
+  #computeRemainingUtxo(original: Cardano.Utxo[], used: Set<Cardano.Utxo>): Set<Cardano.Utxo> {
+    const usedTxIns = new Set([...used].map(([txIn]) => txIn));
+    return new Set(original.filter(([txIn]) => !usedTxIns.has(txIn)));
+  }
+
+  /**
+   * Select additional UTXOs until the fee and min-ADA requirements are
+   * satisfied, then build the final change outputs.
+   *
+   * @param initialInputs The UTXOs already chosen for assets and/or pre-selected by the wallet.
path: packages/input-selection/src/LargeFirstSelection/LargeFirstInputSelector.ts
user: login: AngelCastilloB
body: done here https://github.com/input-output-hk/cardano-js-sdk/pull/1621/commits/307e16b37bb64858c46e4d714100a9f814ef5af6
created_at: 2025-05-13T09:50:06Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,429 @@
+/* eslint-disable max-params */
+import { Cardano, coalesceValueQuantities } from '@cardano-sdk/core';
+import { ChangeAddressResolver } from '../ChangeAddress';
+import {
+  ImplicitTokens,
+  MAX_U64,
+  UtxoSelection,
+  addTokenMaps,
+  getCoinQuantity,
+  hasNegativeAssetValue,
+  mintToImplicitTokens,
+  sortByAssetQuantity,
+  sortByCoins,
+  sortUtxoByTxIn,
+  stubMaxSizeAddress,
+  subtractTokenMaps,
+  toValues
+} from '../util';
+import {
+  ImplicitValue,
+  InputSelectionParameters,
+  InputSelector,
+  SelectionConstraints,
+  SelectionResult
+} from '../types';
+import { InputSelectionError, InputSelectionFailure } from '../InputSelectionError';
+
+import { computeChangeAndAdjustForFee } from '../change';
+
+import uniq from 'lodash/uniq.js';
+
+/**
+ * A `PickAdditionalUtxo` callback for the large-first strategy.
+ *
+ * The callback simply:
+ * 1. Sorts the remaining UTxOs by coin quantity (largest first).
+ * 2. Picks the first UTxO from the sorted list.
+ * 3. Returns the updated `UtxoSelection`.
+ */
+const pickAdditionalLargestUtxo = ({ utxoRemaining, utxoSelected }: UtxoSelection): UtxoSelection => {
+  if (utxoRemaining.length === 0) {
+    return { utxoRemaining, utxoSelected };
+  }
+
+  const sorted = utxoRemaining.sort(([, a], [, b]) => sortByCoins(a, b));
+  const [picked, ...newRemaining] = sorted;
+
+  return {
+    utxoRemaining: newRemaining,
+    utxoSelected: [...utxoSelected, picked]
+  };
+};
+
+/** LargeFirst selection initialization properties. */
+export interface LargeFirstSelectorProps {
+  changeAddressResolver: ChangeAddressResolver;
+}
+
+/**
+ * Input selector that implements a "large-first" strategy.
+ *
+ * This strategy selects the largest UTXOs per asset, one asset at a time, until the requirements
+ * of all assets in the outputs + fees and implicit values are satisfied.
+ */
+export class LargeFirstSelector implements InputSelector {
+  #props: LargeFirstSelectorProps;
+
+  /** Creates a new instance of the LargeFirstSelector. */
+  constructor(props: LargeFirstSelectorProps) {
+    this.#props = props;
+  }
+
+  /**
+   * Selects inputs using a large-first strategy:
+   * Selects largest UTXOs per asset until target is met
+   * Then selects largest UTxOs by ADA
+   * Then iteratively adds more UTxOs if needed to cover fees
+   *
+   * @param params Input selection parameters (available UTXOs, outputs, constraints, etc.)
+   * @returns A complete selection including inputs, outputs, change, and fee.
+   * @throws {InputSelectionError} If the selection cannot satisfy the outputs and fees.
+   */
+  async select(params: InputSelectionParameters): Promise<SelectionResult> {
+    const { utxo, preSelectedUtxo, outputs, constraints, implicitValue } = params;
+
+    const preSelected = [...preSelectedUtxo];
+    const available = [...utxo].filter(
+      ([txIn]) => !preSelected.some(([preTxIn]) => preTxIn.txId === txIn.txId && preTxIn.index === txIn.index)
+    );
+    const allAvailable = [...preSelected, ...available];
+
+    const { totalLovelaceOutput, outputAssets } = this.#computeNetImplicitSelectionValues(
+      new Set<Cardano.Utxo>(allAvailable),
+      outputs,
+      implicitValue
+    );
+
+    let workingUtxo = new Set<Cardano.Utxo>(preSelected);
+    workingUtxo = this.#selectAssets(outputAssets, allAvailable, [...workingUtxo]);
+    workingUtxo = this.#selectLovelace(
+      totalLovelaceOutput,
+      allAvailable,
+      workingUtxo,
+      implicitValue?.coin?.input ?? 0n
+    );
+
+    const { finalSelection, fee, change } = await this.#expandUtxosUntilFeeCovered(
+      workingUtxo,
+      outputs,
+      allAvailable,
+      outputAssets,
+      constraints,
+      this.#props.changeAddressResolver,
+      implicitValue
+    );
+
+    const limit = await constraints.computeSelectionLimit({ change, fee, inputs: finalSelection, outputs });
+    if (finalSelection.size > limit) throw new InputSelectionError(InputSelectionFailure.MaximumInputCountExceeded);
+
+    return {
+      remainingUTxO: this.#computeRemainingUtxo([...utxo], finalSelection),
+      selection: {
+        change,
+        fee,
+        inputs: new Set([...finalSelection].sort(sortUtxoByTxIn)),
+        outputs
+      }
+    };
+  }
+
+  /**
+   * Aggregate Lovelace that enters and leaves the transaction,
+   * taking implicit withdrawals / deposits into account.
+   *
+   * @param inputs   Array of `Value`s held by the selected UTxOs.
+   * @param outputs  Array of explicit transaction outputs.
+   * @param implicit Optional implicit values.
+   * @returns An object with
+   * `totalIn`  — Lovelace provided by UTxOs + withdrawals
+   * `totalOut` — Lovelace required for explicit outputs + deposits
+   */
+  #aggregateLovelace(
+    inputs: Cardano.Value[],
+    outputs: Cardano.Value[],
+    implicit?: ImplicitValue
+  ): { totalIn: bigint; totalOut: bigint } {
+    const utxoAda = getCoinQuantity(inputs);
+    const outputAda = getCoinQuantity(outputs);
+    const withdrawAda = implicit?.coin?.input ?? 0n;
+    const depositAda = implicit?.coin?.deposit ?? 0n;
+
+    return {
+      totalIn: utxoAda + withdrawAda,
+      totalOut: outputAda + depositAda
+    };
+  }
+
+  /**
+   * Compute two token maps:
+   * - required: the exact quantity of every asset that must be provided by inputs in order to satisfy explicit outputs plus any burns.
+   * - available: the quantity of every asset that is actually provided by inputs + positive mint.
+   *
+   * @param inputs   Values contained in the UTxOs selected so far.
+   * @param outputs  Values required by the user’s explicit transaction outputs.
+   * @param implicit Optional `mint` map (positive = forge, negative = burn).
+   * @returns `{ required, available }`
+   */
+  #aggregateAssets(
+    inputs: Cardano.Value[],
+    outputs: Cardano.Value[],
+    implicit?: ImplicitValue
+  ): {
+    required: Cardano.TokenMap;
+    available: Cardano.TokenMap;
+  } {
+    const outputsMap = coalesceValueQuantities(outputs).assets ?? new Map<Cardano.AssetId, bigint>();
+    const utxoMap = coalesceValueQuantities(inputs).assets;
+    const mint = implicit?.mint ?? new Map<Cardano.AssetId, bigint>();
+
+    const posMint = new Map<Cardano.AssetId, bigint>();
+    const negMint = new Map<Cardano.AssetId, bigint>();
+
+    for (const [id, q] of mint) (q > 0n ? posMint : negMint).set(id, q > 0n ? q : -q);
+
+    let required = addTokenMaps(outputsMap, negMint);
+    required = subtractTokenMaps(required, posMint) ?? new Map<Cardano.AssetId, bigint>();
+
+    const available = addTokenMaps(utxoMap, posMint) ?? new Map<Cardano.AssetId, bigint>();
+
+    return { available, required };
+  }
+
+  /**
+   * Computes the total Lovelace and asset output requirements, including the effects
+   * of implicit values such as key deposits, withdrawals, and minting/burning.
+   *
+   * Minting and burning are treated as negative or positive contributions to input balance,
+   * and are subtracted from the output requirements.
+   *
+   * @param inputs The full set of selected UTXOs (including pre-selected).
+   * @param outputs The transaction outputs.
+   * @param implicitValue Optional implicit values including deposits, withdrawals, and minting.
+   * @returns An object with:
+   * `totalLovelaceInput`: Sum of all input Lovelace, including withdrawals.
+   * `totalLovelaceOutput`: Sum of all output Lovelace, including deposits.
+   * `outputAssets`: Asset requirements after accounting for minting/burning.
+   * @throws {InputSelectionError} If balance is insufficient to satisfy the target.
+   */
+  #computeNetImplicitSelectionValues(
+    inputs: Set<Cardano.Utxo>,
+    outputs: Set<Cardano.TxOut>,
+    implicitValue?: ImplicitValue
+  ): {
+    totalLovelaceInput: bigint;
+    totalLovelaceOutput: bigint;
+    outputAssets: Cardano.TokenMap;
+  } {
+    const inputVals = toValues([...inputs]);
+    const outputVals = toValues([...outputs]);
+
+    const { totalIn, totalOut } = this.#aggregateLovelace(inputVals, outputVals, implicitValue);
+    const { required, available } = this.#aggregateAssets(inputVals, outputVals, implicitValue);
+
+    const changeAda = totalIn - totalOut;
+    const changeAssets = subtractTokenMaps(available, required);
+
+    if (inputs.size === 0 || changeAda < 0n || hasNegativeAssetValue(changeAssets))
+      throw new InputSelectionError(InputSelectionFailure.UtxoBalanceInsufficient);
+
+    return {
+      outputAssets: required,
+      totalLovelaceInput: totalIn,
+      totalLovelaceOutput: totalOut
+    };
+  }
+
+  /**
+   * Selects the largest UTXOs per required asset until each target amount is fulfilled.
+   *
+   * @param requiredAssets The asset quantities required by the transaction outputs.
+   * @param allAvailable All available UTXOs (including preselected ones).
+   * @param preSelected The UTXOs already selected for the transaction.
+   * @returns A set of selected UTXOs covering the asset requirements.
+   * @throws {InputSelectionError} If any asset cannot be sufficiently fulfilled.
+   */
+  #selectAssets(
+    requiredAssets: Map<Cardano.AssetId, bigint>,
+    allAvailable: Cardano.Utxo[],
+    preSelected: Cardano.Utxo[]
+  ): Set<Cardano.Utxo> {
+    const selected = new Set(preSelected);
+
+    for (const [assetId, requiredQuantity] of requiredAssets) {
+      const candidates = allAvailable
+        .filter(([_, out]) => (out.value.assets?.get(assetId) ?? 0n) > 0n)
+        .sort(([, a], [, b]) => sortByAssetQuantity(assetId)(a, b));
+
+      let accumulated = 0n;
+
+      for (const candidate of candidates) {
+        selected.add(candidate);
+        accumulated += candidate[1].value.assets?.get(assetId) ?? 0n;
+        if (accumulated >= requiredQuantity) break;
+      }
+
+      if (accumulated < requiredQuantity) throw new InputSelectionError(InputSelectionFailure.UtxoBalanceInsufficient);
+    }
+
+    return selected;
+  }
+
+  /**
+   * Selects UTXOs (largest ADA first) until the total Lovelace covers the target amount.
path: packages/input-selection/src/LargeFirstSelection/LargeFirstInputSelector.ts
user: login: AngelCastilloB
body: done here https://github.com/input-output-hk/cardano-js-sdk/pull/1621/commits/307e16b37bb64858c46e4d714100a9f814ef5af6
created_at: 2025-05-13T09:50:09Z
author_association: MEMBER

PR comments:
user: login: github-actions[bot]
created_at: 2025-05-12T08:37:29Z
body: <details>
<summary><code>dev-preview@us-east-1</code> would change:</summary>

dev-preview, dev-preview-cardanojs-asset-projector, Deployment (apps) would change:

```diff
  # Source: cardanojs/templates/asset-projector.deployment.yaml
  apiVersion: apps/v1
  kind: Deployment
  metadata:
    labels:
      app: asset-projector
      network: preview
      release: dev-preview-cardanojs
    name: dev-preview-cardanojs-asset-projector
  spec:
    selector:
      matchLabels:
        app: asset-projector
        network: preview
        release: dev-preview-cardanojs
    template:
      metadata:
        labels:
          app: asset-projector
          network: preview
          release: dev-preview-cardanojs
      spec:
        containers:
        - args:
          - start-projector
          env:
          - name: LOGGER_MIN_SEVERITY
            value: info
          - name: NETWORK
            value: preview
          - name: OGMIOS_SRV_SERVICE_NAME
            value: dev-preview-cardano-core.dev-preview.svc.cluster.local
          - name: POSTGRES_DB
            value: asset
          - name: POSTGRES_HOST
            value: dev-preview-postgresql
          - name: POSTGRES_PASSWORD
            valueFrom:
              secretKeyRef:
                key: password
                name: asset-owner-user.dev-preview-postgresql.credentials.postgresql.acid.zalan.do
          - name: POSTGRES_POOL_MAX
            value: "2"
          - name: POSTGRES_PORT
            value: "5432"
          - name: POSTGRES_USER
            valueFrom:
              secretKeyRef:
                key: username
                name: asset-owner-user.dev-preview-postgresql.credentials.postgresql.acid.zalan.do
          - name: PROJECTION_NAMES
            value: asset
-         image: 926093910549.dkr.ecr.us-east-1.amazonaws.com/cardano-services:hkld3yk0jil343zxnn7qrcb73zyn1pnq
+         image: 926093910549.dkr.ecr.us-east-1.amazonaws.com/cardano-services:ggd3diyab45gsdfmgaz5ac7yf63hzwzm
          livenessProbe:
            httpGet:
              path: /v1.0.0/health
              port: 3000
            timeoutSeconds: 5
          name: asset-projector
          ports:
          - containerPort: 3000
            name: http
          resources:
            limits:
              cpu: 700m
              memory: 300Mi
            requests:
              cpu: 700m
              memory: 150Mi
          securityContext:
            runAsGroup: 0
            runAsUser: 0
        imagePullSecrets:
        - name: dockerconfigjson
```

dev-preview, dev-preview-cardanojs-backend, Deployment (apps) would change:

```diff
  # Source: cardanojs/templates/backend.deployment.yaml
  apiVersion: apps/v1
  kind: Deployment
  metadata:
    labels:
      app: backend
      network: preview
      release: dev-preview-cardanojs
    name: dev-preview-cardanojs-backend
  spec:
    selector:
      matchLabels:
        app: backend
        network: preview
        release: dev-preview-cardanojs
    template:
      metadata:
        labels:
          app: backend
          network: preview
          release: dev-preview-cardanojs
      spec:
        containers:
        - args:
          - start-provider-server
          env:
          - name: ALLOWED_ORIGINS
            value: chrome-extension://gafhhkghbfjjkeiendhlofajokpaflmk,chrome-extension://efeiemlfnahiidnjglmehaihacglceia,chrome-extension://bjlhpephaokolembmpdcbobbpkjnoheb,chrome-extension://djcdfchkaijggdjokfomholkalbffgil,http://localhost/,http://localhost
          - name: BUILD_INFO
-           value: '{"lastModifiedDate":"20250418115958","rev":"ebd539dbbe2d838c9e68a0ac4d9e0c29d3d7cf91","shortRev":"ebd539d"}'
+           value: '{"lastModifiedDate":"20250514010946","rev":"f2142d8be63d1c0988764b61f894a7347e663a0b","shortRev":"f2142d8"}'
          - name: DISABLE_STAKE_POOL_METRIC_APY
            value: "true"
          - name: ENABLE_METRICS
            value: "true"
          - name: HANDLE_POLICY_IDS
            value: f0ff48bbb7bbe9d59a40f1ce90e9e9d0ff5002ec48f232b49ca0fb9a
          - name: HANDLE_PROVIDER_SERVER_URL
            value: https://preview.api.handle.me
          - name: LOGGER_MIN_SEVERITY
            value: info
          - name: NETWORK
            value: preview
          - name: NODE_ENV
          - name: OGMIOS_SRV_SERVICE_NAME
            value: dev-preview-cardano-core.dev-preview.svc.cluster.local
          - name: PAGINATION_PAGE_SIZE_LIMIT
            value: "5500"
          - name: POSTGRES_DB_DB_SYNC
            value: cardano
          - name: POSTGRES_DB_HANDLE
            value: handle
          - name: POSTGRES_HOST_DB_SYNC
            value: dev-preview-postgresql
          - name: POSTGRES_HOST_HANDLE
            value: dev-preview-postgresql
          - name: POSTGRES_PASSWORD_DB_SYNC
            valueFrom:
              secretKeyRef:
                key: password
                name: cardano-owner-user.dev-preview-postgresql.credentials.postgresql.acid.zalan.do
          - name: POSTGRES_PASSWORD_HANDLE
            valueFrom:
              secretKeyRef:
                key: password
                name: handle-owner-user.dev-preview-postgresql.credentials.postgresql.acid.zalan.do
          - name: POSTGRES_POOL_MAX_DB_SYNC
            value: "50"
          - name: POSTGRES_POOL_MAX_HANDLE
            value: "10"
          - name: POSTGRES_PORT_DB_SYNC
            value: "5432"
          - name: POSTGRES_PORT_HANDLE
            value: "5432"
          - name: POSTGRES_USER_DB_SYNC
            valueFrom:
              secretKeyRef:
                key: username
                name: cardano-owner-user.dev-preview-postgresql.credentials.postgresql.acid.zalan.do
          - name: POSTGRES_USER_HANDLE
            valueFrom:
              secretKeyRef:
                key: username
                name: handle-owner-user.dev-preview-postgresql.credentials.postgresql.acid.zalan.do
          - name: SERVICE_NAMES
            value: asset,network-info,rewards,stake-pool,tx-submit,utxo
          - name: TOKEN_METADATA_SERVER_URL
            value: http://dev-preview-cardano-stack-metadata.dev-preview.svc.cluster.local
          - name: USE_BLOCKFROST
            value: "true"
          - name: USE_KORA_LABS
            value: "true"
-         image: 926093910549.dkr.ecr.us-east-1.amazonaws.com/cardano-services:hkld3yk0jil343zxnn7qrcb73zyn1pnq
+         image: 926093910549.dkr.ecr.us-east-1.amazonaws.com/cardano-services:ggd3diyab45gsdfmgaz5ac7yf63hzwzm
          livenessProbe:
            httpGet:
              path: /v1.0.0/ready
              port: 3000
            periodSeconds: 10
            timeoutSeconds: 5
          name: backend
          ports:
          - containerPort: 3000
            name: http
          resources:
            requests:
              cpu: 1000m
              memory: 350Mi
          securityContext:
            runAsGroup: 0
            runAsUser: 0
        imagePullSecrets:
        - name: dockerconfigjson
```

dev-preview, dev-preview-cardanojs-blockfrost-worker, Deployment (apps) would change:

```diff
  # Source: cardanojs/templates/blockfrost-worker.deployment.yaml
  apiVersion: apps/v1
  kind: Deployment
  metadata:
    labels:
      app: blockfrost-worker
      network: preview
      release: dev-preview-cardanojs
    name: dev-preview-cardanojs-blockfrost-worker
  spec:
    selector:
      matchLabels:
        app: blockfrost-worker
        network: preview
        release: dev-preview-cardanojs
    template:
      metadata:
        labels:
          app: blockfrost-worker
          network: preview
          release: dev-preview-cardanojs
      spec:
        containers:
        - args:
          - start-blockfrost-worker
          env:
          - name: BLOCKFROST_API_KEY
            valueFrom:
              secretKeyRef:
                key: api-key
                name: blockfrost
          - name: LOGGER_MIN_SEVERITY
            value: info
          - name: NETWORK
            value: preview
          - name: NODE_ENV
          - name: POSTGRES_DB_DB_SYNC
            value: cardano
          - name: POSTGRES_HOST_DB_SYNC
            value: dev-preview-postgresql
          - name: POSTGRES_PASSWORD_DB_SYNC
            valueFrom:
              secretKeyRef:
                key: password
                name: cardano-owner-user.dev-preview-postgresql.credentials.postgresql.acid.zalan.do
          - name: POSTGRES_PORT_DB_SYNC
            value: "5432"
          - name: POSTGRES_USER_DB_SYNC
            valueFrom:
              secretKeyRef:
                key: username
                name: cardano-owner-user.dev-preview-postgresql.credentials.postgresql.acid.zalan.do
-         image: 926093910549.dkr.ecr.us-east-1.amazonaws.com/cardano-services:hkld3yk0jil343zxnn7qrcb73zyn1pnq
+         image: 926093910549.dkr.ecr.us-east-1.amazonaws.com/cardano-services:ggd3diyab45gsdfmgaz5ac7yf63hzwzm
          livenessProbe:
            httpGet:
              path: /v1.0.0/health
              port: 3000
            timeoutSeconds: 5
          name: blockfrost-worker
          ports:
          - containerPort: 3000
            name: http
          resources:
            limits:
              cpu: 500m
              memory: 300Mi
            requests:
              cpu: 100m
              memory: 150Mi
          securityContext:
            runAsGroup: 0
            runAsUser: 0
        imagePullSecrets:
        - name: dockerconfigjson
```

dev-preview, dev-preview-cardanojs-chain-history-provider, Deployment (apps) would change:

```diff
  # Source: cardanojs/templates/chain-history-provider.deployment.yaml
  apiVersion: apps/v1
  kind: Deployment
  metadata:
    labels:
      app: chain-history-provider
      network: preview
      release: dev-preview-cardanojs
    name: dev-preview-cardanojs-chain-history-provider
  spec:
    selector:
      matchLabels:
        app: chain-history-provider
        network: preview
        release: dev-preview-cardanojs
    template:
      metadata:
        labels:
          app: chain-history-provider
          network: preview
          release: dev-preview-cardanojs
      spec:
        containers:
        - args:
          - start-provider-server
          env:
          - name: ALLOWED_ORIGINS
            value: chrome-extension://gafhhkghbfjjkeiendhlofajokpaflmk,chrome-extension://efeiemlfnahiidnjglmehaihacglceia,chrome-extension://bjlhpephaokolembmpdcbobbpkjnoheb,chrome-extension://djcdfchkaijggdjokfomholkalbffgil,http://localhost/,http://localhost
          - name: DISABLE_STAKE_POOL_METRIC_APY
            value: "true"
          - name: ENABLE_METRICS
            value: "true"
          - name: HANDLE_POLICY_IDS
            value: f0ff48bbb7bbe9d59a40f1ce90e9e9d0ff5002ec48f232b49ca0fb9a
          - name: LOGGER_MIN_SEVERITY
            value: info
          - name: NETWORK
            value: preview
          - name: NODE_ENV
          - name: OGMIOS_SRV_SERVICE_NAME
            value: dev-preview-cardano-core.dev-preview.svc.cluster.local
          - name: PAGINATION_PAGE_SIZE_LIMIT
            value: "5500"
          - name: POSTGRES_DB_DB_SYNC
            value: cardano
          - name: POSTGRES_HOST_DB_SYNC
            value: dev-preview-postgresql
          - name: POSTGRES_PASSWORD_DB_SYNC
            valueFrom:
              secretKeyRef:
                key: password
                name: cardano-owner-user.dev-preview-postgresql.credentials.postgresql.acid.zalan.do
          - name: POSTGRES_POOL_MAX_DB_SYNC
            value: "50"
          - name: POSTGRES_PORT_DB_SYNC
            value: "5432"
          - name: POSTGRES_USER_DB_SYNC
            valueFrom:
              secretKeyRef:
                key: username
                name: cardano-owner-user.dev-preview-postgresql.credentials.postgresql.acid.zalan.do
          - name: SERVICE_NAMES
            value: chain-history
          - name: TOKEN_METADATA_SERVER_URL
            value: http://dev-preview-cardano-stack-metadata.dev-preview.svc.cluster.local
          - name: USE_BLOCKFROST
            value: "true"
          - name: USE_KORA_LABS
            value: "true"
-         image: 926093910549.dkr.ecr.us-east-1.amazonaws.com/cardano-services:hkld3yk0jil343zxnn7qrcb73zyn1pnq
+         image: 926093910549.dkr.ecr.us-east-1.amazonaws.com/cardano-services:ggd3diyab45gsdfmgaz5ac7yf63hzwzm
          livenessProbe:
            httpGet:
              path: /v1.0.0/ready
              port: 3000
            periodSeconds: 10
            timeoutSeconds: 5
          name: chain-history-provider
          ports:
          - containerPort: 3000
            name: http
          resources:
            requests:
              cpu: 1000m
              memory: 512Mi
          securityContext:
            runAsGroup: 0
            runAsUser: 0
        imagePullSecrets:
        - name: dockerconfigjson
```

dev-preview, dev-preview-cardanojs-handle-projector, Deployment (apps) would change:

```diff
  # Source: cardanojs/templates/handle-projector.deployment.yaml
  apiVersion: apps/v1
  kind: Deployment
  metadata:
    labels:
      app: handle-projector
      network: preview
      release: dev-preview-cardanojs
    name: dev-preview-cardanojs-handle-projector
  spec:
    selector:
      matchLabels:
        app: handle-projector
        network: preview
        release: dev-preview-cardanojs
    template:
      metadata:
        labels:
          app: handle-projector
          network: preview
          release: dev-preview-cardanojs
      spec:
        containers:
        - args:
          - start-projector
          env:
          - name: HANDLE_POLICY_IDS
            value: f0ff48bbb7bbe9d59a40f1ce90e9e9d0ff5002ec48f232b49ca0fb9a
          - name: LOGGER_MIN_SEVERITY
            value: info
          - name: NETWORK
            value: preview
          - name: NODE_ENV
          - name: OGMIOS_SRV_SERVICE_NAME
            value: dev-preview-cardano-core.dev-preview.svc.cluster.local
          - name: POSTGRES_DB
            value: handle
          - name: POSTGRES_HOST
            value: dev-preview-postgresql
          - name: POSTGRES_PASSWORD
            valueFrom:
              secretKeyRef:
                key: password
                name: handle-owner-user.dev-preview-postgresql.credentials.postgresql.acid.zalan.do
          - name: POSTGRES_POOL_MAX
            value: "2"
          - name: POSTGRES_PORT
            value: "5432"
          - name: POSTGRES_USER
            valueFrom:
              secretKeyRef:
                key: username
                name: handle-owner-user.dev-preview-postgresql.credentials.postgresql.acid.zalan.do
          - name: PROJECTION_NAMES
            value: handle
-         image: 926093910549.dkr.ecr.us-east-1.amazonaws.com/cardano-services:hkld3yk0jil343zxnn7qrcb73zyn1pnq
+         image: 926093910549.dkr.ecr.us-east-1.amazonaws.com/cardano-services:ggd3diyab45gsdfmgaz5ac7yf63hzwzm
          livenessProbe:
            httpGet:
              path: /v1.0.0/health
              port: 3000
            timeoutSeconds: 5
          name: handle-projector
          ports:
          - containerPort: 3000
            name: http
          resources:
            limits:
              cpu: 1000m
              memory: 300Mi
            requests:
              cpu: 100m
              memory: 150Mi
          securityContext:
            runAsGroup: 0
            runAsUser: 0
        imagePullSecrets:
        - name: dockerconfigjson
```

dev-preview, dev-preview-cardanojs-handle-provider, Deployment (apps) would change:

```diff
  # Source: cardanojs/templates/handle-provider.deployment.yaml
  apiVersion: apps/v1
  kind: Deployment
  metadata:
    labels:
      app: handle-provider
      network: preview
      release: dev-preview-cardanojs
    name: dev-preview-cardanojs-handle-provider
  spec:
    selector:
      matchLabels:
        app: handle-provider
        network: preview
        release: dev-preview-cardanojs
    template:
      metadata:
        labels:
          app: handle-provider
          network: preview
          release: dev-preview-cardanojs
      spec:
        containers:
        - args:
          - start-provider-server
          env:
          - name: ENABLE_METRICS
            value: "true"
          - name: HANDLE_POLICY_IDS
            value: f0ff48bbb7bbe9d59a40f1ce90e9e9d0ff5002ec48f232b49ca0fb9a
          - name: LOGGER_MIN_SEVERITY
            value: info
          - name: NETWORK
            value: preview
          - name: NODE_ENV
          - name: OGMIOS_SRV_SERVICE_NAME
            value: dev-preview-cardano-core.dev-preview.svc.cluster.local
          - name: POSTGRES_DB_HANDLE
            value: handle
          - name: POSTGRES_HOST_HANDLE
            value: dev-preview-postgresql
          - name: POSTGRES_PASSWORD_HANDLE
            valueFrom:
              secretKeyRef:
                key: password
                name: handle-owner-user.dev-preview-postgresql.credentials.postgresql.acid.zalan.do
          - name: POSTGRES_POOL_MAX_HANDLE
            value: "10"
          - name: POSTGRES_PORT_HANDLE
            value: "5432"
          - name: POSTGRES_USER_HANDLE
            valueFrom:
              secretKeyRef:
                key: username
                name: handle-owner-user.dev-preview-postgresql.credentials.postgresql.acid.zalan.do
          - name: SERVICE_NAMES
            value: handle
-         image: 926093910549.dkr.ecr.us-east-1.amazonaws.com/cardano-services:hkld3yk0jil343zxnn7qrcb73zyn1pnq
+         image: 926093910549.dkr.ecr.us-east-1.amazonaws.com/cardano-services:ggd3diyab45gsdfmgaz5ac7yf63hzwzm
          livenessProbe:
            httpGet:
              path: /v1.0.0/health
              port: 3000
            timeoutSeconds: 5
          name: handle-provider
          ports:
          - containerPort: 3000
            name: http
          resources:
            requests:
              cpu: 700m
              memory: 150Mi
          securityContext:
            runAsGroup: 0
            runAsUser: 0
        imagePullSecrets:
        - name: dockerconfigjson
```

dev-preview, dev-preview-cardanojs-pg-boss-worker, Deployment (apps) would change:

```diff
  # Source: cardanojs/templates/pgboss.deployment.yaml
  apiVersion: apps/v1
  kind: Deployment
  metadata:
    labels:
      app: pg-boss-worker
      network: preview
      release: dev-preview-cardanojs
    name: dev-preview-cardanojs-pg-boss-worker
  spec:
    selector:
      matchLabels:
        app: pg-boss-worker
        network: preview
        release: dev-preview-cardanojs
    template:
      metadata:
        labels:
          app: pg-boss-worker
          network: preview
          release: dev-preview-cardanojs
      spec:
        containers:
        - args:
          - start-pg-boss-worker
          env:
          - name: LOGGER_MIN_SEVERITY
            value: info
          - name: METADATA_FETCH_MODE
            value: smash
          - name: NETWORK
            value: preview
          - name: NETWORK_INFO_PROVIDER_URL
            value: http://dev-preview-cardanojs-backend.dev-preview.svc.cluster.local
          - name: NODE_ENV
          - name: POSTGRES_DB_DB_SYNC
            value: cardano
          - name: POSTGRES_DB_STAKE_POOL
            value: stakepoolv2
          - name: POSTGRES_HOST_DB_SYNC
            value: dev-preview-postgresql
          - name: POSTGRES_HOST_STAKE_POOL
            value: dev-preview-postgresql
          - name: POSTGRES_PASSWORD_DB_SYNC
            valueFrom:
              secretKeyRef:
                key: password
                name: cardano-owner-user.dev-preview-postgresql.credentials.postgresql.acid.zalan.do
          - name: POSTGRES_PASSWORD_STAKE_POOL
            valueFrom:
              secretKeyRef:
                key: password
                name: stakepoolv2-owner-user.dev-preview-postgresql.credentials.postgresql.acid.zalan.do
          - name: POSTGRES_POOL_MAX_DB_SYNC
            value: "5"
          - name: POSTGRES_POOL_MAX_STAKE_POOL
            value: "5"
          - name: POSTGRES_PORT_DB_SYNC
            value: "5432"
          - name: POSTGRES_PORT_STAKE_POOL
            value: "5432"
          - name: POSTGRES_USER_DB_SYNC
            valueFrom:
              secretKeyRef:
                key: username
                name: cardano-owner-user.dev-preview-postgresql.credentials.postgresql.acid.zalan.do
          - name: POSTGRES_USER_STAKE_POOL
            valueFrom:
              secretKeyRef:
                key: username
                name: stakepoolv2-owner-user.dev-preview-postgresql.credentials.postgresql.acid.zalan.do
          - name: QUEUES
            value: pool-delist-schedule,pool-metadata,pool-metrics,pool-rewards
          - name: SMASH_URL
            value: https://preview-smash.world.dev.cardano.org/api/v1
          - name: STAKE_POOL_PROVIDER_URL
            value: http://dev-preview-cardanojs-backend.dev-preview.svc.cluster.local
-         image: 926093910549.dkr.ecr.us-east-1.amazonaws.com/cardano-services:hkld3yk0jil343zxnn7qrcb73zyn1pnq
+         image: 926093910549.dkr.ecr.us-east-1.amazonaws.com/cardano-services:ggd3diyab45gsdfmgaz5ac7yf63hzwzm
          livenessProbe:
            httpGet:
              path: /v1.0.0/health
              port: 3000
          name: pg-boss-worker
          ports:
          - containerPort: 3000
            name: http
          resources:
            limits:
              cpu: 300m
              memory: 300Mi
            requests:
              cpu: 200m
              memory: 150Mi
          securityContext:
            runAsGroup: 0
            runAsUser: 0
          startupProbe:
            httpGet:
              path: /v1.0.0/ready
              port: 3000
            initialDelaySeconds: 80
            periodSeconds: 5
        imagePullSecrets:
        - name: dockerconfigjson
```

dev-preview, dev-preview-cardanojs-stake-pool-projector, Deployment (apps) would change:

```diff
  # Source: cardanojs/templates/stake-pool-projector.deployment.yaml
  apiVersion: apps/v1
  kind: Deployment
  metadata:
    labels:
      app: stake-pool-projector
      network: preview
      release: dev-preview-cardanojs
    name: dev-preview-cardanojs-stake-pool-projector
  spec:
    selector:
      matchLabels:
        app: stake-pool-projector
        network: preview
        release: dev-preview-cardanojs
    template:
      metadata:
        labels:
          app: stake-pool-projector
          network: preview
          release: dev-preview-cardanojs
      spec:
        containers:
        - args:
          - start-projector
          env:
          - name: LOGGER_MIN_SEVERITY
            value: info
          - name: NETWORK
            value: preview
          - name: NODE_ENV
          - name: OGMIOS_SRV_SERVICE_NAME
            value: dev-preview-cardano-core.dev-preview.svc.cluster.local
          - name: POSTGRES_DB
            value: stakepoolv2
          - name: POSTGRES_HOST
            value: dev-preview-postgresql
          - name: POSTGRES_PASSWORD
            valueFrom:
              secretKeyRef:
                key: password
                name: stakepoolv2-owner-user.dev-preview-postgresql.credentials.postgresql.acid.zalan.do
          - name: POSTGRES_POOL_MAX
            value: "2"
          - name: POSTGRES_PORT
            value: "5432"
          - name: POSTGRES_USER
            valueFrom:
              secretKeyRef:
                key: username
                name: stakepoolv2-owner-user.dev-preview-postgresql.credentials.postgresql.acid.zalan.do
          - name: PROJECTION_NAMES
            value: stake-pool,stake-pool-metadata-job,stake-pool-metrics-job,stake-pool-rewards-job
-         image: 926093910549.dkr.ecr.us-east-1.amazonaws.com/cardano-services:hkld3yk0jil343zxnn7qrcb73zyn1pnq
+         image: 926093910549.dkr.ecr.us-east-1.amazonaws.com/cardano-services:ggd3diyab45gsdfmgaz5ac7yf63hzwzm
          livenessProbe:
            httpGet:
              path: /v1.0.0/health
              port: 3000
            timeoutSeconds: 5
          name: stake-pool-projector
          ports:
          - containerPort: 3000
            name: http
          resources:
            limits:
              cpu: 700m
              memory: 300Mi
            requests:
              cpu: 700m
              memory: 150Mi
          securityContext:
            runAsGroup: 0
            runAsUser: 0
        imagePullSecrets:
        - name: dockerconfigjson
```

dev-preview, dev-preview-cardanojs-stake-pool-provider, Deployment (apps) would change:

```diff
  # Source: cardanojs/templates/stake-pool-provider.deployment.yaml
  apiVersion: apps/v1
  kind: Deployment
  metadata:
    labels:
      app: stake-pool-provider
      network: preview
      release: dev-preview-cardanojs
    name: dev-preview-cardanojs-stake-pool-provider
  spec:
    selector:
      matchLabels:
        app: stake-pool-provider
        network: preview
        release: dev-preview-cardanojs
    template:
      metadata:
        labels:
          app: stake-pool-provider
          network: preview
          release: dev-preview-cardanojs
      spec:
        containers:
        - args:
          - start-provider-server
          env:
          - name: DISABLE_STAKE_POOL_METRIC_APY
            value: "true"
          - name: ENABLE_METRICS
            value: "true"
          - name: LOGGER_MIN_SEVERITY
            value: info
          - name: NETWORK
            value: preview
          - name: NODE_ENV
          - name: OGMIOS_SRV_SERVICE_NAME
            value: dev-preview-cardano-core.dev-preview.svc.cluster.local
          - name: OVERRIDE_FUZZY_OPTIONS
            value: "true"
          - name: PAGINATION_PAGE_SIZE_LIMIT
            value: "5500"
          - name: POSTGRES_DB_STAKE_POOL
            value: stakepoolv2
          - name: POSTGRES_HOST_STAKE_POOL
            value: dev-preview-postgresql
          - name: POSTGRES_PASSWORD_STAKE_POOL
            valueFrom:
              secretKeyRef:
                key: password
                name: stakepoolv2-owner-user.dev-preview-postgresql.credentials.postgresql.acid.zalan.do
          - name: POSTGRES_POOL_MAX_STAKE_POOL
            value: "10"
          - name: POSTGRES_PORT_STAKE_POOL
            value: "5432"
          - name: POSTGRES_USER_STAKE_POOL
            valueFrom:
              secretKeyRef:
                key: username
                name: stakepoolv2-owner-user.dev-preview-postgresql.credentials.postgresql.acid.zalan.do
          - name: SERVICE_NAMES
            value: stake-pool
          - name: TOKEN_METADATA_SERVER_URL
            value: http://dev-preview-cardano-stack-metadata.dev-preview.svc.cluster.local
          - name: USE_TYPEORM_STAKE_POOL_PROVIDER
            value: "true"
-         image: 926093910549.dkr.ecr.us-east-1.amazonaws.com/cardano-services:hkld3yk0jil343zxnn7qrcb73zyn1pnq
+         image: 926093910549.dkr.ecr.us-east-1.amazonaws.com/cardano-services:ggd3diyab45gsdfmgaz5ac7yf63hzwzm
          livenessProbe:
            httpGet:
              path: /v1.0.0/health
              port: 3000
            timeoutSeconds: 5
          name: stake-pool-provider
          ports:
          - containerPort: 3000
            name: http
          resources:
            requests:
              cpu: 700m
              memory: 150Mi
          securityContext:
            runAsGroup: 0
            runAsUser: 0
        imagePullSecrets:
        - name: dockerconfigjson
```

dev-preview, dev-preview-cardanojs-wallet-api-projector, Deployment (apps) would change:

```diff
  # Source: cardanojs/templates/wallet-api-projector.deployment.yaml
  apiVersion: apps/v1
  kind: Deployment
  metadata:
    labels:
      app: wallet-api-projector
      network: preview
      release: dev-preview-cardanojs
    name: dev-preview-cardanojs-wallet-api-projector
  spec:
    selector:
      matchLabels:
        app: wallet-api-projector
        network: preview
        release: dev-preview-cardanojs
    template:
      metadata:
        labels:
          app: wallet-api-projector
          network: preview
          release: dev-preview-cardanojs
      spec:
        containers:
        - args:
          - start-projector
          env:
          - name: LOGGER_MIN_SEVERITY
            value: info
          - name: NETWORK
            value: preview
          - name: OGMIOS_SRV_SERVICE_NAME
            value: dev-preview-cardano-core.dev-preview.svc.cluster.local
          - name: POSTGRES_DB
            value: wallet_api
          - name: POSTGRES_HOST
            value: dev-preview-postgresql
          - name: POSTGRES_PASSWORD
            valueFrom:
              secretKeyRef:
                key: password
                name: wallet-api-owner-user.dev-preview-postgresql.credentials.postgresql.acid.zalan.do
          - name: POSTGRES_POOL_MAX
            value: "2"
          - name: POSTGRES_PORT
            value: "5432"
          - name: POSTGRES_USER
            valueFrom:
              secretKeyRef:
                key: username
                name: wallet-api-owner-user.dev-preview-postgresql.credentials.postgresql.acid.zalan.do
          - name: PROJECTION_NAMES
            value: protocol-parameters
-         image: 926093910549.dkr.ecr.us-east-1.amazonaws.com/cardano-services:hkld3yk0jil343zxnn7qrcb73zyn1pnq
+         image: 926093910549.dkr.ecr.us-east-1.amazonaws.com/cardano-services:ggd3diyab45gsdfmgaz5ac7yf63hzwzm
          livenessProbe:
            httpGet:
              path: /v1.0.0/health
              port: 3000
            timeoutSeconds: 5
          name: wallet-api-projector
          ports:
          - containerPort: 3000
            name: http
          resources:
            limits:
              cpu: 1000m
              memory: 300Mi
            requests:
              cpu: 100m
              memory: 150Mi
          securityContext:
            runAsGroup: 0
            runAsUser: 0
        imagePullSecrets:
        - name: dockerconfigjson
```

dev-preview, dev-preview-cardanojs-ws-server, Deployment (apps) would change:

```diff
  # Source: cardanojs/templates/ws-server.deployment.yaml
  apiVersion: apps/v1
  kind: Deployment
  metadata:
    labels:
      app: ws-server
      network: preview
      release: dev-preview-cardanojs
    name: dev-preview-cardanojs-ws-server
  spec:
    selector:
      matchLabels:
        app: ws-server
        network: preview
        release: dev-preview-cardanojs
    template:
      metadata:
        labels:
          app: ws-server
          network: preview
          release: dev-preview-cardanojs
      spec:
        containers:
        - args:
          - start-ws-server
          env:
          - name: DB_CACHE_TTL
            value: "7200"
          - name: NETWORK
            value: preview
          - name: OGMIOS_URL
            value: ws://dev-preview-cardano-core.dev-preview.svc.cluster.local:1337
          - name: POSTGRES_DB_DB_SYNC
            value: cardano
          - name: POSTGRES_HOST_DB_SYNC
            value: dev-preview-postgresql
          - name: POSTGRES_PASSWORD_DB_SYNC
            valueFrom:
              secretKeyRef:
                key: password
                name: cardano-owner-user.dev-preview-postgresql.credentials.postgresql.acid.zalan.do
          - name: POSTGRES_POOL_MAX_DB_SYNC
            value: "2"
          - name: POSTGRES_PORT_DB_SYNC
            value: "5432"
          - name: POSTGRES_USER_DB_SYNC
            valueFrom:
              secretKeyRef:
                key: username
                name: cardano-owner-user.dev-preview-postgresql.credentials.postgresql.acid.zalan.do
-         image: 926093910549.dkr.ecr.us-east-1.amazonaws.com/cardano-services:hkld3yk0jil343zxnn7qrcb73zyn1pnq
+         image: 926093910549.dkr.ecr.us-east-1.amazonaws.com/cardano-services:ggd3diyab45gsdfmgaz5ac7yf63hzwzm
          livenessProbe:
            httpGet:
              path: /health
              port: 3000
          name: ws-server
          ports:
          - containerPort: 3000
            name: http
          resources:
            limits:
              cpu: 300m
              memory: 300Mi
            requests:
              cpu: 200m
              memory: 150Mi
          securityContext:
            runAsGroup: 0
            runAsUser: 0
        imagePullSecrets:
        - name: dockerconfigjson
```
</details>
<details>
<summary><code>dev-preprod@us-east-1@v2</code> would change:</summary>

dev-preprod, dev-preprod-cardanojs-v2-asset-projector, Deployment (apps) would change:

```diff
  # Source: cardanojs/templates/asset-projector.deployment.yaml
  apiVersion: apps/v1
  kind: Deployment
  metadata:
    labels:
      app: asset-projector
      network: preprod
      release: dev-preprod-cardanojs-v2
    name: dev-preprod-cardanojs-v2-asset-projector
  spec:
    selector:
      matchLabels:
        app: asset-projector
        network: preprod
        release: dev-preprod-cardanojs-v2
    template:
      metadata:
        labels:
          app: asset-projector
          network: preprod
          release: dev-preprod-cardanojs-v2
      spec:
        containers:
        - args:
          - start-projector
          env:
          - name: LOGGER_MIN_SEVERITY
            value: info
          - name: NETWORK
            value: preprod
          - name: OGMIOS_SRV_SERVICE_NAME
            value: dev-preprod-cardano-core.dev-preprod.svc.cluster.local
          - name: POSTGRES_DB
            value: asset
          - name: POSTGRES_HOST
            value: dev-preprod-postgresql
          - name: POSTGRES_PASSWORD
            valueFrom:
              secretKeyRef:
                key: password
                name: asset-owner-user.dev-preprod-postgresql.credentials.postgresql.acid.zalan.do
          - name: POSTGRES_POOL_MAX
            value: "2"
          - name: POSTGRES_PORT
            value: "5432"
          - name: POSTGRES_USER
            valueFrom:
              secretKeyRef:
                key: username
                name: asset-owner-user.dev-preprod-postgresql.credentials.postgresql.acid.zalan.do
          - name: PROJECTION_NAMES
            value: asset
-         image: 926093910549.dkr.ecr.us-east-1.amazonaws.com/cardano-services:nfl1lh9x3wvlm5y4wdk168h41lwpaprq
+         image: 926093910549.dkr.ecr.us-east-1.amazonaws.com/cardano-services:ggd3diyab45gsdfmgaz5ac7yf63hzwzm
          livenessProbe:
            httpGet:
              path: /v1.0.0/health
              port: 3000
            timeoutSeconds: 5
          name: asset-projector
          ports:
          - containerPort: 3000
            name: http
          resources:
            limits:
              cpu: 700m
              memory: 300Mi
            requests:
              cpu: 700m
              memory: 150Mi
          securityContext:
            runAsGroup: 0
            runAsUser: 0
        imagePullSecrets:
        - name: dockerconfigjson
```

dev-preprod, dev-preprod-cardanojs-v2-asset-provider, Deployment (apps) would change:

```diff
  # Source: cardanojs/templates/asset-provider.deployment.yaml
  apiVersion: apps/v1
  kind: Deployment
  metadata:
    labels:
      app: asset-provider
      network: preprod
      release: dev-preprod-cardanojs-v2
    name: dev-preprod-cardanojs-v2-asset-provider
  spec:
    selector:
      matchLabels:
        app: asset-provider
        network: preprod
        release: dev-preprod-cardanojs-v2
    template:
      metadata:
        labels:
          app: asset-provider
          network: preprod
          release: dev-preprod-cardanojs-v2
      spec:
        containers:
        - args:
          - start-provider-server
          env:
          - name: ALLOWED_ORIGINS
          - name: ENABLE_METRICS
            value: "true"
          - name: LOGGER_MIN_SEVERITY
            value: info
          - name: NETWORK
            value: preprod
          - name: NODE_ENV
          - name: OGMIOS_SRV_SERVICE_NAME
            value: dev-preprod-cardano-core.dev-preprod.svc.cluster.local
          - name: PAGINATION_PAGE_SIZE_LIMIT
            value: "5500"
          - name: POSTGRES_DB_ASSET
            value: asset
          - name: POSTGRES_HOST_ASSET
            value: dev-preprod-postgresql
          - name: POSTGRES_PASSWORD_ASSET
            valueFrom:
              secretKeyRef:
                key: password
                name: asset-owner-user.dev-preprod-postgresql.credentials.postgresql.acid.zalan.do
          - name: POSTGRES_POOL_MAX_ASSET
            value: "500"
          - name: POSTGRES_PORT_ASSET
            value: "5432"
          - name: POSTGRES_USER_ASSET
            valueFrom:
              secretKeyRef:
                key: username
                name: asset-owner-user.dev-preprod-postgresql.credentials.postgresql.acid.zalan.do
          - name: SERVICE_NAMES
            value: asset
          - name: TOKEN_METADATA_SERVER_URL
            value: http://dev-preprod-cardano-stack-metadata.dev-preprod.svc.cluster.local
          - name: USE_TYPEORM_ASSET_PROVIDER
            value: "true"
-         image: 926093910549.dkr.ecr.us-east-1.amazonaws.com/cardano-services:nfl1lh9x3wvlm5y4wdk168h41lwpaprq
+         image: 926093910549.dkr.ecr.us-east-1.amazonaws.com/cardano-services:ggd3diyab45gsdfmgaz5ac7yf63hzwzm
          livenessProbe:
            httpGet:
              path: /v1.0.0/health
              port: 3000
            timeoutSeconds: 5
          name: asset-provider
          ports:
          - containerPort: 3000
            name: http
          resources:
            requests:
              cpu: 1100m
              memory: 512Mi
          securityContext:
            runAsGroup: 0
            runAsUser: 0
        imagePullSecrets:
        - name: dockerconfigjson
```

dev-preprod, dev-preprod-cardanojs-v2-backend, Deployment (apps) would change:

```diff
  # Source: cardanojs/templates/backend.deployment.yaml
  apiVersion: apps/v1
  kind: Deployment
  metadata:
    labels:
      app: backend
      network: preprod
      release: dev-preprod-cardanojs-v2
    name: dev-preprod-cardanojs-v2-backend
  spec:
    selector:
      matchLabels:
        app: backend
        network: preprod
        release: dev-preprod-cardanojs-v2
    template:
      metadata:
        labels:
          app: backend
          network: preprod
          release: dev-preprod-cardanojs-v2
      spec:
        containers:
        - args:
          - start-provider-server
          env:
          - name: ALLOWED_ORIGINS
          - name: BUILD_INFO
-           value: '{"lastModifiedDate":"20250225153102","rev":"81782c04314662cbc638c56af8e8c60f42699a19","shortRev":"81782c0"}'
+           value: '{"lastModifiedDate":"20250514010946","rev":"f2142d8be63d1c0988764b61f894a7347e663a0b","shortRev":"f2142d8"}'
          - name: DISABLE_STAKE_POOL_METRIC_APY
            value: "true"
          - name: ENABLE_METRICS
            value: "true"
          - name: HANDLE_POLICY_IDS
            value: f0ff48bbb7bbe9d59a40f1ce90e9e9d0ff5002ec48f232b49ca0fb9a
          - name: HANDLE_PROVIDER_SERVER_URL
            value: https://preprod.api.handle.me
          - name: LOGGER_MIN_SEVERITY
            value: info
          - name: NETWORK
            value: preprod
          - name: NODE_ENV
          - name: OGMIOS_SRV_SERVICE_NAME
            value: dev-preprod-cardano-core.dev-preprod.svc.cluster.local
          - name: PAGINATION_PAGE_SIZE_LIMIT
            value: "5500"
          - name: POSTGRES_DB_DB_SYNC
            value: cardano
          - name: POSTGRES_DB_HANDLE
            value: handle
          - name: POSTGRES_HOST_DB_SYNC
            value: dev-preprod-postgresql
          - name: POSTGRES_HOST_HANDLE
            value: dev-preprod-postgresql
          - name: POSTGRES_PASSWORD_DB_SYNC
            valueFrom:
              secretKeyRef:
                key: password
                name: cardano-owner-user.dev-preprod-postgresql.credentials.postgresql.acid.zalan.do
          - name: POSTGRES_PASSWORD_HANDLE
            valueFrom:
              secretKeyRef:
                key: password
                name: handle-owner-user.dev-preprod-postgresql.credentials.postgresql.acid.zalan.do
          - name: POSTGRES_POOL_MAX_DB_SYNC
            value: "50"
          - name: POSTGRES_POOL_MAX_HANDLE
            value: "10"
          - name: POSTGRES_PORT_DB_SYNC
            value: "5432"
          - name: POSTGRES_PORT_HANDLE
            value: "5432"
          - name: POSTGRES_USER_DB_SYNC
            valueFrom:
              secretKeyRef:
                key: username
                name: cardano-owner-user.dev-preprod-postgresql.credentials.postgresql.acid.zalan.do
          - name: POSTGRES_USER_HANDLE
            valueFrom:
              secretKeyRef:
                key: username
                name: handle-owner-user.dev-preprod-postgresql.credentials.postgresql.acid.zalan.do
          - name: SERVICE_NAMES
            value: asset,network-info,rewards,stake-pool,tx-submit,utxo
          - name: TOKEN_METADATA_SERVER_URL
            value: http://dev-preprod-cardano-stack-metadata.dev-preprod.svc.cluster.local
          - name: USE_BLOCKFROST
            value: "true"
          - name: USE_KORA_LABS
            value: "true"
-         image: 926093910549.dkr.ecr.us-east-1.amazonaws.com/cardano-services:nfl1lh9x3wvlm5y4wdk168h41lwpaprq
+         image: 926093910549.dkr.ecr.us-east-1.amazonaws.com/cardano-services:ggd3diyab45gsdfmgaz5ac7yf63hzwzm
          livenessProbe:
            httpGet:
              path: /v1.0.0/ready
              port: 3000
            periodSeconds: 10
            timeoutSeconds: 5
          name: backend
          ports:
          - containerPort: 3000
            name: http
          resources:
            requests:
              cpu: 1000m
              memory: 350Mi
          securityContext:
            runAsGroup: 0
            runAsUser: 0
        imagePullSecrets:
        - name: dockerconfigjson
```

dev-preprod, dev-preprod-cardanojs-v2-blockfrost-worker, Deployment (apps) would change:

```diff
  # Source: cardanojs/templates/blockfrost-worker.deployment.yaml
  apiVersion: apps/v1
  kind: Deployment
  metadata:
    labels:
      app: blockfrost-worker
      network: preprod
      release: dev-preprod-cardanojs-v2
    name: dev-preprod-cardanojs-v2-blockfrost-worker
  spec:
    selector:
      matchLabels:
        app: blockfrost-worker
        network: preprod
        release: dev-preprod-cardanojs-v2
    template:
      metadata:
        labels:
          app: blockfrost-worker
          network: preprod
          release: dev-preprod-cardanojs-v2
      spec:
        containers:
        - args:
          - start-blockfrost-worker
          env:
          - name: BLOCKFROST_API_KEY
            valueFrom:
              secretKeyRef:
                key: api-key
                name: blockfrost
          - name: LOGGER_MIN_SEVERITY
            value: info
          - name: NETWORK
            value: preprod
          - name: NODE_ENV
          - name: POSTGRES_DB_DB_SYNC
            value: cardano
          - name: POSTGRES_HOST_DB_SYNC
            value: dev-preprod-postgresql
          - name: POSTGRES_PASSWORD_DB_SYNC
            valueFrom:
              secretKeyRef:
                key: password
                name: cardano-owner-user.dev-preprod-postgresql.credentials.postgresql.acid.zalan.do
          - name: POSTGRES_PORT_DB_SYNC
            value: "5432"
          - name: POSTGRES_USER_DB_SYNC
            valueFrom:
              secretKeyRef:
                key: username
                name: cardano-owner-user.dev-preprod-postgresql.credentials.postgresql.acid.zalan.do
-         image: 926093910549.dkr.ecr.us-east-1.amazonaws.com/cardano-services:nfl1lh9x3wvlm5y4wdk168h41lwpaprq
+         image: 926093910549.dkr.ecr.us-east-1.amazonaws.com/cardano-services:ggd3diyab45gsdfmgaz5ac7yf63hzwzm
          livenessProbe:
            httpGet:
              path: /v1.0.0/health
              port: 3000
            timeoutSeconds: 5
          name: blockfrost-worker
          ports:
          - containerPort: 3000
            name: http
          resources:
            limits:
              cpu: 500m
              memory: 300Mi
            requests:
              cpu: 100m
              memory: 150Mi
          securityContext:
            runAsGroup: 0
            runAsUser: 0
        imagePullSecrets:
        - name: dockerconfigjson
```

dev-preprod, dev-preprod-cardanojs-v2-chain-history-provider, Deployment (apps) would change:

```diff
  # Source: cardanojs/templates/chain-history-provider.deployment.yaml
  apiVersion: apps/v1
  kind: Deployment
  metadata:
    labels:
      app: chain-history-provider
      network: preprod
      release: dev-preprod-cardanojs-v2
    name: dev-preprod-cardanojs-v2-chain-history-provider
  spec:
    selector:
      matchLabels:
        app: chain-history-provider
        network: preprod
        release: dev-preprod-cardanojs-v2
    template:
      metadata:
        labels:
          app: chain-history-provider
          network: preprod
          release: dev-preprod-cardanojs-v2
      spec:
        containers:
        - args:
          - start-provider-server
          env:
          - name: ALLOWED_ORIGINS
          - name: DISABLE_STAKE_POOL_METRIC_APY
            value: "true"
          - name: ENABLE_METRICS
            value: "true"
          - name: HANDLE_POLICY_IDS
            value: f0ff48bbb7bbe9d59a40f1ce90e9e9d0ff5002ec48f232b49ca0fb9a
          - name: LOGGER_MIN_SEVERITY
            value: info
          - name: NETWORK
            value: preprod
          - name: NODE_ENV
          - name: OGMIOS_SRV_SERVICE_NAME
            value: dev-preprod-cardano-core.dev-preprod.svc.cluster.local
          - name: PAGINATION_PAGE_SIZE_LIMIT
            value: "5500"
          - name: POSTGRES_DB_DB_SYNC
            value: cardano
          - name: POSTGRES_HOST_DB_SYNC
            value: dev-preprod-postgresql
          - name: POSTGRES_PASSWORD_DB_SYNC
            valueFrom:
              secretKeyRef:
                key: password
                name: cardano-owner-user.dev-preprod-postgresql.credentials.postgresql.acid.zalan.do
          - name: POSTGRES_POOL_MAX_DB_SYNC
            value: "50"
          - name: POSTGRES_PORT_DB_SYNC
            value: "5432"
          - name: POSTGRES_USER_DB_SYNC
            valueFrom:
              secretKeyRef:
                key: username
                name: cardano-owner-user.dev-preprod-postgresql.credentials.postgresql.acid.zalan.do
          - name: SERVICE_NAMES
            value: chain-history
          - name: TOKEN_METADATA_SERVER_URL
            value: http://dev-preprod-cardano-stack-metadata.dev-preprod.svc.cluster.local
          - name: USE_BLOCKFROST
            value: "true"
          - name: USE_KORA_LABS
            value: "true"
-         image: 926093910549.dkr.ecr.us-east-1.amazonaws.com/cardano-services:nfl1lh9x3wvlm5y4wdk168h41lwpaprq
+         image: 926093910549.dkr.ecr.us-east-1.amazonaws.com/cardano-services:ggd3diyab45gsdfmgaz5ac7yf63hzwzm
          livenessProbe:
            httpGet:
              path: /v1.0.0/ready
              port: 3000
            periodSeconds: 10
            timeoutSeconds: 5
          name: chain-history-provider
          ports:
          - containerPort: 3000
            name: http
          resources:
            requests:
              cpu: 1000m
              memory: 512Mi
          securityContext:
            runAsGroup: 0
            runAsUser: 0
        imagePullSecrets:
        - name: dockerconfigjson
```

dev-preprod, dev-preprod-cardanojs-v2-handle-projector, Deployment (apps) would change:

```diff
  # Source: cardanojs/templates/handle-projector.deployment.yaml
  apiVersion: apps/v1
  kind: Deployment
  metadata:
    labels:
      app: handle-projector
      network: preprod
      release: dev-preprod-cardanojs-v2
    name: dev-preprod-cardanojs-v2-handle-projector
  spec:
    selector:
      matchLabels:
        app: handle-projector
        network: preprod
        release: dev-preprod-cardanojs-v2
    template:
      metadata:
        labels:
          app: handle-projector
          network: preprod
          release: dev-preprod-cardanojs-v2
      spec:
        containers:
        - args:
          - start-projector
          env:
          - name: HANDLE_POLICY_IDS
            value: f0ff48bbb7bbe9d59a40f1ce90e9e9d0ff5002ec48f232b49ca0fb9a
          - name: LOGGER_MIN_SEVERITY
            value: info
          - name: NETWORK
            value: preprod
          - name: NODE_ENV
          - name: OGMIOS_SRV_SERVICE_NAME
            value: dev-preprod-cardano-core.dev-preprod.svc.cluster.local
          - name: POSTGRES_DB
            value: handle
          - name: POSTGRES_HOST
            value: dev-preprod-postgresql
          - name: POSTGRES_PASSWORD
            valueFrom:
              secretKeyRef:
                key: password
                name: handle-owner-user.dev-preprod-postgresql.credentials.postgresql.acid.zalan.do
          - name: POSTGRES_POOL_MAX
            value: "2"
          - name: POSTGRES_PORT
            value: "5432"
          - name: POSTGRES_USER
            valueFrom:
              secretKeyRef:
                key: username
                name: handle-owner-user.dev-preprod-postgresql.credentials.postgresql.acid.zalan.do
          - name: PROJECTION_NAMES
            value: handle
-         image: 926093910549.dkr.ecr.us-east-1.amazonaws.com/cardano-services:nfl1lh9x3wvlm5y4wdk168h41lwpaprq
+         image: 926093910549.dkr.ecr.us-east-1.amazonaws.com/cardano-services:ggd3diyab45gsdfmgaz5ac7yf63hzwzm
          livenessProbe:
            httpGet:
              path: /v1.0.0/health
              port: 3000
            timeoutSeconds: 5
          name: handle-projector
          ports:
          - containerPort: 3000
            name: http
          resources:
            limits:
              cpu: 1000m
              memory: 300Mi
            requests:
              cpu: 100m
              memory: 150Mi
          securityContext:
            runAsGroup: 0
            runAsUser: 0
        imagePullSecrets:
        - name: dockerconfigjson
```

dev-preprod, dev-preprod-cardanojs-v2-handle-provider, Deployment (apps) would change:

```diff
  # Source: cardanojs/templates/handle-provider.deployment.yaml
  apiVersion: apps/v1
  kind: Deployment
  metadata:
    labels:
      app: handle-provider
      network: preprod
      release: dev-preprod-cardanojs-v2
    name: dev-preprod-cardanojs-v2-handle-provider
  spec:
    selector:
      matchLabels:
        app: handle-provider
        network: preprod
        release: dev-preprod-cardanojs-v2
    template:
      metadata:
        labels:
          app: handle-provider
          network: preprod
          release: dev-preprod-cardanojs-v2
      spec:
        containers:
        - args:
          - start-provider-server
          env:
          - name: ENABLE_METRICS
            value: "true"
          - name: HANDLE_POLICY_IDS
            value: f0ff48bbb7bbe9d59a40f1ce90e9e9d0ff5002ec48f232b49ca0fb9a
          - name: LOGGER_MIN_SEVERITY
            value: info
          - name: NETWORK
            value: preprod
          - name: NODE_ENV
          - name: OGMIOS_SRV_SERVICE_NAME
            value: dev-preprod-cardano-core.dev-preprod.svc.cluster.local
          - name: POSTGRES_DB_HANDLE
            value: handle
          - name: POSTGRES_HOST_HANDLE
            value: dev-preprod-postgresql
          - name: POSTGRES_PASSWORD_HANDLE
            valueFrom:
              secretKeyRef:
                key: password
                name: handle-owner-user.dev-preprod-postgresql.credentials.postgresql.acid.zalan.do
          - name: POSTGRES_POOL_MAX_HANDLE
            value: "10"
          - name: POSTGRES_PORT_HANDLE
            value: "5432"
          - name: POSTGRES_USER_HANDLE
            valueFrom:
              secretKeyRef:
                key: username
                name: handle-owner-user.dev-preprod-postgresql.credentials.postgresql.acid.zalan.do
          - name: SERVICE_NAMES
            value: handle
-         image: 926093910549.dkr.ecr.us-east-1.amazonaws.com/cardano-services:nfl1lh9x3wvlm5y4wdk168h41lwpaprq
+         image: 926093910549.dkr.ecr.us-east-1.amazonaws.com/cardano-services:ggd3diyab45gsdfmgaz5ac7yf63hzwzm
          livenessProbe:
            httpGet:
              path: /v1.0.0/health
              port: 3000
            timeoutSeconds: 5
          name: handle-provider
          ports:
          - containerPort: 3000
            name: http
          resources:
            requests:
              cpu: 700m
              memory: 150Mi
          securityContext:
            runAsGroup: 0
            runAsUser: 0
        imagePullSecrets:
        - name: dockerconfigjson
```

dev-preprod, dev-preprod-cardanojs-v2-pg-boss-worker, Deployment (apps) would change:

```diff
  # Source: cardanojs/templates/pgboss.deployment.yaml
  apiVersion: apps/v1
  kind: Deployment
  metadata:
    labels:
      app: pg-boss-worker
      network: preprod
      release: dev-preprod-cardanojs-v2
    name: dev-preprod-cardanojs-v2-pg-boss-worker
  spec:
    selector:
      matchLabels:
        app: pg-boss-worker
        network: preprod
        release: dev-preprod-cardanojs-v2
    template:
      metadata:
        labels:
          app: pg-boss-worker
          network: preprod
          release: dev-preprod-cardanojs-v2
      spec:
        containers:
        - args:
          - start-pg-boss-worker
          env:
          - name: LOGGER_MIN_SEVERITY
            value: info
          - name: METADATA_FETCH_MODE
            value: smash
          - name: NETWORK
            value: preprod
          - name: NETWORK_INFO_PROVIDER_URL
            value: http://dev-preprod-cardanojs-v2-backend.dev-preprod.svc.cluster.local
          - name: NODE_ENV
          - name: POSTGRES_DB_DB_SYNC
            value: cardano
          - name: POSTGRES_DB_STAKE_POOL
            value: stakepoolv2
          - name: POSTGRES_HOST_DB_SYNC
            value: dev-preprod-postgresql
          - name: POSTGRES_HOST_STAKE_POOL
            value: dev-preprod-postgresql
          - name: POSTGRES_PASSWORD_DB_SYNC
            valueFrom:
              secretKeyRef:
                key: password
                name: cardano-owner-user.dev-preprod-postgresql.credentials.postgresql.acid.zalan.do
          - name: POSTGRES_PASSWORD_STAKE_POOL
            valueFrom:
              secretKeyRef:
                key: password
                name: stakepoolv2-owner-user.dev-preprod-postgresql.credentials.postgresql.acid.zalan.do
          - name: POSTGRES_POOL_MAX_DB_SYNC
            value: "5"
          - name: POSTGRES_POOL_MAX_STAKE_POOL
            value: "5"
          - name: POSTGRES_PORT_DB_SYNC
            value: "5432"
          - name: POSTGRES_PORT_STAKE_POOL
            value: "5432"
          - name: POSTGRES_USER_DB_SYNC
            valueFrom:
              secretKeyRef:
                key: username
                name: cardano-owner-user.dev-preprod-postgresql.credentials.postgresql.acid.zalan.do
          - name: POSTGRES_USER_STAKE_POOL
            valueFrom:
              secretKeyRef:
                key: username
                name: stakepoolv2-owner-user.dev-preprod-postgresql.credentials.postgresql.acid.zalan.do
          - name: QUEUES
            value: pool-delist-schedule,pool-metadata,pool-metrics,pool-rewards
          - name: SMASH_URL
            value: https://preprod-smash.world.dev.cardano.org/api/v1
          - name: STAKE_POOL_PROVIDER_URL
            value: http://dev-preprod-cardanojs-v2-backend.dev-preprod.svc.cluster.local
-         image: 926093910549.dkr.ecr.us-east-1.amazonaws.com/cardano-services:nfl1lh9x3wvlm5y4wdk168h41lwpaprq
+         image: 926093910549.dkr.ecr.us-east-1.amazonaws.com/cardano-services:ggd3diyab45gsdfmgaz5ac7yf63hzwzm
          livenessProbe:
            httpGet:
              path: /v1.0.0/health
              port: 3000
          name: pg-boss-worker
          ports:
          - containerPort: 3000
            name: http
          resources:
            limits:
              cpu: 300m
              memory: 300Mi
            requests:
              cpu: 200m
              memory: 150Mi
          securityContext:
            runAsGroup: 0
            runAsUser: 0
          startupProbe:
            httpGet:
              path: /v1.0.0/ready
              port: 3000
            initialDelaySeconds: 80
            periodSeconds: 5
        imagePullSecrets:
        - name: dockerconfigjson
```

dev-preprod, dev-preprod-cardanojs-v2-stake-pool-projector, Deployment (apps) would change:

```diff
  # Source: cardanojs/templates/stake-pool-projector.deployment.yaml
  apiVersion: apps/v1
  kind: Deployment
  metadata:
    labels:
      app: stake-pool-projector
      network: preprod
      release: dev-preprod-cardanojs-v2
    name: dev-preprod-cardanojs-v2-stake-pool-projector
  spec:
    selector:
      matchLabels:
        app: stake-pool-projector
        network: preprod
        release: dev-preprod-cardanojs-v2
    template:
      metadata:
        labels:
          app: stake-pool-projector
          network: preprod
          release: dev-preprod-cardanojs-v2
      spec:
        containers:
        - args:
          - start-projector
          env:
          - name: LOGGER_MIN_SEVERITY
            value: info
          - name: NETWORK
            value: preprod
          - name: NODE_ENV
          - name: OGMIOS_SRV_SERVICE_NAME
            value: dev-preprod-cardano-core.dev-preprod.svc.cluster.local
          - name: POSTGRES_DB
            value: stakepoolv2
          - name: POSTGRES_HOST
            value: dev-preprod-postgresql
          - name: POSTGRES_PASSWORD
            valueFrom:
              secretKeyRef:
                key: password
                name: stakepoolv2-owner-user.dev-preprod-postgresql.credentials.postgresql.acid.zalan.do
          - name: POSTGRES_POOL_MAX
            value: "2"
          - name: POSTGRES_PORT
            value: "5432"
          - name: POSTGRES_USER
            valueFrom:
              secretKeyRef:
                key: username
                name: stakepoolv2-owner-user.dev-preprod-postgresql.credentials.postgresql.acid.zalan.do
          - name: PROJECTION_NAMES
            value: stake-pool,stake-pool-metadata-job,stake-pool-metrics-job,stake-pool-rewards-job
-         image: 926093910549.dkr.ecr.us-east-1.amazonaws.com/cardano-services:nfl1lh9x3wvlm5y4wdk168h41lwpaprq
+         image: 926093910549.dkr.ecr.us-east-1.amazonaws.com/cardano-services:ggd3diyab45gsdfmgaz5ac7yf63hzwzm
          livenessProbe:
            httpGet:
              path: /v1.0.0/health
              port: 3000
            timeoutSeconds: 5
          name: stake-pool-projector
          ports:
          - containerPort: 3000
            name: http
          resources:
            limits:
              cpu: 700m
              memory: 300Mi
            requests:
              cpu: 700m
              memory: 150Mi
          securityContext:
            runAsGroup: 0
            runAsUser: 0
        imagePullSecrets:
        - name: dockerconfigjson
```

dev-preprod, dev-preprod-cardanojs-v2-stake-pool-provider, Deployment (apps) would change:

```diff
  # Source: cardanojs/templates/stake-pool-provider.deployment.yaml
  apiVersion: apps/v1
  kind: Deployment
  metadata:
    labels:
      app: stake-pool-provider
      network: preprod
      release: dev-preprod-cardanojs-v2
    name: dev-preprod-cardanojs-v2-stake-pool-provider
  spec:
    selector:
      matchLabels:
        app: stake-pool-provider
        network: preprod
        release: dev-preprod-cardanojs-v2
    template:
      metadata:
        labels:
          app: stake-pool-provider
          network: preprod
          release: dev-preprod-cardanojs-v2
      spec:
        containers:
        - args:
          - start-provider-server
          env:
          - name: DISABLE_STAKE_POOL_METRIC_APY
            value: "true"
          - name: ENABLE_METRICS
            value: "true"
          - name: LOGGER_MIN_SEVERITY
            value: info
          - name: NETWORK
            value: preprod
          - name: NODE_ENV
          - name: OGMIOS_SRV_SERVICE_NAME
            value: dev-preprod-cardano-core.dev-preprod.svc.cluster.local
          - name: OVERRIDE_FUZZY_OPTIONS
            value: "true"
          - name: PAGINATION_PAGE_SIZE_LIMIT
            value: "5500"
          - name: POSTGRES_DB_STAKE_POOL
            value: stakepoolv2
          - name: POSTGRES_HOST_STAKE_POOL
            value: dev-preprod-postgresql
          - name: POSTGRES_PASSWORD_STAKE_POOL
            valueFrom:
              secretKeyRef:
                key: password
                name: stakepoolv2-owner-user.dev-preprod-postgresql.credentials.postgresql.acid.zalan.do
          - name: POSTGRES_POOL_MAX_STAKE_POOL
            value: "10"
          - name: POSTGRES_PORT_STAKE_POOL
            value: "5432"
          - name: POSTGRES_USER_STAKE_POOL
            valueFrom:
              secretKeyRef:
                key: username
                name: stakepoolv2-owner-user.dev-preprod-postgresql.credentials.postgresql.acid.zalan.do
          - name: SERVICE_NAMES
            value: stake-pool
          - name: TOKEN_METADATA_SERVER_URL
            value: http://dev-preprod-cardano-stack-metadata.dev-preprod.svc.cluster.local
          - name: USE_TYPEORM_STAKE_POOL_PROVIDER
            value: "true"
-         image: 926093910549.dkr.ecr.us-east-1.amazonaws.com/cardano-services:nfl1lh9x3wvlm5y4wdk168h41lwpaprq
+         image: 926093910549.dkr.ecr.us-east-1.amazonaws.com/cardano-services:ggd3diyab45gsdfmgaz5ac7yf63hzwzm
          livenessProbe:
            httpGet:
              path: /v1.0.0/health
              port: 3000
            timeoutSeconds: 5
          name: stake-pool-provider
          ports:
          - containerPort: 3000
            name: http
          resources:
            requests:
              cpu: 700m
              memory: 150Mi
          securityContext:
            runAsGroup: 0
            runAsUser: 0
        imagePullSecrets:
        - name: dockerconfigjson
```

dev-preprod, dev-preprod-cardanojs-v2-wallet-api-projector, Deployment (apps) would change:

```diff
  # Source: cardanojs/templates/wallet-api-projector.deployment.yaml
  apiVersion: apps/v1
  kind: Deployment
  metadata:
    labels:
      app: wallet-api-projector
      network: preprod
      release: dev-preprod-cardanojs-v2
    name: dev-preprod-cardanojs-v2-wallet-api-projector
  spec:
    selector:
      matchLabels:
        app: wallet-api-projector
        network: preprod
        release: dev-preprod-cardanojs-v2
    template:
      metadata:
        labels:
          app: wallet-api-projector
          network: preprod
          release: dev-preprod-cardanojs-v2
      spec:
        containers:
        - args:
          - start-projector
          env:
          - name: LOGGER_MIN_SEVERITY
            value: info
          - name: NETWORK
            value: preprod
          - name: OGMIOS_SRV_SERVICE_NAME
            value: dev-preprod-cardano-core.dev-preprod.svc.cluster.local
          - name: POSTGRES_DB
            value: wallet_api
          - name: POSTGRES_HOST
            value: dev-preprod-postgresql
          - name: POSTGRES_PASSWORD
            valueFrom:
              secretKeyRef:
                key: password
                name: wallet-api-owner-user.dev-preprod-postgresql.credentials.postgresql.acid.zalan.do
          - name: POSTGRES_POOL_MAX
            value: "2"
          - name: POSTGRES_PORT
            value: "5432"
          - name: POSTGRES_USER
            valueFrom:
              secretKeyRef:
                key: username
                name: wallet-api-owner-user.dev-preprod-postgresql.credentials.postgresql.acid.zalan.do
          - name: PROJECTION_NAMES
            value: protocol-parameters
-         image: 926093910549.dkr.ecr.us-east-1.amazonaws.com/cardano-services:nfl1lh9x3wvlm5y4wdk168h41lwpaprq
+         image: 926093910549.dkr.ecr.us-east-1.amazonaws.com/cardano-services:ggd3diyab45gsdfmgaz5ac7yf63hzwzm
          livenessProbe:
            httpGet:
              path: /v1.0.0/health
              port: 3000
            timeoutSeconds: 5
          name: wallet-api-projector
          ports:
          - containerPort: 3000
            name: http
          resources:
            limits:
              cpu: 1000m
              memory: 300Mi
            requests:
              cpu: 100m
              memory: 150Mi
          securityContext:
            runAsGroup: 0
            runAsUser: 0
        imagePullSecrets:
        - name: dockerconfigjson
```

dev-preprod, dev-preprod-cardanojs-v2-ws-server, Deployment (apps) would change:

```diff
  # Source: cardanojs/templates/ws-server.deployment.yaml
  apiVersion: apps/v1
  kind: Deployment
  metadata:
    labels:
      app: ws-server
      network: preprod
      release: dev-preprod-cardanojs-v2
    name: dev-preprod-cardanojs-v2-ws-server
  spec:
    selector:
      matchLabels:
        app: ws-server
        network: preprod
        release: dev-preprod-cardanojs-v2
    template:
      metadata:
        labels:
          app: ws-server
          network: preprod
          release: dev-preprod-cardanojs-v2
      spec:
        containers:
        - args:
          - start-ws-server
          env:
          - name: DB_CACHE_TTL
            value: "7200"
          - name: NETWORK
            value: preprod
          - name: OGMIOS_URL
            value: ws://dev-preprod-cardano-core.dev-preprod.svc.cluster.local:1337
          - name: POSTGRES_DB_DB_SYNC
            value: cardano
          - name: POSTGRES_HOST_DB_SYNC
            value: dev-preprod-postgresql
          - name: POSTGRES_PASSWORD_DB_SYNC
            valueFrom:
              secretKeyRef:
                key: password
                name: cardano-owner-user.dev-preprod-postgresql.credentials.postgresql.acid.zalan.do
          - name: POSTGRES_POOL_MAX_DB_SYNC
            value: "2"
          - name: POSTGRES_PORT_DB_SYNC
            value: "5432"
          - name: POSTGRES_USER_DB_SYNC
            valueFrom:
              secretKeyRef:
                key: username
                name: cardano-owner-user.dev-preprod-postgresql.credentials.postgresql.acid.zalan.do
-         image: 926093910549.dkr.ecr.us-east-1.amazonaws.com/cardano-services:nfl1lh9x3wvlm5y4wdk168h41lwpaprq
+         image: 926093910549.dkr.ecr.us-east-1.amazonaws.com/cardano-services:ggd3diyab45gsdfmgaz5ac7yf63hzwzm
          livenessProbe:
            httpGet:
              path: /health
              port: 3000
          name: ws-server
          ports:
          - containerPort: 3000
            name: http
          resources:
            limits:
              cpu: 300m
              memory: 300Mi
            requests:
              cpu: 200m
              memory: 150Mi
          securityContext:
            runAsGroup: 0
            runAsUser: 0
        imagePullSecrets:
        - name: dockerconfigjson
```
</details>
<details>
<summary><code>dev-mainnet@us-east-1</code> would change:</summary>

dev-mainnet, dev-mainnet-cardanojs-asset-projector, Deployment (apps) would change:

```diff
  # Source: cardanojs/templates/asset-projector.deployment.yaml
  apiVersion: apps/v1
  kind: Deployment
  metadata:
    labels:
      app: asset-projector
      network: mainnet
      release: dev-mainnet-cardanojs
    name: dev-mainnet-cardanojs-asset-projector
  spec:
    selector:
      matchLabels:
        app: asset-projector
        network: mainnet
        release: dev-mainnet-cardanojs
    template:
      metadata:
        labels:
          app: asset-projector
          network: mainnet
          release: dev-mainnet-cardanojs
      spec:
        containers:
        - args:
          - start-projector
          env:
          - name: LOGGER_MIN_SEVERITY
            value: info
          - name: NETWORK
            value: mainnet
          - name: OGMIOS_SRV_SERVICE_NAME
            value: dev-mainnet-cardano-core.dev-mainnet.svc.cluster.local
          - name: POSTGRES_DB
            value: asset
          - name: POSTGRES_HOST
            value: dev-mainnet-postgresql
          - name: POSTGRES_PASSWORD
            valueFrom:
              secretKeyRef:
                key: password
                name: asset-owner-user.dev-mainnet-postgresql.credentials.postgresql.acid.zalan.do
          - name: POSTGRES_POOL_MAX
            value: "2"
          - name: POSTGRES_PORT
            value: "5432"
          - name: POSTGRES_USER
            valueFrom:
              secretKeyRef:
                key: username
                name: asset-owner-user.dev-mainnet-postgresql.credentials.postgresql.acid.zalan.do
          - name: PROJECTION_NAMES
            value: asset
-         image: 926093910549.dkr.ecr.us-east-1.amazonaws.com/cardano-services:nfl1lh9x3wvlm5y4wdk168h41lwpaprq
+         image: 926093910549.dkr.ecr.us-east-1.amazonaws.com/cardano-services:ggd3diyab45gsdfmgaz5ac7yf63hzwzm
          livenessProbe:
            httpGet:
              path: /v1.0.0/health
              port: 3000
            timeoutSeconds: 5
          name: asset-projector
          ports:
          - containerPort: 3000
            name: http
          resources:
            limits:
              cpu: 700m
              memory: 300Mi
            requests:
              cpu: 700m
              memory: 150Mi
          securityContext:
            runAsGroup: 0
            runAsUser: 0
        imagePullSecrets:
        - name: dockerconfigjson
```

dev-mainnet, dev-mainnet-cardanojs-backend, Deployment (apps) would change:

```diff
  # Source: cardanojs/templates/backend.deployment.yaml
  apiVersion: apps/v1
  kind: Deployment
  metadata:
    labels:
      app: backend
      network: mainnet
      release: dev-mainnet-cardanojs
    name: dev-mainnet-cardanojs-backend
  spec:
    replicas: 2
    selector:
      matchLabels:
        app: backend
        network: mainnet
        release: dev-mainnet-cardanojs
    template:
      metadata:
        labels:
          app: backend
          network: mainnet
          release: dev-mainnet-cardanojs
      spec:
        containers:
        - args:
          - start-provider-server
          env:
          - name: ALLOWED_ORIGINS
            value: chrome-extension://gafhhkghbfjjkeiendhlofajokpaflmk,chrome-extension://efeiemlfnahiidnjglmehaihacglceia,chrome-extension://bjlhpephaokolembmpdcbobbpkjnoheb,chrome-extension://djcdfchkaijggdjokfomholkalbffgil,http://localhost/,http://localhost
          - name: BUILD_INFO
-           value: '{"lastModifiedDate":"20250225153102","rev":"81782c04314662cbc638c56af8e8c60f42699a19","shortRev":"81782c0"}'
+           value: '{"lastModifiedDate":"20250514010946","rev":"f2142d8be63d1c0988764b61f894a7347e663a0b","shortRev":"f2142d8"}'
          - name: DISABLE_STAKE_POOL_METRIC_APY
            value: "true"
          - name: ENABLE_METRICS
            value: "true"
          - name: HANDLE_POLICY_IDS
            value: f0ff48bbb7bbe9d59a40f1ce90e9e9d0ff5002ec48f232b49ca0fb9a
          - name: HANDLE_PROVIDER_SERVER_URL
            value: https://api.handle.me
          - name: LOGGER_MIN_SEVERITY
            value: info
          - name: NETWORK
            value: mainnet
          - name: NODE_ENV
          - name: OGMIOS_SRV_SERVICE_NAME
            value: dev-mainnet-cardano-core.dev-mainnet.svc.cluster.local
          - name: PAGINATION_PAGE_SIZE_LIMIT
            value: "5500"
          - name: POSTGRES_DB_DB_SYNC
            value: cardano
          - name: POSTGRES_DB_HANDLE
            value: handle
          - name: POSTGRES_HOST_DB_SYNC
            value: dev-mainnet-postgresql
          - name: POSTGRES_HOST_HANDLE
            value: dev-mainnet-postgresql
          - name: POSTGRES_PASSWORD_DB_SYNC
            valueFrom:
              secretKeyRef:
                key: password
                name: cardano-owner-user.dev-mainnet-postgresql.credentials.postgresql.acid.zalan.do
          - name: POSTGRES_PASSWORD_HANDLE
            valueFrom:
              secretKeyRef:
                key: password
                name: handle-owner-user.dev-mainnet-postgresql.credentials.postgresql.acid.zalan.do
          - name: POSTGRES_POOL_MAX_DB_SYNC
            value: "50"
          - name: POSTGRES_POOL_MAX_HANDLE
            value: "10"
          - name: POSTGRES_PORT_DB_SYNC
            value: "5432"
          - name: POSTGRES_PORT_HANDLE
            value: "5432"
          - name: POSTGRES_USER_DB_SYNC
            valueFrom:
              secretKeyRef:
                key: username
                name: cardano-owner-user.dev-mainnet-postgresql.credentials.postgresql.acid.zalan.do
          - name: POSTGRES_USER_HANDLE
            valueFrom:
              secretKeyRef:
                key: username
                name: handle-owner-user.dev-mainnet-postgresql.credentials.postgresql.acid.zalan.do
          - name: SERVICE_NAMES
            value: asset,network-info,rewards,stake-pool,tx-submit,utxo
          - name: TOKEN_METADATA_SERVER_URL
            value: http://dev-mainnet-cardano-stack-metadata.dev-mainnet.svc.cluster.local
          - name: USE_BLOCKFROST
            value: "true"
          - name: USE_KORA_LABS
            value: "true"
-         image: 926093910549.dkr.ecr.us-east-1.amazonaws.com/cardano-services:nfl1lh9x3wvlm5y4wdk168h41lwpaprq
+         image: 926093910549.dkr.ecr.us-east-1.amazonaws.com/cardano-services:ggd3diyab45gsdfmgaz5ac7yf63hzwzm
          livenessProbe:
            httpGet:
              path: /v1.0.0/ready
              port: 3000
            periodSeconds: 10
            timeoutSeconds: 5
          name: backend
          ports:
          - containerPort: 3000
            name: http
          resources:
            requests:
              cpu: 1000m
              memory: 350Mi
          securityContext:
            runAsGroup: 0
            runAsUser: 0
        imagePullSecrets:
        - name: dockerconfigjson
```

dev-mainnet, dev-mainnet-cardanojs-blockfrost-worker, Deployment (apps) would change:

```diff
  # Source: cardanojs/templates/blockfrost-worker.deployment.yaml
  apiVersion: apps/v1
  kind: Deployment
  metadata:
    labels:
      app: blockfrost-worker
      network: mainnet
      release: dev-mainnet-cardanojs
    name: dev-mainnet-cardanojs-blockfrost-worker
  spec:
    selector:
      matchLabels:
        app: blockfrost-worker
        network: mainnet
        release: dev-mainnet-cardanojs
    template:
      metadata:
        labels:
          app: blockfrost-worker
          network: mainnet
          release: dev-mainnet-cardanojs
      spec:
        containers:
        - args:
          - start-blockfrost-worker
          env:
          - name: BLOCKFROST_API_KEY
            valueFrom:
              secretKeyRef:
                key: api-key
                name: blockfrost
          - name: LOGGER_MIN_SEVERITY
            value: info
          - name: NETWORK
            value: mainnet
          - name: NODE_ENV
          - name: POSTGRES_DB_DB_SYNC
            value: cardano
          - name: POSTGRES_HOST_DB_SYNC
            value: dev-mainnet-postgresql
          - name: POSTGRES_PASSWORD_DB_SYNC
            valueFrom:
              secretKeyRef:
                key: password
                name: cardano-owner-user.dev-mainnet-postgresql.credentials.postgresql.acid.zalan.do
          - name: POSTGRES_PORT_DB_SYNC
            value: "5432"
          - name: POSTGRES_USER_DB_SYNC
            valueFrom:
              secretKeyRef:
                key: username
                name: cardano-owner-user.dev-mainnet-postgresql.credentials.postgresql.acid.zalan.do
-         image: 926093910549.dkr.ecr.us-east-1.amazonaws.com/cardano-services:nfl1lh9x3wvlm5y4wdk168h41lwpaprq
+         image: 926093910549.dkr.ecr.us-east-1.amazonaws.com/cardano-services:ggd3diyab45gsdfmgaz5ac7yf63hzwzm
          livenessProbe:
            httpGet:
              path: /v1.0.0/health
              port: 3000
            timeoutSeconds: 5
          name: blockfrost-worker
          ports:
          - containerPort: 3000
            name: http
          resources:
            limits:
              cpu: 500m
              memory: 300Mi
            requests:
              cpu: 100m
              memory: 150Mi
          securityContext:
            runAsGroup: 0
            runAsUser: 0
        imagePullSecrets:
        - name: dockerconfigjson
```

dev-mainnet, dev-mainnet-cardanojs-chain-history-provider, Deployment (apps) would change:

```diff
  # Source: cardanojs/templates/chain-history-provider.deployment.yaml
  apiVersion: apps/v1
  kind: Deployment
  metadata:
    labels:
      app: chain-history-provider
      network: mainnet
      release: dev-mainnet-cardanojs
    name: dev-mainnet-cardanojs-chain-history-provider
  spec:
    replicas: 2
    selector:
      matchLabels:
        app: chain-history-provider
        network: mainnet
        release: dev-mainnet-cardanojs
    template:
      metadata:
        labels:
          app: chain-history-provider
          network: mainnet
          release: dev-mainnet-cardanojs
      spec:
        containers:
        - args:
          - start-provider-server
          env:
          - name: ALLOWED_ORIGINS
            value: chrome-extension://gafhhkghbfjjkeiendhlofajokpaflmk,chrome-extension://efeiemlfnahiidnjglmehaihacglceia,chrome-extension://bjlhpephaokolembmpdcbobbpkjnoheb,chrome-extension://djcdfchkaijggdjokfomholkalbffgil,http://localhost/,http://localhost
          - name: DISABLE_STAKE_POOL_METRIC_APY
            value: "true"
          - name: ENABLE_METRICS
            value: "true"
          - name: HANDLE_POLICY_IDS
            value: f0ff48bbb7bbe9d59a40f1ce90e9e9d0ff5002ec48f232b49ca0fb9a
          - name: LOGGER_MIN_SEVERITY
            value: info
          - name: NETWORK
            value: mainnet
          - name: NODE_ENV
          - name: OGMIOS_SRV_SERVICE_NAME
            value: dev-mainnet-cardano-core.dev-mainnet.svc.cluster.local
          - name: PAGINATION_PAGE_SIZE_LIMIT
            value: "5500"
          - name: POSTGRES_DB_DB_SYNC
            value: cardano
          - name: POSTGRES_HOST_DB_SYNC
            value: dev-mainnet-postgresql
          - name: POSTGRES_PASSWORD_DB_SYNC
            valueFrom:
              secretKeyRef:
                key: password
                name: cardano-owner-user.dev-mainnet-postgresql.credentials.postgresql.acid.zalan.do
          - name: POSTGRES_POOL_MAX_DB_SYNC
            value: "50"
          - name: POSTGRES_PORT_DB_SYNC
            value: "5432"
          - name: POSTGRES_USER_DB_SYNC
            valueFrom:
              secretKeyRef:
                key: username
                name: cardano-owner-user.dev-mainnet-postgresql.credentials.postgresql.acid.zalan.do
          - name: SERVICE_NAMES
            value: chain-history
          - name: TOKEN_METADATA_SERVER_URL
            value: http://dev-mainnet-cardano-stack-metadata.dev-mainnet.svc.cluster.local
          - name: USE_BLOCKFROST
            value: "true"
          - name: USE_KORA_LABS
            value: "true"
-         image: 926093910549.dkr.ecr.us-east-1.amazonaws.com/cardano-services:nfl1lh9x3wvlm5y4wdk168h41lwpaprq
+         image: 926093910549.dkr.ecr.us-east-1.amazonaws.com/cardano-services:ggd3diyab45gsdfmgaz5ac7yf63hzwzm
          livenessProbe:
            httpGet:
              path: /v1.0.0/ready
              port: 3000
            periodSeconds: 10
            timeoutSeconds: 5
          name: chain-history-provider
          ports:
          - containerPort: 3000
            name: http
          resources:
            requests:
              cpu: 1000m
              memory: 512Mi
          securityContext:
            runAsGroup: 0
            runAsUser: 0
        imagePullSecrets:
        - name: dockerconfigjson
```

dev-mainnet, dev-mainnet-cardanojs-handle-projector, Deployment (apps) would change:

```diff
  # Source: cardanojs/templates/handle-projector.deployment.yaml
  apiVersion: apps/v1
  kind: Deployment
  metadata:
    labels:
      app: handle-projector
      network: mainnet
      release: dev-mainnet-cardanojs
    name: dev-mainnet-cardanojs-handle-projector
  spec:
    selector:
      matchLabels:
        app: handle-projector
        network: mainnet
        release: dev-mainnet-cardanojs
    template:
      metadata:
        labels:
          app: handle-projector
          network: mainnet
          release: dev-mainnet-cardanojs
      spec:
        containers:
        - args:
          - start-projector
          env:
          - name: HANDLE_POLICY_IDS
            value: f0ff48bbb7bbe9d59a40f1ce90e9e9d0ff5002ec48f232b49ca0fb9a
          - name: LOGGER_MIN_SEVERITY
            value: info
          - name: NETWORK
            value: mainnet
          - name: NODE_ENV
          - name: OGMIOS_SRV_SERVICE_NAME
            value: dev-mainnet-cardano-core.dev-mainnet.svc.cluster.local
          - name: POSTGRES_DB
            value: handle
          - name: POSTGRES_HOST
            value: dev-mainnet-postgresql
          - name: POSTGRES_PASSWORD
            valueFrom:
              secretKeyRef:
                key: password
                name: handle-owner-user.dev-mainnet-postgresql.credentials.postgresql.acid.zalan.do
          - name: POSTGRES_POOL_MAX
            value: "2"
          - name: POSTGRES_PORT
            value: "5432"
          - name: POSTGRES_USER
            valueFrom:
              secretKeyRef:
                key: username
                name: handle-owner-user.dev-mainnet-postgresql.credentials.postgresql.acid.zalan.do
          - name: PROJECTION_NAMES
            value: handle
-         image: 926093910549.dkr.ecr.us-east-1.amazonaws.com/cardano-services:nfl1lh9x3wvlm5y4wdk168h41lwpaprq
+         image: 926093910549.dkr.ecr.us-east-1.amazonaws.com/cardano-services:ggd3diyab45gsdfmgaz5ac7yf63hzwzm
          livenessProbe:
            httpGet:
              path: /v1.0.0/health
              port: 3000
            timeoutSeconds: 5
          name: handle-projector
          ports:
          - containerPort: 3000
            name: http
          resources:
            limits:
              cpu: 1000m
              memory: 300Mi
            requests:
              cpu: 100m
              memory: 150Mi
          securityContext:
            runAsGroup: 0
            runAsUser: 0
        imagePullSecrets:
        - name: dockerconfigjson
```

dev-mainnet, dev-mainnet-cardanojs-handle-provider, Deployment (apps) would change:

```diff
  # Source: cardanojs/templates/handle-provider.deployment.yaml
  apiVersion: apps/v1
  kind: Deployment
  metadata:
    labels:
      app: handle-provider
      network: mainnet
      release: dev-mainnet-cardanojs
    name: dev-mainnet-cardanojs-handle-provider
  spec:
    selector:
      matchLabels:
        app: handle-provider
        network: mainnet
        release: dev-mainnet-cardanojs
    template:
      metadata:
        labels:
          app: handle-provider
          network: mainnet
          release: dev-mainnet-cardanojs
      spec:
        containers:
        - args:
          - start-provider-server
          env:
          - name: ENABLE_METRICS
            value: "true"
          - name: HANDLE_POLICY_IDS
            value: f0ff48bbb7bbe9d59a40f1ce90e9e9d0ff5002ec48f232b49ca0fb9a
          - name: LOGGER_MIN_SEVERITY
            value: info
          - name: NETWORK
            value: mainnet
          - name: NODE_ENV
          - name: OGMIOS_SRV_SERVICE_NAME
            value: dev-mainnet-cardano-core.dev-mainnet.svc.cluster.local
          - name: POSTGRES_DB_HANDLE
            value: handle
          - name: POSTGRES_HOST_HANDLE
            value: dev-mainnet-postgresql
          - name: POSTGRES_PASSWORD_HANDLE
            valueFrom:
              secretKeyRef:
                key: password
                name: handle-owner-user.dev-mainnet-postgresql.credentials.postgresql.acid.zalan.do
          - name: POSTGRES_POOL_MAX_HANDLE
            value: "10"
          - name: POSTGRES_PORT_HANDLE
            value: "5432"
          - name: POSTGRES_USER_HANDLE
            valueFrom:
              secretKeyRef:
                key: username
                name: handle-owner-user.dev-mainnet-postgresql.credentials.postgresql.acid.zalan.do
          - name: SERVICE_NAMES
            value: handle
-         image: 926093910549.dkr.ecr.us-east-1.amazonaws.com/cardano-services:nfl1lh9x3wvlm5y4wdk168h41lwpaprq
+         image: 926093910549.dkr.ecr.us-east-1.amazonaws.com/cardano-services:ggd3diyab45gsdfmgaz5ac7yf63hzwzm
          livenessProbe:
            httpGet:
              path: /v1.0.0/health
              port: 3000
            timeoutSeconds: 5
          name: handle-provider
          ports:
          - containerPort: 3000
            name: http
          resources:
            requests:
              cpu: 700m
              memory: 150Mi
          securityContext:
            runAsGroup: 0
            runAsUser: 0
        imagePullSecrets:
        - name: dockerconfigjson
```

dev-mainnet, dev-mainnet-cardanojs-pg-boss-worker, Deployment (apps) would change:

```diff
  # Source: cardanojs/templates/pgboss.deployment.yaml
  apiVersion: apps/v1
  kind: Deployment
  metadata:
    labels:
      app: pg-boss-worker
      network: mainnet
      release: dev-mainnet-cardanojs
    name: dev-mainnet-cardanojs-pg-boss-worker
  spec:
    selector:
      matchLabels:
        app: pg-boss-worker
        network: mainnet
        release: dev-mainnet-cardanojs
    template:
      metadata:
        labels:
          app: pg-boss-worker
          network: mainnet
          release: dev-mainnet-cardanojs
      spec:
        containers:
        - args:
          - start-pg-boss-worker
          env:
          - name: LOGGER_MIN_SEVERITY
            value: info
          - name: METADATA_FETCH_MODE
            value: smash
          - name: NETWORK
            value: mainnet
          - name: NETWORK_INFO_PROVIDER_URL
            value: http://dev-mainnet-cardanojs-backend.dev-mainnet.svc.cluster.local
          - name: NODE_ENV
          - name: POSTGRES_DB_DB_SYNC
            value: cardano
          - name: POSTGRES_DB_STAKE_POOL
            value: stakepoolv2
          - name: POSTGRES_HOST_DB_SYNC
            value: dev-mainnet-postgresql
          - name: POSTGRES_HOST_STAKE_POOL
            value: dev-mainnet-postgresql
          - name: POSTGRES_PASSWORD_DB_SYNC
            valueFrom:
              secretKeyRef:
                key: password
                name: cardano-owner-user.dev-mainnet-postgresql.credentials.postgresql.acid.zalan.do
          - name: POSTGRES_PASSWORD_STAKE_POOL
            valueFrom:
              secretKeyRef:
                key: password
                name: stakepoolv2-owner-user.dev-mainnet-postgresql.credentials.postgresql.acid.zalan.do
          - name: POSTGRES_POOL_MAX_DB_SYNC
            value: "5"
          - name: POSTGRES_POOL_MAX_STAKE_POOL
            value: "5"
          - name: POSTGRES_PORT_DB_SYNC
            value: "5432"
          - name: POSTGRES_PORT_STAKE_POOL
            value: "5432"
          - name: POSTGRES_USER_DB_SYNC
            valueFrom:
              secretKeyRef:
                key: username
                name: cardano-owner-user.dev-mainnet-postgresql.credentials.postgresql.acid.zalan.do
          - name: POSTGRES_USER_STAKE_POOL
            valueFrom:
              secretKeyRef:
                key: username
                name: stakepoolv2-owner-user.dev-mainnet-postgresql.credentials.postgresql.acid.zalan.do
          - name: QUEUES
            value: pool-delist-schedule,pool-metadata,pool-metrics,pool-rewards
          - name: SMASH_URL
            value: https://smash.cardano-mainnet.iohk.io/api/v1
          - name: STAKE_POOL_PROVIDER_URL
            value: http://dev-mainnet-cardanojs-backend.dev-mainnet.svc.cluster.local
-         image: 926093910549.dkr.ecr.us-east-1.amazonaws.com/cardano-services:nfl1lh9x3wvlm5y4wdk168h41lwpaprq
+         image: 926093910549.dkr.ecr.us-east-1.amazonaws.com/cardano-services:ggd3diyab45gsdfmgaz5ac7yf63hzwzm
          livenessProbe:
            httpGet:
              path: /v1.0.0/health
              port: 3000
          name: pg-boss-worker
          ports:
          - containerPort: 3000
            name: http
          resources:
            limits:
              cpu: 300m
              memory: 300Mi
            requests:
              cpu: 200m
              memory: 150Mi
          securityContext:
            runAsGroup: 0
            runAsUser: 0
          startupProbe:
            httpGet:
              path: /v1.0.0/ready
              port: 3000
            initialDelaySeconds: 80
            periodSeconds: 5
        imagePullSecrets:
        - name: dockerconfigjson
```

dev-mainnet, dev-mainnet-cardanojs-stake-pool-projector, Deployment (apps) would change:

```diff
  # Source: cardanojs/templates/stake-pool-projector.deployment.yaml
  apiVersion: apps/v1
  kind: Deployment
  metadata:
    labels:
      app: stake-pool-projector
      network: mainnet
      release: dev-mainnet-cardanojs
    name: dev-mainnet-cardanojs-stake-pool-projector
  spec:
    selector:
      matchLabels:
        app: stake-pool-projector
        network: mainnet
        release: dev-mainnet-cardanojs
    template:
      metadata:
        labels:
          app: stake-pool-projector
          network: mainnet
          release: dev-mainnet-cardanojs
      spec:
        containers:
        - args:
          - start-projector
          env:
          - name: LOGGER_MIN_SEVERITY
            value: info
          - name: NETWORK
            value: mainnet
          - name: NODE_ENV
          - name: OGMIOS_SRV_SERVICE_NAME
            value: dev-mainnet-cardano-core.dev-mainnet.svc.cluster.local
          - name: POSTGRES_DB
            value: stakepoolv2
          - name: POSTGRES_HOST
            value: dev-mainnet-postgresql
          - name: POSTGRES_PASSWORD
            valueFrom:
              secretKeyRef:
                key: password
                name: stakepoolv2-owner-user.dev-mainnet-postgresql.credentials.postgresql.acid.zalan.do
          - name: POSTGRES_POOL_MAX
            value: "2"
          - name: POSTGRES_PORT
            value: "5432"
          - name: POSTGRES_USER
            valueFrom:
              secretKeyRef:
                key: username
                name: stakepoolv2-owner-user.dev-mainnet-postgresql.credentials.postgresql.acid.zalan.do
          - name: PROJECTION_NAMES
            value: stake-pool,stake-pool-metadata-job,stake-pool-metrics-job,stake-pool-rewards-job
-         image: 926093910549.dkr.ecr.us-east-1.amazonaws.com/cardano-services:nfl1lh9x3wvlm5y4wdk168h41lwpaprq
+         image: 926093910549.dkr.ecr.us-east-1.amazonaws.com/cardano-services:ggd3diyab45gsdfmgaz5ac7yf63hzwzm
          livenessProbe:
            httpGet:
              path: /v1.0.0/health
              port: 3000
            timeoutSeconds: 5
          name: stake-pool-projector
          ports:
          - containerPort: 3000
            name: http
          resources:
            limits:
              cpu: 700m
              memory: 300Mi
            requests:
              cpu: 700m
              memory: 150Mi
          securityContext:
            runAsGroup: 0
            runAsUser: 0
        imagePullSecrets:
        - name: dockerconfigjson
```

dev-mainnet, dev-mainnet-cardanojs-stake-pool-provider, Deployment (apps) would change:

```diff
  # Source: cardanojs/templates/stake-pool-provider.deployment.yaml
  apiVersion: apps/v1
  kind: Deployment
  metadata:
    labels:
      app: stake-pool-provider
      network: mainnet
      release: dev-mainnet-cardanojs
    name: dev-mainnet-cardanojs-stake-pool-provider
  spec:
    selector:
      matchLabels:
        app: stake-pool-provider
        network: mainnet
        release: dev-mainnet-cardanojs
    template:
      metadata:
        labels:
          app: stake-pool-provider
          network: mainnet
          release: dev-mainnet-cardanojs
      spec:
        containers:
        - args:
          - start-provider-server
          env:
          - name: DISABLE_STAKE_POOL_METRIC_APY
            value: "true"
          - name: ENABLE_METRICS
            value: "true"
          - name: LOGGER_MIN_SEVERITY
            value: info
          - name: NETWORK
            value: mainnet
          - name: NODE_ENV
          - name: OGMIOS_SRV_SERVICE_NAME
            value: dev-mainnet-cardano-core.dev-mainnet.svc.cluster.local
          - name: OVERRIDE_FUZZY_OPTIONS
            value: "true"
          - name: PAGINATION_PAGE_SIZE_LIMIT
            value: "5500"
          - name: POSTGRES_DB_STAKE_POOL
            value: stakepoolv2
          - name: POSTGRES_HOST_STAKE_POOL
            value: dev-mainnet-postgresql
          - name: POSTGRES_PASSWORD_STAKE_POOL
            valueFrom:
              secretKeyRef:
                key: password
                name: stakepoolv2-owner-user.dev-mainnet-postgresql.credentials.postgresql.acid.zalan.do
          - name: POSTGRES_POOL_MAX_STAKE_POOL
            value: "10"
          - name: POSTGRES_PORT_STAKE_POOL
            value: "5432"
          - name: POSTGRES_USER_STAKE_POOL
            valueFrom:
              secretKeyRef:
                key: username
                name: stakepoolv2-owner-user.dev-mainnet-postgresql.credentials.postgresql.acid.zalan.do
          - name: SERVICE_NAMES
            value: stake-pool
          - name: TOKEN_METADATA_SERVER_URL
            value: http://dev-mainnet-cardano-stack-metadata.dev-mainnet.svc.cluster.local
          - name: USE_TYPEORM_STAKE_POOL_PROVIDER
            value: "true"
-         image: 926093910549.dkr.ecr.us-east-1.amazonaws.com/cardano-services:nfl1lh9x3wvlm5y4wdk168h41lwpaprq
+         image: 926093910549.dkr.ecr.us-east-1.amazonaws.com/cardano-services:ggd3diyab45gsdfmgaz5ac7yf63hzwzm
          livenessProbe:
            httpGet:
              path: /v1.0.0/health
              port: 3000
            timeoutSeconds: 5
          name: stake-pool-provider
          ports:
          - containerPort: 3000
            name: http
          resources:
            requests:
              cpu: 700m
              memory: 150Mi
          securityContext:
            runAsGroup: 0
            runAsUser: 0
        imagePullSecrets:
        - name: dockerconfigjson
```

dev-mainnet, dev-mainnet-cardanojs-ws-server, Deployment (apps) would change:

```diff
  # Source: cardanojs/templates/ws-server.deployment.yaml
  apiVersion: apps/v1
  kind: Deployment
  metadata:
    labels:
      app: ws-server
      network: mainnet
      release: dev-mainnet-cardanojs
    name: dev-mainnet-cardanojs-ws-server
  spec:
    selector:
      matchLabels:
        app: ws-server
        network: mainnet
        release: dev-mainnet-cardanojs
    template:
      metadata:
        labels:
          app: ws-server
          network: mainnet
          release: dev-mainnet-cardanojs
      spec:
        containers:
        - args:
          - start-ws-server
          env:
          - name: DB_CACHE_TTL
            value: "7200"
          - name: NETWORK
            value: mainnet
          - name: OGMIOS_URL
            value: ws://dev-mainnet-cardano-core.dev-mainnet.svc.cluster.local:1337
          - name: POSTGRES_DB_DB_SYNC
            value: cardano
          - name: POSTGRES_HOST_DB_SYNC
            value: dev-mainnet-postgresql
          - name: POSTGRES_PASSWORD_DB_SYNC
            valueFrom:
              secretKeyRef:
                key: password
                name: cardano-owner-user.dev-mainnet-postgresql.credentials.postgresql.acid.zalan.do
          - name: POSTGRES_POOL_MAX_DB_SYNC
            value: "2"
          - name: POSTGRES_PORT_DB_SYNC
            value: "5432"
          - name: POSTGRES_USER_DB_SYNC
            valueFrom:
              secretKeyRef:
                key: username
                name: cardano-owner-user.dev-mainnet-postgresql.credentials.postgresql.acid.zalan.do
-         image: 926093910549.dkr.ecr.us-east-1.amazonaws.com/cardano-services:nfl1lh9x3wvlm5y4wdk168h41lwpaprq
+         image: 926093910549.dkr.ecr.us-east-1.amazonaws.com/cardano-services:ggd3diyab45gsdfmgaz5ac7yf63hzwzm
          livenessProbe:
            httpGet:
              path: /health
              port: 3000
          name: ws-server
          ports:
          - containerPort: 3000
            name: http
          resources:
            limits:
              cpu: 300m
              memory: 300Mi
            requests:
              cpu: 200m
              memory: 150Mi
          securityContext:
            runAsGroup: 0
            runAsUser: 0
        imagePullSecrets:
        - name: dockerconfigjson
```
</details>
<!-- nix-helm-diff-777f3796-c80d-4d68-bf4f-8faad564f03f -->
author_association: NONE
user: login: AngelCastilloB
created_at: 2025-05-13T07:19:28Z
body: > Great work! 🚀 There are some build errors to resolve (CI fail)

Fixed @mkazlauskas , but dismissed the review
author_association: MEMBER