Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Error al componer la transacción: UTxO Balance Insufficient #720

Open
noroxi7 opened this issue Feb 24, 2025 · 8 comments
Open

Error al componer la transacción: UTxO Balance Insufficient #720

noroxi7 opened this issue Feb 24, 2025 · 8 comments

Comments

@noroxi7
Copy link

noroxi7 commented Feb 24, 2025

Trying to send transaction with assets and there is an error with utxos when i chech that wallet have the funds

  import * as CardanoWasm from '@emurgo/cardano-serialization-lib-nodejs';
  import { Responses } from '@blockfrost/blockfrost-js';
  
  // Definición del tipo UTXO (ajusta según tu definición real)
  interface UTXO {
    address: string;
    tx_hash: string;
    tx_index: number;
    output_index: number;
    amount: { unit: string; quantity: string }[];
    block: string;
    data_hash: string | null;
    inline_datum: string | null;
    reference_script_hash: string | null;
  }
  
  export const composeTransactionCNTS = (
    address: string,
    outputAddress: string,
    outputAmount: string, // cantidad de ADA a enviar
    outputTokens: { policyId: string; assetName: string; quantity: string }[], // tokens a enviar
    commissionAddress: string,
    commissionAmount: string,
    utxos: UTXO[],
    params: {
      protocolParams: Responses['epoch_param_content'];
      currentSlot: number;
    },
  ): {
    txHash: string | null;
    txBody: CardanoWasm.TransactionBody | null;
    error?: string;
  } => {
    try {
      if (!utxos || utxos.length === 0) {
        throw Error(`No se encontraron UTXOs en la dirección ${address}`);
      }
      // Crear el TransactionBuilder usando los parámetros del protocolo
      const txBuilder = CardanoWasm.TransactionBuilder.new(
        CardanoWasm.TransactionBuilderConfigBuilder.new()
          .fee_algo(
            CardanoWasm.LinearFee.new(
              CardanoWasm.BigNum.from_str(params.protocolParams.min_fee_a.toString()),
              CardanoWasm.BigNum.from_str(params.protocolParams.min_fee_b.toString())
            )
          )
          .pool_deposit(CardanoWasm.BigNum.from_str(params.protocolParams.pool_deposit))
          .key_deposit(CardanoWasm.BigNum.from_str(params.protocolParams.key_deposit))
          .coins_per_utxo_byte(CardanoWasm.BigNum.from_str(params.protocolParams.coins_per_utxo_size!))
          .max_value_size(parseInt(params.protocolParams.max_val_size!))
          .max_tx_size(params.protocolParams.max_tx_size)
          .build()
      );
      const outputAddr = CardanoWasm.Address.from_bech32(outputAddress);
      const changeAddr = CardanoWasm.Address.from_bech32(address);
      const commissionAddr = CardanoWasm.Address.from_bech32(commissionAddress);
  
      // Establecer el TTL (Time-To-Live) de la transacción
      const ttl = params.currentSlot + 7200;
      txBuilder.set_ttl(ttl);
  
      // Construir el Value para la salida principal (incluye ADA y tokens)
      const outputValue = CardanoWasm.Value.new(CardanoWasm.BigNum.from_str(outputAmount));
      if (outputTokens && outputTokens.length > 0) {
        console.log('get into outputTokens conditional')
        const multiAsset = CardanoWasm.MultiAsset.new();
        outputTokens.forEach((token: { policyId: string; assetName: string; quantity: string }) => {
          const scriptHash = CardanoWasm.ScriptHash.from_bytes(Buffer.from(token.policyId, 'hex'));
          const assetName = CardanoWasm.AssetName.new(Buffer.from(token.assetName));
          let assets = multiAsset.get(scriptHash);
          if (assets === undefined) {
            assets = CardanoWasm.Assets.new();
          }
          assets.insert(assetName, CardanoWasm.BigNum.from_str(token.quantity));
          multiAsset.insert(scriptHash, assets);
        });
        outputValue.set_multiasset(multiAsset);
  
      }
      // Agregar salida principal
      txBuilder.add_output(
        CardanoWasm.TransactionOutput.new(outputAddr, outputValue)
      );
  
      // Agregar salida para la comisión (solo ADA)
      txBuilder.add_output(
        CardanoWasm.TransactionOutput.new(
          commissionAddr,
          CardanoWasm.Value.new(CardanoWasm.BigNum.from_str(commissionAmount))
        )
      );
      console.log(txBuilder)
      // Procesar cada UTXO disponible (incluyendo los que tienen tokens)
      const unspentOutputs = CardanoWasm.TransactionUnspentOutputs.new();
      for (const singleUtxo of utxos) {
        // Buscar el valor en lovelace dentro del UTXO
        const lovelaceEntry = singleUtxo.amount.find(
          (a: { unit: string; quantity: string }) => a.unit === 'lovelace'
        );
        if (!lovelaceEntry) continue;
  
        const inputValue = CardanoWasm.Value.new(
          CardanoWasm.BigNum.from_str(lovelaceEntry.quantity)
        );
  
        // Procesar tokens (multiactivos) si existen en el UTXO
        const multiAsset = CardanoWasm.MultiAsset.new();
        singleUtxo.amount.forEach((asset: { unit: string; quantity: string }) => {
          if (asset.unit !== 'lovelace') {
            // Se asume que asset.unit es la concatenación de policyId y assetName en hexadecimal.
            const policyId = asset.unit.slice(0, 56);
            const assetNameHex = asset.unit.slice(56);
            const scriptHash = CardanoWasm.ScriptHash.from_bytes(Buffer.from(policyId, 'hex'));
            const assetName = CardanoWasm.AssetName.new(Buffer.from(assetNameHex, 'hex'));
            let assets = multiAsset.get(scriptHash);
            if (assets === undefined) {
              assets = CardanoWasm.Assets.new();
            }
            assets.insert(assetName, CardanoWasm.BigNum.from_str(asset.quantity));
            multiAsset.insert(scriptHash, assets);
          }
        });
        if (multiAsset.len() > 0) {
          inputValue.set_multiasset(multiAsset);
        }
  
        // Crear el input y la salida correspondiente para el UTXO
        const input = CardanoWasm.TransactionInput.new(
          CardanoWasm.TransactionHash.from_bytes(Buffer.from(singleUtxo.tx_hash, 'hex')),
          singleUtxo.output_index
        );
        const output = CardanoWasm.TransactionOutput.new(changeAddr, inputValue);
        unspentOutputs.add(CardanoWasm.TransactionUnspentOutput.new(input, output));
      }
      // Seleccionar los inputs usando la estrategia LargestFirst
      txBuilder.add_inputs_from(unspentOutputs, CardanoWasm.CoinSelectionStrategyCIP2.LargestFirstMultiAsset);
      // Agregar salida de cambio en caso de que haya fondos sobrantes (incluyendo tokens)
      txBuilder.add_change_if_needed(changeAddr);
      // Construir el cuerpo de la transacción y calcular su hash
      const txBody = txBuilder.build();
      const txHash = Buffer.from(CardanoWasm.hash_transaction(txBody).to_bytes()).toString('hex');
  
      return {
        txHash,
        txBody,
      };
    } catch (err) {
      console.error('Error al componer la transacción:', err);
      return {
        txHash: null,
        txBody: null,
        error: err instanceof Error ? err.message : String(err)
      };
    }
  };

