cardano-serialization-lib
cardano-serialization-lib copied to clipboard
add_change_if_needed not handling native assets in utxo
when add_change_if_needed
is used along with add_mint_asset_and_output_min_required_coin
,
seems to be unable to balance transaction correctly when given a utxo that contains native assets (with nft metadata).
add_change_if_needed
fails with
missing input for some native asset
txBuilder
const policyId = Buffer.from(
nativeScript.hash(0).to_bytes()
).toString('hex')
const generalTxMeta = genMetadata(cslModule)(policyId, metadata)
txBuilder.set_metadata(generalTxMeta)
const assetName = AssetName.new(Buffer.from(assetNameStr, 'utf-8'))
const assetNumber = Int.new_i32(assetNum)
txBuilder.add_mint_asset_and_output_min_required_coin(
nativeScript,
assetName,
assetNumber,
addr
)
txBuilder.set_ttl(timelockExpirySlot)
const [input] = utxos.map(u => ({
utxo: u.input(),
value: u.output().amount(),
}))
txBuilder.add_key_input(
addr_keyhash,
input.utxo,
input.value
)
txBuilder.add_change_if_needed(addr)
const tx = txBuilder.build_tx()
Hi, @shroomist , provide full example of the sequence of calls to the builder please, so we can try to reproduce
Updated with the code sample. Issue boils down to that I'm able to mint with some utxos and unable with some other.
Having the same issue in some instances.
@MartinSchere @shroomist can you provide the utxos where you aren't able to?
I have run into the same issue trying to send an asset.
Here is two UTXOs on the testnet I'm having the problem with: "73a0cdfde20a2482164821ee765977444ed47bc7acf25cb77809b200c90ee295#0" "e5eb46c4c72fb84c443a03be5be48dca2740a77015b30c4c633fb0755f31f73f#0"
I haven't tried others.
Issue boils down to that I'm able to mint with some utxos and unable with some other.
@shroomist , the critical question there is what is the value of those inputs that you have problems minting with? In the provided example I see you are setting a single input - what is the value of that simple input and does it cover both the fee and the min-required-ada value that is being set to the output with the new asset AND the min-required-ada value that has to be included in the change when the assets from the input will be sent back to you as change?
I have run into the same issue trying to send an asset.
Here is two UTXOs on the testnet I'm having the problem with: "73a0cdfde20a2482164821ee765977444ed47bc7acf25cb77809b200c90ee295#0" "e5eb46c4c72fb84c443a03be5be48dca2740a77015b30c4c633fb0755f31f73f#0"
I haven't tried others.
@bakon11 , do you mean that when you set either single one of those utxos as inputs - the minting doesn't work and you get "not enough funds"? If so then it is definitely quite a correct behavior in the case of the second utxo with only 2 ADA in it and the same exact problem as mentioned in the previous comment.
What you are trying to do is:
- Spend 2 ADA and the Anthony009 token
- Create new output where a new token will be minted AND THE MIN REQUIRED ADA value will be added as well (by calling
*_and_output_min_required_coin
function) , which is approximately ~1.3 ADA for a single asset - Ask tx-builder to create additional change output which is supposed to include the Anthony009 token that goes back to your wallet and that also requires the minimum ada value to be included (approx ~1.3 ADA).
So you are trying to spend one utxo of [2 ADA and 1 token] and produce 2 utxos of [~1.3 ADA and 1 token] EACH and also manage to pay fees at the same time.
Related info: https://github.com/Emurgo/yoroi-frontend/blob/develop/docs/readme/cardano-token-deposit-explained.md
Although it would definitely weird in the case of the "73a0cdfde20a2482164821ee765977444ed47bc7acf25cb77809b200c90ee295#0" utxo being present in the inputs, as I see it contains 1000 tADA in it.
@bakon11 , can you plz confirm that if you include that single utxo in the inputs and try to perform mint with minimal value included - then you get the "not enough funds" error?
Having the actual code snippet of how are you building a transaction would be even better.
@vsubhuman
I apologize I actually read the initial issue wrong. I was actually trying to send an asset over from one address to another. and ran into the missing input for some native asset
issue when using txBuilder.add_change_if_needed
.
Here is what I was doing when I got the errors.
So basically if I have only one input and assetsValue set to 1500000
I got the above error and unbalanced UTXO error.
// asset vars for testing.
const assetsToSend: any = await CardanoWasm.MultiAsset.new();
const assets = await CardanoWasm.Assets.new();
const policyStr = "1b015f3c7a362d7cfbdb3e0067ee8ae3d8d73d8289ad548418052acf"
const assetStr = "466972654e4654";
const assetName = await CardanoWasm.AssetName.new(Buffer.from(assetStr, "hex"));
// creating asset value for lovelaces
const assetsValue = await CardanoWasm.Value.new( CardanoWasm.BigNum.from_str('1500000') );
// Inserting Native token
await assets.insert(
assetName,
CardanoWasm.BigNum.from_str('1')
);
// Adding asset to multiasset
await assetsToSend.insert(
CardanoWasm.ScriptHash.from_bytes(Buffer.from(policyStr, "hex")),
assets
);
// Setting value with multiasset
await assetsValue.set_multiasset(assetsToSend)
// set utxo input
console.log("adding input");
await txBuilder.add_input(
CardanoWasm.Address.from_bech32(changeAddress),
CardanoWasm.TransactionInput.new(
CardanoWasm.TransactionHash.from_bytes(
Buffer.from(txix, "hex")
), // tx hash
txixIndex, // index
),
CardanoWasm.Value.new(CardanoWasm.BigNum.from_str(inputValue))
);
// output for asset transfer
console.log( "adding output");
await txBuilder.add_output(
CardanoWasm.TransactionOutputBuilder.new()
.with_address( CardanoWasm.Address.from_bech32(outputAddress) )
.next()
.with_asset_and_min_required_coin( assetsToSend, CardanoWasm.BigNum.from_str('34482') )
.build()
);
// set the time to live - the absolute slot value before the tx becomes invalid
console.log("setting ttl");
await txBuilder.set_ttl(txTTL);
// calculate the min fee required and send any change to an address
console.log("setting change");
await txBuilder.add_change_if_needed( CardanoWasm.Address.from_bech32(changeAddress) );
I solved it by: setting assetsValue to 0 and adding an extra input with the new assetsValue
set to 0
// asset vars for testing.
const assetsToSend: any = await CardanoWasm.MultiAsset.new();
const assets = await CardanoWasm.Assets.new();
const policyStr = "1b015f3c7a362d7cfbdb3e0067ee8ae3d8d73d8289ad548418052acf"
const assetStr = "466972654e4654";
const assetName = await CardanoWasm.AssetName.new(Buffer.from(assetStr, "hex"));
// creating asset value for lovelaces
const assetsValue = await CardanoWasm.Value.new( CardanoWasm.BigNum.from_str('0') );
// Inserting Native token
await assets.insert(
assetName,
CardanoWasm.BigNum.from_str('1')
);
// Adding asset to multiasset
await assetsToSend.insert(
CardanoWasm.ScriptHash.from_bytes(Buffer.from(policyStr, "hex")),
assets
);
// Setting value with multiasset
await assetsValue.set_multiasset(assetsToSend)
// set utxo input
console.log("adding input");
await txBuilder.add_input(
CardanoWasm.Address.from_bech32(changeAddress),
CardanoWasm.TransactionInput.new(
CardanoWasm.TransactionHash.from_bytes(
Buffer.from(txix, "hex")
), // tx hash
txixIndex, // index
),
CardanoWasm.Value.new(CardanoWasm.BigNum.from_str(inputValue))
);
await txBuilder.add_input(
CardanoWasm.Address.from_bech32(changeAddress),
CardanoWasm.TransactionInput.new(
CardanoWasm.TransactionHash.from_bytes(
Buffer.from(txix, "hex")
), // tx hash
txixIndex, // index
),
assetsValue
);
// output for asset transfer
console.log( "adding output");
await txBuilder.add_output(
CardanoWasm.TransactionOutputBuilder.new()
.with_address( CardanoWasm.Address.from_bech32(outputAddress) )
.next()
.with_asset_and_min_required_coin( assetsToSend, CardanoWasm.BigNum.from_str('34482') )
.build()
);
// set the time to live - the absolute slot value before the tx becomes invalid
console.log("setting ttl");
await txBuilder.set_ttl(txTTL);
// calculate the min fee required and send any change to an address
console.log("setting change");
await txBuilder.add_change_if_needed( CardanoWasm.Address.from_bech32(changeAddress) );
Again I'm not sure if I'm doing this correctly anyways but the TX does succeed, just trying to piece together from what I find around one forums.
Thank you.
Again I'm not sure if I'm doing this correctly anyways but the TX does succeed, just trying to piece together from what I find around one forums.
@bakon11 , I see that in the first example you are only specifying the ADA value of your input, without specifying that it has any assets in it, which is why the tx-builder cannot know these assets are there and fails to calculate the change (it's an offline library it doesn't check the actual blockchain it only works with the information you give it):
In the second example you have resolved it by just adding the same exact input (same utxo link) again and specifying the assets value with zero coin in it, which convinces the builder that the assets are present in the input after all:
You can make it in a cleaner way by just combining the parsed ADA input amount with the assets in a single Value object, something like:
// Inserting Native token
await assets.insert(
assetName,
CardanoWasm.BigNum.from_str('1'),
);
// Adding asset to multiasset
await assetsToSend.insert(
CardanoWasm.ScriptHash.from_bytes(Buffer.from(policyStr, "hex")),
assets,
);
// Input Value
const inputValue = await CardanoWasm.Value.new( CardanoWasm.BigNum.from_str(inputValue) );
// Same assets are in the input and are being sent
await inputValue.set_multiasset(assetsToSend);
// set utxo input
console.log("adding input");
await txBuilder.add_input(
CardanoWasm.Address.from_bech32(changeAddress),
CardanoWasm.TransactionInput.new(
CardanoWasm.TransactionHash.from_bytes(
Buffer.from(txix, "hex")
), // tx hash
txixIndex, // index
),
inputValue,
);
// proceed same as in the first example
That way the assetsToSend
will just be used in both two places - add then to the inputValue
and later add them to the output. No need to add a separate input for them.
ahh actually sorry forgot to add that this is a function I pass inputValue
to from another source like you mention.
The input value I pass is the UTXO full lovelace amount. What happens then is the builder adds the value I pass in inputValue
fand the amount that's assigned to assetsValue
and so when I pass the TX to cardano-cli
is gives UTXO out of balance error.
That's why when I set the assetsValue
to 0 in the second example, it only accepts the inputValue
from the functions param.
export const genTX = async ( accountKey: any, txix: string, txixIndex: number, inputValue: string, utxoKey:any, outputAddress: string, outputValue: string, changeAddress: string, txTTL: number ) => {
try{
// instantiate the tx builder with the Cardano protocol parameters - these may change later on
const txBuilder = await CardanoWasm.TransactionBuilder.new(
CardanoWasm.TransactionBuilderConfigBuilder.new()
.fee_algo( CardanoWasm.LinearFee.new(CardanoWasm.BigNum.from_str('44'),CardanoWasm.BigNum.from_str('155381')))
.pool_deposit(CardanoWasm.BigNum.from_str('500000000'),)
.key_deposit( CardanoWasm.BigNum.from_str('2000000'),)
.coins_per_utxo_word(CardanoWasm.BigNum.from_str('34482'))
.max_value_size(5000)
.max_tx_size(16384)
.build()
);
// asset vars for testing.
const assetsToSend: any = await CardanoWasm.MultiAsset.new();
const assets = await CardanoWasm.Assets.new();
const policyStr = "1b015f3c7a362d7cfbdb3e0067ee8ae3d8d73d8289ad548418052acf"
const assetStr = "466972654e4654";
const assetName = await CardanoWasm.AssetName.new(Buffer.from(assetStr, "hex"));
// creating asset value for lovelaces
const assetsValue = await CardanoWasm.Value.new( CardanoWasm.BigNum.from_str('0') );
// Inserting Native token
await assets.insert(
assetName,
CardanoWasm.BigNum.from_str('1')
);
// Adding asset to multiasset
await assetsToSend.insert(
CardanoWasm.ScriptHash.from_bytes(Buffer.from(policyStr, "hex")),
assets
);
// Setting value with multiasset
await assetsValue.set_multiasset(assetsToSend)
// set utxo input
console.log("adding input");
await txBuilder.add_input(
CardanoWasm.Address.from_bech32(changeAddress),
CardanoWasm.TransactionInput.new(
CardanoWasm.TransactionHash.from_bytes(
Buffer.from(txix, "hex")
), // tx hash
txixIndex, // index
),
CardanoWasm.Value.new(CardanoWasm.BigNum.from_str(inputValue))
);
await txBuilder.add_input(
CardanoWasm.Address.from_bech32(changeAddress),
CardanoWasm.TransactionInput.new(
CardanoWasm.TransactionHash.from_bytes(
Buffer.from(txix, "hex")
), // tx hash
txixIndex, // index
),
assetsValue
);
// output for asset transfer
console.log( "adding output");
await txBuilder.add_output(
CardanoWasm.TransactionOutputBuilder.new()
.with_address( CardanoWasm.Address.from_bech32(outputAddress) )
.next()
.with_asset_and_min_required_coin( assetsToSend, CardanoWasm.BigNum.from_str('34482') )
.build()
);
// set the time to live - the absolute slot value before the tx becomes invalid
console.log("setting ttl");
await txBuilder.set_ttl(txTTL);
// calculate the min fee required and send any change to an address
console.log("setting change");
await txBuilder.add_change_if_needed( CardanoWasm.Address.from_bech32(changeAddress) );
// once the transaction is ready, we build it to get the tx body without witnesses
console.log("Building and singing TX");
const txBody = await txBuilder.build();
const txHash = await CardanoWasm.hash_transaction(txBody);
// add keyhash witnesses
const witnesses = await CardanoWasm.TransactionWitnessSet.new();
const vkeyWitnesses = await CardanoWasm.Vkeywitnesses.new();
const vkeyWitness = await await CardanoWasm.make_vkey_witness(txHash, utxoKey.to_raw_key());
await vkeyWitnesses.add(vkeyWitness);
await witnesses.set_vkeys(vkeyWitnesses);
// create the finalized transaction with witnesses
const transaction = await CardanoWasm.Transaction.new(
txBody,
witnesses,
undefined, //metadata
);
const txHex = await Buffer.from(transaction.to_bytes()).toString("hex");
console.log(txHex);
return(txHex);
}catch(error){
console.log( error );
return( error );
};
};
@vsubhuman how to use txBuilder.add_inputs_from() I want to use this but I am getting below error UTxO Balance Insufficient
let all_utxos = await this.getUtxosHex();
let ts = this.S.TransactionUnspentOutputs.new();
all_utxos.forEach((element) => {
ts.add(
this.S.TransactionUnspentOutput.from_bytes(Buffer.from(element, "hex"))
);
});
txBuilder.add_inputs_from(ts, this.S.CoinSelectionStrategyCIP2.LargestFirstMultiAsset);
UTxO Balance Insufficient
@mailtodanish , not enough utxo to cover all the output amount
@vsubhuman I am able to send all assets using nami but here it saying "UTxO Balance Insufficient"
const assetsNftsToSend = this.S.MultiAsset.new();
const assets = this.S.Assets.new();
const assetName = this.S.AssetName.new(
Buffer.from(process.env.NEXT_PUBLIC_SWAP_TOKEN_NAME, "utf-8")
);
assets.insert(assetName, this.S.BigNum.from_str("1"));
assetsNftsToSend.insert(
this.S.ScriptHash.from_bytes(Buffer.from(token_policy, "hex")),
assets
);
const userOutput = this.S.TransactionOutputBuilder.new()
.with_address(this.S.Address.from_bech32(process.env.NEXT_PUBLIC_SWAP_SENT_ADDRESS))
.next()
.with_asset_and_min_required_coin(
assetsNftsToSend,
this.S.BigNum.from_str(protocolParameters.coinsPerUtxoWord)
)
.build();
outputs.add(userOutput);
for (let i = 0; i < outputs.len(); i++) {
txBuilder.add_output(outputs.get(i));
}
let all_utxos = await this.getUtxosHex();
let ts = this.S.TransactionUnspentOutputs.new();
all_utxos.forEach((element) => {
console.log(element);
ts.add(
this.S.TransactionUnspentOutput.from_bytes(Buffer.from(element, "hex"))
);
});
txBuilder.add_inputs_from(ts, this.S.CoinSelectionStrategyCIP2.LargestFirstMultiAsset);
txBuilder.add_change_if_needed(this.S.Address.from_bech32(userAddr));
Right now I have to use LargestFirstMultiasset because RandomImprove is giving Not enough ADA leftover to include non-ADA assets in a change address
error,
Right now I have to use LargestFirstMultiasset because RandomImprove is giving
Not enough ADA leftover to include non-ADA assets in a change address
error,
@vsubhuman Even LargestFirstMultiasset
got that error occasionally. It looks like add_inputs_from
doesn't consider the minimum ADA for change output, thus sometimes we don't have enough ADA in a tx's inputs to cover for minimum ADA in change output if we call add_inputs_from
then call add_change_if_needed
Indeed. Can confirm that this is happenning for me too.
Right now I have to use LargestFirstMultiasset because RandomImprove is giving
Not enough ADA leftover to include non-ADA assets in a change address
error,
@rooooooooob , check it plz when possible
let mut output_total = self
.get_explicit_output()?
.checked_add(&Value::new(&self.get_deposit()?))?
.checked_add(&Value::new(&self.min_fee()?))?;
Output total is defined there, and the only modification I see is the following:
*output_total = output_total.checked_add(&Value::new(&input_fee))?;
We would have to check for every individual output and calculate the min_ada_required
, then add that to ouptut_total
to fix this I think
Now my code is running and working with below change
Buffer.from(process.env.NEXT_PUBLIC_SWAP_TOKEN_NAME, "hex")
refer my code above.
Hello
I'm having an issue when using add_change_if_needed in a burning transaction.
I've added, along with other inputs that doesn't have native assets, the utxo that has the token to be burned:
const input = emurgo.TransactionInput.new(
emurgo.TransactionHash.from_bytes(Buffer.from(burnUtxo.txHash, "hex")),
burnUtxo.index
);
const assetsToSend = emurgo.MultiAsset.new();
const assets = emurgo.Assets.new();
assets.insert(
emurgo.AssetName.new(Buffer.from(burnAssetName, "hex")),
emurgo.BigNum.from_str("1"),
);
assetsToSend.insert(
emurgo.ScriptHash.from_bytes(Buffer.from(burnPolicy, "hex")),
assets,
);
const inputValue = emurgo.Value.new( emurgo.BigNum.from_str(burnUtxo.value) );
inputValue.set_multiasset(assetsToSend);
txBuilder.add_input(
emurgo.Address.from_bech32(burnUtxo.address),
input,
inputValue
);
Then i've set the minting inputs:
const mint = emurgo.Mint.new();
const assets = emurgo.MintAssets.new();
const assetName = emurgo.AssetName.new(Buffer.from(burnAssetName, "hex"))
const IntFunction = burn ? "new_negative" : "new";
assets.insert(
assetName,
emurgo.Int[IntFunction](emurgo.BigNum.from_str("1"))
);
const scriptHash = emurgo.ScriptHash.from_bytes(
Buffer.from(burnPolicy, "hex")
);
mint.insert(scriptHash, assets);
txBuilder.set_mint(mint, nativeScripts);
And finally the outputs:
const maOutputValue = emurgo.Value.new(
emurgo.BigNum.from_str("1655136")
);
const multiAsset = emurgo.MultiAsset.new();
const assets = emurgo.Assets.new();
const assetName = emurgo.AssetName.new(Buffer.from(burnAssetName, "hex"))
assets.insert(assetName, emurgo.BigNum.from_str("1"));
const scriptHash = emurgo.ScriptHash.from_bytes(
Buffer.from(burnPolicy, "hex")
);
multiAsset.insert(scriptHash, assets);
maOutputValue.set_multiasset(multiAsset);
txBuilder.add_output(
emurgo.TransactionOutput.new(destinationAddress, maOutputValue)
);
And when I try to add the change with txBuilder.add_change_if_needed(wasmPaymentAddress);
I get the following error:
missing input for some native asset
I tried what was mentioned in https://github.com/Emurgo/cardano-serialization-lib/issues/285#issuecomment-1042310196 but I didn't get it working.
Hello @lnmg,
the missing input for some native asset
error happens if there are no inputs or no outputs for an asset. and if I run a very similar code to what you have it is working for me. Here is the code I used:
const input = TransactionInput.new(
TransactionHash.from_bytes(
Buffer.from(
"c31b6d4bbdc6dd17741aa6f30f51adb98c55b320cd7ba6fafcd05486f88b7538",
"hex"
)
),
0
);
const assetsToSend = MultiAsset.new();
const assets = Assets.new();
assets.insert(
AssetName.new(Buffer.from("AssetName", "utf-8")),
BigNum.from_str("1")
);
assetsToSend.insert(scriptHash, assets);
const inputValue = Value.new(BigNum.from_str("13792801"));
inputValue.set_multiasset(assetsToSend);
txBuilder.add_input(
Address.from_bech32(
"addr1qy3w6z855vlgx0ma33xf53x5wnqud574pzdf5s03jp59rp3627hhjyls27xwmke4e4ewn27rv3qcntakvp7wd53dqahqcf0ssn"
),
input,
inputValue
);
const outputValue = Value.new(BigNum.from_str("1379280"));
outputValue.set_multiasset(assetsToSend);
txBuilder.add_output(TransactionOutput.new(
Address.from_bech32(
"addr1qy3w6z855vlgx0ma33xf53x5wnqud574pzdf5s03jp59rp3627hhjyls27xwmke4e4ewn27rv3qcntakvp7wd53dqahqcf0ssn"
),
outputValue
));
I don't have the mint part but I don't think that should be the problem.
Like I mentioned this issue happens when there are no inputs or no outputs for an asset so you might be accidentaly adding another asset input that doesn't have output or an asset output that doesn't have an input or maybe the burnAssetName
and/or burnPolicy
change in between the two places where you add inputs and outputs.
Can you check if those variables are the same and there are no other input/output additions in some other place in the code?
@ozgrakkurt I tried hardcoding the values just to be sure that the name and policy don't change in between. I also double-checked the selected utxo, and it's the only one that I set that has native assets. Is there anything else I can check?
@lnmg can you try without the minting inputs part and also try to reproduce this with a minimal example and send that one. Meanwhile I am trying to debug this
Not sure which is the status of this issue, but I can confirm that the change calculation fails if there are both, native assets AND a minting output in the transaction. When there is not a minting output, the change is properly calculated even with native assets.
In my case, I tested minting an amount of the same asset that should also be added to the change output. Nevertheless, I am not getting the message commented above, i.e.:
missing input for some native asset
but instead, the transaction is just wrong. It added the minting output but not the assets change, so it gets unbalanced.
I can confirm that the change calculation fails if there are both, native assets AND a minting output in the transaction.
Hi, @miguelaeh! Would that be possible for you to share the code of how you are creating the transaction, and especially how exactly are you adding the mint? And also the produced transaction hex?
We will try to get this resolved at priority if we can reproduce it from an example.
Hi @miguelaeh ! Could you tell me which version of CSL do you use ?
Could you tell me which version of CSL do you use ?
The commented test was done with 10.2.0
. It is a bit old but we still need to test the project with the new one.
Would that be possible for you to share the code of how you are creating the transaction, and especially how exactly are you adding the mint? And also the produced transaction hex?
I cannot share the exact code because I built some wrappers around the library. But just building a Tx that includes a native asset mint output AND that needs to add change for a native asset reproduces it.
For example:
________
1234 lovelace + 333 'policy.name' -> | TX | -> minUtxO lovelace + 555 'policy.name' (mint output)
4321 lovelace -> | | -> change (rest of lovelace + 333 'policy.name')
| _________ |
(the numbers are just examples)
I think that is the minimum tx that will reproduce it, even though my original Tx had 2 minting outputs.
I used add_mint_asset_and_output_min_required_coin
for the mint and add_change_if_needed
at the end to balance the Tx
https://github.com/Emurgo/cardano-serialization-lib/pull/520 is fixing how the tx-balance is being asserted during the change-building and makes the lib return proper more informative error messages about the exact cause of the problem. Will add a comment here when we have a beta version published with this change included so anyone can help test if that helps to solve the issue.