Closed caetanix closed 3 months ago
Hi @caetanix ! Unfortunately I don't know which CSL version you use. Could you tell me which one do you use ? Also could you isolate a code example to reproduce it on our side, you can create a fake address and fake utxos. And CBOR of your tx would be very helpful.
Also by the provided link I see only one 1 ada tx, and this tx has only 1 output that doesn't match with your code where you create and output and also call add_change_if_needed()
. The add_change_if_needed()
also should produce an output except case when you don't have enough ada to cover a new output
Hello, i'm using last version, "@emurgo/cardano-serialization-lib-nodejs": "11.5.0",
I have an account with 8k ADA, and when i use this same code, to sent it works good, generates 2 outputs.
Here is the hash: https://preview.beta.explorer.cardano.org/en/transaction/5a7e9c2f823a7a0220e1d0a601c3057fa75c2ceded6d718503152bc8b591dde2/utxOs
CBOR: 132,164,0,129,130,88,32,65,242,12,0,156,135,86,252,27,234,41,100,228,160,248,219,51,177,115,141,235,222,151,140,231,45,71,55,11,202,10,179,1,1,130,130,88,57,0,56,252,38,162,92,119,111,165,178,32,186,222,215,6,107,96,27,52,141,246,105,98,142,3,67,253,149,234,85,234,9,251,139,157,121,227,52,67,63,254,70,23,174,144,247,3,24,57,48,250,41,124,129,211,243,40,26,0,15,66,64,130,88,57,0,43,234,83,159,55,158,211,201,6,131,41,253,212,153,54,21,1,129,245,29,98,9,151,213,102,56,187,58,41,73,60,115,106,195,142,239,126,90,7,198,119,97,34,87,161,39,176,38,129,37,165,25,180,133,146,129,27,0,0,0,2,13,52,60,15,2,26,0,2,146,45,3,26,2,176,64,193,161,0,129,130,88,32,40,195,29,133,252,151,31,210,115,170,154,177,233,178,164,5,66,88,233,198,176,31,135,109,61,53,164,89,168,148,192,171,88,64,240,251,228,183,223,124,246,180,215,156,77,155,197,87,247,152,14,188,109,132,73,183,236,167,72,118,38,99,78,130,207,56,24,64,239,63,195,162,146,176,37,70,110,19,200,254,195,180,45,75,121,64,76,110,97,125,185,122,234,64,241,69,28,3,245,246
So, i have done another test... I have sent 4x1 ADA to an address... and after i sent 1 ADA
Here is the hash: https://preview.beta.explorer.cardano.org/en/transaction/70d79ff0c623246b5e5411c97d479a98259bf5f991439e84cb2b1884cfba1616/utxOs
CBOR: 132,164,0,130,130,88,32,65,242,12,0,156,135,86,252,27,234,41,100,228,160,248,219,51,177,115,141,235,222,151,140,231,45,71,55,11,202,10,179,0,130,88,32,90,126,156,47,130,58,122,2,32,225,208,166,1,195,5,127,167,92,44,237,237,109,113,133,3,21,43,200,181,145,221,226,0,1,129,130,88,57,0,43,234,83,159,55,158,211,201,6,131,41,253,212,153,54,21,1,129,245,29,98,9,151,213,102,56,187,58,41,73,60,115,106,195,142,239,126,90,7,198,119,97,34,87,161,39,176,38,129,37,165,25,180,133,146,129,26,0,15,66,64,2,26,0,15,66,64,3,26,2,176,65,93,161,0,129,130,88,32,194,67,146,111,157,0,96,11,184,28,36,195,117,97,164,67,176,213,7,253,20,220,80,15,8,205,249,80,170,100,245,244,88,64,47,169,164,122,126,219,101,103,75,166,61,23,221,90,163,220,26,35,216,149,10,134,15,141,22,25,137,107,3,219,54,75,249,237,14,96,156,200,204,188,112,54,20,74,182,131,186,241,184,209,26,46,100,221,145,87,119,161,96,166,220,2,74,6,245,246
The transaction uses 2 inputs (of previous 4x1 ADA), but only 1 output... 1 ADA + 1 ADA of FEE The address still have 2 ADA, so have enough to generate another outputs... but address have 4 ADA, but uses 4 utxo..
But maybe the problem is relatated to the use 2 inputs of 1 ADA ? and because uses 1 ADA to sent.... and the second input is also 1 ADA, and if the fee is only 168977, the input will keep only 831023 and is less than minimum_coins, so the blockchain uses all the remain fee ?
I test with balance of 4 ADA, i have sent 2 ADA, so the transaction uses 3 inputs of 1 ADA, and 1 output only.... 2 ADA of output and 1 ADA of fee
Here is the hash: https://preview.beta.explorer.cardano.org/en/transaction/57ead77ade60009dd61cfac23c6467ddbcf48830bfea31c6ff0864ecca2df027/utxOs
CBOR 132,164,0,131,130,88,32,19,233,72,255,160,220,155,25,86,82,155,192,36,227,30,86,11,67,204,247,250,191,207,155,30,55,4,59,114,51,29,119,0,130,88,32,56,43,235,114,116,43,53,135,48,24,10,139,234,11,179,222,98,153,126,162,165,119,34,159,212,209,216,211,192,12,239,94,0,130,88,32,221,100,53,225,231,3,117,142,81,189,235,6,138,1,38,181,13,200,3,191,128,160,85,209,54,182,6,115,64,142,236,144,0,1,129,130,88,57,0,43,234,83,159,55,158,211,201,6,131,41,253,212,153,54,21,1,129,245,29,98,9,151,213,102,56,187,58,41,73,60,115,106,195,142,239,126,90,7,198,119,97,34,87,161,39,176,38,129,37,165,25,180,133,146,129,26,0,30,132,128,2,26,0,15,66,64,3,26,2,176,68,182,161,0,129,130,88,32,194,67,146,111,157,0,96,11,184,28,36,195,117,97,164,67,176,213,7,253,20,220,80,15,8,205,249,80,170,100,245,244,88,64,178,205,228,122,200,206,222,56,202,2,31,152,106,57,127,176,13,115,3,100,238,241,141,118,83,99,37,17,109,128,218,197,162,208,198,101,248,22,61,11,247,79,34,199,86,127,205,57,104,174,86,167,229,38,228,183,56,42,100,92,209,46,110,1,245,246
full code: ` const API = new Blockfrost.BlockFrostAPI({ projectId: paramProjectId, });
const entropy = bip39.mnemonicToEntropy(paramMnemonic);
const bip32PrvKey = CardanoWasm.Bip32PrivateKey.from_bip39_entropy(
Buffer.from(entropy, "hex"),
Buffer.from("")
);
const networkId = CardanoWasm.NetworkInfo.testnet_preview().network_id();
const accountIndex = 0;
const addressIndex = 0;
const accountKey = bip32PrvKey
.derive(harden(1852)) // purpose
.derive(harden(1815)) // coin type
.derive(harden(accountIndex)); // account #
const utxoKey = accountKey
.derive(0) // external
.derive(addressIndex);
const stakeKey = accountKey
.derive(2) // chimeric
.derive(0)
.to_public();
const signKey = utxoKey.to_raw_key();
// Retrieve protocol parameters
const protocolParams = await API.epochsLatestParameters();
// Retrieve utxo for the address
let utxo = await API.addressesUtxosAll(paramPublicKey);
// Get current blockchain slot from latest block
const latestBlock = await API.blocksLatest();
const currentSlot = latestBlock.slot;
// Prepare transaction
const params = {
protocolParams,
currentSlot,
};
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))
.max_value_size(parseInt(params.protocolParams.max_val_size))
.max_tx_size(parseInt(params.protocolParams.max_tx_size))
.coins_per_utxo_byte(CardanoWasm.BigNum.from_str(params.protocolParams.coins_per_utxo_size))
.build(),
);
const outputAddr = CardanoWasm.Address.from_bech32(paramAddress);
const changeAddr = CardanoWasm.Address.from_bech32(paramPublicKey);
const ttl = params.currentSlot + 7200;
txBuilder.set_ttl(ttl);
txBuilder.add_output(
CardanoWasm.TransactionOutput.new(
outputAddr,
CardanoWasm.Value.new(CardanoWasm.BigNum.from_str(paramAmount.toString())),
),
);
const lovelaceUtxos = utxo.filter(u => !u.amount.find(a => a.unit !== 'lovelace'));
const unspentOutputs = CardanoWasm.TransactionUnspentOutputs.new();
for (const utxo of lovelaceUtxos) {
const amount = utxo.amount.find(a => a.unit === 'lovelace')?.quantity;
if (!amount) continue;
const inputValue = CardanoWasm.Value.new(CardanoWasm.BigNum.from_str(amount.toString()));
const input = CardanoWasm.TransactionInput.new(
CardanoWasm.TransactionHash.from_bytes(Buffer.from(utxo.tx_hash, 'hex')),
utxo.output_index,
);
const output = CardanoWasm.TransactionOutput.new(changeAddr, inputValue);
unspentOutputs.add(CardanoWasm.TransactionUnspentOutput.new(input, output));
}
txBuilder.add_inputs_from(unspentOutputs, CardanoWasm.CoinSelectionStrategyCIP2.LargestFirst);
txBuilder.add_change_if_needed(changeAddr);
const txBody = txBuilder.build();
// Sign transaction
const txHash = CardanoWasm.hash_transaction(txBody);
const witnesses = CardanoWasm.TransactionWitnessSet.new();
const vkeyWitnesses = CardanoWasm.Vkeywitnesses.new();
vkeyWitnesses.add(CardanoWasm.make_vkey_witness(txHash, signKey));
witnesses.set_vkeys(vkeyWitnesses);
const transaction = CardanoWasm.Transaction.new(txBody, witnesses);
console.log(transaction.to_bytes().toString())
// txSubmit endpoint returns transaction hash on successful submit
const hash = await API.txSubmit(transaction.to_bytes());
console.log('Transaction successfully submitted: '+hash);
`
Let me add some explanations:
Current implementation of add_inputs_from
doesn't take into account change, add_change_if_needed
calculates also fee and if (outputs + fee) < total_inputs_value it will try to add a change output. But each output has minimal ada value there is a case when your ((total_outputs_value + fee) - (total_inputs)) is less than minimal ada for change output and in this case all leftovers will be added into fee because it is impossible to create change output due insufficient ada.
And seems it is your case with 2 ada inputs and 1 ada output
Hi again, yes its related with ADA amounts, so everything its OK! Thanks for your help! Best Regards
I use this library and blockfrost to submit the transaction, but i detect when an address have 2 inputs of 1 ADA, and after send 1 ADA to another address, the fee of this transaction is 1 ADA.
I have created an example here: https://preview.beta.explorer.cardano.org/en/address/addr_test1qqu0cf4zt3mklfdjyzada4cxddspkdyd7e5k9rsrg07et6j4agylhzua083ngsellerp0t5s7up3swfslg5heqwn7v5q8vrpjq
2 inputs of 1 ADA 1 output of 2 ADA (1 ADA sent + 1 ADA fee)
this is my code...
const bip39 = require('bip39'); const Blockfrost = require('@blockfrost/blockfrost-js'); const CardanoWasm = require('@emurgo/cardano-serialization-lib-nodejs');
exports.transaction = async (req, res) => {
};
const deriveAddressPrvKey = (bipPrvKey, mainnet) => { const networkId = mainnet ? CardanoWasm.NetworkInfo.mainnet().network_id() : CardanoWasm.NetworkInfo.testnet_preview().network_id(); const accountIndex = 0; const addressIndex = 0;
};
const harden = (num) => { return 0x80000000 + num; };
const mnemonicToPrivateKey = (mnemonic) => { const entropy = bip39.mnemonicToEntropy(mnemonic);
};
const composeTransaction = (address, outputAddress, outputAmount, utxos, params) => {
};
const signTransaction = (txBody, signKey) => { const txHash = CardanoWasm.hash_transaction(txBody); const witnesses = CardanoWasm.TransactionWitnessSet.new(); const vkeyWitnesses = CardanoWasm.Vkeywitnesses.new();
};