createVTTransaction function

Future createVTTransaction({
  1. required List<ValueTransferOutput> outputs,
  2. required WitPrivateKey privateKey,
  3. Address? changeAddress,
  4. required dynamic networkSource,
  5. required UtxoSelectionStrategy utxoStrategy,
  6. FeeType? feeType,
  7. int fee = 0,
  8. String? metadata,
})

Implementation

Future<dynamic> createVTTransaction({
  required List<ValueTransferOutput> outputs,
  required WitPrivateKey privateKey,
  Address? changeAddress,
  required dynamic networkSource,
  required UtxoSelectionStrategy utxoStrategy,
  FeeType? feeType,
  int fee = 0,
  String? metadata,
}) async {
  List<ValueTransferOutput> allOutputs = outputs;

  if (metadata != null) {
    allOutputs.add(createMetadataOutput(metadata));
  }

  int outputValue = 0;
  int totalUtxoValue = 0;
  int selectedUtxoValue = 0;
  allOutputs.forEach((ValueTransferOutput output) {
    outputValue += output.value.toInt();
  });

  Address signerAddress = Address.fromAddress(privateKey.publicKey.address);
  // if the changeAddress param is left blank send the change to the signer
  Address _changeAddress = changeAddress ?? signerAddress;

  await signerAddress.getUtxoInfo(source: networkSource);
  List<Input> inputs = [];

  List<Utxo> utxoPool = signerAddress.utxoPool!.sortUtxos(utxoStrategy);
  utxoPool.forEach((utxo) {
    totalUtxoValue += utxo.value;
  });

  if (totalUtxoValue < outputValue) {
    return TransactionError(-1, 'Insufficient funds.');
  }

  List<Utxo> selectedUtxos = [];

  var _outputValue = outputValue;

  while (_outputValue > 0) {
    Utxo utxo = utxoPool.first;
    utxoPool.removeAt(0);
    selectedUtxos.add(utxo);
    _outputValue -= utxo.value;
  }

  selectedUtxos.forEach((Utxo utxo) {
    inputs.add(utxo.toInput());
    print('${utxo.toInput().outputPointer.jsonMap(asHex: true)} ${utxo.value}');
    selectedUtxoValue += utxo.value;
  });
  print(selectedUtxoValue);
  int change = selectedUtxoValue - outputValue;

  if (change > 0) {
    // receive the change to this address
    FeeType absFee = FeeType.Absolute;
    // if feeType is null use Absolute fee
    switch (feeType ?? absFee) {
      case FeeType.Absolute:
        if (change > fee) {
          allOutputs.add(_changeAddress.receive(change - fee));
        } else if (change == fee) {
          // do nothing with the change since it is the fee.
        } else {
          // get additional utxos to cover the fee
          while (fee > change) {
            Utxo nextUtxo = utxoPool.first;
            utxoPool.removeAt(0);
            selectedUtxos.add(nextUtxo);
            inputs.add(nextUtxo.toInput());
            selectedUtxoValue += nextUtxo.value;
            change = selectedUtxoValue - outputValue;
          }
        }
        break;
      case FeeType.Weighted:
        print('Current Change: $change');
        var inputCount = inputs.length;
        var outputCount = allOutputs.length;
        var currentWeight = vttWeight(inputCount, outputCount);
        print('Inputs: $inputCount, Outputs: $outputCount');
        print('Current Weight -> $currentWeight');
        if (change == currentWeight) {
          // do nothing with the change since it is the currentWeight
        } else if (change > currentWeight) {
          // account for the weight of an additional output for the change
          int newWeight = vttWeight(inputCount, outputCount + 1);
          // is the new weight greater than the change?
          if (newWeight > change) {
            // need additional utxos to cover the weighted fee
            while (newWeight > change) {
              Utxo nextUtxo = utxoPool.first;
              utxoPool.removeAt(0);
              inputs.add(nextUtxo.toInput());
              selectedUtxoValue += nextUtxo.value;
              change = selectedUtxoValue - outputValue;
              newWeight = vttWeight(inputs.length, outputCount + 1);
            }
          }
          allOutputs.add(_changeAddress.receive(change - newWeight));
        } else {
          // need additional utxos to cover the weighted fee
        }
        break;
    }
  }

  VTTransactionBody body =
      VTTransactionBody(inputs: inputs, outputs: allOutputs);
  VTTransaction transaction = VTTransaction(body: body, signatures: []);

  KeyedSignature signature =
      signerAddress.signHash(transaction.transactionID, privateKey);
  for (int i = 0; i < transaction.body.inputs.length; i++) {
    transaction.signatures.add(signature);
  }
  return transaction;
}