bloxbean / cardano-client-lib

Cardano client library in Java
https://cardano-client.dev
MIT License
118 stars 47 forks source link

transfer calculate fee Bug #16

Open LB6264 opened 2 years ago

LB6264 commented 2 years ago

hello My balance is 3.328735 ADA Same input parameter: 3 ADA

Yoroi Wallet costs 0.328735 ADA

Val fee = feeCalculationService. CalculateFee (paymentTransaction detailsParams, null) to calculate the charge is: 0.176017 ADA

Is there a bug in calculating the handling fee The end result is total transfer failure

LB6264 commented 2 years ago

Sorry, I am a Chinese developer, my English is not good, with the help of translation sentences, I hope you can help answer

satran004 commented 2 years ago

Are you doing a simple ADA transfer transaction ? Is there any native token in the utxo ?

If it's just a ADA transfer, 0.176 ADA txn fee looks normal. 0.328 ADA is definitely more for only ADA transaction.

You may want to reproduce the same on testnet.

LB6264 commented 2 years ago

{ "address": "addr1qxsjehae7esrs5xqhrqeph...............pjnh", "amount": [{ "unit": "lovelace", "quantity": "3328735" }], "stake_address": "stake1u8c3knx0cvma8hlz............m", "type": "shelley", "script": false }

min_fee_a:44, min_fee_b":155381, size:469 fee:176017 = min_fee_a*size + min_fee_b But the same address ,Yoroi Wallet costs 0.328735 ADA, I don't know what the problem is

LB6264 commented 2 years ago

9429c432d80b638677694d9605f465d the same address ,Yoroi Wallet costs 0.328735 ADA,

LB6264 commented 2 years ago

{ "error": "Bad Request", "message": "\"transaction submit error ShelleyTxValidationError ShelleyBasedEraAlonzo (ApplyTxError [UtxowFailure (WrappedShelleyEraFailure (UtxoFailure (ValueNotConservedUTxO (Value 12475948 (fromList [(PolicyID {policyID = ScriptHash \\"07309ed26f7636932617826b6991c7b6d8a7fa8fea66c5b9f020e687\\"},fromList [(\\"MYToken\\",56)])])) (Value 37427844 (fromList [(PolicyID {policyID = ScriptHash \\"07309ed26f7636932617826b6991c7b6d8a7fa8fea66c5b9f020e687\\"},fromList [(\\"MYToken\\",168)])])))))])\"", "status_code": 400 }

Above is the transaction of error, doubt and poundage calculation

satran004 commented 2 years ago

It looks like mismatch in total input and total output amount. Not sure but may be due to min ada amount requirement in utxo (due to native tokens). You may want to add few ADA to the account (2 or 3 ADA enough) and try the transaction again.

It could be a bug due to some edge case.

Is it possible to provide the utxo inputs used in this transaction ? Please change the txn hash or address in the utxo and paste here. Keep the amount including native tokens as it is. It will help me to debug the issue.

LB6264 commented 2 years ago

----------------------------------- [{"tx_hash":"f3c464be15a5e29a1a6d322c5cd040c87075d1cfc89d4b397568d14c0ba53cd9","tx_index":1,"output_index":1,"amount":[{"unit":"lovelace","quantity":"1452846"},{"unit":"07309ed26f7636932617826b6991c7b6d8a7fa8fea66c5b9f020e6874d59546f6b656e","quantity":"56"}],"block":"10974607187afacfeed5e3eaf4342e06bad0f26de438319eca18fa93f6a1541e","data_hash":null},{"tx_hash":"d60669420bc15d3f359b74f5177cd4035325c22f7a67cf96d466472acf145ecb","tx_index":0,"output_index":0,"amount":[{"unit":"lovelace","quantity":"10000000"}],"block":"eee6cd082d297f85406cff49f83aa3ebd1083dae3c8997ec03f4a17f49297bf6","data_hash":null}]

inputs

TransactionInput{transactionId=d60669420bc15d3f359b74f5177cd4035325c22f7a67cf96d466472acf145ecb, index=0} TransactionInput{transactionId=f3c464be15a5e29a1a6d322c5cd040c87075d1cfc89d4b397568d14c0ba53cd9, index=1}

outputs

TransactionOutput{address='addr_test1qq68lmuklk4rntgnvgy3t5g86kfyvhqm877km7593t4cvkm4nyle5utujmjxgnvm84mlql6cm7l3qhtm7yr5datr8j7qcvunl0', value=10400000} TransactionOutput{address='addr_test1qz3s0c370u8zzqn302nppuxl840gm6qdmjwqnxmqxme657ze964mar2m3r5jjv4qrsf62yduqns0tsw0hvzwar07qasqeamp0c', value=881273}

fee

0.171573 ADA

result