Running in NODEJS
"@blockfrost/blockfrost-js": "^6.0.0",
"@emurgo/cardano-serialization-lib-nodejs": "^12.0.1",

@lisicky
Copy link
Contributor

lisicky commented Feb 25, 2025

Hi @noroxi7 !

Instead of

txBuilder.add_inputs_from(unspentOutputs, CardanoWasm.CoinSelectionStrategyCIP2.LargestFirstMultiAsset);
txBuilder.add_change_if_needed(changeAddr);

try to use

const changeConfig = CardanoWasm.ChangeConfig.new(changeAddress)
txBuilder.add_inputs_from_and_change(unspentOutputs, CardanoWasm.CoinSelectionStrategyCIP2.LargestFirstMultiAsset, changeConfig);

If it won’t work, check please your balance

@noroxi7
Copy link
Author

noroxi7 commented Feb 25, 2025

The balance on the wallet is enough for the tx, only use 1 ADA as fee, and send 2 ADA with the tokens, have 13 ADA in the wallet with tokens. Changed the lines you told me and i got the same error @lisicky

@noroxi7
Copy link
Author

noroxi7 commented Feb 25, 2025

Ok i thing 10ADA is setted as collateral, why is that? but i transfered 10 ADA more it keeps giving me the same response. I also unfrack it but nothing

@lisicky
Copy link
Contributor

lisicky commented Feb 25, 2025

It depends which utxos you use for coin selection.
Could you check total amount of unspentOutputs before txBuilder.add_inputs_from and total amount of outputs ?

@noroxi7
Copy link
Author

noroxi7 commented Feb 25, 2025

Seems like is getting all the utxos right, this is the return of console.log(unspentOutputs.to_json()) @lisicky

[
  {
    "input": {
      "transaction_id": "1a6033fb26d552f522464ffdcfd7558825b2dbe14b7586f1926c3383c756a979",
      "index": 0
    },
    "output": {
      "address": "addr1qx37ydfc3celyt5592xs90jtqkt0nluut394d075fs3hgu2yj02azpagpk0xe45pw25zeax2pznprcvadz2c3hx9djcq4xgjyk",
      "amount": {
        "coin": "1172320",
        "multiasset": {
          "c6b65a008d444e6653b08fce101b32c43a35cd190daf3f89ee69ce1c": {
            "536d6f6c20436174": "66057"
          }
        }
      },
      "plutus_data": null,
      "script_ref": null
    }
  },
  {
    "input": {
      "transaction_id": "ca59a384b8ef6ecdcbe30280a1e0bd14fb68103eba0cbb21a130f0e4a2411991",
      "index": 0
    },
    "output": {
      "address": "addr1qx37ydfc3celyt5592xs90jtqkt0nluut394d075fs3hgu2yj02azpagpk0xe45pw25zeax2pznprcvadz2c3hx9djcq4xgjyk",
      "amount": {
        "coin": "10000000",
        "multiasset": null
      },
      "plutus_data": null,
      "script_ref": null
    }
  },
  {
    "input": {
      "transaction_id": "78af89dc5bcefe1e3964e4b44a30e8894f556aeafe15fa707fbeddfd258a1356",
      "index": 0
    },
    "output": {
      "address": "addr1qx37ydfc3celyt5592xs90jtqkt0nluut394d075fs3hgu2yj02azpagpk0xe45pw25zeax2pznprcvadz2c3hx9djcq4xgjyk",
      "amount": {
        "coin": "11581852",
        "multiasset": null
      },
      "plutus_data": null,
      "script_ref": null
    }
  }
]

@noroxi7
Copy link
Author

noroxi7 commented Feb 25, 2025

Ok fixed, so the asset name was malformed, buffer formed from hex but not received as hex format! Thanks for the help, where I can fund the documentation to the nodejs aplication ?

@lisicky
Copy link
Contributor

lisicky commented Feb 26, 2025

@noroxi7 what application do you mean ?

@noroxi7
Copy link
Author

noroxi7 commented Feb 26, 2025

Sorry, the complete documentation of this lib @lisicky

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants