use of com.hedera.mirror.common.domain.transaction.CustomFee in project hedera-mirror-node by hashgraph.
the class EntityRecordItemListener method insertCustomFees.
/**
* Inserts custom fees. Returns the list of collectors automatically associated with the newly created token if the
* custom fees are from a token create transaction
*
* @param customFeeList protobuf custom fee list
* @param consensusTimestamp consensus timestamp of the corresponding transaction
* @param isTokenCreate if the transaction with the custom fees is a token create
* @param tokenId the token id the custom fees are attached to
* @return A list of collectors automatically associated with the token if it's a token create transaction
*/
private Set<EntityId> insertCustomFees(List<com.hederahashgraph.api.proto.java.CustomFee> customFeeList, long consensusTimestamp, boolean isTokenCreate, EntityId tokenId) {
Set<EntityId> autoAssociatedAccounts = new HashSet<>();
CustomFee.Id id = new CustomFee.Id(consensusTimestamp, tokenId);
for (var protoCustomFee : customFeeList) {
EntityId collector = EntityId.of(protoCustomFee.getFeeCollectorAccountId());
CustomFee customFee = new CustomFee();
customFee.setId(id);
customFee.setCollectorAccountId(collector);
var feeCase = protoCustomFee.getFeeCase();
boolean chargedInAttachedToken;
switch(feeCase) {
case FIXED_FEE:
chargedInAttachedToken = parseFixedFee(customFee, protoCustomFee.getFixedFee(), tokenId);
break;
case FRACTIONAL_FEE:
// only FT can have fractional fee
parseFractionalFee(customFee, protoCustomFee.getFractionalFee());
chargedInAttachedToken = true;
break;
case ROYALTY_FEE:
// only NFT can have royalty fee, and fee can't be paid in NFT. Thus though royalty fee has a
// fixed fee fallback, the denominating token of the fixed fee can't be the NFT itself
parseRoyaltyFee(customFee, protoCustomFee.getRoyaltyFee(), tokenId);
chargedInAttachedToken = false;
break;
default:
log.error("Invalid CustomFee FeeCase {}", feeCase);
throw new InvalidDatasetException(String.format("Invalid CustomFee FeeCase %s", feeCase));
}
if (isTokenCreate && chargedInAttachedToken) {
// if it's from a token create transaction, and the fee is charged in the attached token, the attached
// token and the collector should have been auto associated
autoAssociatedAccounts.add(collector);
}
entityListener.onCustomFee(customFee);
}
if (customFeeList.isEmpty()) {
// for empty custom fees, add a single row with only the timestamp and tokenId.
CustomFee customFee = new CustomFee();
customFee.setId(id);
entityListener.onCustomFee(customFee);
}
return autoAssociatedAccounts;
}
use of com.hedera.mirror.common.domain.transaction.CustomFee in project hedera-mirror-node by hashgraph.
the class EntityRecordItemListenerTokenTest method deletedDbCustomFees.
private static List<CustomFee> deletedDbCustomFees(long consensusTimestamp, EntityId tokenId) {
CustomFee customFee = new CustomFee();
customFee.setId(new CustomFee.Id(consensusTimestamp, tokenId));
return List.of(customFee);
}
use of com.hedera.mirror.common.domain.transaction.CustomFee in project hedera-mirror-node by hashgraph.
the class EntityRecordItemListenerTokenTest method nonEmptyCustomFees.
private static List<CustomFee> nonEmptyCustomFees(long consensusTimestamp, EntityId tokenId, TokenType tokenType) {
List<CustomFee> customFees = new ArrayList<>();
CustomFee.Id id = new CustomFee.Id(consensusTimestamp, tokenId);
EntityId treasury = EntityId.of(PAYER);
CustomFee fixedFee1 = new CustomFee();
fixedFee1.setAmount(11L);
fixedFee1.setCollectorAccountId(FEE_COLLECTOR_ACCOUNT_ID_1);
fixedFee1.setId(id);
customFees.add(fixedFee1);
CustomFee fixedFee2 = new CustomFee();
fixedFee2.setAmount(12L);
fixedFee2.setCollectorAccountId(FEE_COLLECTOR_ACCOUNT_ID_2);
fixedFee2.setDenominatingTokenId(FEE_DOMAIN_TOKEN_ID);
fixedFee2.setId(id);
customFees.add(fixedFee2);
CustomFee fixedFee3 = new CustomFee();
fixedFee3.setAmount(13L);
fixedFee3.setCollectorAccountId(FEE_COLLECTOR_ACCOUNT_ID_2);
fixedFee3.setDenominatingTokenId(tokenId);
fixedFee3.setId(id);
customFees.add(fixedFee3);
if (tokenType == FUNGIBLE_COMMON) {
// fractional fees only apply for fungible tokens
CustomFee fractionalFee1 = new CustomFee();
fractionalFee1.setAmount(14L);
fractionalFee1.setAmountDenominator(31L);
fractionalFee1.setCollectorAccountId(FEE_COLLECTOR_ACCOUNT_ID_3);
fractionalFee1.setMaximumAmount(100L);
fractionalFee1.setNetOfTransfers(true);
fractionalFee1.setId(id);
customFees.add(fractionalFee1);
CustomFee fractionalFee2 = new CustomFee();
fractionalFee2.setAmount(15L);
fractionalFee2.setAmountDenominator(32L);
fractionalFee2.setCollectorAccountId(treasury);
fractionalFee2.setMaximumAmount(110L);
fractionalFee2.setNetOfTransfers(false);
fractionalFee2.setId(id);
customFees.add(fractionalFee2);
} else {
// royalty fees only apply for non-fungible tokens
CustomFee royaltyFee1 = new CustomFee();
royaltyFee1.setRoyaltyNumerator(14L);
royaltyFee1.setRoyaltyDenominator(31L);
royaltyFee1.setCollectorAccountId(FEE_COLLECTOR_ACCOUNT_ID_3);
royaltyFee1.setId(id);
customFees.add(royaltyFee1);
// with fallback fee
CustomFee royaltyFee2 = new CustomFee();
royaltyFee2.setRoyaltyNumerator(15L);
royaltyFee2.setRoyaltyDenominator(32L);
royaltyFee2.setCollectorAccountId(treasury);
// fallback fee in form of fixed fee
royaltyFee2.setAmount(103L);
royaltyFee2.setDenominatingTokenId(FEE_DOMAIN_TOKEN_ID);
royaltyFee2.setId(id);
customFees.add(royaltyFee2);
}
return customFees;
}
use of com.hedera.mirror.common.domain.transaction.CustomFee in project hedera-mirror-node by hashgraph.
the class EntityRecordItemListenerTokenTest method provideTokenCreateArguments.
private static Stream<Arguments> provideTokenCreateArguments(TokenType tokenType) {
List<CustomFee> nonEmptyCustomFees = nonEmptyCustomFees(CREATE_TIMESTAMP, DOMAIN_TOKEN_ID, tokenType);
EntityId treasury = EntityId.of(PAYER);
// fractional fees only apply for FT, thus FEE_COLLECTOR_ACCOUNT_ID_3 (collector of a fractional fee for FT, and
// a royalty fee in case of NFT) will be auto enabled only for FT custom fees
List<EntityId> autoEnabledAccounts = tokenType == FUNGIBLE_COMMON ? List.of(treasury, FEE_COLLECTOR_ACCOUNT_ID_2, FEE_COLLECTOR_ACCOUNT_ID_3) : List.of(treasury, FEE_COLLECTOR_ACCOUNT_ID_2);
return Stream.of(TokenCreateArguments.builder().autoEnabledAccounts(List.of(treasury)).createdTimestamp(CREATE_TIMESTAMP).customFees(deletedDbCustomFees(CREATE_TIMESTAMP, DOMAIN_TOKEN_ID)).customFeesDescription("empty custom fees").tokenId(DOMAIN_TOKEN_ID).build().toArguments(), TokenCreateArguments.builder().autoEnabledAccounts(autoEnabledAccounts).createdTimestamp(CREATE_TIMESTAMP).customFees(nonEmptyCustomFees).customFeesDescription("non-empty custom fees").freezeKey(true).freezeStatus(TokenFreezeStatusEnum.UNFROZEN).tokenId(DOMAIN_TOKEN_ID).build().toArguments(), TokenCreateArguments.builder().autoEnabledAccounts(autoEnabledAccounts).createdTimestamp(CREATE_TIMESTAMP).customFees(nonEmptyCustomFees).customFeesDescription("non-empty custom fees").freezeDefault(true).freezeKey(true).freezeStatus(TokenFreezeStatusEnum.UNFROZEN).tokenId(DOMAIN_TOKEN_ID).build().toArguments(), TokenCreateArguments.builder().autoEnabledAccounts(autoEnabledAccounts).createdTimestamp(CREATE_TIMESTAMP).customFees(nonEmptyCustomFees).customFeesDescription("non-empty custom fees").kycKey(true).kycStatus(TokenKycStatusEnum.GRANTED).tokenId(DOMAIN_TOKEN_ID).build().toArguments(), TokenCreateArguments.builder().autoEnabledAccounts(autoEnabledAccounts).createdTimestamp(CREATE_TIMESTAMP).customFees(nonEmptyCustomFees).customFeesDescription("non-empty custom fees").tokenId(DOMAIN_TOKEN_ID).build().toArguments(), TokenCreateArguments.builder().autoEnabledAccounts(autoEnabledAccounts).createdTimestamp(CREATE_TIMESTAMP).customFees(nonEmptyCustomFees).customFeesDescription("non-empty custom fees").pauseKey(true).tokenId(DOMAIN_TOKEN_ID).build().toArguments());
}
use of com.hedera.mirror.common.domain.transaction.CustomFee in project hedera-mirror-node by hashgraph.
the class EntityRecordItemListenerTokenTest method tokenFeeScheduleUpdate.
@ParameterizedTest(name = "{0}")
@EnumSource(value = TokenType.class, names = { "FUNGIBLE_COMMON", "NON_FUNGIBLE_UNIQUE" })
void tokenFeeScheduleUpdate(TokenType tokenType) {
// given
// create the token entity with empty custom fees
createTokenEntity(TOKEN_ID, tokenType, SYMBOL, CREATE_TIMESTAMP, false, false, false);
// update fee schedule
long updateTimestamp = CREATE_TIMESTAMP + 10L;
Entity expectedEntity = createEntity(DOMAIN_TOKEN_ID, TOKEN_REF_KEY, PAYER.getAccountNum(), AUTO_RENEW_PERIOD, false, EXPIRY_NS, TOKEN_CREATE_MEMO, null, CREATE_TIMESTAMP, CREATE_TIMESTAMP);
List<CustomFee> newCustomFees = nonEmptyCustomFees(updateTimestamp, DOMAIN_TOKEN_ID, tokenType);
List<CustomFee> expectedCustomFees = Lists.newArrayList(deletedDbCustomFees(CREATE_TIMESTAMP, DOMAIN_TOKEN_ID));
expectedCustomFees.addAll(newCustomFees);
long expectedSupply = tokenType == FUNGIBLE_COMMON ? INITIAL_SUPPLY : 0;
// when
updateTokenFeeSchedule(TOKEN_ID, updateTimestamp, newCustomFees);
// then
assertEntity(expectedEntity);
assertTokenInRepository(TOKEN_ID, true, CREATE_TIMESTAMP, CREATE_TIMESTAMP, SYMBOL, expectedSupply);
assertCustomFeesInDb(expectedCustomFees);
}
Aggregations