{"error":"Bad Request","message":"\"transaction submit error ShelleyTxValidationError ShelleyBasedEraAlonzo (ApplyTxError [UtxowFailure (WrappedShelleyEraFailure (UtxoFailure (OutputTooSmallUTxO [(Addr Testnet (KeyHashObj (KeyHash \\"a307e23e7f0e2102717aa610f0df3d5e8de80ddc9c099b6036f3aa78\\")) (StakeRefBase (KeyHashObj (KeyHash \\"592eabbe8d5b88e92932a01c13a511bc04e0f5c1cfbb04ee8dfe0760\\"))),Value 881273 (fromList [(PolicyID {policyID = ScriptHash \\"07309ed26f7636932617826b6991c7b6d8a7fa8fea66c5b9f020e687\\"},fromList [(\\"MYToken\\",56)])]),SNothing)])))])\"","status_code":400}

The above is the input and output of parameters, please help me to check the specific error reason,thank you There seems to be a problem with the combined payment, it is correct to input the quantity below 10ADA, but when it exceeds 10ADA, there is an error when it involves multiple accounts. I don't know if there is a Bug in the calculation rule of Cardano-client-lib, you need to help verify it according to the above parameters, thank you

satran004 commented 2 years ago

"when it exceeds 10ADA, there is an error when it involves multiple accounts"

So multiple senders and receivers ?

It could be a bug.

satran004 commented 2 years ago

Meanwhile, as a workaround, you may want to manually construct transaction (without TransactionHelper). Here's a sample https://gist.github.com/satran004/979e3da6bc819e9fe0568f40c2887196

Alternatively, you can also provide your own UtxoSelectionStrategy if you still want to use higher level api like TransactionHelper https://github.com/bloxbean/cardano-client-lib#utxoselectionstrategy

LB6264 commented 2 years ago

Hello, I still haven't solved this problem. Could you fix the bug when I transferred ADA

satran004 commented 2 years ago

Hi @LB6264 , I just started looking into the utxos sample you sent earlier. The 2nd output has value 881273 lovelace which is less than min ada required for an Utxo (~ 1 ada). That could be the reason for "OutputTooSmallUTxO". So looks like insufficient balance at sender address.

Also, the output (TransactionOutput) is missing the native token (unit: 07309ed26f7636932617826b6991c7b6d8a7fa8fea66c5b9f020e6874d59546f6b656e).

Can you please also attach your code sample where you are creating and posting the transaction ?

But in your 1st sample, the error message was different "ValueNotConservedUTxO".

Also, what do you mean by when multiple accounts involved ? Same mnemonic and multiple addresses at different indexes for the sender ?

BTW, by default the client-lib works with sender address at index 0 and only utxos specific to that address. It doesn't automatically scan all address at different indexes for the mnemonic. To use an address at different index, you have to use this constructor

Account(Network network, String mnemonic, int index)
LB6264 commented 2 years ago

This problem is easily replicated, for example:

I have 10 Adas in the balance, and I want to transfer all 10 Adas. Cardano-client-lib is used, but I cannot get the expected result.

suspend fun signerTransaction( sender: Account, receiver: String, amount: String, ethToken: EthToken ): String { LogCat.d("-------signerTransaction-----amount-----:$amount") return withIO { val paymentTransaction = PaymentTransaction() paymentTransaction.sender = sender paymentTransaction.receiver = receiver if (ethToken.isTokenCoin()) { paymentTransaction.amount = BigInteger.valueOf(amount.toLong()) paymentTransaction.unit = ethToken.tokenAddress } else { paymentTransaction.amount = BigInteger.valueOf(MathHelper.mul(amount, ONE_ADA.toString()).toLong()) paymentTransaction.unit = LOVELACE }

        //Calculate Time to Live
        val detailsParams = buildTransactionDetailsParams()
        //Calculate fee
        val fee = feeCalculationService.calculateFee(paymentTransaction, detailsParams, null)
        paymentTransaction.fee = fee
        LogCat.d("手续费:${paymentTransaction.fee}")
        val result: Result<TransactionResult> = transactionHelperService.transfer(
            paymentTransaction,
            detailsParams
        ) as Result<TransactionResult>

// LogCat.d("-----------------${result}") // LogCat.d("-----------------${Numeric.toHexString(result.value.signedTxn)}") val hash = result.value.transactionId LogCat.d("--------交易---------$hash") if (!waitForTransaction(hash)) { withMain { //保存到缓存 val info = TradeInfo() info.timeStamp = (System.currentTimeMillis() / 1000).toString() info.hash = hash info.itemType = 1 info.inputsAddress.add(sender.baseAddress()) info.outputsAddress.add(receiver) info.value = paymentTransaction.amount.toString() info.fee = paymentTransaction.fee.toString().parseDouble()

                EthTradeAgent.saveConfirmTradeCache(ethToken, info)
            }
        }
        result.value.transactionId
    }

}
LB6264 commented 2 years ago

testnet mnemonic: cruise express sleep donate goat jeans thing wage lumber drill pudding fiction session weekend kite manage indicate any together news peanut basic poem ordinary

The balance 11.664970 ADA is enough, transfer 11ADA, there is also a mistake,See the picture below for details e8cd027efe9be552ec8ce9c8ef6adda

satran004 commented 2 years ago

@LB6264 Thanks. Will check today.

prediator commented 2 years ago

@satran004 i am facing similar issue.

Address:- addr_test1qrark97feve329u6n0w7xr73e5mu8llvs8nl83cu3x8k4peseqdyq0sxh5x6u50dsqkn8jzuymn2v64ztqr7g0auvpps697cqa

Using the same sample code provided in the cardano-client-examples to mint asset. I am not able to mint it. It throws ValueNotConservedUTxO

Keys keys = KeyGenUtil.generateKey();

VerificationKey vkey = keys.getVkey();
SecretKey skey = keys.getSkey();

ScriptPubkey scriptPubkey = ScriptPubkey.create(vkey);
String policyId = null;

policyId = scriptPubkey.getPolicyId();

assets.setMetaData(policyId);

MultiAsset multiAsset = new MultiAsset();
multiAsset.setPolicyId(policyId);
Asset asset = new Asset(HexUtil.encodeHexString(request.getName().getBytes(StandardCharsets.UTF_8)),
        BigInteger.ONE);
multiAsset.getAssets().add(asset);

MintTransaction mintTransaction = MintTransaction.builder().sender(sender)
        .receiver(request.getReceiverAddress()).mintAssets(Arrays.asList(multiAsset))
        .policyScript(scriptPubkey).policyKeys(Arrays.asList(skey)).build();

String meta = adaWalletService.buildNftJson(request, policyId);

Metadata metadata = JsonNoSchemaToMetadataConverter.jsonToCborMetadata(meta);

BigInteger fee = backendService.getFeeCalculationService().calculateFee(mintTransaction,
        TransactionDetailsParams.builder().ttl(adaWalletService.getTtl(backendService)).build(),
        metadata);

mintTransaction.setFee(fee);

Result<TransactionResult> result = backendService.getTransactionHelperService().mintToken(
        mintTransaction,
        TransactionDetailsParams.builder().ttl(adaWalletService.getTtl(backendService)).build(),
        metadata);

UTXO's

[
    {
        "tx_hash": "527aa2dcb78a5d64219960ecad7bc0c09aa38ee46ed54df73bde29baed1dea11",
        "tx_index": 0,
        "output_index": 0,
        "amount": [
            {
                "unit": "lovelace",
                "quantity": "3247042"
            },
            {
                "unit": "5572ae9a3540ddafd3c4fb8553cf77187f21123f003a27676d6fe25c54657374",
                "quantity": "1"
            },
            {
                "unit": "61a6c9c9bb5f44a0f7435f5de7972fea764cd59f6ae1630a2664710b54657374",
                "quantity": "1"
            }
        ],
        "block": "4906f9d49950715392688e3c7ed8995a4ed7e8e5d03b1ac409d4b94a8ebe44ad",
        "data_hash": null
    },
    {
        "tx_hash": "527aa2dcb78a5d64219960ecad7bc0c09aa38ee46ed54df73bde29baed1dea11",
        "tx_index": 1,
        "output_index": 1,
        "amount": [
            {
                "unit": "lovelace",
                "quantity": "1444443"
            },
            {
                "unit": "a8c6dadd99bd0cb546bbf02e4c5934962b71a7d8e5f482ef8a17727754657374",
                "quantity": "1"
            }
        ],
        "block": "4906f9d49950715392688e3c7ed8995a4ed7e8e5d03b1ac409d4b94a8ebe44ad",
        "data_hash": null
    },
    {
        "tx_hash": "ce2de0809604679ac8d1f569a734520222766c0e060ae93b47078aff90666f53",
        "tx_index": 0,
        "output_index": 0,
        "amount": [
            {
                "unit": "lovelace",
                "quantity": "1000000"
            }
        ],
        "block": "1d2758fd73de5a7ad76fbae627d714317def29e7f8b9a14115fb47b24679e58e",
        "data_hash": null
    },
    {
        "tx_hash": "5ea56fc0e95ced066af439a629aa72220768357daaee25c9fb368e759a3752e1",
        "tx_index": 0,
        "output_index": 0,
        "amount": [
            {
                "unit": "lovelace",
                "quantity": "1000000"
            }
        ],
        "block": "fcbd88856f1eed9431df5c14ae42cd153a9ee16b16c85d4ded3c83d76b638dd5",
        "data_hash": null
    }
]
satran004 commented 2 years ago

