Skip to content

Commit 8d0c719

Browse files
committed
Avoid double-charging a LoanSet inner's counterparty in a Batch, since it is already counted among the BatchSigners
1 parent 2aca6b7 commit 8d0c719

2 files changed

Lines changed: 29 additions & 4 deletions

File tree

xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/client/fees/FeeUtils.java

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,7 @@ public static ComputedNetworkFees computeFee(final FeeParams feeParams) {
197197
final long signatureUnits = signatureUnits(feeParams);
198198
final long feeUnits = transactionType == TransactionType.LOAN_PAY ?
199199
signatureUnits * feeParams.loanPaymentFeeIncrements().longValue() :
200-
signatureUnits + surchargeUnits(feeParams, transaction);
200+
signatureUnits + surchargeUnits(feeParams, transaction, false);
201201

202202
return scaleByBaseFees(baseFees, feeUnits);
203203
}
@@ -229,7 +229,7 @@ private static ComputedNetworkFees computeFeeForBatch(
229229
} else {
230230
// An inner transaction never carries signatures or fee sponsorship, so it costs one base fee plus whatever
231231
// surcharge its type attracts.
232-
feeUnits += 1L + surchargeUnits(feeParams, inner);
232+
feeUnits += 1L + surchargeUnits(feeParams, inner, true);
233233
}
234234
}
235235

@@ -284,10 +284,12 @@ private static long signatureUnits(final FeeParams feeParams) {
284284
* @param feeParams The {@link FeeParams} being applied.
285285
* @param transaction The {@link Transaction} being priced, which may be an inner transaction of a Batch rather than
286286
* {@link FeeParams#transaction()} itself.
287+
* @param isInner {@code true} when {@code transaction} is a Batch inner, whose signatures are counted by the
288+
* outer Batch rather than by the inner itself.
287289
*
288290
* @return A number of extra base fees, which is zero for most transaction types.
289291
*/
290-
private static long surchargeUnits(final FeeParams feeParams, final Transaction transaction) {
292+
private static long surchargeUnits(final FeeParams feeParams, final Transaction transaction, final boolean isInner) {
291293
final TransactionType transactionType = transaction.transactionType();
292294

293295
if (CONFIDENTIAL_MPT_TRANSACTION_TYPES.contains(transactionType)) {
@@ -297,7 +299,8 @@ private static long surchargeUnits(final FeeParams feeParams, final Transaction
297299
return fulfillmentUnits((EscrowFinish) transaction);
298300
}
299301
if (transactionType == TransactionType.LOAN_SET) {
300-
return feeParams.counterpartySignatureCount().longValue();
302+
// Charge the counterparty only for a standalone LoanSet; a Batch inner's is counted via the BatchSigners.
303+
return isInner ? 0L : feeParams.counterpartySignatureCount().longValue();
301304
}
302305
return 0L;
303306
}

xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/client/fees/FeeUtilsTest.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -842,6 +842,15 @@ void batchPricesAMixOfConfidentialAndRegularInners() {
842842
);
843843
}
844844

845+
@Test
846+
void batchDoesNotDoubleChargeALoanSetInnerCounterparty() {
847+
// The counterparty is counted once among the batchSigners; the inner adds no counterparty surcharge, so 7 not 8.
848+
// 2 outer + 3 batchSigners {bob, carol, dave} + (1 payment + 1 loanSet) inners
849+
Batch batch = batch(ALICE, innerPayment(BOB, 1), innerLoanSet(CAROL, DAVE, 1));
850+
assertThat(batch.requiredSigners()).containsExactlyInAnyOrder(BOB, CAROL, DAVE);
851+
assertFeeUnits(paramsFor(batch), 7);
852+
}
853+
845854
@Test
846855
void standaloneConfidentialSendCostsTenBaseFees() {
847856
assertFeeUnits(paramsFor(innerConfidentialSend(ALICE, 1)), 10);
@@ -1057,6 +1066,19 @@ private LoanSet loanSet() {
10571066
.build();
10581067
}
10591068

1069+
private LoanSet innerLoanSet(final Address account, final Address counterparty, final int sequence) {
1070+
return LoanSet.builder()
1071+
.account(account)
1072+
.counterparty(counterparty)
1073+
.fee(XrpCurrencyAmount.ofDrops(0))
1074+
.sequence(UnsignedInteger.valueOf(sequence))
1075+
.flags(org.xrpl.xrpl4j.model.flags.LoanSetFlags.of(
1076+
org.xrpl.xrpl4j.model.flags.TransactionFlags.INNER_BATCH_TXN.getValue()))
1077+
.loanBrokerId(Hash256.of("C031EFE677CDEF1C5F43475B374A16F990EE184F76015CB7548D34B500F72BFB"))
1078+
.principalRequested(Amount.of("1000000"))
1079+
.build();
1080+
}
1081+
10601082
private LoanPay loanPay() {
10611083
return LoanPay.builder()
10621084
.account(ALICE)

0 commit comments

Comments
 (0)