Skip to content
Open
Show file tree
Hide file tree
Changes from 43 commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
df68997
WIP: update crypto services to use new simple fees
aderevets Oct 28, 2025
b21e8d6
WIP: update crypto services to use new simple fees
aderevets Oct 28, 2025
d713ee2
WIP: update crypto services to use new simple fees
aderevets Oct 28, 2025
b2a87cd
WIP: update crypto services to use new simple fees
aderevets Oct 28, 2025
31b6cf3
WIP: update crypto services to use new simple fees
aderevets Oct 28, 2025
95d1987
WIP: fix codacy alerts
aderevets Oct 29, 2025
96676c5
Merge branch 'main' into 20443/update-crypto-services-to-new-simple-fee
aderevets Oct 29, 2025
9c70a6c
WIP: remove duplicate convertion code
aderevets Oct 29, 2025
ea625cb
Merge remote-tracking branch 'origin/20443/update-crypto-services-to-…
aderevets Oct 29, 2025
498085f
WIP: remove duplicate convertion code
aderevets Oct 29, 2025
f7197f6
WIP: refactor tests
aderevets Oct 29, 2025
7ada0df
WIP: refactor tests
aderevets Oct 29, 2025
8cf90bf
WIP: refactor tests
aderevets Oct 29, 2025
182e806
WIP: add hapi test
aderevets Oct 29, 2025
6ac8c83
WIP: bump codecov
aderevets Oct 30, 2025
828151d
Merge branch 'refs/heads/main' into 20443/update-crypto-services-to-n…
aderevets Oct 30, 2025
636c859
WIP: fix meaningful codacy alerts
aderevets Oct 30, 2025
7824a07
WIP: fix meaningful codacy alerts
aderevets Oct 31, 2025
750075d
WIP: fix josh PR comments
aderevets Oct 31, 2025
9856c22
WIP: fix allowance calculations
aderevets Oct 31, 2025
f6548c3
WIP: revert crypto services. keep only crypto transfer with real values.
aderevets Nov 3, 2025
a4da07a
WIP: revert crypto services. keep only crypto transfer with real values.
aderevets Nov 3, 2025
c11e6c7
Merge remote-tracking branch 'origin/20443/update-crypto-services-to-…
aderevets Nov 3, 2025
e221be2
WIP: revert crypto services. keep only crypto transfer with real values.
aderevets Nov 3, 2025
fc2d7c7
WIP: revert crypto services. keep only crypto transfer with real values.
aderevets Nov 3, 2025
aaf7c4a
WIP: adjust crypto transfer parameters
aderevets Nov 4, 2025
4ca01b2
Merge branch 'main' into 20443/update-crypto-services-to-new-simple-fee
aderevets Nov 4, 2025
c66a9ab
WIP: adjust crypto transfer parameters
aderevets Nov 4, 2025
a73fa33
Merge branch 'main' into 20443/update-crypto-services-to-new-simple-fee
aderevets Nov 4, 2025
e289a2a
Merge branch 'main' into 20443/update-crypto-services-to-new-simple-fee
aderevets Nov 4, 2025
60ea61b
WIP: adjust crypto transfer parameters
aderevets Nov 4, 2025
5e324f4
Merge remote-tracking branch 'origin/20443/update-crypto-services-to-…
aderevets Nov 4, 2025
a40f433
WIP: bump code coverage
aderevets Nov 4, 2025
d74ac10
WIP: introduce SimpleFeeCalculator and refactor CryptoTransferHandler
aderevets Nov 5, 2025
59fbc50
WIP: introduce SimpleFeeCalculator and refactor CryptoTransferHandler
aderevets Nov 5, 2025
9ee5558
WIP: revert CryptoTransferHandler changes add CryptoDelete CryptoCreate
aderevets Nov 5, 2025
303e4de
WIP: revert CryptoTransferHandler changes add CryptoDelete CryptoCreate
aderevets Nov 5, 2025
4c2f38e
Merge branch 'main' into 20443/update-crypto-services-to-new-simple-fee
aderevets Nov 5, 2025
0a76237
WIP: revert CryptoTransferHandler changes add CryptoDelete CryptoCreate
aderevets Nov 5, 2025
fb94b6c
Merge remote-tracking branch 'origin/20443/update-crypto-services-to-…
aderevets Nov 5, 2025
3126115
WIP: revert CryptoTransferHandler changes add CryptoDelete CryptoCreate
aderevets Nov 5, 2025
5f80dd4
WIP: revert CryptoTransferHandler changes add CryptoDelete CryptoCreate
aderevets Nov 5, 2025
0c1db1e
Merge branch 'main' into 20443/update-crypto-services-to-new-simple-fee
aderevets Nov 5, 2025
0c14289
WIP: address PR comments
aderevets Nov 5, 2025
caaf245
Merge remote-tracking branch 'origin/20443/update-crypto-services-to-…
aderevets Nov 5, 2025
1ecfb14
Merge branch 'main' into 20443/update-crypto-services-to-new-simple-fee
aderevets Nov 5, 2025
da6fe20
Merge branch 'main' into 20443/update-crypto-services-to-new-simple-fee
aderevets Nov 6, 2025
1c48896
WIP: move calculators to separate package
aderevets Nov 6, 2025
bf94994
Merge remote-tracking branch 'origin/20443/update-crypto-services-to-…
aderevets Nov 6, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
// SPDX-License-Identifier: Apache-2.0
package com.hedera.node.app.spi.fees;