The balance 11.664970 ADA is enough, transfer 11ADA, there is also a mistake,See the picture below for details

@LB6264, Thanks for the account details. I tried it.

So, when you are sending out 11 ADA, there is not enough ADA for the change output which goes back to sender.

So for example, in this case, there are two transaction outputs :-

  1. receiver - 11 ADA
  2. change address (sender) - ~ 0.494970 ADA (after fee deduction)

Also, the input utxo has a native token, so the minimum amount needed in change address (#2) is around 1.4 ADA (protocol requirement). But as only 0.494 ADA available it fails. So any transfer above ~10.2 will fail.

But I think, there is also an issue in lib.

  1. During fee calculation, lib creates an actual transaction and computes the fee based on the txn size. But because of this edge scenario, fee computation fails with "Insufficient utxo amount error". I think, fee computation should happen without any error. So that, if you want to send all the ADA from an account, it can be computed in the calling application.

  2. Lib should provide a better way to send all the ADA from sender account to another (if there is no native token)

Currently, we have to keep minimum ~1.x ADA in sender acc. You can not make the sender account empty using client-lib.

You can check this address addr_test1qz3s0c370u8zzqn302nppuxl840gm6qdmjwqnxmqxme657ze964mar2m3r5jjv4qrsf62yduqns0tsw0hvzwar07qasqeamp0c

Let me think how we can fix this.

But as I had mentioned earlier, you can programmatically build Transaction using TransactionInput/Ouput lower level apis if you want better control. You don't need to go through higher level apis like TransactionHelper. https://gist.github.com/satran004/979e3da6bc819e9fe0568f40c2887196

satran004 commented 2 years ago

@prediator please check the above comment if it applies to your issue.

Also, it looks like you have 3 tokens in that address. The min ada requirement increases with additional native tokens in utxo.

LB6264 commented 2 years ago

@satran004 Thank you for your patient solution. I have tried to use programmatically build Transaction using TransactionInput/Ouput lower level apis, but there will be problems when calculating edge conditions and bringing native tokens. I hope you can fix this problem as soon as possible in lib.

prediator commented 2 years ago

@satran004

Thanks for the detail explanation.

Is there any way from the lib to get the exact minada required for a native tokens minting, so that we can prompt user to add sufficient ADA to the wallet.

satran004 commented 2 years ago

@LB6264 I have done a partial fix for now. You need to build the project locally to try out the fix.

Fee calculation should not throw any error now. Also, if there is no native tokens available at sender address, you should be able to transfer all ADA from sender address to receiver. But for that you need to calculate the transfer amount in your application. First calculate the fee and then amount = balance - fee. Set the calculated amount in the PaymentTransaction.amount(). Please refer to the below code snippet.

Note: - But, if you are sending out partial ADA amount, make sure the remaining amount at sender address is greater than minimum ADA amount (~1.4 ADA) after transfer.

But if you have one or more native tokens at the sender address, you first need to transfer out all native tokens and then only you can send out all remaining ADA. I will check if these two steps could be simplified.

Let me know if you find any issue. I will continue with more testing.

 PaymentTransaction paymentTransaction =
                PaymentTransaction.builder()
                        .sender(sender)
                        .receiver(receiver)
                        .amount(BigInteger.valueOf(0))  //Any amount just for fee calculation
                        .unit(LOVELACE)
                        .build();

        long ttl = blockService.getLastestBlock().getValue().getSlot() + 1000;
        TransactionDetailsParams detailsParams =
                TransactionDetailsParams.builder()
                        .ttl(ttl)
                        .build();

        BigInteger fee = feeCalculationService.calculateFee(paymentTransaction, detailsParams
                , null);

        paymentTransaction.setFee(fee);

        BigInteger balance = new BigInteger("11500000");
        BigInteger amtToTransfer = balance.subtract(paymentTransaction.getFee());
        paymentTransaction.setAmount(amtToTransfer); //Set the remaining amount after fee

        Result<TransactionResult> result = transactionHelperService.transfer(paymentTransaction, detailsParams);
satran004 commented 2 years ago

@satran004

Thanks for the detail explanation.

Is there any way from the lib to get the exact minada required for a native tokens minting, so that we can prompt user to add sufficient ADA to the wallet.

@prediator You can check MinAdaCalculator class. But you need to create TransactionOutput object manually. Right now, there is no high level api using MintTransaction api.

https://github.com/bloxbean/cardano-client-lib/blob/master/src/main/java/com/bloxbean/cardano/client/common/MinAdaCalculator.java

Check the test cases for usage :

https://github.com/bloxbean/cardano-client-lib/blob/master/src/test/java/com/bloxbean/cardano/client/common/MinAdaCalculatorTest.java