cardano-client-lib
cardano-client-lib copied to clipboard
coinselection when wallet amount is close to output
scenario:
wallet has 3ADA, lovelace 3000000
building a transaction with two outputs, one that mints one NFT and then sends one ada, lovelace 1000000 to another address. Getting an error com.bloxbean.cardano.client.api.exception.InsufficientBalanceException: Not enough funds for [{lovelace=159310}]
coin selection algo LargestFirstUtxoSelectionStrategy, although I have tried randomImprov, it results in a different error
now the strange thing is if the wallet starts with 5000000 lovelace, same outputs the transaction goes through.
it could be straight cardano issue and not library related, although I thought to try here first
@kang-chen Can you please provide an example of transaction building code to reproduce the error?
yeap this is the gist of it, replaced destination address and mnenomic strings:
https://gist.github.com/kang-chen/c9219417cc56e13698549182318fd0cc
Thanks @kang-chen . I will check
@kang-chen I have found the root cause of this issue. This is an edge case scenario that can be fixed by optimizing the generated outputs. In your example, you will need around 3.2 ADA at the sender's address.
Let me explain the root cause.
The current implementation of TxBuilder creates transaction outputs for all application-defined outputs (1 ADA to the fee address, 1 token to the receiver's address). Additionally, it creates a change output for the sender's address.
When the fee address is not equal to the change address, there will be 3 outputs, and each output needs a minimum of 1 ADA and a transaction fee. Therefore, the total ADA required at the sender's address will be more than 3 ADA.
However, when the fee address is the same as the change address, ideally, the fee output and the change output should be merged so that it doesn't require a minimum ADA for one additional output.
Currently, change output merging is only supported before balanceTx, but it is not done during the input selection phase. A simple optimization in createFromSender
to merge the change output with any output that has the same address should fix this issue.
I will try to address this in the next release.
Thanks @satran004 for the detailed explanation, going to test it tomorrow. Want me to close the ticket or leave it open for reference in the next release?
Let’s keep it open
Tested, with 3200000 lovelace it doesn't work but 3500000 lovelace works. Can test further but it's trial and error.
Still using LargestFirstUtxoSelectionStrategy for coinSelection.
@kang-chen This issue has been fixed. If you want to test, you can use the current snapshot version. 0.5.0-alpha.5-SNAPSHOT
Please check the README page to find out how to configure for snapshot release. For fix/enhancements detail, you can check PR #290
Hey @satran004 so I had a chance to test today with:
implementation 'com.bloxbean.cardano:cardano-client-lib:0.5.0-alpha.5-SNAPSHOT'
implementation 'com.bloxbean.cardano:cardano-client-backend-blockfrost:0.5.0-alpha.5-SNAPSHOT'
neither 2.4 or 2.5 ADA worked, which should be enough to cover the minting Ada attachment and the 1.0 Ada output to external address. Still using LargestFirstUtxoSelectionStrategy.
error (with 2.5 ADA)
com.bloxbean.cardano.client.api.exception.InsufficientBalanceException: Not enough funds for [{lovelace=607590}]
I'll take a look at your PR but not sure if I can help.
Thanks @kang-chen for trying it out.
I've quickly verified it with both the Composable Function Api and Quick Tx using alpha.5. It appears to work for me with 2.5 Ada.
https://preprod.cardanoscan.io/transaction/4e7e502e71c96a1a4f1d7a15002cf739032c8777f102fdd5f000b597443ad414?tab=utxo
https://preprod.cardanoscan.io/transaction/2aa33e3daff4600fc5627e526ddc6193ad8d130b90c599bc562d77e872b765aa
Here are my examples for Composable Function and Quick Tx
Composable Functions
public void transfer() throws Exception {
String senderMnemonic = "xxxxxxx";
Account sender = new Account(Networks.testnet(), senderMnemonic);
String senderAddress = sender.baseAddress();
String receiver1 = "addr_test1qqwpl7h3g84mhr36wpetk904p7fchx2vst0z696lxk8ujsjyruqwmlsm344gfux3nsj6njyzj3ppvrqtt36cp9xyydzqzumz82";
Policy policy = PolicyUtil.createMultiSigScriptAllPolicy("policy-1", 1);
//NFT-1
MultiAsset multiAsset1 = new MultiAsset();
multiAsset1.setPolicyId(policy.getPolicyId());
Asset asset = new Asset("TestNFT", BigInteger.valueOf(1));
multiAsset1.getAssets().add(asset);
//Define outputs
//Output using Output class
Output mintOutput = Output.builder()
.address(senderAddress)
.policyId(policy.getPolicyId())
.assetName("TestNFT")
.qty(BigInteger.valueOf(1))
.build();
Output feeOutput = Output.builder()
.address(receiver1)
.assetName(LOVELACE)
.qty(adaToLovelace(1))
.build();
//Create TxBuilder function
TxBuilder txBuilder =
mintOutput.mintOutputBuilder()
.and(feeOutput.mintOutputBuilder())
.buildInputs(createFromSender(senderAddress, senderAddress))
.andThen(mintCreator(policy.getPolicyScript(), multiAsset1))
.andThen(balanceTx(senderAddress, 2));
//Build and sign transaction
Transaction signedTransaction = TxBuilderContext.init(utxoSupplier, protocolParamsSupplier)
.buildAndSign(txBuilder, signerFrom(sender).andThen(signerFrom(policy)));
Result<String> result = backendService.getTransactionService().submitTransaction(signedTransaction.serialize());
System.out.println(result);
}
QuickTx Api
void transfer() {
Policy policy = PolicyUtil.createMultiSigScriptAtLeastPolicy("test_policy", 1, 1);
String mn = "xxxxxx";
Account fromAcc = new Account(Networks.testnet(), mn);
QuickTxBuilder quickTxBuilder = new QuickTxBuilder(backendService);
Tx tx = new Tx()
.payToAddress(fromAcc.baseAddress(), Amount.ada(1))
.mintAssets(policy.getPolicyScript(), new Asset("MyAsset", BigInteger.valueOf(1)), receiver1)
.from(fromAcc.baseAddress());
Result<String> result = quickTxBuilder.compose(tx)
.withSigner(SignerProviders.signerFrom(fromAcc))
.withSigner(SignerProviders.signerFrom(policy))
.completeAndWait(System.out::println);
assertTrue(result.isSuccessful());
checkIfUtxoAvailable(result.getValue(), sender1Addr);
}
update: I will revisit this and check out the difference between our transaction code, haven't had the time just yet.
Thank you!
谢谢@kang-chen尝试一下。
我已经使用 alpha.5 通过 Composable Function Api 和 Quick Tx 快速验证了它。它似乎适合我使用 2.5 Ada。
https://preprod.cardanoscan.io/transaction/4e7e502e71c96a1a4f1d7a15002cf739032c8777f102fdd5f000b597443ad414?tab=utxo
https://preprod.cardanoscan.io/transaction/2aa33e3daff4600fc5627e526ddc6193ad8d130b90c599bc562d77e872b765aa
以下是我的可组合函数和 Quick Tx 示例
可组合函数
public void transfer() throws Exception { String senderMnemonic = "xxxxxxx"; Account sender = new Account(Networks.testnet(), senderMnemonic); String senderAddress = sender.baseAddress(); String receiver1 = "addr_test1qqwpl7h3g84mhr36wpetk904p7fchx2vst0z696lxk8ujsjyruqwmlsm344gfux3nsj6njyzj3ppvrqtt36cp9xyydzqzumz82"; Policy policy = PolicyUtil.createMultiSigScriptAllPolicy("policy-1", 1); //NFT-1 MultiAsset multiAsset1 = new MultiAsset(); multiAsset1.setPolicyId(policy.getPolicyId()); Asset asset = new Asset("TestNFT", BigInteger.valueOf(1)); multiAsset1.getAssets().add(asset); //Define outputs //Output using Output class Output mintOutput = Output.builder() .address(senderAddress) .policyId(policy.getPolicyId()) .assetName("TestNFT") .qty(BigInteger.valueOf(1)) .build(); Output feeOutput = Output.builder() .address(receiver1) .assetName(LOVELACE) .qty(adaToLovelace(1)) .build(); //Create TxBuilder function TxBuilder txBuilder = mintOutput.mintOutputBuilder() .and(feeOutput.mintOutputBuilder()) .buildInputs(createFromSender(senderAddress, senderAddress)) .andThen(mintCreator(policy.getPolicyScript(), multiAsset1)) .andThen(balanceTx(senderAddress, 2)); //Build and sign transaction Transaction signedTransaction = TxBuilderContext.init(utxoSupplier, protocolParamsSupplier) .buildAndSign(txBuilder, signerFrom(sender).andThen(signerFrom(policy))); Result<String> result = backendService.getTransactionService().submitTransaction(signedTransaction.serialize()); System.out.println(result); }
QuickTx API
void transfer() { Policy policy = PolicyUtil.createMultiSigScriptAtLeastPolicy("test_policy", 1, 1); String mn = "xxxxxx"; Account fromAcc = new Account(Networks.testnet(), mn); QuickTxBuilder quickTxBuilder = new QuickTxBuilder(backendService); Tx tx = new Tx() .payToAddress(fromAcc.baseAddress(), Amount.ada(1)) .mintAssets(policy.getPolicyScript(), new Asset("MyAsset", BigInteger.valueOf(1)), receiver1) .from(fromAcc.baseAddress()); Result<String> result = quickTxBuilder.compose(tx) .withSigner(SignerProviders.signerFrom(fromAcc)) .withSigner(SignerProviders.signerFrom(policy)) .completeAndWait(System.out::println); assertTrue(result.isSuccessful()); checkIfUtxoAvailable(result.getValue(), sender1Addr); }
Output output=Output. builder() is the handling fee set internally in buildAndSign. So how to set the handling fee before. The remaining balance after deducting the handling fee is fully transferred out. The current demo cannot achieve this
@satran004 @BlockByteMath I revisited this finally. Still can't process the transaction with 2.5 ADA unfortunately, the min amount that works seems to be 3.5ADA. I tried pretty much mimicking your transaction, making sure the version was correct snapshot. The only difference between our two txn mine had a couple of metadata data fields attached.
TxBuilder txBuilder = createFromMintOutput(mintOutput).and(feeOutput.outputBuilder()) .buildInputs(createFromSender(senderAddress, senderAddress)) .andThen(mintCreator(policy.getPolicyScript(), multiAsset)) .andThen(metadataProvider(nftMetadata)) .andThen(metadataProvider(transactionMetadata)) .andThen(balanceTx(senderAddress, 2));
Which I removed for the test.
I haven't tried preprod, I'm testing on preview.
What do you want to do, leave the issue until alpha.5 is released and then close?
Thanks @kang-chen . The latest release is 0.5.0-beta2 :) . It should be fixed in that. I will verify it again tomorrow.
Thanks @kang-chen . The latest release is 0.5.0-beta2 :) . It should be fixed in that. I will verify it again tomorrow.
Did a quick test with:
implementation 'com.bloxbean.cardano:cardano-client-lib:0.5.0-beta3-SNAPSHOT' implementation 'com.bloxbean.cardano:cardano-client-backend-blockfrost:0.5.0-beta3-SNAPSHOT'
I tried with LargestFirstUtxoSelectionStrategy and DefaultUtxoSelectionStrategyImpl on preview network
Still fails to process with 2.5 ADA
Just giving you a quick update as I was already setup to test. Thanks