import static org.hiero.hapi.fees.FeeScheduleUtils.lookupExtraFee;

import edu.umd.cs.findbugs.annotations.NonNull;
import java.util.List;
import java.util.function.Function;
import org.hiero.hapi.support.fees.Extra;
import org.hiero.hapi.support.fees.ExtraFeeReference;
import org.hiero.hapi.support.fees.FeeSchedule;

/**
* Base class for simple fee calculators. Provides reusable utility methods for common fee
* calculation patterns per HIP-1261.
*
* <p>Subclasses implement {@link SimpleFeeCalculator} directly and can use the static utility
* methods provided here to avoid code duplication.
*/
public abstract class AbstractSimpleFeeCalculator implements SimpleFeeCalculator {

Check warning on line 20 in hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/fees/AbstractSimpleFeeCalculator.java

View check run for this annotation

Codecov / codecov/patch

hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/fees/AbstractSimpleFeeCalculator.java#L20

Added line #L20 was not covered by tests

/**
* Utility: Calculates extra fees from a list of ExtraFeeReferences.
* Subclasses provide a lambda/method reference to map Extra enum to usage count.
*
* <p>This implements the common pattern: base + sum(overage * feePerUnit) where overage =
* max(0, used - included).
*
* @param extras The list of extra fee references to process
* @param feeSchedule The fee schedule to lookup extra fee costs
* @param usageMapper Function that returns usage count for each Extra type
* @return Total extra fees
*/
protected static long calculateExtraFees(
@NonNull final List<ExtraFeeReference> extras,
@NonNull final FeeSchedule feeSchedule,
@NonNull final Function<Extra, Long> usageMapper) {
long total = 0;

Check warning on line 38 in hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/fees/AbstractSimpleFeeCalculator.java

View check run for this annotation

Codecov / codecov/patch

hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/fees/AbstractSimpleFeeCalculator.java#L38

Added line #L38 was not covered by tests
for (final var ref : extras) {
final long used = usageMapper.apply(ref.name());

Check warning on line 40 in hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/fees/AbstractSimpleFeeCalculator.java

View check run for this annotation

Codecov / codecov/patch

hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/fees/AbstractSimpleFeeCalculator.java#L40

Added line #L40 was not covered by tests
if (used > ref.includedCount()) {
final long overage = used - ref.includedCount();
total += overage * lookupExtraFee(feeSchedule, ref).fee();

Check warning on line 43 in hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/fees/AbstractSimpleFeeCalculator.java

View check run for this annotation

Codecov / codecov/patch

hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/fees/AbstractSimpleFeeCalculator.java#L42-L43

Added lines #L42 - L43 were not covered by tests
}
}
return total;

Check warning on line 46 in hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/fees/AbstractSimpleFeeCalculator.java

View check run for this annotation

Codecov / codecov/patch

hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/fees/AbstractSimpleFeeCalculator.java#L45-L46

Added lines #L45 - L46 were not covered by tests
}

/**
* Utility: Calculates network fee from node fee and multiplier.
* @param nodeFee The calculated node fee
* @param multiplier The network multiplier from fee schedule
* @return Network fee
*/
protected static long calculateNetworkFee(long nodeFee, int multiplier) {
return nodeFee * multiplier;

Check warning on line 56 in hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/fees/AbstractSimpleFeeCalculator.java

View check run for this annotation

Codecov / codecov/patch

hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/fees/AbstractSimpleFeeCalculator.java#L56

Added line #L56 was not covered by tests
}

/**
* Creates a builder for constructing usage mappers.
* Usage example:
* <pre>
* var usageMapper = usageBuilder()
* .withSignatures(context.numTxnSignatures())
* .withKeys(keyCount)
* .build();
* </pre>
*
* @return a new UsageBuilder instance
*/
protected static UsageBuilder usageBuilder() {
return new UsageBuilder();

Check warning on line 72 in hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/fees/AbstractSimpleFeeCalculator.java

View check run for this annotation

Codecov / codecov/patch

hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/fees/AbstractSimpleFeeCalculator.java#L72

Added line #L72 was not covered by tests
}

/**
* Fluent builder for creating Extra usage mappers per HIP-1261.
* Provides named methods for each Extra type to clearly document which extras
* a transaction uses.
*/
protected static final class UsageBuilder {
private final long[] counts = new long[Extra.values().length];

Check warning on line 81 in hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/fees/AbstractSimpleFeeCalculator.java

View check run for this annotation

Codecov / codecov/patch

hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/fees/AbstractSimpleFeeCalculator.java#L81

Added line #L81 was not covered by tests

private UsageBuilder() {}

Check warning on line 83 in hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/fees/AbstractSimpleFeeCalculator.java

View check run for this annotation

Codecov / codecov/patch

hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/fees/AbstractSimpleFeeCalculator.java#L83

Added line #L83 was not covered by tests

public UsageBuilder withSignatures(long count) {
counts[Extra.SIGNATURES.ordinal()] = count;
return this;

Check warning on line 87 in hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/fees/AbstractSimpleFeeCalculator.java

View check run for this annotation

Codecov / codecov/patch

hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/fees/AbstractSimpleFeeCalculator.java#L86-L87

Added lines #L86 - L87 were not covered by tests
}

public UsageBuilder withBytes(long count) {
counts[Extra.BYTES.ordinal()] = count;
return this;

Check warning on line 92 in hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/fees/AbstractSimpleFeeCalculator.java

View check run for this annotation

Codecov / codecov/patch

hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/fees/AbstractSimpleFeeCalculator.java#L91-L92

Added lines #L91 - L92 were not covered by tests
}

public UsageBuilder withKeys(long count) {
counts[Extra.KEYS.ordinal()] = count;
return this;

Check warning on line 97 in hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/fees/AbstractSimpleFeeCalculator.java

View check run for this annotation

Codecov / codecov/patch

hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/fees/AbstractSimpleFeeCalculator.java#L96-L97

Added lines #L96 - L97 were not covered by tests
}

public UsageBuilder withTokenTypes(long count) {
counts[Extra.TOKEN_TYPES.ordinal()] = count;
return this;

Check warning on line 102 in hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/fees/AbstractSimpleFeeCalculator.java

View check run for this annotation

Codecov / codecov/patch

hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/fees/AbstractSimpleFeeCalculator.java#L101-L102

Added lines #L101 - L102 were not covered by tests
}

public UsageBuilder withNftSerials(long count) {
counts[Extra.NFT_SERIALS.ordinal()] = count;
return this;

Check warning on line 107 in hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/fees/AbstractSimpleFeeCalculator.java

View check run for this annotation

Codecov / codecov/patch

hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/fees/AbstractSimpleFeeCalculator.java#L106-L107

Added lines #L106 - L107 were not covered by tests
}

public UsageBuilder withAccounts(long count) {
counts[Extra.ACCOUNTS.ordinal()] = count;
return this;

Check warning on line 112 in hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/fees/AbstractSimpleFeeCalculator.java

View check run for this annotation

Codecov / codecov/patch

hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/fees/AbstractSimpleFeeCalculator.java#L111-L112

Added lines #L111 - L112 were not covered by tests
}

public UsageBuilder withStandardFungibleTokens(long count) {
counts[Extra.STANDARD_FUNGIBLE_TOKENS.ordinal()] = count;
return this;

Check warning on line 117 in hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/fees/AbstractSimpleFeeCalculator.java

View check run for this annotation

Codecov / codecov/patch

hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/fees/AbstractSimpleFeeCalculator.java#L116-L117

Added lines #L116 - L117 were not covered by tests
}

public UsageBuilder withStandardNonFungibleTokens(long count) {
counts[Extra.STANDARD_NON_FUNGIBLE_TOKENS.ordinal()] = count;
return this;

Check warning on line 122 in hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/fees/AbstractSimpleFeeCalculator.java

View check run for this annotation

Codecov / codecov/patch

hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/fees/AbstractSimpleFeeCalculator.java#L121-L122

Added lines #L121 - L122 were not covered by tests
}

public UsageBuilder withCustomFeeFungibleTokens(long count) {
counts[Extra.CUSTOM_FEE_FUNGIBLE_TOKENS.ordinal()] = count;
return this;

Check warning on line 127 in hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/fees/AbstractSimpleFeeCalculator.java

View check run for this annotation

Codecov / codecov/patch

hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/fees/AbstractSimpleFeeCalculator.java#L126-L127

Added lines #L126 - L127 were not covered by tests
}

public UsageBuilder withCustomFeeNonFungibleTokens(long count) {
counts[Extra.CUSTOM_FEE_NON_FUNGIBLE_TOKENS.ordinal()] = count;
return this;

Check warning on line 132 in hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/fees/AbstractSimpleFeeCalculator.java

View check run for this annotation

Codecov / codecov/patch

hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/fees/AbstractSimpleFeeCalculator.java#L131-L132

Added lines #L131 - L132 were not covered by tests
}

public UsageBuilder withCreatedAutoAssociations(long count) {
counts[Extra.CREATED_AUTO_ASSOCIATIONS.ordinal()] = count;
return this;

Check warning on line 137 in hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/fees/AbstractSimpleFeeCalculator.java

View check run for this annotation

Codecov / codecov/patch

hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/fees/AbstractSimpleFeeCalculator.java#L136-L137

Added lines #L136 - L137 were not covered by tests
}

public UsageBuilder withCreatedAccounts(long count) {
counts[Extra.CREATED_ACCOUNTS.ordinal()] = count;
return this;

Check warning on line 142 in hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/fees/AbstractSimpleFeeCalculator.java

View check run for this annotation

Codecov / codecov/patch

hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/fees/AbstractSimpleFeeCalculator.java#L141-L142

Added lines #L141 - L142 were not covered by tests
}

public UsageBuilder withCustomFee(long count) {
counts[Extra.CUSTOM_FEE.ordinal()] = count;
return this;

Check warning on line 147 in hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/fees/AbstractSimpleFeeCalculator.java

View check run for this annotation

Codecov / codecov/patch

hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/fees/AbstractSimpleFeeCalculator.java#L146-L147

Added lines #L146 - L147 were not covered by tests
}

public UsageBuilder withGas(long count) {
counts[Extra.GAS.ordinal()] = count;
return this;

Check warning on line 152 in hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/fees/AbstractSimpleFeeCalculator.java

View check run for this annotation

Codecov / codecov/patch

hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/fees/AbstractSimpleFeeCalculator.java#L151-L152

Added lines #L151 - L152 were not covered by tests
}

public UsageBuilder withAllowances(long count) {
counts[Extra.ALLOWANCES.ordinal()] = count;
return this;

Check warning on line 157 in hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/fees/AbstractSimpleFeeCalculator.java

View check run for this annotation

Codecov / codecov/patch

hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/fees/AbstractSimpleFeeCalculator.java#L156-L157

Added lines #L156 - L157 were not covered by tests
}

public UsageBuilder withAirdrops(long count) {
counts[Extra.AIRDROPS.ordinal()] = count;
return this;

Check warning on line 162 in hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/fees/AbstractSimpleFeeCalculator.java

View check run for this annotation

Codecov / codecov/patch

hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/fees/AbstractSimpleFeeCalculator.java#L161-L162

Added lines #L161 - L162 were not covered by tests
}

/**
* Builds the usage mapper function.
* @return Function that maps Extra types to their usage counts
*/
public Function<Extra, Long> build() {
return extra -> counts[extra.ordinal()];

Check warning on line 170 in hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/fees/AbstractSimpleFeeCalculator.java

View check run for this annotation

Codecov / codecov/patch

hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/fees/AbstractSimpleFeeCalculator.java#L170

Added line #L170 was not covered by tests
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
// SPDX-License-Identifier: Apache-2.0
/*
* Copyright (C) 2025 Hedera Hashgraph, LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.hedera.node.app.spi.fees;

import com.hedera.hapi.node.base.AccountID;
import com.hedera.hapi.node.base.TokenID;
import com.hedera.hapi.node.state.token.Account;
import com.hedera.hapi.node.state.token.Token;
import com.hedera.hapi.node.transaction.Query;
import com.hedera.hapi.node.transaction.TransactionBody;
import com.hedera.pbj.runtime.io.buffer.Bytes;
import com.swirlds.config.api.Configuration;
import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.Nullable;
import java.util.Optional;
import java.util.OptionalInt;
import org.hiero.hapi.fees.FeeResult;

/** Calculates transaction and query fees. Null context = approximate, non-null = exact using state. */
public interface SimpleFeeCalculator {

/** Provides state access for exact fee calculation. */
interface TxContext {
int cryptoVerificationsRequired();

boolean existsAccountWith(@NonNull Bytes alias);

boolean existsAccount(@NonNull AccountID accountId);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need these two ? Can't we say if getAccount is null it doesn't exist?


@NonNull
Optional<Account> getAccount(@NonNull AccountID accountId);

boolean tokenHasCustomFees(@NonNull TokenID tokenId);

@NonNull
OptionalInt customFeeCount(@NonNull TokenID tokenId);

@NonNull
Optional<Token> getToken(@NonNull TokenID tokenId);

boolean existsTokenRelation(@NonNull AccountID accountId, @NonNull TokenID tokenId);

@NonNull
Configuration configuration();

int numTxnSignatures();

@NonNull
FeeCalculatorFactory feeCalculatorFactory();
}

/** Provides state access for exact query fee calculation. */
interface QueryContext {
int cryptoVerificationsRequired();
}

@NonNull
FeeResult calculateTxFee(@NonNull TransactionBody body, @Nullable TxContext context);

@NonNull
FeeResult calculateQueryFee(@NonNull Query query, @Nullable QueryContext context);
}
Original file line number Diff line number Diff line change
Expand Up @@ -132,8 +132,12 @@
}

return switch (feeContext.body().data().kind()) {
case CONSENSUS_CREATE_TOPIC, CONSENSUS_SUBMIT_MESSAGE, CONSENSUS_UPDATE_TOPIC, CONSENSUS_DELETE_TOPIC ->
true;
case CONSENSUS_CREATE_TOPIC,
CONSENSUS_SUBMIT_MESSAGE,
CONSENSUS_UPDATE_TOPIC,
CONSENSUS_DELETE_TOPIC,
CRYPTO_DELETE,
CRYPTO_CREATE_ACCOUNT -> true;

Check warning on line 140 in hedera-node/hedera-app/src/main/java/com/hedera/node/app/workflows/dispatcher/TransactionDispatcher.java

View check run for this annotation

Codecov / codecov/patch

hedera-node/hedera-app/src/main/java/com/hedera/node/app/workflows/dispatcher/TransactionDispatcher.java#L140

Added line #L140 was not covered by tests
default -> false;
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,19 @@
},

"extras": [
{ "name": "SIGNATURES", "fee": 60000000 },
{ "name": "BYTES", "fee": 300 },
{ "name": "KEYS", "fee": 2200000 },
{ "name": "TOKEN_TYPES", "fee": 3 },
{ "name": "NFT_SERIALS", "fee": 1 },
{ "name": "ACCOUNTS", "fee": 3 },
{ "name": "STANDARD_FUNGIBLE_TOKENS", "fee": 3 },
{ "name": "STANDARD_NON_FUNGIBLE_TOKENS", "fee": 3 },
{ "name": "CUSTOM_FEE_FUNGIBLE_TOKENS", "fee": 3 },
{ "name": "CUSTOM_FEE_NON_FUNGIBLE_TOKENS", "fee": 3 },
{ "name": "CREATED_AUTO_ASSOCIATIONS", "fee": 3 },
{ "name": "CREATED_ACCOUNTS", "fee": 3 },
{ "name": "CUSTOM_FEE", "fee": 1 },
{ "name": "SIGNATURES", "fee": 1000000 },
{ "name": "BYTES", "fee": 110 },
{ "name": "KEYS", "fee": 100000000 },
{ "name": "TOKEN_TYPES", "fee": 0 },
{ "name": "NFT_SERIALS", "fee": 8900000 },
{ "name": "ACCOUNTS", "fee": 0 },
{ "name": "STANDARD_FUNGIBLE_TOKENS", "fee": 9000000 },
{ "name": "STANDARD_NON_FUNGIBLE_TOKENS", "fee": 100000 },
{ "name": "CUSTOM_FEE_FUNGIBLE_TOKENS", "fee": 19000000 },
{ "name": "CUSTOM_FEE_NON_FUNGIBLE_TOKENS", "fee": 10100000 },
{ "name": "CREATED_AUTO_ASSOCIATIONS", "fee": 0 },
{ "name": "CREATED_ACCOUNTS", "fee": 0 },
{ "name": "CUSTOM_FEE", "fee": 0 },
{ "name": "GAS", "fee": 1 },
{ "name": "ALLOWANCES", "fee": 2000 },
{ "name": "AIRDROPS", "fee": 8800 }
Expand All @@ -51,6 +51,13 @@
{ "name": "KEYS", "includedCount": 1 }
]
},
{
"name": "CryptoDelete",
"baseFee": 5,
"extras": [
{ "name": "SIGNATURES", "includedCount": 1 }
]
},
{
"name": "CryptoTransfer",
"baseFee": 18,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
// SPDX-License-Identifier: Apache-2.0
package com.hedera.node.app.service.token.impl.handlers;

import static java.util.Objects.requireNonNull;
import static org.hiero.hapi.fees.FeeScheduleUtils.lookupServiceFee;

import com.hedera.hapi.node.base.HederaFunctionality;
import com.hedera.hapi.node.base.Key;
import com.hedera.hapi.node.base.SubType;
import com.hedera.hapi.node.transaction.Query;
import com.hedera.hapi.node.transaction.TransactionBody;
import com.hedera.node.app.spi.fees.AbstractSimpleFeeCalculator;
import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.Nullable;
import org.hiero.hapi.fees.FeeResult;

/** Calculates CryptoCreate fees. Per HIP-1261, uses SIGNATURES and KEYS extras. */
public class CryptoCreateFeeCalculator extends AbstractSimpleFeeCalculator {

Check warning on line 18 in hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/handlers/CryptoCreateFeeCalculator.java

View check run for this annotation

Codecov / codecov/patch

hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/handlers/CryptoCreateFeeCalculator.java#L18

Added line #L18 was not covered by tests

@Override
@NonNull
public FeeResult calculateTxFee(@NonNull final TransactionBody body, @Nullable final TxContext context) {
requireNonNull(body, "body");
final var op = body.cryptoCreateAccountOrThrow();

Check warning on line 24 in hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/handlers/CryptoCreateFeeCalculator.java

View check run for this annotation

Codecov / codecov/patch

hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/handlers/CryptoCreateFeeCalculator.java#L23-L24

Added lines #L23 - L24 were not covered by tests
// Count keys in the account being created
final long keyCount = op.hasKey() ? countKeys(op.key()) : 0L;

// Build usage mapper - fluent API makes fee structure explicit
final var usageMapper = usageBuilder()
.withSignatures(context.numTxnSignatures())
.withKeys(keyCount)
.build();

Check warning on line 32 in hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/handlers/CryptoCreateFeeCalculator.java

View check run for this annotation

Codecov / codecov/patch

hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/handlers/CryptoCreateFeeCalculator.java#L29-L32

Added lines #L29 - L32 were not covered by tests

// Get fee schedule
final var feeCalculator = context.feeCalculatorFactory().feeCalculator(SubType.DEFAULT);
final var feeSchedule = feeCalculator.getSimpleFeesSchedule();
final var serviceDef = lookupServiceFee(feeSchedule, HederaFunctionality.CRYPTO_CREATE);

Check warning on line 37 in hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/handlers/CryptoCreateFeeCalculator.java

View check run for this annotation

Codecov / codecov/patch

hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/handlers/CryptoCreateFeeCalculator.java#L35-L37

Added lines #L35 - L37 were not covered by tests

// Calculate fees
long nodeFee = feeSchedule.node().baseFee()
+ calculateExtraFees(feeSchedule.node().extras(), feeSchedule, usageMapper);

Check warning on line 41 in hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/handlers/CryptoCreateFeeCalculator.java

View check run for this annotation

Codecov / codecov/patch

hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/handlers/CryptoCreateFeeCalculator.java#L40-L41

Added lines #L40 - L41 were not covered by tests

long serviceFee = serviceDef.baseFee() + calculateExtraFees(serviceDef.extras(), feeSchedule, usageMapper);

Check warning on line 43 in hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/handlers/CryptoCreateFeeCalculator.java

View check run for this annotation

Codecov / codecov/patch

hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/handlers/CryptoCreateFeeCalculator.java#L43

Added line #L43 was not covered by tests

long networkFee = calculateNetworkFee(nodeFee, feeSchedule.network().multiplier());

Check warning on line 45 in hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/handlers/CryptoCreateFeeCalculator.java

View check run for this annotation

Codecov / codecov/patch

hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/handlers/CryptoCreateFeeCalculator.java#L45

Added line #L45 was not covered by tests

// Return result
final var result = new FeeResult();
result.node = nodeFee;
result.network = networkFee;
result.service = serviceFee;
return result;

Check warning on line 52 in hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/handlers/CryptoCreateFeeCalculator.java

View check run for this annotation

Codecov / codecov/patch

hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/handlers/CryptoCreateFeeCalculator.java#L48-L52

Added lines #L48 - L52 were not covered by tests
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can all this code be moved to AbstractSimpleFeeCalculator ? Since it will be the same for every handler

}

/**
* Counts all keys including nested ones in threshold/key lists.
*/
private long countKeys(Key key) {
return switch (key.key().kind()) {
case ED25519, ECDSA_SECP256K1, ECDSA_384 -> 1L;

Check warning on line 60 in hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/handlers/CryptoCreateFeeCalculator.java

View check run for this annotation

Codecov / codecov/patch

hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/handlers/CryptoCreateFeeCalculator.java#L60

Added line #L60 was not covered by tests
case THRESHOLD_KEY ->
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We support other key types also right ?

Copy link
Contributor Author

@aderevets aderevets Nov 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, we support other key types (like CONTRACT_ID, DELEGATABLE_CONTRACT_ID, RSA_3072) at the API level, but they're intentionally not counted for fee calculation. The current implementation only counts cryptographic keys (ED25519, ECDSA_SECP256K1, ECDSA_384) and recursively processes THRESHOLD_KEY and KEY_LIST. Contract ID keys return 0 because they don't contain actual key material—they're authorization references. Old fee model charges based on cryptographic complexity, not authorization complexity (as I understand). ECDSA_384 and RSA_3072 are deprecated but still handled for backward compatibility.

key.thresholdKeyOrThrow().keys().keys().stream()
.mapToLong(this::countKeys)
.sum();

Check warning on line 64 in hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/handlers/CryptoCreateFeeCalculator.java

View check run for this annotation

Codecov / codecov/patch

hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/handlers/CryptoCreateFeeCalculator.java#L62-L64

Added lines #L62 - L64 were not covered by tests
case KEY_LIST ->
key.keyListOrThrow().keys().stream().mapToLong(this::countKeys).sum();
default -> 0L;

Check warning on line 67 in hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/handlers/CryptoCreateFeeCalculator.java

View check run for this annotation

Codecov / codecov/patch

hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/handlers/CryptoCreateFeeCalculator.java#L66-L67

Added lines #L66 - L67 were not covered by tests
};
}

@Override
@NonNull
public FeeResult calculateQueryFee(@NonNull final Query query, @Nullable final QueryContext context) {
throw new UnsupportedOperationException("Query fee calculation not implemented for CRYPTO_CREATE");

Check warning on line 74 in hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/handlers/CryptoCreateFeeCalculator.java

View check run for this annotation

Codecov / codecov/patch

hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/handlers/CryptoCreateFeeCalculator.java#L74

Added line #L74 was not covered by tests
}
}
Loading
Loading