0

I am trying to implement the Unlocking of NFT from an "always true" smart contract. I am using the Cardano serialization library for this. I can lock the NFT successfully, but when i unlock the NFT then i get the issue of Fee imbalancing issue i think. Maybe i am missing something and stuck on this. I am facing this error ""transaction submit error ShelleyTxValidationError ShelleyBasedEraBabbage (ApplyTxError [UtxowFailure (UtxoFailure (FromAlonzoUtxoFail (ValueNotConservedUTxO (Value 1297310 (fromList [(PolicyID {policyID = ScriptHash \"80786e0ffb709cf2da414f0496be530a129c23f18c7a493abbf8589c\"},fromList [(61727445796573,1)])])) (Value 2050770 (fromList [(PolicyID {policyID = ScriptHash \"80786e0ffb709cf2da414f0496be530a129c23f18c7a493abbf8589c\"},fromList [(61727445796573,1)])])))))])""

Given below is my code for unlocking NFT.

    assetName = undefined,
    assetPolicyId = undefined,
    storeWalletSelected = undefined,
    transactionHashLocked = undefined
  ) {
    
    const txBuilder = await this.initTransactionBuilder();
    const ScriptAddress = this.sLib.Address.from_bech32(this.addressScriptBech32);
    const walletAddress = await this.getAddress()
    const shelleyChangeAddress = this.sLib.Address.from_bech32(walletAddress)
    const protocolParams = this.getProtocolParameters();

    let multiAsset = this.sLib.MultiAsset.new();
    let assets = this.sLib.Assets.new()
    assets.insert(
      this.sLib.AssetName.new(Buffer.from(assetName, "hex")), // Asset Name
      this.sLib.BigNum.from_str(this.assetAmountToSend.toString()) // How much to send
    );

    multiAsset.insert(
      this.sLib.ScriptHash.from_bytes(Buffer.from(assetPolicyId, "hex")), // PolicyID
      assets
    );

    txBuilder.add_input(
      ScriptAddress,
      this.sLib.TransactionInput.new(
        this.sLib.TransactionHash.from_bytes(Buffer.from(transactionHashLocked, "hex")),
        this.transactionIndxLocked.toString()),
      this.sLib.Value.new_from_assets(multiAsset)
    ) // how much lovelace is at that UTXO


    txBuilder.set_fee(this.sLib.BigNum.from_str(Number(this.manualFee).toString()))

    const scripts = this.sLib.PlutusScripts.new();
    scripts.add(this.sLib.PlutusScript.from_bytes(Buffer.from(this.plutusScriptCborHex, "hex"))); //from cbor of plutus script

    // Add outputs
    const outputValStr = Number(this.manualFee).toString();

    let txOutputBuilder = this.sLib.TransactionOutputBuilder.new();
    txOutputBuilder = txOutputBuilder.with_address(shelleyChangeAddress);
    txOutputBuilder = txOutputBuilder.next();
    txOutputBuilder = txOutputBuilder.with_asset_and_min_required_coin(
      multiAsset,
      this.sLib.BigNum.from_str(protocolParams.coinsPerUtxoWord)
    )
    
    const txOutput = txOutputBuilder.build();
    txBuilder.add_output(txOutput)

    // once the transaction is ready, we build it to get the tx body without witnesses
    const txBody = txBuilder.build();

    const collateral = await this.getCollateral(storeWalletSelected);
    const inputs = this.sLib.TransactionInputs.new();
    collateral.forEach((utxo) => inputs.add(utxo.input()) );

    let datums = this.sLib.PlutusList.new();
    // datums.add(PlutusData.from_bytes(Buffer.from(this.state.datumStr, "utf8")))
    datums.add(this.sLib.PlutusData.new_integer(
      this.sLib.BigInt.from_str(this.datumStr)))

    const redeemers = this.sLib.Redeemers.new();

    const data = this.sLib.PlutusData.new_constr_plutus_data(
        this.sLib.ConstrPlutusData.new(
          this.sLib.BigNum.from_str("0"),
          this.sLib.PlutusList.new()
        )
    );

    const redeemer = this.sLib.Redeemer.new(
      this.sLib.RedeemerTag.new_spend(),
      this.sLib.BigNum.from_str("0"),
      data,
      this.sLib.ExUnits.new(
        this.sLib.BigNum.from_str("7000000"),
        this.sLib.BigNum.from_str("3000000000")
      )
    );

    redeemers.add(redeemer)

    //Tx witness
    const transactionWitnessSet = this.sLib.TransactionWitnessSet.new();

    transactionWitnessSet.set_plutus_scripts(scripts)
    transactionWitnessSet.set_plutus_data(datums)
    transactionWitnessSet.set_redeemers(redeemers)

    const costModel = this.sLib.TxBuilderConstants.plutus_vasil_cost_models().get(this.sLib.Language.new_plutus_v1())    

    const costModels = this.sLib.Costmdls.new();
    costModels.insert(this.sLib.Language.new_plutus_v1(), costModel);

    const scriptDataHash = this.sLib.hash_script_data(redeemers, costModels, datums);
    txBody.set_script_data_hash(scriptDataHash);

    txBody.set_collateral(inputs)

    const baseAddress = this.sLib.BaseAddress.from_address(shelleyChangeAddress)
    const requiredSigners = this.sLib.Ed25519KeyHashes.new();
    requiredSigners.add(baseAddress.payment_cred().to_keyhash())

    txBody.set_required_signers(requiredSigners);

    const tx = this.sLib.Transaction.new(
      txBody,
      this.sLib.TransactionWitnessSet.from_bytes(transactionWitnessSet.to_bytes())
    )

    let txVkeyWitnesses = await this.wallet.signTx(Buffer.from(tx.to_bytes(), "utf8").toString("hex"), true);
    txVkeyWitnesses = this.sLib.TransactionWitnessSet.from_bytes(Buffer.from(txVkeyWitnesses, "hex"));

    transactionWitnessSet.set_vkeys(txVkeyWitnesses.vkeys());
    const signedTx = this.sLib.Transaction.new(tx.body(), transactionWitnessSet);

    const submittedTxHash = await this.wallet.submitTx(Buffer.from(signedTx.to_bytes(), "utf8").toString("hex"));
    console.log('Tx Hash NFT Reedemed: ', submittedTxHash);
    return submittedTxHash
  }

The protocol Parameters used in this are

getProtocolParameters () {
   return {
     linearFee: {
         minFeeA: "44",
         minFeeB: "155381",
     },
     minUtxo: "34482",
     poolDeposit: "500000000",
     keyDeposit: "2000000",
     maxValSize: 5000,
     maxTxSize: 16384,
     priceMem: 0.0577,
     priceStep: 0.0000721,
     coinsPerUtxoWord: "34482",
   }
 }

The manual fee variable is

this.manualFee = 900000

Kindly suggest me some solution if you find any.

3 Answers 3

0

Looks like the error is telling you that you have a utxo problem. Your inputs must match your outputs. This could be that they're mismatched, or you're missing the input altogether. https://iohk.zendesk.com/hc/en-us/articles/900001220843-Transaction-errors-ValueNotConservedUTxO

I would also look at add_key_input as defined in the example here: https://github.com/Emurgo/cardano-serialization-lib/blob/master/doc/getting-started/minting-nfts.md

It seems like you're only setting the collateral with the inputs, but never actually setting the utxo's as input for the transaction.

3
  • Sorry i didn't get it what you are trying to mention. Like i do have set the txInput ` txBuilder.add_input( ScriptAddress, this.sLib.TransactionInput.new( this.sLib.TransactionHash.from_bytes(Buffer.from(transactionHashLocked, "hex")), this.transactionIndxLocked.toString()), this.sLib.Value.new_from_assets(multiAsset) ) ` Also i am setting the UTXO. and yes i know there is some imabalance in my inputs and outputs but i am unable to figure it out that how do i resolve this because i am new to cardano and this is the CSL modified code.
    – duaa azhar
    Apr 20, 2023 at 5:26
  • @duaaazhar Ah ok, good. So you know it's a balance issue. To find the balance issue ... I would console.log() any time you set a value in the script, and keep a running sum so you can compare that with the utxo's you're adding in. Then you should be able to find the mismatch. However, maybe try something like txBuilder.add_change_if_needed(addr) and see if that doesn't just resolve the issue. I looked here for some reference. github.com/Emurgo/cardano-serialization-lib/issues/285
    – PREEB
    Apr 20, 2023 at 12:41
  • Hi @PREEB , I have answered this in the reply to my question. Can you please help me out in that query? Thanks in Advance.
    – duaa azhar
    May 5, 2023 at 12:46
0

I have tried the txBuilder.add_change_if_needed(addr) but it didnt work. Can you please tell me, that can a NFT alone be locked in a smart contract? Like i have tried that by sending the nft to smart contract by using function

txOutputBuilder.with_asset_and_min_required_coin(
      multiAsset,
      this.sLib.BigNum.from_str(protocolParams.coinsPerUtxoWord)
    )

So NFT gets locked with some minimal ADA of about 1.30593 (maybe its because of coinsPerUtxoWord). But when i wanna unlock that NFT from smart contract, how should i specify that minimal amount in my txInput as its according to coinsPerUtxoWord. And when i set the NFT alone in input, then the transaction gets unbalance, and shows the issue of imbalanced input and output of tx. I think this got imbalance because at output it requires some ADA for fee whereas i am passing NFT asset in input. To resolve this i have tried to pass UTXOS in txInput by using

const txUnspentOutputs = await this.getTxUnspentOutputs();
 txBuilder.add_inputs_from(txUnspentOutputs, 3)

and also i used later txBuilder.add_change_if_needed(shelleyChangeAddress) so that the change gets back to the changeAddress But again its showing me the issue of

""transaction submit error ShelleyTxValidationError ShelleyBasedEraBabbage (ApplyTxError [UtxowFailure (UtxoFailure (FromAlonzoUtxoFail (ValueNotConservedUTxO ( Value 9996312330 (fromList [(PolicyID {policyID = ScriptHash \"80786e0ffb709cf2da414f0496be530a129c23f18c7a493abbf8589c\"},fromList [(617274426c61636b6e5768697465,1)])])) ( Value 9994984850 (fromList [(PolicyID {policyID = ScriptHash \"80786e0ffb709cf2da414f0496be530a129c23f18c7a493abbf8589c\"},fromList [(617274426c61636b6e5768697465,1)])]))))),UtxowFailure (UtxoFailure (FromAlonzoUtxoFail (FeeTooSmallUTxO (Coin 798505) (Coin 171969))))])""

I think the main issue is in this line of code i.e. txOutputBuilder = txOutputBuilder.with_asset_and_min_required_coin(multiAsset, this.sLib.BigNum.from_str(protocolParams.coinsPerUtxoWord)) of txOutput as given. But i dont know what to use instead of this nor i found any useful reading resource regarding it.

let txOutputBuilder = this.sLib.TransactionOutputBuilder.new();
    txOutputBuilder = txOutputBuilder.with_address(shelleyChangeAddress);
    txOutputBuilder = txOutputBuilder.next();
 
    txOutputBuilder = txOutputBuilder.with_asset_and_min_required_coin(multiAsset, this.sLib.BigNum.from_str(protocolParams.coinsPerUtxoWord))
  const txOutput = txOutputBuilder.build();
    
  
    txBuilder.add_output(txOutput)
    console.log("txBuilder after adding output: ", txBuilder)

Can you please help me out in this, I think i am a bit close just missing some points maybe. Your little guidance can be quite helpful. Thanks

0

As of today the Cardano Serialization Library is no longer maintained and you shouldn't rely on it.

The closest to the CSL is the Cardano Multiplatform library that still relies on Rust + WASM.

If you don't want to load 10 MB of WASM in your web app you can also try cardano-ledger-ts(for the types) + plu-ts-offchain(for the transaction builder)

npm install @harmoniclabs/cardano-ledger-ts @harmoniclabs/plu-ts-offchain

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.