balancedOutputsWithChange method

Result<List<ShelleyTransactionOutput>, String> balancedOutputsWithChange({
  1. required ShelleyAddress changeAddress,
  2. required BlockchainCache cache,
  3. Coin fee = 0,
})

Balance transaction by adding/modifying change outputs to match inputs and fee. If no change adjustments are needed, returns input list. All native tokens must have a CurrencyAsset in the cache. To avoid adding multiple change outputs, changeAddress should be the same for a given tx.

Implementation

Result<List<ShelleyTransactionOutput>, String> balancedOutputsWithChange({
  required ShelleyAddress changeAddress,
  required BlockchainCache cache,
  Coin fee = 0,
}) {
  //get the differences for each native token:
  final sumResult = sumCurrencyIO(cache: cache, fee: fee);
  if (sumResult.isErr()) return Err(sumResult.unwrapErr());
  final Map<AssetId, Coin> sums = sumResult.unwrap();
  final isAllZeros = sums.values.every((sum) => sum == coinZero);
  //if balanced - nothing to do - return existing list
  if (isAllZeros) return Ok(this.outputs);
  final targetAddress = changeAddress.toBech32();
  //copy all outputs except for the change output
  List<ShelleyTransactionOutput> outputs =
      this.outputs.where((o) => o.address != targetAddress).toList();
  //find change output if it exists
  ShelleyTransactionOutput? changeOutput =
      firstWhere(this.outputs, targetAddress);
  //break-up assetIds into policyId and name and put in nested maps
  final groupByResult = _groupByPolicyIdThenName(sums: sums, cache: cache);
  if (groupByResult.isErr()) return Err(groupByResult.unwrapErr());
  Map<String, Map<String, Coin>> byPolicyIdThenName = groupByResult.unwrap();
  //build new change output
  List<ShelleyMultiAsset> multiAssets = [];
  Coin lovelace = coinZero;
  for (final policyId in byPolicyIdThenName.keys) {
    Map<String, Coin> byName = byPolicyIdThenName[policyId]!;
    List<ShelleyAsset> assets = [];
    for (final name in byName.keys) {
      final Coin value = _existingBalance(
          changeOutput: changeOutput, policyId: policyId, name: name);
      if (policyId == '' && name == lovelaceHex) {
        //special handling for lovelace
        lovelace = value + (byName[name] ?? coinZero);
      } else {
        assets.add(ShelleyAsset(
            name: name, value: value + (byName[name] ?? coinZero)));
      }
    }
    if (assets.isNotEmpty) {
      multiAssets.add(ShelleyMultiAsset(policyId: policyId, assets: assets));
    }
  }
  final value = ShelleyValue(coin: lovelace, multiAssets: multiAssets);
  outputs.add(ShelleyTransactionOutput(address: targetAddress, value: value));
  return Ok(outputs);
}