diff --git a/hedera-node/configuration/dev/application.properties b/hedera-node/configuration/dev/application.properties index 09a31a8fed17..f07fd7492b85 100644 --- a/hedera-node/configuration/dev/application.properties +++ b/hedera-node/configuration/dev/application.properties @@ -9,6 +9,7 @@ contracts.systemContract.cancelAirdrops.enabled=true contracts.systemContract.claimAirdrops.enabled=true contracts.systemContract.rejectTokens.enabled=true contracts.systemContract.setUnlimitedAutoAssociations.enabled=true +contracts.systemContract.metadataKeyAndFieldSupport.enabled=true # Needed for end-end tests running on mod-service code staking.periodMins=1 staking.fees.nodeRewardPercentage=10 diff --git a/hedera-node/configuration/previewnet/application.properties b/hedera-node/configuration/previewnet/application.properties index 3d2e74f04cdf..975a285cb48f 100644 --- a/hedera-node/configuration/previewnet/application.properties +++ b/hedera-node/configuration/previewnet/application.properties @@ -14,3 +14,4 @@ contracts.systemContract.rejectTokens.enabled=true contracts.systemContract.setUnlimitedAutoAssociations.enabled=true ledger.id=0x02 entities.unlimitedAutoAssociationsEnabled=true +contracts.systemContract.metadataKeyAndFieldSupport.enabled=true diff --git a/hedera-node/hapi-utils/src/main/java/com/hedera/node/app/hapi/utils/contracts/ParsingConstants.java b/hedera-node/hapi-utils/src/main/java/com/hedera/node/app/hapi/utils/contracts/ParsingConstants.java index fb0d100a1418..e598ee762ba6 100644 --- a/hedera-node/hapi-utils/src/main/java/com/hedera/node/app/hapi/utils/contracts/ParsingConstants.java +++ b/hedera-node/hapi-utils/src/main/java/com/hedera/node/app/hapi/utils/contracts/ParsingConstants.java @@ -60,8 +60,9 @@ private ParsingConstants() { "(" + "string,string,address,string,bool,int64,bool," + TOKEN_KEY + ARRAY_BRACKETS + "," + EXPIRY + ")"; public static final String HEDERA_TOKEN_V3 = "(" + "string,string,address,string,bool,int64,bool," + TOKEN_KEY + ARRAY_BRACKETS + "," + EXPIRY_V2 + ")"; - public static final String HEDERA_TOKEN_V4 = "(" + "string,string,address,string,bool,uint32,bool," + TOKEN_KEY - + ARRAY_BRACKETS + "," + EXPIRY + ",bytes" + ")"; + public static final String HEDERA_TOKEN_WITH_METADATA = "(" + "string,string,address,string,bool,int64,bool," + + TOKEN_KEY + ARRAY_BRACKETS + "," + EXPIRY_V2 + ",bytes)"; + public static final String TOKEN_INFO = "(" + HEDERA_TOKEN_V2 + ",int64,bool,bool,bool," diff --git a/hedera-node/hedera-config/src/main/java/com/hedera/node/config/data/ContractsConfig.java b/hedera-node/hedera-config/src/main/java/com/hedera/node/config/data/ContractsConfig.java index 0105cd14b6e2..c78cb886ecfe 100644 --- a/hedera-node/hedera-config/src/main/java/com/hedera/node/config/data/ContractsConfig.java +++ b/hedera-node/hedera-config/src/main/java/com/hedera/node/config/data/ContractsConfig.java @@ -81,6 +81,9 @@ public record ContractsConfig( @ConfigProperty(value = "systemContract.accountService.isAuthorizedRawEnabled", defaultValue = "true") @NetworkProperty boolean systemContractAccountServiceIsAuthorizedRawEnabled, + @ConfigProperty(value = "systemContract.metadataKeyAndFieldSupport.enabled", defaultValue = "false") + @NetworkProperty + boolean metadataKeyAndFieldEnabled, @ConfigProperty(value = "systemContract.updateCustomFees.enabled", defaultValue = "true") @NetworkProperty boolean systemContractUpdateCustomFeesEnabled, @ConfigProperty(value = "systemContract.tokenInfo.v2.enabled", defaultValue = "false") @NetworkProperty diff --git a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/processors/CustomMessageCallProcessor.java b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/processors/CustomMessageCallProcessor.java index 9969e988cf47..e2373b28381a 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/processors/CustomMessageCallProcessor.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/processors/CustomMessageCallProcessor.java @@ -21,7 +21,7 @@ import static com.hedera.node.app.service.contract.impl.exec.failure.CustomExceptionalHaltReason.INSUFFICIENT_CHILD_RECORDS; import static com.hedera.node.app.service.contract.impl.exec.failure.CustomExceptionalHaltReason.INVALID_CONTRACT_ID; import static com.hedera.node.app.service.contract.impl.exec.failure.CustomExceptionalHaltReason.INVALID_SIGNATURE; -import static com.hedera.node.app.service.contract.impl.exec.systemcontracts.hts.create.CreateTranslator.CREATE_FUNCTIONS; +import static com.hedera.node.app.service.contract.impl.exec.systemcontracts.hts.create.CreateTranslator.createSelectorsMap; import static com.hedera.node.app.service.contract.impl.exec.utils.FrameUtils.acquiredSenderAuthorizationViaDelegateCall; import static com.hedera.node.app.service.contract.impl.exec.utils.FrameUtils.alreadyHalted; import static com.hedera.node.app.service.contract.impl.exec.utils.FrameUtils.isTopLevelTransaction; @@ -187,7 +187,7 @@ private boolean isTokenCreation(MessageFrame frame) { return false; } var selector = frame.getInputData().slice(0, 4).toArray(); - return CREATE_FUNCTIONS.stream().anyMatch(s -> Arrays.equals(s.selector(), selector)); + return createSelectorsMap.keySet().stream().anyMatch(s -> Arrays.equals(s.selector(), selector)); } public boolean isImplicitCreationEnabled(@NonNull Configuration config) { diff --git a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/ReturnTypes.java b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/ReturnTypes.java index 890f5195260c..a152cd92d7d6 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/ReturnTypes.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/ReturnTypes.java @@ -66,6 +66,10 @@ private ReturnTypes() { protected static final String EXPIRY_FIELDS = // second, autoRenewAccount, autoRenewPeriod "(uint32,address,uint32)"; + // TODO: consider this expiry type for TokenV3. Might need to add another function to handle this. + protected static final String EXPIRY_FIELDS_V2 = + // second, autoRenewAccount, autoRenewPeriod + "(int64,address,int64)"; protected static final String CUSTOM_FEES = // FixedFee array // amount, tokenId, useHbarsForPayment, useCurrentTokenForPayment, feeCollector @@ -130,7 +134,7 @@ private ReturnTypes() { + "(" + TOKEN_FIELDS + TOKEN_KEYS - + EXPIRY_FIELDS + + EXPIRY_FIELDS_V2 + ",bytes" // metadata + ")" + STATUS_FIELDS // totalSupply, deleted, defaultKycStatus, pauseStatus @@ -166,7 +170,7 @@ private ReturnTypes() { + "(" + TOKEN_FIELDS + TOKEN_KEYS - + EXPIRY_FIELDS + + EXPIRY_FIELDS_V2 + ",bytes" // metadata + ")" + STATUS_FIELDS @@ -204,7 +208,7 @@ private ReturnTypes() { + "(" + TOKEN_FIELDS + TOKEN_KEYS - + EXPIRY_FIELDS + + EXPIRY_FIELDS_V2 + ",bytes" // metadata + ")" + STATUS_FIELDS // totalSupply, deleted, defaultKycStatus, pauseStatus diff --git a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/create/ClassicCreatesCall.java b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/create/ClassicCreatesCall.java index 161c601cd289..eb3a8e9c623a 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/create/ClassicCreatesCall.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/create/ClassicCreatesCall.java @@ -37,7 +37,6 @@ import static com.hedera.node.app.service.contract.impl.exec.utils.FrameUtils.contractsConfigOf; import static com.hedera.node.app.service.contract.impl.exec.utils.FrameUtils.stackIncludesActiveAddress; import static com.hedera.node.app.service.contract.impl.utils.ConversionUtils.asEvmAddress; -import static com.hedera.node.app.service.contract.impl.utils.ConversionUtils.asHeadlongAddress; import static com.hedera.node.app.service.contract.impl.utils.ConversionUtils.headlongAddressOf; import static com.hedera.node.app.service.contract.impl.utils.ConversionUtils.pbjToBesuAddress; import static java.util.Objects.requireNonNull; @@ -56,7 +55,6 @@ import com.hedera.node.app.service.contract.impl.exec.scope.SpecificCryptoVerificationStrategy; import com.hedera.node.app.service.contract.impl.exec.scope.VerificationStrategy; import com.hedera.node.app.service.contract.impl.exec.systemcontracts.common.AbstractCall; -import com.hedera.node.app.service.contract.impl.exec.systemcontracts.hts.AddressIdConverter; import com.hedera.node.app.service.contract.impl.hevm.HederaWorldUpdater; import com.hedera.node.app.service.contract.impl.records.ContractCallStreamBuilder; import com.hedera.node.config.data.ContractsConfig; @@ -85,11 +83,10 @@ public ClassicCreatesCall( @NonNull final HederaWorldUpdater.Enhancement enhancement, @Nullable final TransactionBody syntheticCreate, @NonNull final VerificationStrategy verificationStrategy, - @NonNull final Address spender, - @NonNull final AddressIdConverter addressIdConverter) { + @NonNull final AccountID spender) { super(systemContractGasCalculator, enhancement, false); this.verificationStrategy = requireNonNull(verificationStrategy); - this.spenderId = addressIdConverter.convert(asHeadlongAddress(spender.toArrayUnsafe())); + this.spenderId = spender; this.syntheticCreate = syntheticCreate; } diff --git a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/create/CreateDecoder.java b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/create/CreateDecoder.java index 1c3e226ebcfe..1a668bd30294 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/create/CreateDecoder.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/create/CreateDecoder.java @@ -17,6 +17,7 @@ package com.hedera.node.app.service.contract.impl.exec.systemcontracts.hts.create; import static com.hedera.node.app.service.contract.impl.exec.systemcontracts.hts.create.CreateSyntheticTxnFactory.createToken; +import static com.hedera.node.app.service.contract.impl.exec.systemcontracts.hts.create.CreateSyntheticTxnFactory.createTokenWithMetadata; import static com.hedera.node.app.service.contract.impl.utils.ConversionUtils.asNumericContractId; import com.esaulpaugh.headlong.abi.Tuple; @@ -34,6 +35,7 @@ import com.hedera.node.app.service.contract.impl.exec.utils.TokenExpiryWrapper; import com.hedera.node.app.service.contract.impl.exec.utils.TokenKeyWrapper; import com.hedera.node.app.service.contract.impl.utils.ConversionUtils; +import com.hedera.pbj.runtime.io.buffer.Bytes; import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.Nullable; import java.math.BigInteger; @@ -53,6 +55,7 @@ public class CreateDecoder { private static final int FRACTIONAL_FEE = 4; private static final int NFT_FIXED_FEE = 1; private static final int NFT_ROYALTY_FEE = 2; + private static final int METADATA = 9; @Inject public CreateDecoder() { @@ -122,6 +125,32 @@ public TransactionBody decodeCreateFungibleTokenV3( return bodyFor(tokenCreateWrapper); } + /** + * Decodes a call to {@link CreateTranslator#CREATE_FUNGIBLE_TOKEN_WITH_METADATA} into a synthetic {@link TransactionBody}. + * + * @param encoded the encoded call + * @param senderId the sender account ID + * @param nativeOperations the native operations + * @param addressIdConverter the address ID converter + * @return the synthetic transaction body + */ + public TransactionBody decodeCreateFungibleTokenWithMetadata( + @NonNull final byte[] encoded, + @NonNull final AccountID senderId, + @NonNull final HederaNativeOperations nativeOperations, + @NonNull final AddressIdConverter addressIdConverter) { + final var call = CreateTranslator.CREATE_FUNGIBLE_TOKEN_WITH_METADATA.decodeCall(encoded); + final TokenCreateWrapper tokenCreateWrapper = getTokenCreateWrapperWithMetadata( + call.get(HEDERA_TOKEN), + true, + call.get(INIT_SUPPLY), + call.get(DECIMALS), + senderId, + nativeOperations, + addressIdConverter); + return bodyForWithMeta(tokenCreateWrapper); + } + /** * Decodes a call to {@link CreateTranslator#CREATE_FUNGIBLE_WITH_CUSTOM_FEES_V1} into a synthetic {@link TransactionBody}. * @@ -204,6 +233,33 @@ public TransactionBody decodeCreateFungibleTokenWithCustomFeesV3( return bodyFor(tokenCreateWrapper); } + /** + * Decodes a call to {@link CreateTranslator#CREATE_FUNGIBLE_TOKEN_WITH_METADATA_AND_CUSTOM_FEES} into a synthetic {@link TransactionBody}. + * + * @param encoded the encoded call + * @param senderId the sender account ID + * @param nativeOperations the native operations + * @param addressIdConverter the address ID converter + * @return the synthetic transaction body + */ + public TransactionBody decodeCreateFungibleTokenWithMetadataAndCustomFees( + @NonNull final byte[] encoded, + @NonNull final AccountID senderId, + @NonNull final HederaNativeOperations nativeOperations, + @NonNull final AddressIdConverter addressIdConverter) { + final var call = CreateTranslator.CREATE_FUNGIBLE_TOKEN_WITH_METADATA_AND_CUSTOM_FEES.decodeCall(encoded); + final TokenCreateWrapper tokenCreateWrapper = getTokenCreateWrapperWithMetadataAndCustomFees( + call.get(HEDERA_TOKEN), + call.get(INIT_SUPPLY), + call.get(DECIMALS), + call.get(FIXED_FEE), + call.get(FRACTIONAL_FEE), + senderId, + nativeOperations, + addressIdConverter); + return bodyForWithMeta(tokenCreateWrapper); + } + /** * Decodes a call to {@link CreateTranslator#CREATE_NON_FUNGIBLE_TOKEN_V1} into a synthetic {@link TransactionBody}. * @@ -255,6 +311,26 @@ public TransactionBody decodeCreateNonFungibleV3( return bodyFor(tokenCreateWrapper); } + /** + * Decodes a call to {@link CreateTranslator#CREATE_NON_FUNGIBLE_TOKEN_WITH_METADATA} into a synthetic {@link TransactionBody}. + * + * @param encoded the encoded call + * @param senderId the sender account ID + * @param nativeOperations the native operations + * @param addressIdConverter the address ID converter + * @return the synthetic transaction body + */ + public TransactionBody decodeCreateNonFungibleWithMetadata( + @NonNull final byte[] encoded, + @NonNull final AccountID senderId, + @NonNull final HederaNativeOperations nativeOperations, + @NonNull final AddressIdConverter addressIdConverter) { + final var call = CreateTranslator.CREATE_NON_FUNGIBLE_TOKEN_WITH_METADATA.decodeCall(encoded); + final TokenCreateWrapper tokenCreateWrapper = getTokenCreateWrapperNonFungibleWithMetadata( + call.get(HEDERA_TOKEN), senderId, nativeOperations, addressIdConverter); + return bodyForWithMeta(tokenCreateWrapper); + } + /** * Decodes a call to {@link CreateTranslator#CREATE_NON_FUNGIBLE_TOKEN_WITH_CUSTOM_FEES_V1} into a synthetic {@link TransactionBody}. * @@ -321,11 +397,36 @@ public TransactionBody decodeCreateNonFungibleWithCustomFeesV3( return bodyFor(tokenCreateWrapper); } + /** + * Decodes a call to {@link CreateTranslator#CREATE_NON_FUNGIBLE_TOKEN_WITH_METADATA_AND_CUSTOM_FEES} into a synthetic {@link TransactionBody}. + * + * @param encoded the encoded call + * @param senderId the sender account ID + * @param nativeOperations the native operations + * @param addressIdConverter the address ID converter + * @return the synthetic transaction body + */ + public TransactionBody decodeCreateNonFungibleWithMetadataAndCustomFees( + @NonNull final byte[] encoded, + @NonNull final AccountID senderId, + @NonNull final HederaNativeOperations nativeOperations, + @NonNull final AddressIdConverter addressIdConverter) { + final var call = CreateTranslator.CREATE_NON_FUNGIBLE_TOKEN_WITH_METADATA_AND_CUSTOM_FEES.decodeCall(encoded); + final TokenCreateWrapper tokenCreateWrapper = getTokenCreateWrapperNonFungibleWithMetadataAndCustomFees( + call.get(HEDERA_TOKEN), + call.get(NFT_FIXED_FEE), + call.get(NFT_ROYALTY_FEE), + senderId, + nativeOperations, + addressIdConverter); + return bodyForWithMeta(tokenCreateWrapper); + } + private TransactionBody bodyOf(@NonNull final TokenCreateTransactionBody.Builder tokenCreate) { return TransactionBody.newBuilder().tokenCreation(tokenCreate).build(); } - private static TokenCreateWrapper getTokenCreateWrapper( + private TokenCreateWrapper getTokenCreateWrapper( @NonNull final Tuple tokenCreateStruct, final boolean isFungible, final long initSupply, @@ -371,7 +472,22 @@ private static TokenCreateWrapper getTokenCreateWrapper( return tokenCreateWrapper; } - private static TokenCreateWrapper getTokenCreateWrapperFungibleWithCustomFees( + public TokenCreateWrapper getTokenCreateWrapperWithMetadata( + @NonNull final Tuple tokenCreateStruct, + final boolean isFungible, + final long initSupply, + final int decimals, + @NonNull final AccountID senderId, + @NonNull final HederaNativeOperations nativeOperations, + @NonNull final AddressIdConverter addressIdConverter) { + final var tokenCreateWrapper = getTokenCreateWrapper( + tokenCreateStruct, isFungible, initSupply, decimals, senderId, nativeOperations, addressIdConverter); + + tokenCreateWrapper.setMetadata(Bytes.wrap((byte[]) tokenCreateStruct.get(9))); + return tokenCreateWrapper; + } + + private TokenCreateWrapper getTokenCreateWrapperFungibleWithCustomFees( @NonNull final Tuple tokenCreateStruct, final long initSupply, final int decimals, @@ -389,7 +505,25 @@ private static TokenCreateWrapper getTokenCreateWrapperFungibleWithCustomFees( return tokenCreateWrapper; } - private static TokenCreateWrapper getTokenCreateWrapperNonFungible( + private TokenCreateWrapper getTokenCreateWrapperWithMetadataAndCustomFees( + @NonNull final Tuple tokenCreateStruct, + final long initSupply, + final int decimals, + @NonNull final Tuple[] fixedFeesTuple, + @NonNull final Tuple[] fractionalFeesTuple, + @NonNull final AccountID senderId, + @NonNull final HederaNativeOperations nativeOperations, + @NonNull final AddressIdConverter addressIdConverter) { + final var tokenCreateWrapper = getTokenCreateWrapperWithMetadata( + tokenCreateStruct, true, initSupply, decimals, senderId, nativeOperations, addressIdConverter); + final var fixedFees = decodeFixedFees(fixedFeesTuple, addressIdConverter); + final var fractionalFess = decodeFractionalFees(fractionalFeesTuple, addressIdConverter); + tokenCreateWrapper.setFixedFees(fixedFees); + tokenCreateWrapper.setFractionalFees(fractionalFess); + return tokenCreateWrapper; + } + + private TokenCreateWrapper getTokenCreateWrapperNonFungible( @NonNull final Tuple tokenCreateStruct, @NonNull final AccountID senderId, @NonNull final HederaNativeOperations nativeOperations, @@ -400,7 +534,18 @@ private static TokenCreateWrapper getTokenCreateWrapperNonFungible( tokenCreateStruct, false, initSupply, decimals, senderId, nativeOperations, addressIdConverter); } - private static TokenCreateWrapper getTokenCreateWrapperNonFungibleWithCustomFees( + private TokenCreateWrapper getTokenCreateWrapperNonFungibleWithMetadata( + @NonNull final Tuple tokenCreateStruct, + @NonNull final AccountID senderId, + @NonNull final HederaNativeOperations nativeOperations, + @NonNull final AddressIdConverter addressIdConverter) { + final long initSupply = 0L; + final int decimals = 0; + return getTokenCreateWrapperWithMetadata( + tokenCreateStruct, false, initSupply, decimals, senderId, nativeOperations, addressIdConverter); + } + + private TokenCreateWrapper getTokenCreateWrapperNonFungibleWithCustomFees( @NonNull final Tuple tokenCreateStruct, @NonNull final Tuple[] fixedFeesTuple, @NonNull final Tuple[] royaltyFeesTuple, @@ -418,7 +563,25 @@ private static TokenCreateWrapper getTokenCreateWrapperNonFungibleWithCustomFees return tokenCreateWrapper; } - private static List decodeTokenKeys( + private TokenCreateWrapper getTokenCreateWrapperNonFungibleWithMetadataAndCustomFees( + @NonNull final Tuple tokenCreateStruct, + @NonNull final Tuple[] fixedFeesTuple, + @NonNull final Tuple[] royaltyFeesTuple, + @NonNull final AccountID senderId, + @NonNull final HederaNativeOperations nativeOperations, + @NonNull final AddressIdConverter addressIdConverter) { + final var fixedFees = decodeFixedFees(fixedFeesTuple, addressIdConverter); + final var royaltyFees = decodeRoyaltyFees(royaltyFeesTuple, addressIdConverter); + final long initSupply = 0L; + final int decimals = 0; + final var tokenCreateWrapper = getTokenCreateWrapperWithMetadata( + tokenCreateStruct, false, initSupply, decimals, senderId, nativeOperations, addressIdConverter); + tokenCreateWrapper.setFixedFees(fixedFees); + tokenCreateWrapper.setRoyaltyFees(royaltyFees); + return tokenCreateWrapper; + } + + private List decodeTokenKeys( @NonNull final Tuple[] tokenKeysTuples, @NonNull final AddressIdConverter addressIdConverter) { // TokenKey @@ -455,7 +618,7 @@ private static List decodeTokenKeys( return tokenKeys; } - private static TokenExpiryWrapper decodeTokenExpiry( + private TokenExpiryWrapper decodeTokenExpiry( @NonNull final Tuple expiryTuple, @NonNull final AddressIdConverter addressIdConverter) { // Expiry @@ -472,7 +635,7 @@ private static TokenExpiryWrapper decodeTokenExpiry( second, autoRenewAccount.accountNum() == 0 ? null : autoRenewAccount, autoRenewPeriod); } - public static List decodeFixedFees( + public List decodeFixedFees( @NonNull final Tuple[] fixedFeesTuples, @NonNull final AddressIdConverter addressIdConverter) { // FixedFee @@ -499,7 +662,7 @@ public static List decodeFixedFees( return fixedFees; } - public static List decodeFractionalFees( + public List decodeFractionalFees( @NonNull final Tuple[] fractionalFeesTuples, @NonNull final AddressIdConverter addressIdConverter) { // FractionalFee @@ -529,7 +692,7 @@ public static List decodeFractionalFees( return fractionalFees; } - public static List decodeRoyaltyFees( + public List decodeRoyaltyFees( @NonNull final Tuple[] royaltyFeesTuples, @NonNull final AddressIdConverter addressIdConverter) { // RoyaltyFee @@ -573,4 +736,12 @@ public static List decodeRoyaltyFees( return null; } } + + private @Nullable TransactionBody bodyForWithMeta(@NonNull final TokenCreateWrapper tokenCreateWrapper) { + try { + return bodyOf(createTokenWithMetadata(tokenCreateWrapper)); + } catch (IllegalArgumentException ignore) { + return null; + } + } } diff --git a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/create/CreateDecoderFunction.java b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/create/CreateDecoderFunction.java new file mode 100644 index 000000000000..bdc4ff4ce8f9 --- /dev/null +++ b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/create/CreateDecoderFunction.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2024 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.service.contract.impl.exec.systemcontracts.hts.create; + +import com.hedera.hapi.node.base.AccountID; +import com.hedera.hapi.node.transaction.TransactionBody; +import com.hedera.node.app.service.contract.impl.exec.scope.HederaNativeOperations; +import com.hedera.node.app.service.contract.impl.exec.systemcontracts.hts.AddressIdConverter; + +@FunctionalInterface +public interface CreateDecoderFunction { + + TransactionBody decode( + byte[] input, AccountID senderID, HederaNativeOperations nativeOps, AddressIdConverter converter); +} diff --git a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/create/CreateSyntheticTxnFactory.java b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/create/CreateSyntheticTxnFactory.java index 31ec5323c35f..0a39c1902e04 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/create/CreateSyntheticTxnFactory.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/create/CreateSyntheticTxnFactory.java @@ -26,6 +26,7 @@ import com.hedera.node.app.service.contract.impl.exec.utils.TokenCreateWrapper.FixedFeeWrapper; import com.hedera.node.app.service.contract.impl.exec.utils.TokenCreateWrapper.FractionalFeeWrapper; import com.hedera.node.app.service.contract.impl.exec.utils.TokenCreateWrapper.RoyaltyFeeWrapper; +import com.hedera.node.app.service.contract.impl.exec.utils.TokenKeyWrapper; import edu.umd.cs.findbugs.annotations.NonNull; import java.util.function.Function; import java.util.stream.Stream; @@ -69,6 +70,14 @@ public static TokenCreateTransactionBody.Builder createToken(@NonNull final Toke return txnBodyBuilder; } + public static TokenCreateTransactionBody.Builder createTokenWithMetadata( + @NonNull final TokenCreateWrapper tokenCreateWrapper) { + final var transactionBodyBuilder = createToken(tokenCreateWrapper); + setMetadata(tokenCreateWrapper, transactionBodyBuilder); + setMetadataKey(tokenCreateWrapper, transactionBodyBuilder); + return transactionBodyBuilder; + } + private static void setTokenKeys( @NonNull final TokenCreateWrapper tokenCreateWrapper, final Builder txnBodyBuilder) { tokenCreateWrapper.getTokenKeys().forEach(tokenKeyWrapper -> { @@ -115,6 +124,21 @@ private static void setExpiry( } } + private static void setMetadataKey( + @NonNull final TokenCreateWrapper tokenCreateWrapper, final Builder txnBodyBuilder) { + tokenCreateWrapper.getTokenKeys().stream() + .filter(TokenKeyWrapper::isUsedForMetadataKey) + .map(tokenKeyWrapper -> tokenKeyWrapper.key().asGrpc()) + .forEach(txnBodyBuilder::metadataKey); + } + + private static void setMetadata( + @NonNull final TokenCreateWrapper tokenCreateWrapper, final Builder txnBodyBuilder) { + if (tokenCreateWrapper.getMetadata() != null) { + txnBodyBuilder.metadata(tokenCreateWrapper.getMetadata()); + } + } + private static void addCustomFees( @NonNull final TokenCreateWrapper tokenCreateWrapper, @NonNull final Builder txnBodyBuilder) { final var fractionalFees = diff --git a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/create/CreateTranslator.java b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/create/CreateTranslator.java index b223e89f4b57..7007c57a4578 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/create/CreateTranslator.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/create/CreateTranslator.java @@ -24,6 +24,7 @@ import static com.hedera.node.app.hapi.utils.contracts.ParsingConstants.HEDERA_TOKEN_V1; import static com.hedera.node.app.hapi.utils.contracts.ParsingConstants.HEDERA_TOKEN_V2; import static com.hedera.node.app.hapi.utils.contracts.ParsingConstants.HEDERA_TOKEN_V3; +import static com.hedera.node.app.hapi.utils.contracts.ParsingConstants.HEDERA_TOKEN_WITH_METADATA; import static com.hedera.node.app.hapi.utils.contracts.ParsingConstants.ROYALTY_FEE; import static com.hedera.node.app.hapi.utils.contracts.ParsingConstants.ROYALTY_FEE_V2; @@ -31,9 +32,11 @@ import com.hedera.hapi.node.transaction.TransactionBody; import com.hedera.node.app.service.contract.impl.exec.systemcontracts.common.AbstractCallTranslator; import com.hedera.node.app.service.contract.impl.exec.systemcontracts.hts.HtsCallAttempt; +import com.hedera.node.config.data.ContractsConfig; import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.Nullable; -import java.util.HashSet; +import java.util.HashMap; +import java.util.Map; import java.util.Set; import javax.inject.Inject; @@ -120,47 +123,81 @@ public class CreateTranslator extends AbstractCallTranslator { + ")", "(int64,address)"); + public static final Function CREATE_FUNGIBLE_TOKEN_WITH_METADATA = + new Function("createFungibleToken(" + HEDERA_TOKEN_WITH_METADATA + ",int64,int32)", "(int64,address)"); + public static final Function CREATE_FUNGIBLE_TOKEN_WITH_METADATA_AND_CUSTOM_FEES = new Function( + "createFungibleTokenWithCustomFees(" + + HEDERA_TOKEN_WITH_METADATA + + ",int64,int32," + + FIXED_FEE_V2 + + ARRAY_BRACKETS + + "," + + FRACTIONAL_FEE_V2 + + ARRAY_BRACKETS + + ")", + "(int64,address)"); + public static final Function CREATE_NON_FUNGIBLE_TOKEN_WITH_METADATA = + new Function("createNonFungibleToken(" + HEDERA_TOKEN_WITH_METADATA + ")", "(int64,address)"); + public static final Function CREATE_NON_FUNGIBLE_TOKEN_WITH_METADATA_AND_CUSTOM_FEES = new Function( + "createNonFungibleTokenWithCustomFees(" + + HEDERA_TOKEN_WITH_METADATA + + "," + + FIXED_FEE_V2 + + ARRAY_BRACKETS + + "," + + ROYALTY_FEE_V2 + + ARRAY_BRACKETS + + ")", + "(int64,address)"); + /** * A set of `Function` objects representing various create functions for fungible and non-fungible tokens. * This set is used in {@link com.hedera.node.app.service.contract.impl.exec.processors.CustomMessageCallProcessor} * to determine if a given call attempt is a creation call, because we do not allow sending value to Hedera system contracts * except in the case of token creation */ - public static final Set CREATE_FUNCTIONS = new HashSet<>(Set.of( - CREATE_FUNGIBLE_TOKEN_V1, - CREATE_FUNGIBLE_TOKEN_V2, - CREATE_FUNGIBLE_TOKEN_V3, - CREATE_FUNGIBLE_WITH_CUSTOM_FEES_V1, - CREATE_FUNGIBLE_WITH_CUSTOM_FEES_V2, - CREATE_FUNGIBLE_WITH_CUSTOM_FEES_V3, - CREATE_NON_FUNGIBLE_TOKEN_V1, - CREATE_NON_FUNGIBLE_TOKEN_V2, - CREATE_NON_FUNGIBLE_TOKEN_V3, - CREATE_NON_FUNGIBLE_TOKEN_WITH_CUSTOM_FEES_V1, - CREATE_NON_FUNGIBLE_TOKEN_WITH_CUSTOM_FEES_V2, - CREATE_NON_FUNGIBLE_TOKEN_WITH_CUSTOM_FEES_V3)); - - private final CreateDecoder decoder; + public static final Map createSelectorsMap = new HashMap<>(); @Inject - public CreateTranslator(CreateDecoder decoder) { - // Dagger2 - this.decoder = decoder; + public CreateTranslator(final CreateDecoder decoder) { + createSelectorsMap.put(CREATE_FUNGIBLE_TOKEN_V1, decoder::decodeCreateFungibleTokenV1); + createSelectorsMap.put(CREATE_FUNGIBLE_TOKEN_V2, decoder::decodeCreateFungibleTokenV2); + createSelectorsMap.put(CREATE_FUNGIBLE_TOKEN_V3, decoder::decodeCreateFungibleTokenV3); + createSelectorsMap.put(CREATE_FUNGIBLE_TOKEN_WITH_METADATA, decoder::decodeCreateFungibleTokenWithMetadata); + createSelectorsMap.put(CREATE_FUNGIBLE_WITH_CUSTOM_FEES_V1, decoder::decodeCreateFungibleTokenWithCustomFeesV1); + createSelectorsMap.put(CREATE_FUNGIBLE_WITH_CUSTOM_FEES_V2, decoder::decodeCreateFungibleTokenWithCustomFeesV2); + createSelectorsMap.put(CREATE_FUNGIBLE_WITH_CUSTOM_FEES_V3, decoder::decodeCreateFungibleTokenWithCustomFeesV3); + createSelectorsMap.put( + CREATE_FUNGIBLE_TOKEN_WITH_METADATA_AND_CUSTOM_FEES, + decoder::decodeCreateFungibleTokenWithMetadataAndCustomFees); + createSelectorsMap.put(CREATE_NON_FUNGIBLE_TOKEN_V1, decoder::decodeCreateNonFungibleV1); + createSelectorsMap.put(CREATE_NON_FUNGIBLE_TOKEN_V2, decoder::decodeCreateNonFungibleV2); + createSelectorsMap.put(CREATE_NON_FUNGIBLE_TOKEN_V3, decoder::decodeCreateNonFungibleV3); + createSelectorsMap.put(CREATE_NON_FUNGIBLE_TOKEN_WITH_METADATA, decoder::decodeCreateNonFungibleWithMetadata); + createSelectorsMap.put( + CREATE_NON_FUNGIBLE_TOKEN_WITH_CUSTOM_FEES_V1, decoder::decodeCreateNonFungibleWithCustomFeesV1); + createSelectorsMap.put( + CREATE_NON_FUNGIBLE_TOKEN_WITH_CUSTOM_FEES_V2, decoder::decodeCreateNonFungibleWithCustomFeesV2); + createSelectorsMap.put( + CREATE_NON_FUNGIBLE_TOKEN_WITH_CUSTOM_FEES_V3, decoder::decodeCreateNonFungibleWithCustomFeesV3); + createSelectorsMap.put( + CREATE_NON_FUNGIBLE_TOKEN_WITH_METADATA_AND_CUSTOM_FEES, + decoder::decodeCreateNonFungibleWithMetadataAndCustomFees); } @Override public boolean matches(@NonNull HtsCallAttempt attempt) { - return attempt.isSelector(CREATE_FUNGIBLE_TOKEN_V1, CREATE_FUNGIBLE_TOKEN_V2, CREATE_FUNGIBLE_TOKEN_V3) - || attempt.isSelector( - CREATE_FUNGIBLE_WITH_CUSTOM_FEES_V1, - CREATE_FUNGIBLE_WITH_CUSTOM_FEES_V2, - CREATE_FUNGIBLE_WITH_CUSTOM_FEES_V3) - || attempt.isSelector( - CREATE_NON_FUNGIBLE_TOKEN_V1, CREATE_NON_FUNGIBLE_TOKEN_V2, CREATE_NON_FUNGIBLE_TOKEN_V3) - || attempt.isSelector( - CREATE_NON_FUNGIBLE_TOKEN_WITH_CUSTOM_FEES_V1, - CREATE_NON_FUNGIBLE_TOKEN_WITH_CUSTOM_FEES_V2, - CREATE_NON_FUNGIBLE_TOKEN_WITH_CUSTOM_FEES_V3); + final var metaConfigEnabled = + attempt.configuration().getConfigData(ContractsConfig.class).metadataKeyAndFieldEnabled(); + final var metaSelectors = Set.of( + CREATE_FUNGIBLE_TOKEN_WITH_METADATA, + CREATE_FUNGIBLE_TOKEN_WITH_METADATA_AND_CUSTOM_FEES, + CREATE_NON_FUNGIBLE_TOKEN_WITH_METADATA, + CREATE_NON_FUNGIBLE_TOKEN_WITH_METADATA_AND_CUSTOM_FEES); + return createSelectorsMap.keySet().stream() + .anyMatch(selector -> metaSelectors.contains(selector) + ? attempt.isSelectorIfConfigEnabled(selector, metaConfigEnabled) + : attempt.isSelector(selector)); } @Override @@ -170,8 +207,7 @@ public ClassicCreatesCall callFrom(@NonNull HtsCallAttempt attempt) { attempt.enhancement(), nominalBodyFor(attempt), attempt.defaultVerificationStrategy(), - attempt.senderAddress(), - attempt.addressIdConverter()); + attempt.senderId()); } private @Nullable TransactionBody nominalBodyFor(@NonNull final HtsCallAttempt attempt) { @@ -180,36 +216,10 @@ public ClassicCreatesCall callFrom(@NonNull HtsCallAttempt attempt) { final var nativeOperations = attempt.nativeOperations(); final var addressIdConverter = attempt.addressIdConverter(); - if (attempt.isSelector(CREATE_FUNGIBLE_TOKEN_V1)) { - return decoder.decodeCreateFungibleTokenV1(inputBytes, senderId, nativeOperations, addressIdConverter); - } else if (attempt.isSelector(CREATE_FUNGIBLE_TOKEN_V2)) { - return decoder.decodeCreateFungibleTokenV2(inputBytes, senderId, nativeOperations, addressIdConverter); - } else if (attempt.isSelector(CREATE_FUNGIBLE_TOKEN_V3)) { - return decoder.decodeCreateFungibleTokenV3(inputBytes, senderId, nativeOperations, addressIdConverter); - } else if (attempt.isSelector(CREATE_FUNGIBLE_WITH_CUSTOM_FEES_V1)) { - return decoder.decodeCreateFungibleTokenWithCustomFeesV1( - inputBytes, senderId, nativeOperations, addressIdConverter); - } else if (attempt.isSelector(CREATE_FUNGIBLE_WITH_CUSTOM_FEES_V2)) { - return decoder.decodeCreateFungibleTokenWithCustomFeesV2( - inputBytes, senderId, nativeOperations, addressIdConverter); - } else if (attempt.isSelector(CREATE_FUNGIBLE_WITH_CUSTOM_FEES_V3)) { - return decoder.decodeCreateFungibleTokenWithCustomFeesV3( - inputBytes, senderId, nativeOperations, addressIdConverter); - } else if (attempt.isSelector(CREATE_NON_FUNGIBLE_TOKEN_V1)) { - return decoder.decodeCreateNonFungibleV1(inputBytes, senderId, nativeOperations, addressIdConverter); - } else if (attempt.isSelector(CREATE_NON_FUNGIBLE_TOKEN_V2)) { - return decoder.decodeCreateNonFungibleV2(inputBytes, senderId, nativeOperations, addressIdConverter); - } else if (attempt.isSelector(CREATE_NON_FUNGIBLE_TOKEN_V3)) { - return decoder.decodeCreateNonFungibleV3(inputBytes, senderId, nativeOperations, addressIdConverter); - } else if (attempt.isSelector(CREATE_NON_FUNGIBLE_TOKEN_WITH_CUSTOM_FEES_V1)) { - return decoder.decodeCreateNonFungibleWithCustomFeesV1( - inputBytes, senderId, nativeOperations, addressIdConverter); - } else if (attempt.isSelector(CREATE_NON_FUNGIBLE_TOKEN_WITH_CUSTOM_FEES_V2)) { - return decoder.decodeCreateNonFungibleWithCustomFeesV2( - inputBytes, senderId, nativeOperations, addressIdConverter); - } else { - return decoder.decodeCreateNonFungibleWithCustomFeesV3( - inputBytes, senderId, nativeOperations, addressIdConverter); - } + return createSelectorsMap.entrySet().stream() + .filter(entry -> attempt.isSelector(entry.getKey())) + .map(entry -> entry.getValue().decode(inputBytes, senderId, nativeOperations, addressIdConverter)) + .findFirst() + .orElse(null); } } diff --git a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/tokenkey/TokenKeyTranslator.java b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/tokenkey/TokenKeyTranslator.java index 468d4c3e7053..780ef94ba69b 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/tokenkey/TokenKeyTranslator.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/tokenkey/TokenKeyTranslator.java @@ -26,6 +26,7 @@ import com.hedera.node.app.service.contract.impl.exec.systemcontracts.common.Call; import com.hedera.node.app.service.contract.impl.exec.systemcontracts.hts.HtsCallAttempt; import com.hedera.node.app.service.contract.impl.exec.systemcontracts.hts.ReturnTypes; +import com.hedera.node.config.data.ContractsConfig; import edu.umd.cs.findbugs.annotations.NonNull; import java.math.BigInteger; import javax.inject.Inject; @@ -56,15 +57,19 @@ public Call callFrom(@NonNull final HtsCallAttempt attempt) { final var args = TOKEN_KEY.decodeCall(attempt.input().toArrayUnsafe()); final var token = attempt.linkedToken(fromHeadlongAddress(args.get(0))); final BigInteger keyType = args.get(1); + + final boolean metadataSupport = + attempt.configuration().getConfigData(ContractsConfig.class).metadataKeyAndFieldEnabled(); return new TokenKeyCall( attempt.systemContractGasCalculator(), attempt.enhancement(), attempt.isStaticCall(), token, - getTokenKey(token, keyType.intValue())); + getTokenKey(token, keyType.intValue(), metadataSupport)); } - private Key getTokenKey(Token token, int keyType) throws InvalidTransactionException { + public Key getTokenKey(final Token token, final int keyType, final boolean metadataSupport) + throws InvalidTransactionException { if (token == null) { return null; } @@ -76,6 +81,7 @@ private Key getTokenKey(Token token, int keyType) throws InvalidTransactionExcep case 16 -> token.supplyKey(); case 32 -> token.feeScheduleKey(); case 64 -> token.pauseKey(); + case 128 -> metadataSupport ? token.metadataKey() : null; default -> null; }; } diff --git a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/update/UpdateDecoder.java b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/update/UpdateDecoder.java index d49314754f2b..119d176031da 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/update/UpdateDecoder.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/update/UpdateDecoder.java @@ -33,6 +33,7 @@ import com.hedera.hapi.node.state.token.Token; import com.hedera.hapi.node.token.TokenUpdateNftsTransactionBody; import com.hedera.hapi.node.token.TokenUpdateTransactionBody; +import com.hedera.hapi.node.token.TokenUpdateTransactionBody.Builder; import com.hedera.hapi.node.transaction.TransactionBody; import com.hedera.node.app.service.contract.impl.exec.systemcontracts.hts.AddressIdConverter; import com.hedera.node.app.service.contract.impl.exec.systemcontracts.hts.DispatchForResponseCodeHtsCall; @@ -41,6 +42,7 @@ import com.hedera.node.app.service.contract.impl.exec.utils.TokenExpiryWrapper; import com.hedera.node.app.service.contract.impl.exec.utils.TokenKeyWrapper; import com.hedera.node.app.service.contract.impl.utils.ConversionUtils; +import com.hedera.node.config.data.ContractsConfig; import com.hedera.pbj.runtime.io.buffer.Bytes; import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.Nullable; @@ -110,7 +112,8 @@ private static boolean isKnownImmutable(@Nullable final Token token) { public @Nullable TransactionBody decodeTokenUpdateV1(@NonNull final HtsCallAttempt attempt) { final var call = UpdateTranslator.TOKEN_UPDATE_INFO_FUNCTION_V1.decodeCall( attempt.input().toArrayUnsafe()); - return decodeTokenUpdate(call, attempt.addressIdConverter()); + final var decoded = decodeTokenUpdate(call, attempt.addressIdConverter()); + return TransactionBody.newBuilder().tokenUpdate(decoded).build(); } /** @@ -122,7 +125,21 @@ private static boolean isKnownImmutable(@Nullable final Token token) { public @Nullable TransactionBody decodeTokenUpdateV2(@NonNull final HtsCallAttempt attempt) { final var call = UpdateTranslator.TOKEN_UPDATE_INFO_FUNCTION_V2.decodeCall( attempt.input().toArrayUnsafe()); - return decodeTokenUpdate(call, attempt.addressIdConverter()); + final var decoded = decodeTokenUpdate(call, attempt.addressIdConverter()); + return TransactionBody.newBuilder().tokenUpdate(decoded).build(); + } + + /** + * Decodes a call to {@link UpdateTranslator#TOKEN_UPDATE_INFO_FUNCTION_WITH_METADATA} into a synthetic {@link TransactionBody}. + * + * @param attempt the attempt + * @return the synthetic transaction body + */ + public TransactionBody decodeTokenUpdateWithMetadata(@NonNull final HtsCallAttempt attempt) { + final var call = UpdateTranslator.TOKEN_UPDATE_INFO_FUNCTION_WITH_METADATA.decodeCall( + attempt.input().toArrayUnsafe()); + final var decoded = decodeUpdateWithMeta(call, attempt.addressIdConverter()); + return TransactionBody.newBuilder().tokenUpdate(decoded).build(); } /** @@ -134,7 +151,8 @@ private static boolean isKnownImmutable(@Nullable final Token token) { public @Nullable TransactionBody decodeTokenUpdateV3(@NonNull final HtsCallAttempt attempt) { final var call = UpdateTranslator.TOKEN_UPDATE_INFO_FUNCTION_V3.decodeCall( attempt.input().toArrayUnsafe()); - return decodeTokenUpdate(call, attempt.addressIdConverter()); + final var decoded = decodeTokenUpdate(call, attempt.addressIdConverter()); + return TransactionBody.newBuilder().tokenUpdate(decoded).build(); } /** @@ -161,7 +179,7 @@ public TransactionBody decodeTokenUpdateExpiryV2(@NonNull final HtsCallAttempt a return decodeTokenUpdateExpiry(call, attempt.addressIdConverter()); } - private @Nullable TransactionBody decodeTokenUpdate( + private TokenUpdateTransactionBody.Builder decodeTokenUpdate( @NonNull final Tuple call, @NonNull final AddressIdConverter addressIdConverter) { final var tokenId = ConversionUtils.asTokenId(call.get(TOKEN_ADDRESS)); final var hederaToken = (Tuple) call.get(HEDERA_TOKEN); @@ -200,16 +218,28 @@ public TransactionBody decodeTokenUpdateExpiryV2(@NonNull final HtsCallAttempt a && tokenExpiry.autoRenewPeriod().seconds() != 0) { txnBodyBuilder.autoRenewPeriod(tokenExpiry.autoRenewPeriod()); } + addKeys(tokenKeys, txnBodyBuilder); + return txnBodyBuilder; + } - try { - return bodyWith(tokenKeys, txnBodyBuilder); - } catch (IllegalArgumentException ignore) { - return null; + public TokenUpdateTransactionBody.Builder decodeUpdateWithMeta( + @NonNull final Tuple call, @NonNull final AddressIdConverter addressIdConverter) { + final var tokenUpdateTransactionBody = decodeTokenUpdate(call, addressIdConverter); + final var hederaToken = (Tuple) call.get(HEDERA_TOKEN); + final Bytes tokenMetadata = hederaToken.size() > 9 ? Bytes.wrap((byte[]) hederaToken.get(9)) : null; + if (tokenMetadata != null && tokenMetadata.length() > 0) { + tokenUpdateTransactionBody.metadata(tokenMetadata); } + final List tokenKeys = decodeTokenKeys(hederaToken.get(7), addressIdConverter); + addKeys(tokenKeys, tokenUpdateTransactionBody); + addMetaKey(tokenKeys, tokenUpdateTransactionBody); + return tokenUpdateTransactionBody; } @Nullable public TransactionBody decodeTokenUpdateKeys(@NonNull final HtsCallAttempt attempt) { + final boolean metadataSupport = + attempt.configuration().getConfigData(ContractsConfig.class).metadataKeyAndFieldEnabled(); final var call = UpdateKeysTranslator.TOKEN_UPDATE_KEYS_FUNCTION.decodeCall( attempt.input().toArrayUnsafe()); @@ -219,9 +249,12 @@ public TransactionBody decodeTokenUpdateKeys(@NonNull final HtsCallAttempt attem // Build the transaction body final var txnBodyBuilder = TokenUpdateTransactionBody.newBuilder(); txnBodyBuilder.token(tokenId); - + addKeys(tokenKeys, txnBodyBuilder); + if (metadataSupport) { + addMetaKey(tokenKeys, txnBodyBuilder); + } try { - return bodyWith(tokenKeys, txnBodyBuilder); + return TransactionBody.newBuilder().tokenUpdate(txnBodyBuilder).build(); } catch (IllegalArgumentException ignore) { return null; } @@ -243,36 +276,47 @@ public TransactionBody decodeUpdateNFTsMetadata(@NonNull final HtsCallAttempt at return TransactionBody.newBuilder().tokenUpdateNfts(txnBodyBuilder).build(); } - private TransactionBody bodyWith( - final List tokenKeys, final TokenUpdateTransactionBody.Builder builder) { + private void addKeys(final List tokenKeys, final TokenUpdateTransactionBody.Builder builder) { tokenKeys.forEach(tokenKeyWrapper -> { final var key = tokenKeyWrapper.key().asGrpc(); if (key == Key.DEFAULT) { throw new IllegalArgumentException(); } - if (tokenKeyWrapper.isUsedForAdminKey()) { - builder.adminKey(key); - } - if (tokenKeyWrapper.isUsedForKycKey()) { - builder.kycKey(key); - } - if (tokenKeyWrapper.isUsedForFreezeKey()) { - builder.freezeKey(key); - } - if (tokenKeyWrapper.isUsedForWipeKey()) { - builder.wipeKey(key); - } - if (tokenKeyWrapper.isUsedForSupplyKey()) { - builder.supplyKey(key); - } - if (tokenKeyWrapper.isUsedForFeeScheduleKey()) { - builder.feeScheduleKey(key); - } - if (tokenKeyWrapper.isUsedForPauseKey()) { - builder.pauseKey(key); + setUsedKeys(builder, tokenKeyWrapper, key); + }); + } + + private void setUsedKeys(Builder builder, TokenKeyWrapper tokenKeyWrapper, Key key) { + if (tokenKeyWrapper.isUsedForAdminKey()) { + builder.adminKey(key); + } + if (tokenKeyWrapper.isUsedForKycKey()) { + builder.kycKey(key); + } + if (tokenKeyWrapper.isUsedForFreezeKey()) { + builder.freezeKey(key); + } + if (tokenKeyWrapper.isUsedForWipeKey()) { + builder.wipeKey(key); + } + if (tokenKeyWrapper.isUsedForSupplyKey()) { + builder.supplyKey(key); + } + if (tokenKeyWrapper.isUsedForFeeScheduleKey()) { + builder.feeScheduleKey(key); + } + if (tokenKeyWrapper.isUsedForPauseKey()) { + builder.pauseKey(key); + } + } + + private void addMetaKey(final List tokenKeys, final TokenUpdateTransactionBody.Builder builder) { + tokenKeys.forEach(tokenKeyWrapper -> { + final var key = tokenKeyWrapper.key().asGrpc(); + if (tokenKeyWrapper.isUsedForMetadataKey()) { + builder.metadataKey(key); } }); - return TransactionBody.newBuilder().tokenUpdate(builder).build(); } private TransactionBody decodeTokenUpdateExpiry( @@ -299,7 +343,7 @@ private TransactionBody decodeTokenUpdateExpiry( return TransactionBody.newBuilder().tokenUpdate(txnBodyBuilder).build(); } - private static List decodeTokenKeys( + private List decodeTokenKeys( @NonNull final Tuple[] tokenKeysTuples, @NonNull final AddressIdConverter addressIdConverter) { final List tokenKeys = new ArrayList<>(tokenKeysTuples.length); for (final var tokenKeyTuple : tokenKeysTuples) { @@ -325,13 +369,13 @@ private static List decodeTokenKeys( return tokenKeys; } - private static TokenExpiryWrapper decodeTokenExpiry( + private TokenExpiryWrapper decodeTokenExpiry( @NonNull final Tuple expiryTuple, @NonNull final AddressIdConverter addressIdConverter) { final var second = (long) expiryTuple.get(0); final var autoRenewAccount = addressIdConverter.convert(expiryTuple.get(1)); final var autoRenewPeriod = Duration.newBuilder().seconds(expiryTuple.get(2)).build(); return new TokenExpiryWrapper( - second, autoRenewAccount.accountNum() == 0 ? null : autoRenewAccount, autoRenewPeriod); + second, autoRenewAccount.accountNumOrElse(0L) == 0 ? null : autoRenewAccount, autoRenewPeriod); } } diff --git a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/update/UpdateDecoderFunction.java b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/update/UpdateDecoderFunction.java new file mode 100644 index 000000000000..15fb96902014 --- /dev/null +++ b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/update/UpdateDecoderFunction.java @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2024 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.service.contract.impl.exec.systemcontracts.hts.update; + +import com.hedera.hapi.node.transaction.TransactionBody; +import com.hedera.node.app.service.contract.impl.exec.systemcontracts.hts.HtsCallAttempt; +import edu.umd.cs.findbugs.annotations.NonNull; + +@FunctionalInterface +public interface UpdateDecoderFunction { + + TransactionBody decode(@NonNull final HtsCallAttempt input); +} diff --git a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/update/UpdateTranslator.java b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/update/UpdateTranslator.java index cf5c3eb35deb..3482224681cd 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/update/UpdateTranslator.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/update/UpdateTranslator.java @@ -19,6 +19,7 @@ import static com.hedera.node.app.hapi.utils.contracts.ParsingConstants.ARRAY_BRACKETS; import static com.hedera.node.app.hapi.utils.contracts.ParsingConstants.EXPIRY; import static com.hedera.node.app.hapi.utils.contracts.ParsingConstants.EXPIRY_V2; +import static com.hedera.node.app.hapi.utils.contracts.ParsingConstants.HEDERA_TOKEN_WITH_METADATA; import static com.hedera.node.app.hapi.utils.contracts.ParsingConstants.TOKEN_KEY; import com.esaulpaugh.headlong.abi.Function; @@ -32,7 +33,10 @@ import com.hedera.node.app.service.contract.impl.exec.systemcontracts.hts.HtsCallAttempt; import com.hedera.node.app.service.contract.impl.exec.systemcontracts.hts.ReturnTypes; import com.hedera.node.app.service.contract.impl.hevm.HederaWorldUpdater; +import com.hedera.node.config.data.ContractsConfig; import edu.umd.cs.findbugs.annotations.NonNull; +import java.util.HashMap; +import java.util.Map; import javax.inject.Inject; public class UpdateTranslator extends AbstractCallTranslator { @@ -49,19 +53,28 @@ public class UpdateTranslator extends AbstractCallTranslator { new Function(UPDATE_TOKEN_INFO_STRING + HEDERA_TOKEN_STRUCT_V2 + ")", ReturnTypes.INT); public static final Function TOKEN_UPDATE_INFO_FUNCTION_V3 = new Function(UPDATE_TOKEN_INFO_STRING + HEDERA_TOKEN_STRUCT_V3 + ")", ReturnTypes.INT); + public static final Function TOKEN_UPDATE_INFO_FUNCTION_WITH_METADATA = + new Function(UPDATE_TOKEN_INFO_STRING + HEDERA_TOKEN_WITH_METADATA + ")", ReturnTypes.INT); - private final UpdateDecoder decoder; + private static final Map updateSelectorsMap = new HashMap<>(); @Inject - public UpdateTranslator(UpdateDecoder decoder) { - // Dagger2 - this.decoder = decoder; + public UpdateTranslator(final UpdateDecoder decoder) { + updateSelectorsMap.put(TOKEN_UPDATE_INFO_FUNCTION_V1, decoder::decodeTokenUpdateV1); + updateSelectorsMap.put(TOKEN_UPDATE_INFO_FUNCTION_V2, decoder::decodeTokenUpdateV2); + updateSelectorsMap.put(TOKEN_UPDATE_INFO_FUNCTION_V3, decoder::decodeTokenUpdateV3); + updateSelectorsMap.put(TOKEN_UPDATE_INFO_FUNCTION_WITH_METADATA, decoder::decodeTokenUpdateWithMetadata); } @Override public boolean matches(@NonNull HtsCallAttempt attempt) { - return attempt.isSelector( - TOKEN_UPDATE_INFO_FUNCTION_V1, TOKEN_UPDATE_INFO_FUNCTION_V2, TOKEN_UPDATE_INFO_FUNCTION_V3); + final boolean metadataSupport = + attempt.configuration().getConfigData(ContractsConfig.class).metadataKeyAndFieldEnabled(); + + return updateSelectorsMap.keySet().stream() + .anyMatch(selector -> selector.equals(TOKEN_UPDATE_INFO_FUNCTION_WITH_METADATA) + ? attempt.isSelectorIfConfigEnabled(selector, metadataSupport) + : attempt.isSelector(selector)); } @Override @@ -79,12 +92,10 @@ public static long gasRequirement( } private TransactionBody nominalBodyFor(@NonNull final HtsCallAttempt attempt) { - if (attempt.isSelector(TOKEN_UPDATE_INFO_FUNCTION_V1)) { - return decoder.decodeTokenUpdateV1(attempt); - } else if (attempt.isSelector(TOKEN_UPDATE_INFO_FUNCTION_V2)) { - return decoder.decodeTokenUpdateV2(attempt); - } else { - return decoder.decodeTokenUpdateV3(attempt); - } + return updateSelectorsMap.entrySet().stream() + .filter(entry -> attempt.isSelector(entry.getKey())) + .map(entry -> entry.getValue().decode(attempt)) + .findFirst() + .orElse(null); } } diff --git a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/utils/TokenCreateWrapper.java b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/utils/TokenCreateWrapper.java index 6bcf3e7ef31c..064226343a39 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/utils/TokenCreateWrapper.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/utils/TokenCreateWrapper.java @@ -27,6 +27,7 @@ import com.hedera.hapi.node.transaction.FractionalFee; import com.hedera.hapi.node.transaction.RoyaltyFee; import com.hedera.node.app.hapi.utils.InvalidTransactionException; +import com.hedera.pbj.runtime.io.buffer.Bytes; import com.hederahashgraph.api.proto.java.ResponseCodeEnum; import java.util.List; @@ -46,8 +47,8 @@ public class TokenCreateWrapper { private List fixedFees; private List fractionalFees; - private List royaltyFees; + private Bytes metadata; public TokenCreateWrapper( final boolean isFungible, @@ -77,6 +78,7 @@ public TokenCreateWrapper( this.fixedFees = List.of(); this.fractionalFees = List.of(); this.royaltyFees = List.of(); + this.metadata = Bytes.EMPTY; } public boolean isFungible() { @@ -127,6 +129,10 @@ public TokenExpiryWrapper getExpiry() { return expiry; } + public Bytes getMetadata() { + return metadata; + } + public List getFixedFees() { return fixedFees; } @@ -139,6 +145,10 @@ public List getRoyaltyFees() { return royaltyFees; } + public void setMetadata(final Bytes metadata) { + this.metadata = metadata; + } + public void setFixedFees(final List fixedFees) { this.fixedFees = fixedFees; } diff --git a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/utils/TokenKeyWrapper.java b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/utils/TokenKeyWrapper.java index ba7808c17a41..730208e0e3f1 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/utils/TokenKeyWrapper.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/utils/TokenKeyWrapper.java @@ -44,4 +44,8 @@ public boolean isUsedForFeeScheduleKey() { public boolean isUsedForPauseKey() { return (keyType & 64) != 0; } + + public boolean isUsedForMetadataKey() { + return (keyType & 128) != 0; + } } diff --git a/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/systemcontracts/hts/create/ClassicCreatesCallTest.java b/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/systemcontracts/hts/create/ClassicCreatesCallTest.java index fe4ac20ad676..fc8ef728b6b8 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/systemcontracts/hts/create/ClassicCreatesCallTest.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/systemcontracts/hts/create/ClassicCreatesCallTest.java @@ -28,7 +28,6 @@ import static com.hedera.node.app.service.contract.impl.test.TestHelpers.EIP_1014_ADDRESS; import static com.hedera.node.app.service.contract.impl.test.TestHelpers.FUNGIBLE_TOKEN_ID; import static com.hedera.node.app.service.contract.impl.test.TestHelpers.SENDER_ID; -import static com.hedera.node.app.service.contract.impl.test.TestHelpers.asHeadlongAddress; import static com.hedera.node.app.service.contract.impl.test.TestHelpers.readableRevertReason; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.ArgumentMatchers.any; @@ -134,6 +133,22 @@ void createFungibleTokenHappyPathV3() { result.getOutput()); } + @Test + void createFungibleTokenWitheMetaHappyPath() { + commonGivens(); + given(recordBuilder.status()).willReturn(SUCCESS); + + final var result = subject.execute(frame).fullResult().result(); + + assertEquals(MessageFrame.State.COMPLETED_SUCCESS, result.getState()); + assertEquals( + Bytes.wrap(CreateTranslator.CREATE_FUNGIBLE_TOKEN_WITH_METADATA + .getOutputs() + .encodeElements((long) SUCCESS.protoOrdinal(), tokenId) + .array()), + result.getOutput()); + } + @Test void createFungibleTokenWithCustomFeesHappyPathV1() { commonGivens(); @@ -182,6 +197,22 @@ void createFungibleTokenWithCustomFeesHappyPathV3() { result.getOutput()); } + @Test + void createFungibleTokenWithMetaAndCustomFeesHappyPath() { + commonGivens(); + given(recordBuilder.status()).willReturn(SUCCESS); + + final var result = subject.execute(frame).fullResult().result(); + + assertEquals(MessageFrame.State.COMPLETED_SUCCESS, result.getState()); + assertEquals( + Bytes.wrap(CreateTranslator.CREATE_FUNGIBLE_TOKEN_WITH_METADATA_AND_CUSTOM_FEES + .getOutputs() + .encodeElements((long) SUCCESS.protoOrdinal(), tokenId) + .array()), + result.getOutput()); + } + @Test void createNonFungibleTokenHappyPathV1() { commonGivens(); @@ -230,6 +261,22 @@ void createNonFungibleTokenHappyPathV3() { result.getOutput()); } + @Test + void createNonFungibleTokenWithMetaHappyPath() { + commonGivens(); + given(recordBuilder.status()).willReturn(SUCCESS); + + final var result = subject.execute(frame).fullResult().result(); + + assertEquals(MessageFrame.State.COMPLETED_SUCCESS, result.getState()); + assertEquals( + Bytes.wrap(CreateTranslator.CREATE_NON_FUNGIBLE_TOKEN_WITH_METADATA + .getOutputs() + .encodeElements((long) SUCCESS.protoOrdinal(), tokenId) + .array()), + result.getOutput()); + } + @Test void createNonFungibleTokenWithCustomFeesHappyPathV1() { commonGivens(); @@ -278,6 +325,22 @@ void createNonFungibleTokenWithCustomFeesHappyPathV3() { result.getOutput()); } + @Test + void createNonFungibleTokenWithMetaAndCustomFeesHappyPath() { + commonGivens(); + given(recordBuilder.status()).willReturn(SUCCESS); + + final var result = subject.execute(frame).fullResult().result(); + + assertEquals(MessageFrame.State.COMPLETED_SUCCESS, result.getState()); + assertEquals( + Bytes.wrap(CreateTranslator.CREATE_NON_FUNGIBLE_TOKEN_WITH_METADATA_AND_CUSTOM_FEES + .getOutputs() + .encodeElements((long) SUCCESS.protoOrdinal(), tokenId) + .array()), + result.getOutput()); + } + @Test void requiresNonGasCostToBeProvidedAsValue() { commonGivens(200_000L, 99_999L, true); @@ -315,8 +378,6 @@ private void commonGivens(long baseCost, long value, boolean shouldBePreempted) given(frame.getValue()).willReturn(Wei.of(value)); given(gasCalculator.feeCalculatorPriceInTinyBars(any(), any())).willReturn(baseCost); stack.push(frame); - given(addressIdConverter.convert(asHeadlongAddress(FRAME_SENDER_ADDRESS))) - .willReturn(A_NEW_ACCOUNT_ID); if (!shouldBePreempted) { given(frame.getMessageFrameStack()).willReturn(stack); @@ -332,12 +393,7 @@ private void commonGivens(long baseCost, long value, boolean shouldBePreempted) given(frame.getBlockValues()).willReturn(blockValues); given(blockValues.getTimestamp()).willReturn(Timestamp.DEFAULT.seconds()); subject = new ClassicCreatesCall( - gasCalculator, - mockEnhancement(), - PRETEND_CREATE_TOKEN, - verificationStrategy, - FRAME_SENDER_ADDRESS, - addressIdConverter); + gasCalculator, mockEnhancement(), PRETEND_CREATE_TOKEN, verificationStrategy, A_NEW_ACCOUNT_ID); lenient().when(recordBuilder.tokenID()).thenReturn(FUNGIBLE_TOKEN_ID); } diff --git a/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/systemcontracts/hts/create/CreateDecoderTest.java b/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/systemcontracts/hts/create/CreateDecoderTest.java new file mode 100644 index 000000000000..c3789f3e495f --- /dev/null +++ b/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/systemcontracts/hts/create/CreateDecoderTest.java @@ -0,0 +1,314 @@ +/* + * Copyright (C) 2024 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.service.contract.impl.test.exec.systemcontracts.hts.create; + +import static com.hedera.hapi.node.base.TokenSupplyType.FINITE; +import static com.hedera.node.app.service.contract.impl.test.TestHelpers.FUNGIBLE_TOKEN_ID; +import static com.hedera.node.app.service.contract.impl.test.TestHelpers.SENDER_ID; +import static com.hedera.node.app.service.contract.impl.test.exec.systemcontracts.hts.create.CreateTestHelper.CREATE_FUNGIBLE_V1_TUPLE; +import static com.hedera.node.app.service.contract.impl.test.exec.systemcontracts.hts.create.CreateTestHelper.CREATE_FUNGIBLE_V2_TUPLE; +import static com.hedera.node.app.service.contract.impl.test.exec.systemcontracts.hts.create.CreateTestHelper.CREATE_FUNGIBLE_V3_TUPLE; +import static com.hedera.node.app.service.contract.impl.test.exec.systemcontracts.hts.create.CreateTestHelper.CREATE_FUNGIBLE_WITH_FEES_V1_TUPLE; +import static com.hedera.node.app.service.contract.impl.test.exec.systemcontracts.hts.create.CreateTestHelper.CREATE_FUNGIBLE_WITH_FEES_V2_TUPLE; +import static com.hedera.node.app.service.contract.impl.test.exec.systemcontracts.hts.create.CreateTestHelper.CREATE_FUNGIBLE_WITH_FEES_V3_TUPLE; +import static com.hedera.node.app.service.contract.impl.test.exec.systemcontracts.hts.create.CreateTestHelper.CREATE_FUNGIBLE_WITH_META_AND_FEES_TUPLE; +import static com.hedera.node.app.service.contract.impl.test.exec.systemcontracts.hts.create.CreateTestHelper.CREATE_FUNGIBLE_WITH_META_TUPLE; +import static com.hedera.node.app.service.contract.impl.test.exec.systemcontracts.hts.create.CreateTestHelper.CREATE_NON_FUNGIBLE_V1_TUPLE; +import static com.hedera.node.app.service.contract.impl.test.exec.systemcontracts.hts.create.CreateTestHelper.CREATE_NON_FUNGIBLE_V2_TUPLE; +import static com.hedera.node.app.service.contract.impl.test.exec.systemcontracts.hts.create.CreateTestHelper.CREATE_NON_FUNGIBLE_V3_TUPLE; +import static com.hedera.node.app.service.contract.impl.test.exec.systemcontracts.hts.create.CreateTestHelper.CREATE_NON_FUNGIBLE_WITH_FEES_V1_TUPLE; +import static com.hedera.node.app.service.contract.impl.test.exec.systemcontracts.hts.create.CreateTestHelper.CREATE_NON_FUNGIBLE_WITH_FEES_V2_TUPLE; +import static com.hedera.node.app.service.contract.impl.test.exec.systemcontracts.hts.create.CreateTestHelper.CREATE_NON_FUNGIBLE_WITH_FEES_V3_TUPLE; +import static com.hedera.node.app.service.contract.impl.test.exec.systemcontracts.hts.create.CreateTestHelper.CREATE_NON_FUNGIBLE_WITH_META_AND_FEES_TUPLE; +import static com.hedera.node.app.service.contract.impl.test.exec.systemcontracts.hts.create.CreateTestHelper.CREATE_NON_FUNGIBLE_WITH_META_TUPLE; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatList; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.BDDMockito.given; + +import com.hedera.hapi.node.base.TokenType; +import com.hedera.hapi.node.transaction.CustomFee; +import com.hedera.hapi.node.transaction.TransactionBody; +import com.hedera.node.app.service.contract.impl.exec.scope.HederaNativeOperations; +import com.hedera.node.app.service.contract.impl.exec.systemcontracts.hts.AddressIdConverter; +import com.hedera.node.app.service.contract.impl.exec.systemcontracts.hts.create.CreateDecoder; +import com.hedera.node.app.service.contract.impl.exec.systemcontracts.hts.create.CreateTranslator; +import com.hedera.pbj.runtime.io.buffer.Bytes; +import java.util.List; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +class CreateDecoderTest { + + @Mock + private HederaNativeOperations nativeOperations; + + @Mock + private AddressIdConverter addressIdConverter; + + private CreateDecoder subject; + + @BeforeEach + void setUp() { + subject = new CreateDecoder(); + given(addressIdConverter.convert(any())).willReturn(SENDER_ID); + } + + @Test + void decodeCreateTokenV1() { + byte[] inputBytes = CreateTranslator.CREATE_FUNGIBLE_TOKEN_V1 + .encodeCall(CREATE_FUNGIBLE_V1_TUPLE) + .array(); + final TransactionBody transaction = + subject.decodeCreateFungibleTokenV1(inputBytes, SENDER_ID, nativeOperations, addressIdConverter); + tokenAssertions(transaction); + assertThatList(transaction.tokenCreation().customFees()).isEmpty(); + } + + @Test + void decodeCreateTokenV2() { + byte[] inputBytes = CreateTranslator.CREATE_FUNGIBLE_TOKEN_V2 + .encodeCall(CREATE_FUNGIBLE_V2_TUPLE) + .array(); + final TransactionBody transaction = + subject.decodeCreateFungibleTokenV2(inputBytes, SENDER_ID, nativeOperations, addressIdConverter); + tokenAssertions(transaction); + assertThatList(transaction.tokenCreation().customFees()).isEmpty(); + } + + @Test + void decodeCreateTokenV3() { + byte[] inputBytes = CreateTranslator.CREATE_FUNGIBLE_TOKEN_V3 + .encodeCall(CREATE_FUNGIBLE_V3_TUPLE) + .array(); + final TransactionBody transaction = + subject.decodeCreateFungibleTokenV3(inputBytes, SENDER_ID, nativeOperations, addressIdConverter); + tokenAssertions(transaction); + assertThatList(transaction.tokenCreation().customFees()).isEmpty(); + } + + @Test + void decodeCreateTokenWithMeta() { + byte[] inputBytes = CreateTranslator.CREATE_FUNGIBLE_TOKEN_WITH_METADATA + .encodeCall(CREATE_FUNGIBLE_WITH_META_TUPLE) + .array(); + final TransactionBody transaction = subject.decodeCreateFungibleTokenWithMetadata( + inputBytes, SENDER_ID, nativeOperations, addressIdConverter); + tokenAssertions(transaction); + assertThatList(transaction.tokenCreation().customFees()).isEmpty(); + assertNotNull(transaction.tokenCreation().metadata()); + assertEquals(Bytes.wrap("metadata"), transaction.tokenCreation().metadata()); + } + + @Test + void decodeCreateTokenWithCustomFeesV1() { + byte[] inputBytes = CreateTranslator.CREATE_FUNGIBLE_WITH_CUSTOM_FEES_V1 + .encodeCall(CREATE_FUNGIBLE_WITH_FEES_V1_TUPLE) + .array(); + final TransactionBody transaction = subject.decodeCreateFungibleTokenWithCustomFeesV1( + inputBytes, SENDER_ID, nativeOperations, addressIdConverter); + tokenAssertions(transaction); + assertThatList(transaction.tokenCreation().customFees()).isNotEmpty(); + customFeesAssertions(transaction.tokenCreation().customFees()); + } + + @Test + void decodeCreateTokenWithCustomFeesV2() { + byte[] inputBytes = CreateTranslator.CREATE_FUNGIBLE_WITH_CUSTOM_FEES_V2 + .encodeCall(CREATE_FUNGIBLE_WITH_FEES_V2_TUPLE) + .array(); + final TransactionBody transaction = subject.decodeCreateFungibleTokenWithCustomFeesV2( + inputBytes, SENDER_ID, nativeOperations, addressIdConverter); + tokenAssertions(transaction); + assertThatList(transaction.tokenCreation().customFees()).isNotEmpty(); + customFeesAssertions(transaction.tokenCreation().customFees()); + } + + @Test + void decodeCreateTokenWithCustomFeesV3() { + byte[] inputBytes = CreateTranslator.CREATE_FUNGIBLE_WITH_CUSTOM_FEES_V3 + .encodeCall(CREATE_FUNGIBLE_WITH_FEES_V3_TUPLE) + .array(); + final TransactionBody transaction = subject.decodeCreateFungibleTokenWithCustomFeesV3( + inputBytes, SENDER_ID, nativeOperations, addressIdConverter); + tokenAssertions(transaction); + assertThatList(transaction.tokenCreation().customFees()).isNotEmpty(); + customFeesAssertions(transaction.tokenCreation().customFees()); + } + + @Test + void decodeCreateTokenWithMetaAndCustomFees() { + byte[] inputBytes = CreateTranslator.CREATE_FUNGIBLE_TOKEN_WITH_METADATA_AND_CUSTOM_FEES + .encodeCall(CREATE_FUNGIBLE_WITH_META_AND_FEES_TUPLE) + .array(); + final TransactionBody transaction = subject.decodeCreateFungibleTokenWithMetadataAndCustomFees( + inputBytes, SENDER_ID, nativeOperations, addressIdConverter); + tokenAssertions(transaction); + assertThatList(transaction.tokenCreation().customFees()).isNotEmpty(); + customFeesAssertions(transaction.tokenCreation().customFees()); + assertNotNull(transaction.tokenCreation().metadata()); + assertEquals(Bytes.wrap("metadata"), transaction.tokenCreation().metadata()); + } + + @Test + void decodeCreateNonFungibleV1() { + byte[] inputBytes = CreateTranslator.CREATE_NON_FUNGIBLE_TOKEN_V1 + .encodeCall(CREATE_NON_FUNGIBLE_V1_TUPLE) + .array(); + final TransactionBody transaction = + subject.decodeCreateNonFungibleV1(inputBytes, SENDER_ID, nativeOperations, addressIdConverter); + nftAssertions(transaction); + assertThatList(transaction.tokenCreation().customFees()).isEmpty(); + } + + @Test + void decodeCreateNonFungibleV2() { + byte[] inputBytes = CreateTranslator.CREATE_NON_FUNGIBLE_TOKEN_V2 + .encodeCall(CREATE_NON_FUNGIBLE_V2_TUPLE) + .array(); + final TransactionBody transaction = + subject.decodeCreateNonFungibleV2(inputBytes, SENDER_ID, nativeOperations, addressIdConverter); + nftAssertions(transaction); + assertThatList(transaction.tokenCreation().customFees()).isEmpty(); + } + + @Test + void decodeCreateNonFungibleV3() { + byte[] inputBytes = CreateTranslator.CREATE_NON_FUNGIBLE_TOKEN_V3 + .encodeCall(CREATE_NON_FUNGIBLE_V3_TUPLE) + .array(); + final TransactionBody transaction = + subject.decodeCreateNonFungibleV3(inputBytes, SENDER_ID, nativeOperations, addressIdConverter); + nftAssertions(transaction); + assertThatList(transaction.tokenCreation().customFees()).isEmpty(); + } + + @Test + void decodeCreateNonFungibleWithMetadata() { + byte[] inputBytes = CreateTranslator.CREATE_NON_FUNGIBLE_TOKEN_WITH_METADATA + .encodeCall(CREATE_NON_FUNGIBLE_WITH_META_TUPLE) + .array(); + final TransactionBody transaction = subject.decodeCreateNonFungibleWithMetadata( + inputBytes, SENDER_ID, nativeOperations, addressIdConverter); + nftAssertions(transaction); + assertThatList(transaction.tokenCreation().customFees()).isEmpty(); + assertNotNull(transaction.tokenCreation().metadata()); + assertEquals(Bytes.wrap("metadata"), transaction.tokenCreation().metadata()); + } + + @Test + void decodeCreateNonFungibleWithCustomFeesV1() { + byte[] inputBytes = CreateTranslator.CREATE_NON_FUNGIBLE_TOKEN_WITH_CUSTOM_FEES_V1 + .encodeCall(CREATE_NON_FUNGIBLE_WITH_FEES_V1_TUPLE) + .array(); + final TransactionBody transaction = subject.decodeCreateNonFungibleWithCustomFeesV1( + inputBytes, SENDER_ID, nativeOperations, addressIdConverter); + nftAssertions(transaction); + assertThatList(transaction.tokenCreation().customFees()).isNotEmpty(); + customFeesAssertions(transaction.tokenCreation().customFees()); + } + + @Test + void decodeCreateNonFungibleWithCustomFeesV2() { + byte[] inputBytes = CreateTranslator.CREATE_NON_FUNGIBLE_TOKEN_WITH_CUSTOM_FEES_V2 + .encodeCall(CREATE_NON_FUNGIBLE_WITH_FEES_V2_TUPLE) + .array(); + final TransactionBody transaction = subject.decodeCreateNonFungibleWithCustomFeesV2( + inputBytes, SENDER_ID, nativeOperations, addressIdConverter); + nftAssertions(transaction); + assertThatList(transaction.tokenCreation().customFees()).isNotEmpty(); + customFeesAssertions(transaction.tokenCreation().customFees()); + } + + @Test + void decodeCreateNonFungibleWithCustomFeesV3() { + byte[] inputBytes = CreateTranslator.CREATE_NON_FUNGIBLE_TOKEN_WITH_CUSTOM_FEES_V3 + .encodeCall(CREATE_NON_FUNGIBLE_WITH_FEES_V3_TUPLE) + .array(); + final TransactionBody transaction = subject.decodeCreateNonFungibleWithCustomFeesV3( + inputBytes, SENDER_ID, nativeOperations, addressIdConverter); + nftAssertions(transaction); + assertThatList(transaction.tokenCreation().customFees()).isNotEmpty(); + customFeesAssertions(transaction.tokenCreation().customFees()); + } + + @Test + void decodeCreateNonFungibleWithMetadataAndCustomFees() { + byte[] inputBytes = CreateTranslator.CREATE_NON_FUNGIBLE_TOKEN_WITH_METADATA_AND_CUSTOM_FEES + .encodeCall(CREATE_NON_FUNGIBLE_WITH_META_AND_FEES_TUPLE) + .array(); + final TransactionBody transaction = subject.decodeCreateNonFungibleWithMetadataAndCustomFees( + inputBytes, SENDER_ID, nativeOperations, addressIdConverter); + nftAssertions(transaction); + assertThatList(transaction.tokenCreation().customFees()).isNotEmpty(); + customFeesAssertions(transaction.tokenCreation().customFees()); + assertNotNull(transaction.tokenCreation().metadata()); + assertEquals(Bytes.wrap("metadata"), transaction.tokenCreation().metadata()); + } + + private void tokenAssertions(final TransactionBody transaction) { + assertThat(transaction).isNotNull(); + final var tokenCreation = transaction.tokenCreation(); + assertNotNull(tokenCreation); + assertEquals(10L, tokenCreation.initialSupply()); + assertEquals(5, tokenCreation.decimals()); + assertEquals("name", tokenCreation.name()); + assertEquals("symbol", tokenCreation.symbol()); + assertEquals(SENDER_ID, tokenCreation.treasury()); + assertEquals("memo", tokenCreation.memo()); + assertFalse(tokenCreation.freezeDefault()); + assertEquals(1000L, tokenCreation.maxSupply()); + assertEquals(FINITE, tokenCreation.supplyType()); + assertEquals(TokenType.FUNGIBLE_COMMON, tokenCreation.tokenType()); + } + + private void nftAssertions(final TransactionBody transaction) { + assertThat(transaction).isNotNull(); + final var tokenCreation = transaction.tokenCreation(); + assertNotNull(tokenCreation); + assertEquals("name", tokenCreation.name()); + assertEquals("symbol", tokenCreation.symbol()); + assertEquals(SENDER_ID, tokenCreation.treasury()); + assertEquals("memo", tokenCreation.memo()); + assertFalse(tokenCreation.freezeDefault()); + assertEquals(0L, tokenCreation.initialSupply()); + assertEquals(TokenType.NON_FUNGIBLE_UNIQUE, tokenCreation.tokenType()); + } + + private void customFeesAssertions(final List customFees) { + assertThatList(customFees).size().isEqualTo(2); + final var customFee1 = customFees.get(0); + assertTrue(customFee1.hasFixedFee()); + assertEquals(2L, customFee1.fixedFee().amount()); + assertNull(customFee1.fixedFee().denominatingTokenId()); + assertEquals(SENDER_ID, customFee1.feeCollectorAccountId()); + final var customFee2 = customFees.get(1); + assertTrue(customFee2.hasFixedFee()); + assertEquals(3L, customFee2.fixedFee().amount()); + assertEquals(FUNGIBLE_TOKEN_ID, customFee2.fixedFee().denominatingTokenId()); + assertEquals(SENDER_ID, customFee2.feeCollectorAccountId()); + } +} diff --git a/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/systemcontracts/hts/create/CreateTestHelper.java b/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/systemcontracts/hts/create/CreateTestHelper.java new file mode 100644 index 000000000000..447b6e3d3088 --- /dev/null +++ b/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/systemcontracts/hts/create/CreateTestHelper.java @@ -0,0 +1,250 @@ +/* + * Copyright (C) 2024 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.service.contract.impl.test.exec.systemcontracts.hts.create; + +import static com.hedera.node.app.service.contract.impl.test.TestHelpers.EXPECTED_FIXED_CUSTOM_FEES; +import static com.hedera.node.app.service.contract.impl.test.TestHelpers.NON_FUNGIBLE_TOKEN_HEADLONG_ADDRESS; + +import com.esaulpaugh.headlong.abi.Tuple; +import java.math.BigInteger; + +public class CreateTestHelper { + + public static final Tuple CREATE_FUNGIBLE_V1_TUPLE = new Tuple( + Tuple.of( + "name", + "symbol", + NON_FUNGIBLE_TOKEN_HEADLONG_ADDRESS, + "memo", + true, + 1000L, + false, + new Tuple[] {}, + Tuple.of(0L, NON_FUNGIBLE_TOKEN_HEADLONG_ADDRESS, 0L)), + BigInteger.valueOf(10L), + BigInteger.valueOf(5L)); + + public static final Tuple CREATE_FUNGIBLE_V2_TUPLE = new Tuple( + Tuple.of( + "name", + "symbol", + NON_FUNGIBLE_TOKEN_HEADLONG_ADDRESS, + "memo", + true, + 1000L, + false, + new Tuple[] {}, + Tuple.of(0L, NON_FUNGIBLE_TOKEN_HEADLONG_ADDRESS, 0L)), + BigInteger.valueOf(10L), + 5L); + + public static final Tuple CREATE_FUNGIBLE_V3_TUPLE = new Tuple( + Tuple.of( + "name", + "symbol", + NON_FUNGIBLE_TOKEN_HEADLONG_ADDRESS, + "memo", + true, + 1000L, + false, + new Tuple[] {}, + Tuple.of(0L, NON_FUNGIBLE_TOKEN_HEADLONG_ADDRESS, 0L)), + 10L, + 5); + + public static final Tuple CREATE_FUNGIBLE_WITH_META_TUPLE = new Tuple( + Tuple.of( + "name", + "symbol", + NON_FUNGIBLE_TOKEN_HEADLONG_ADDRESS, + "memo", + true, + 1000L, + false, + new Tuple[] {}, + Tuple.of(0L, NON_FUNGIBLE_TOKEN_HEADLONG_ADDRESS, 0L), + "metadata".getBytes()), + 10L, + 5); + + public static final Tuple CREATE_FUNGIBLE_WITH_FEES_V1_TUPLE = new Tuple( + Tuple.of( + "name", + "symbol", + NON_FUNGIBLE_TOKEN_HEADLONG_ADDRESS, + "memo", + true, + 1000L, + false, + new Tuple[] {}, + Tuple.of(0L, NON_FUNGIBLE_TOKEN_HEADLONG_ADDRESS, 0L)), + BigInteger.valueOf(10L), + BigInteger.valueOf(5L), + EXPECTED_FIXED_CUSTOM_FEES.toArray(Tuple[]::new), + new Tuple[] {}); + + public static final Tuple CREATE_FUNGIBLE_WITH_FEES_V2_TUPLE = new Tuple( + Tuple.of( + "name", + "symbol", + NON_FUNGIBLE_TOKEN_HEADLONG_ADDRESS, + "memo", + true, + 1000L, + false, + new Tuple[] {}, + Tuple.of(0L, NON_FUNGIBLE_TOKEN_HEADLONG_ADDRESS, 0L)), + BigInteger.valueOf(10L), + 5L, + EXPECTED_FIXED_CUSTOM_FEES.toArray(Tuple[]::new), + new Tuple[] {}); + + public static final Tuple CREATE_FUNGIBLE_WITH_FEES_V3_TUPLE = new Tuple( + Tuple.of( + "name", + "symbol", + NON_FUNGIBLE_TOKEN_HEADLONG_ADDRESS, + "memo", + true, + 1000L, + false, + new Tuple[] {}, + Tuple.of(0L, NON_FUNGIBLE_TOKEN_HEADLONG_ADDRESS, 0L)), + 10L, + 5, + EXPECTED_FIXED_CUSTOM_FEES.toArray(Tuple[]::new), + new Tuple[] {}); + + public static final Tuple CREATE_FUNGIBLE_WITH_META_AND_FEES_TUPLE = new Tuple( + Tuple.of( + "name", + "symbol", + NON_FUNGIBLE_TOKEN_HEADLONG_ADDRESS, + "memo", + true, + 1000L, + false, + new Tuple[] {}, + Tuple.of(0L, NON_FUNGIBLE_TOKEN_HEADLONG_ADDRESS, 0L), + "metadata".getBytes()), + 10L, + 5, + EXPECTED_FIXED_CUSTOM_FEES.toArray(Tuple[]::new), + new Tuple[] {}); + + public static final Tuple CREATE_NON_FUNGIBLE_V1_TUPLE = new Tuple(Tuple.of( + "name", + "symbol", + NON_FUNGIBLE_TOKEN_HEADLONG_ADDRESS, + "memo", + true, + 0L, + false, + new Tuple[] {}, + Tuple.of(0L, NON_FUNGIBLE_TOKEN_HEADLONG_ADDRESS, 0L))); + + public static final Tuple CREATE_NON_FUNGIBLE_V2_TUPLE = new Tuple(Tuple.of( + "name", + "symbol", + NON_FUNGIBLE_TOKEN_HEADLONG_ADDRESS, + "memo", + true, + 1000L, + false, + new Tuple[] {}, + Tuple.of(0L, NON_FUNGIBLE_TOKEN_HEADLONG_ADDRESS, 0L))); + + public static final Tuple CREATE_NON_FUNGIBLE_V3_TUPLE = new Tuple(Tuple.of( + "name", + "symbol", + NON_FUNGIBLE_TOKEN_HEADLONG_ADDRESS, + "memo", + true, + 1000L, + false, + new Tuple[] {}, + Tuple.of(0L, NON_FUNGIBLE_TOKEN_HEADLONG_ADDRESS, 0L))); + + public static final Tuple CREATE_NON_FUNGIBLE_WITH_META_TUPLE = new Tuple(Tuple.of( + "name", + "symbol", + NON_FUNGIBLE_TOKEN_HEADLONG_ADDRESS, + "memo", + true, + 1000L, + false, + new Tuple[] {}, + Tuple.of(0L, NON_FUNGIBLE_TOKEN_HEADLONG_ADDRESS, 0L), + "metadata".getBytes())); + + public static final Tuple CREATE_NON_FUNGIBLE_WITH_FEES_V1_TUPLE = new Tuple( + Tuple.of( + "name", + "symbol", + NON_FUNGIBLE_TOKEN_HEADLONG_ADDRESS, + "memo", + true, + 1000L, + false, + new Tuple[] {}, + Tuple.of(0L, NON_FUNGIBLE_TOKEN_HEADLONG_ADDRESS, 0L)), + EXPECTED_FIXED_CUSTOM_FEES.toArray(Tuple[]::new), + new Tuple[] {}); + + public static final Tuple CREATE_NON_FUNGIBLE_WITH_FEES_V2_TUPLE = new Tuple( + Tuple.of( + "name", + "symbol", + NON_FUNGIBLE_TOKEN_HEADLONG_ADDRESS, + "memo", + true, + 1000L, + false, + new Tuple[] {}, + Tuple.of(0L, NON_FUNGIBLE_TOKEN_HEADLONG_ADDRESS, 0L)), + EXPECTED_FIXED_CUSTOM_FEES.toArray(Tuple[]::new), + new Tuple[] {}); + + public static final Tuple CREATE_NON_FUNGIBLE_WITH_FEES_V3_TUPLE = new Tuple( + Tuple.of( + "name", + "symbol", + NON_FUNGIBLE_TOKEN_HEADLONG_ADDRESS, + "memo", + true, + 1000L, + false, + new Tuple[] {}, + Tuple.of(0L, NON_FUNGIBLE_TOKEN_HEADLONG_ADDRESS, 0L)), + EXPECTED_FIXED_CUSTOM_FEES.toArray(Tuple[]::new), + new Tuple[] {}); + + public static final Tuple CREATE_NON_FUNGIBLE_WITH_META_AND_FEES_TUPLE = new Tuple( + Tuple.of( + "name", + "symbol", + NON_FUNGIBLE_TOKEN_HEADLONG_ADDRESS, + "memo", + true, + 1000L, + false, + new Tuple[] {}, + Tuple.of(0L, NON_FUNGIBLE_TOKEN_HEADLONG_ADDRESS, 0L), + "metadata".getBytes()), + EXPECTED_FIXED_CUSTOM_FEES.toArray(Tuple[]::new), + new Tuple[] {}); +} diff --git a/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/systemcontracts/hts/create/CreateTranslatorTest.java b/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/systemcontracts/hts/create/CreateTranslatorTest.java index 9074e7364da2..520e9908b5f1 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/systemcontracts/hts/create/CreateTranslatorTest.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/systemcontracts/hts/create/CreateTranslatorTest.java @@ -20,6 +20,8 @@ import static com.hedera.node.app.service.contract.impl.exec.systemcontracts.hts.create.CreateTranslator.CREATE_FUNGIBLE_TOKEN_V1; import static com.hedera.node.app.service.contract.impl.exec.systemcontracts.hts.create.CreateTranslator.CREATE_FUNGIBLE_TOKEN_V2; import static com.hedera.node.app.service.contract.impl.exec.systemcontracts.hts.create.CreateTranslator.CREATE_FUNGIBLE_TOKEN_V3; +import static com.hedera.node.app.service.contract.impl.exec.systemcontracts.hts.create.CreateTranslator.CREATE_FUNGIBLE_TOKEN_WITH_METADATA; +import static com.hedera.node.app.service.contract.impl.exec.systemcontracts.hts.create.CreateTranslator.CREATE_FUNGIBLE_TOKEN_WITH_METADATA_AND_CUSTOM_FEES; import static com.hedera.node.app.service.contract.impl.exec.systemcontracts.hts.create.CreateTranslator.CREATE_FUNGIBLE_WITH_CUSTOM_FEES_V1; import static com.hedera.node.app.service.contract.impl.exec.systemcontracts.hts.create.CreateTranslator.CREATE_FUNGIBLE_WITH_CUSTOM_FEES_V2; import static com.hedera.node.app.service.contract.impl.exec.systemcontracts.hts.create.CreateTranslator.CREATE_FUNGIBLE_WITH_CUSTOM_FEES_V3; @@ -29,17 +31,45 @@ import static com.hedera.node.app.service.contract.impl.exec.systemcontracts.hts.create.CreateTranslator.CREATE_NON_FUNGIBLE_TOKEN_WITH_CUSTOM_FEES_V1; import static com.hedera.node.app.service.contract.impl.exec.systemcontracts.hts.create.CreateTranslator.CREATE_NON_FUNGIBLE_TOKEN_WITH_CUSTOM_FEES_V2; import static com.hedera.node.app.service.contract.impl.exec.systemcontracts.hts.create.CreateTranslator.CREATE_NON_FUNGIBLE_TOKEN_WITH_CUSTOM_FEES_V3; +import static com.hedera.node.app.service.contract.impl.exec.systemcontracts.hts.create.CreateTranslator.CREATE_NON_FUNGIBLE_TOKEN_WITH_METADATA; +import static com.hedera.node.app.service.contract.impl.exec.systemcontracts.hts.create.CreateTranslator.CREATE_NON_FUNGIBLE_TOKEN_WITH_METADATA_AND_CUSTOM_FEES; +import static com.hedera.node.app.service.contract.impl.test.TestHelpers.SENDER_ID; import static com.hedera.node.app.service.contract.impl.test.exec.systemcontracts.CallAttemptHelpers.prepareHtsAttemptWithSelector; +import static com.hedera.node.app.service.contract.impl.test.exec.systemcontracts.CallAttemptHelpers.prepareHtsAttemptWithSelectorAndCustomConfig; +import static com.hedera.node.app.service.contract.impl.test.exec.systemcontracts.hts.create.CreateTestHelper.CREATE_FUNGIBLE_V1_TUPLE; +import static com.hedera.node.app.service.contract.impl.test.exec.systemcontracts.hts.create.CreateTestHelper.CREATE_FUNGIBLE_V2_TUPLE; +import static com.hedera.node.app.service.contract.impl.test.exec.systemcontracts.hts.create.CreateTestHelper.CREATE_FUNGIBLE_V3_TUPLE; +import static com.hedera.node.app.service.contract.impl.test.exec.systemcontracts.hts.create.CreateTestHelper.CREATE_FUNGIBLE_WITH_FEES_V1_TUPLE; +import static com.hedera.node.app.service.contract.impl.test.exec.systemcontracts.hts.create.CreateTestHelper.CREATE_FUNGIBLE_WITH_FEES_V2_TUPLE; +import static com.hedera.node.app.service.contract.impl.test.exec.systemcontracts.hts.create.CreateTestHelper.CREATE_FUNGIBLE_WITH_FEES_V3_TUPLE; +import static com.hedera.node.app.service.contract.impl.test.exec.systemcontracts.hts.create.CreateTestHelper.CREATE_FUNGIBLE_WITH_META_AND_FEES_TUPLE; +import static com.hedera.node.app.service.contract.impl.test.exec.systemcontracts.hts.create.CreateTestHelper.CREATE_FUNGIBLE_WITH_META_TUPLE; +import static com.hedera.node.app.service.contract.impl.test.exec.systemcontracts.hts.create.CreateTestHelper.CREATE_NON_FUNGIBLE_V1_TUPLE; +import static com.hedera.node.app.service.contract.impl.test.exec.systemcontracts.hts.create.CreateTestHelper.CREATE_NON_FUNGIBLE_V2_TUPLE; +import static com.hedera.node.app.service.contract.impl.test.exec.systemcontracts.hts.create.CreateTestHelper.CREATE_NON_FUNGIBLE_V3_TUPLE; +import static com.hedera.node.app.service.contract.impl.test.exec.systemcontracts.hts.create.CreateTestHelper.CREATE_NON_FUNGIBLE_WITH_FEES_V1_TUPLE; +import static com.hedera.node.app.service.contract.impl.test.exec.systemcontracts.hts.create.CreateTestHelper.CREATE_NON_FUNGIBLE_WITH_FEES_V2_TUPLE; +import static com.hedera.node.app.service.contract.impl.test.exec.systemcontracts.hts.create.CreateTestHelper.CREATE_NON_FUNGIBLE_WITH_FEES_V3_TUPLE; +import static com.hedera.node.app.service.contract.impl.test.exec.systemcontracts.hts.create.CreateTestHelper.CREATE_NON_FUNGIBLE_WITH_META_AND_FEES_TUPLE; +import static com.hedera.node.app.service.contract.impl.test.exec.systemcontracts.hts.create.CreateTestHelper.CREATE_NON_FUNGIBLE_WITH_META_TUPLE; +import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.BDDMockito.given; import com.hedera.node.app.service.contract.impl.exec.gas.SystemContractGasCalculator; import com.hedera.node.app.service.contract.impl.exec.scope.VerificationStrategies; +import com.hedera.node.app.service.contract.impl.exec.scope.VerificationStrategy; import com.hedera.node.app.service.contract.impl.exec.systemcontracts.hts.AddressIdConverter; import com.hedera.node.app.service.contract.impl.exec.systemcontracts.hts.HtsCallAttempt; +import com.hedera.node.app.service.contract.impl.exec.systemcontracts.hts.create.ClassicCreatesCall; import com.hedera.node.app.service.contract.impl.exec.systemcontracts.hts.create.CreateDecoder; import com.hedera.node.app.service.contract.impl.exec.systemcontracts.hts.create.CreateTranslator; import com.hedera.node.app.service.contract.impl.hevm.HederaWorldUpdater; +import com.hedera.node.app.service.contract.impl.test.exec.systemcontracts.common.CallTestBase; +import com.hedera.node.config.data.ContractsConfig; +import com.swirlds.config.api.Configuration; +import org.apache.tuweni.bytes.Bytes; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -47,7 +77,7 @@ import org.mockito.junit.jupiter.MockitoExtension; @ExtendWith(MockitoExtension.class) -public class CreateTranslatorTest { +public class CreateTranslatorTest extends CallTestBase { @Mock private HtsCallAttempt attempt; @@ -64,6 +94,15 @@ public class CreateTranslatorTest { @Mock private VerificationStrategies verificationStrategies; + @Mock + private VerificationStrategy verificationStrategy; + + @Mock + private ContractsConfig contractsConfig; + + @Mock + Configuration configuration; + private CreateDecoder decoder = new CreateDecoder(); private CreateTranslator subject; @@ -109,6 +148,20 @@ void matchesCreateFungibleTokenV3() { assertTrue(subject.matches(attempt)); } + @Test + void matchesCreateFungibleTokenWithMetadata() { + enableConfig(); + attempt = prepareHtsAttemptWithSelectorAndCustomConfig( + CREATE_FUNGIBLE_TOKEN_WITH_METADATA, + subject, + enhancement, + addressIdConverter, + verificationStrategies, + gasCalculator, + configuration); + assertTrue(subject.matches(attempt)); + } + @Test void matchesCreateFungibleTokenWithCustomFeesV1() { attempt = prepareHtsAttemptWithSelector( @@ -145,6 +198,20 @@ void matchesCreateFungibleTokenWithCustomFeesV3() { assertTrue(subject.matches(attempt)); } + @Test + void matchesCreateFungibleTokenWithMetadataAndCustomFees() { + enableConfig(); + attempt = prepareHtsAttemptWithSelectorAndCustomConfig( + CREATE_FUNGIBLE_TOKEN_WITH_METADATA_AND_CUSTOM_FEES, + subject, + enhancement, + addressIdConverter, + verificationStrategies, + gasCalculator, + configuration); + assertTrue(subject.matches(attempt)); + } + @Test void matchesCreateNonFungibleTokenV1() { attempt = prepareHtsAttemptWithSelector( @@ -181,6 +248,20 @@ void matchesCreateNonFungibleTokenV3() { assertTrue(subject.matches(attempt)); } + @Test + void matchesCreateNonFungibleTokenWithMetadata() { + enableConfig(); + attempt = prepareHtsAttemptWithSelectorAndCustomConfig( + CREATE_NON_FUNGIBLE_TOKEN_WITH_METADATA, + subject, + enhancement, + addressIdConverter, + verificationStrategies, + gasCalculator, + configuration); + assertTrue(subject.matches(attempt)); + } + @Test void matchesCreateNonFungibleTokenWithCustomFeesV1() { attempt = prepareHtsAttemptWithSelector( @@ -217,10 +298,259 @@ void matchesCreateNonFungibleTokenWithCustomFeesV3() { assertTrue(subject.matches(attempt)); } + @Test + void matchesCreateNonFungibleTokenWithMetadataAndCustomFees() { + enableConfig(); + attempt = prepareHtsAttemptWithSelectorAndCustomConfig( + CREATE_NON_FUNGIBLE_TOKEN_WITH_METADATA_AND_CUSTOM_FEES, + subject, + enhancement, + addressIdConverter, + verificationStrategies, + gasCalculator, + configuration); + assertTrue(subject.matches(attempt)); + } + @Test void falseOnInvalidSelector() { attempt = prepareHtsAttemptWithSelector( BURN_TOKEN_V2, subject, enhancement, addressIdConverter, verificationStrategies, gasCalculator); assertFalse(subject.matches(attempt)); } + + @Test + void callFromCreateTokenV1() { + byte[] inputBytes = Bytes.wrapByteBuffer(CREATE_FUNGIBLE_TOKEN_V1.encodeCall(CREATE_FUNGIBLE_V1_TUPLE)) + .toArray(); + given(attempt.inputBytes()).willReturn(inputBytes); + given(attempt.enhancement()).willReturn(mockEnhancement()); + given(attempt.addressIdConverter()).willReturn(addressIdConverter); + given(attempt.senderId()).willReturn(SENDER_ID); + given(attempt.defaultVerificationStrategy()).willReturn(verificationStrategy); + given(attempt.systemContractGasCalculator()).willReturn(gasCalculator); + + assertThat(subject.callFrom(attempt)).isInstanceOf(ClassicCreatesCall.class); + } + + @Test + void callFromCreateTokenV2() { + byte[] inputBytes = Bytes.wrapByteBuffer(CREATE_FUNGIBLE_TOKEN_V2.encodeCall(CREATE_FUNGIBLE_V2_TUPLE)) + .toArray(); + given(attempt.inputBytes()).willReturn(inputBytes); + given(attempt.enhancement()).willReturn(mockEnhancement()); + given(attempt.addressIdConverter()).willReturn(addressIdConverter); + given(attempt.senderId()).willReturn(SENDER_ID); + given(attempt.defaultVerificationStrategy()).willReturn(verificationStrategy); + given(attempt.systemContractGasCalculator()).willReturn(gasCalculator); + + assertThat(subject.callFrom(attempt)).isInstanceOf(ClassicCreatesCall.class); + } + + @Test + void callFromCreateTokenV3() { + byte[] inputBytes = Bytes.wrapByteBuffer(CREATE_FUNGIBLE_TOKEN_V3.encodeCall(CREATE_FUNGIBLE_V3_TUPLE)) + .toArray(); + given(attempt.inputBytes()).willReturn(inputBytes); + given(attempt.enhancement()).willReturn(mockEnhancement()); + given(attempt.addressIdConverter()).willReturn(addressIdConverter); + given(attempt.senderId()).willReturn(SENDER_ID); + given(attempt.defaultVerificationStrategy()).willReturn(verificationStrategy); + given(attempt.systemContractGasCalculator()).willReturn(gasCalculator); + + assertThat(subject.callFrom(attempt)).isInstanceOf(ClassicCreatesCall.class); + } + + @Test + void callFromCreateTokenWithMeta() { + byte[] inputBytes = Bytes.wrapByteBuffer( + CREATE_FUNGIBLE_TOKEN_WITH_METADATA.encodeCall(CREATE_FUNGIBLE_WITH_META_TUPLE)) + .toArray(); + given(attempt.inputBytes()).willReturn(inputBytes); + given(attempt.enhancement()).willReturn(mockEnhancement()); + given(attempt.addressIdConverter()).willReturn(addressIdConverter); + given(attempt.senderId()).willReturn(SENDER_ID); + given(attempt.defaultVerificationStrategy()).willReturn(verificationStrategy); + given(attempt.systemContractGasCalculator()).willReturn(gasCalculator); + assertThat(subject.callFrom(attempt)).isInstanceOf(ClassicCreatesCall.class); + } + + @Test + void callFromCreateTokenWithCustomFeesV1() { + byte[] inputBytes = Bytes.wrapByteBuffer( + CREATE_FUNGIBLE_WITH_CUSTOM_FEES_V1.encodeCall(CREATE_FUNGIBLE_WITH_FEES_V1_TUPLE)) + .toArray(); + given(attempt.inputBytes()).willReturn(inputBytes); + given(attempt.enhancement()).willReturn(mockEnhancement()); + given(attempt.addressIdConverter()).willReturn(addressIdConverter); + given(attempt.senderId()).willReturn(SENDER_ID); + given(attempt.defaultVerificationStrategy()).willReturn(verificationStrategy); + given(attempt.systemContractGasCalculator()).willReturn(gasCalculator); + + assertThat(subject.callFrom(attempt)).isInstanceOf(ClassicCreatesCall.class); + } + + @Test + void callFromCreateTokenWithCustomFeesV2() { + byte[] inputBytes = Bytes.wrapByteBuffer( + CREATE_FUNGIBLE_WITH_CUSTOM_FEES_V2.encodeCall(CREATE_FUNGIBLE_WITH_FEES_V2_TUPLE)) + .toArray(); + given(attempt.inputBytes()).willReturn(inputBytes); + given(attempt.enhancement()).willReturn(mockEnhancement()); + given(attempt.addressIdConverter()).willReturn(addressIdConverter); + given(attempt.senderId()).willReturn(SENDER_ID); + given(attempt.defaultVerificationStrategy()).willReturn(verificationStrategy); + given(attempt.systemContractGasCalculator()).willReturn(gasCalculator); + + assertThat(subject.callFrom(attempt)).isInstanceOf(ClassicCreatesCall.class); + } + + @Test + void callFromCreateTokenWithCustomFeesV3() { + byte[] inputBytes = Bytes.wrapByteBuffer( + CREATE_FUNGIBLE_WITH_CUSTOM_FEES_V3.encodeCall(CREATE_FUNGIBLE_WITH_FEES_V3_TUPLE)) + .toArray(); + given(attempt.inputBytes()).willReturn(inputBytes); + given(attempt.enhancement()).willReturn(mockEnhancement()); + given(attempt.addressIdConverter()).willReturn(addressIdConverter); + given(attempt.senderId()).willReturn(SENDER_ID); + given(attempt.defaultVerificationStrategy()).willReturn(verificationStrategy); + given(attempt.systemContractGasCalculator()).willReturn(gasCalculator); + + assertThat(subject.callFrom(attempt)).isInstanceOf(ClassicCreatesCall.class); + } + + @Test + void callFromCreateTokenWithMetaAndCustomFees() { + byte[] inputBytes = Bytes.wrapByteBuffer(CREATE_FUNGIBLE_TOKEN_WITH_METADATA_AND_CUSTOM_FEES.encodeCall( + CREATE_FUNGIBLE_WITH_META_AND_FEES_TUPLE)) + .toArray(); + given(attempt.inputBytes()).willReturn(inputBytes); + given(attempt.enhancement()).willReturn(mockEnhancement()); + given(attempt.addressIdConverter()).willReturn(addressIdConverter); + given(attempt.senderId()).willReturn(SENDER_ID); + given(attempt.defaultVerificationStrategy()).willReturn(verificationStrategy); + given(attempt.systemContractGasCalculator()).willReturn(gasCalculator); + assertThat(subject.callFrom(attempt)).isInstanceOf(ClassicCreatesCall.class); + } + + @Test + void callFromCreateNftV1() { + byte[] inputBytes = Bytes.wrapByteBuffer(CREATE_NON_FUNGIBLE_TOKEN_V1.encodeCall(CREATE_NON_FUNGIBLE_V1_TUPLE)) + .toArray(); + given(attempt.inputBytes()).willReturn(inputBytes); + given(attempt.enhancement()).willReturn(mockEnhancement()); + given(attempt.addressIdConverter()).willReturn(addressIdConverter); + given(attempt.senderId()).willReturn(SENDER_ID); + given(attempt.defaultVerificationStrategy()).willReturn(verificationStrategy); + given(attempt.systemContractGasCalculator()).willReturn(gasCalculator); + + assertThat(subject.callFrom(attempt)).isInstanceOf(ClassicCreatesCall.class); + } + + @Test + void callFromCreateNftV2() { + byte[] inputBytes = Bytes.wrapByteBuffer(CREATE_NON_FUNGIBLE_TOKEN_V2.encodeCall(CREATE_NON_FUNGIBLE_V2_TUPLE)) + .toArray(); + given(attempt.inputBytes()).willReturn(inputBytes); + given(attempt.enhancement()).willReturn(mockEnhancement()); + given(attempt.addressIdConverter()).willReturn(addressIdConverter); + given(attempt.senderId()).willReturn(SENDER_ID); + given(attempt.defaultVerificationStrategy()).willReturn(verificationStrategy); + given(attempt.systemContractGasCalculator()).willReturn(gasCalculator); + + assertThat(subject.callFrom(attempt)).isInstanceOf(ClassicCreatesCall.class); + } + + @Test + void callFromCreateNftV3() { + byte[] inputBytes = Bytes.wrapByteBuffer(CREATE_NON_FUNGIBLE_TOKEN_V3.encodeCall(CREATE_NON_FUNGIBLE_V3_TUPLE)) + .toArray(); + given(attempt.inputBytes()).willReturn(inputBytes); + given(attempt.enhancement()).willReturn(mockEnhancement()); + given(attempt.addressIdConverter()).willReturn(addressIdConverter); + given(attempt.senderId()).willReturn(SENDER_ID); + given(attempt.defaultVerificationStrategy()).willReturn(verificationStrategy); + given(attempt.systemContractGasCalculator()).willReturn(gasCalculator); + + assertThat(subject.callFrom(attempt)).isInstanceOf(ClassicCreatesCall.class); + } + + @Test + void callFromCreateNftWithMeta() { + byte[] inputBytes = Bytes.wrapByteBuffer( + CREATE_NON_FUNGIBLE_TOKEN_WITH_METADATA.encodeCall(CREATE_NON_FUNGIBLE_WITH_META_TUPLE)) + .toArray(); + given(attempt.inputBytes()).willReturn(inputBytes); + given(attempt.enhancement()).willReturn(mockEnhancement()); + given(attempt.addressIdConverter()).willReturn(addressIdConverter); + given(attempt.senderId()).willReturn(SENDER_ID); + given(attempt.defaultVerificationStrategy()).willReturn(verificationStrategy); + given(attempt.systemContractGasCalculator()).willReturn(gasCalculator); + assertThat(subject.callFrom(attempt)).isInstanceOf(ClassicCreatesCall.class); + } + + @Test + void callFromCreateNftWithCustomFeesV1() { + byte[] inputBytes = Bytes.wrapByteBuffer(CREATE_NON_FUNGIBLE_TOKEN_WITH_CUSTOM_FEES_V1.encodeCall( + CREATE_NON_FUNGIBLE_WITH_FEES_V1_TUPLE)) + .toArray(); + given(attempt.inputBytes()).willReturn(inputBytes); + given(attempt.enhancement()).willReturn(mockEnhancement()); + given(attempt.addressIdConverter()).willReturn(addressIdConverter); + given(attempt.senderId()).willReturn(SENDER_ID); + given(attempt.defaultVerificationStrategy()).willReturn(verificationStrategy); + given(attempt.systemContractGasCalculator()).willReturn(gasCalculator); + + assertThat(subject.callFrom(attempt)).isInstanceOf(ClassicCreatesCall.class); + } + + @Test + void callFromCreateNftWithCustomFeesV2() { + byte[] inputBytes = Bytes.wrapByteBuffer(CREATE_NON_FUNGIBLE_TOKEN_WITH_CUSTOM_FEES_V2.encodeCall( + CREATE_NON_FUNGIBLE_WITH_FEES_V2_TUPLE)) + .toArray(); + given(attempt.inputBytes()).willReturn(inputBytes); + given(attempt.enhancement()).willReturn(mockEnhancement()); + given(attempt.addressIdConverter()).willReturn(addressIdConverter); + given(attempt.senderId()).willReturn(SENDER_ID); + given(attempt.defaultVerificationStrategy()).willReturn(verificationStrategy); + given(attempt.systemContractGasCalculator()).willReturn(gasCalculator); + + assertThat(subject.callFrom(attempt)).isInstanceOf(ClassicCreatesCall.class); + } + + @Test + void callFromCreateNftWithCustomFeesV3() { + byte[] inputBytes = Bytes.wrapByteBuffer(CREATE_NON_FUNGIBLE_TOKEN_WITH_CUSTOM_FEES_V3.encodeCall( + CREATE_NON_FUNGIBLE_WITH_FEES_V3_TUPLE)) + .toArray(); + given(attempt.inputBytes()).willReturn(inputBytes); + given(attempt.enhancement()).willReturn(mockEnhancement()); + given(attempt.addressIdConverter()).willReturn(addressIdConverter); + given(attempt.senderId()).willReturn(SENDER_ID); + given(attempt.defaultVerificationStrategy()).willReturn(verificationStrategy); + given(attempt.systemContractGasCalculator()).willReturn(gasCalculator); + + assertThat(subject.callFrom(attempt)).isInstanceOf(ClassicCreatesCall.class); + } + + @Test + void callFromCreateNftWithMetaAndCustomFees() { + byte[] inputBytes = Bytes.wrapByteBuffer(CREATE_NON_FUNGIBLE_TOKEN_WITH_METADATA_AND_CUSTOM_FEES.encodeCall( + CREATE_NON_FUNGIBLE_WITH_META_AND_FEES_TUPLE)) + .toArray(); + given(attempt.inputBytes()).willReturn(inputBytes); + given(attempt.enhancement()).willReturn(mockEnhancement()); + given(attempt.addressIdConverter()).willReturn(addressIdConverter); + given(attempt.senderId()).willReturn(SENDER_ID); + given(attempt.defaultVerificationStrategy()).willReturn(verificationStrategy); + given(attempt.systemContractGasCalculator()).willReturn(gasCalculator); + assertThat(subject.callFrom(attempt)).isInstanceOf(ClassicCreatesCall.class); + } + + private void enableConfig() { + given(configuration.getConfigData(ContractsConfig.class)).willReturn(contractsConfig); + given(contractsConfig.metadataKeyAndFieldEnabled()).willReturn(true); + } } diff --git a/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/systemcontracts/hts/tokenkey/TokenKeyTranslatorTest.java b/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/systemcontracts/hts/tokenkey/TokenKeyTranslatorTest.java index aae187c0e519..daab372ef262 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/systemcontracts/hts/tokenkey/TokenKeyTranslatorTest.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/systemcontracts/hts/tokenkey/TokenKeyTranslatorTest.java @@ -26,6 +26,8 @@ import static org.mockito.BDDMockito.given; import com.esaulpaugh.headlong.abi.Tuple; +import com.hedera.hapi.node.base.Key; +import com.hedera.hapi.node.state.token.Token; import com.hedera.node.app.service.contract.impl.exec.gas.SystemContractGasCalculator; import com.hedera.node.app.service.contract.impl.exec.scope.VerificationStrategies; import com.hedera.node.app.service.contract.impl.exec.systemcontracts.hts.AddressIdConverter; @@ -33,11 +35,14 @@ import com.hedera.node.app.service.contract.impl.exec.systemcontracts.hts.tokenkey.TokenKeyCall; import com.hedera.node.app.service.contract.impl.exec.systemcontracts.hts.tokenkey.TokenKeyTranslator; import com.hedera.node.app.service.contract.impl.hevm.HederaWorldUpdater.Enhancement; +import com.hedera.node.config.testfixtures.HederaTestConfigBuilder; +import com.hedera.pbj.runtime.io.buffer.Bytes; import java.math.BigInteger; -import org.apache.tuweni.bytes.Bytes; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; @@ -82,12 +87,35 @@ void matchesFailsIfIncorrectSelectorTest() { @Test void callFromTest() { final Tuple tuple = new Tuple(FUNGIBLE_TOKEN_HEADLONG_ADDRESS, BigInteger.ZERO); - final Bytes inputBytes = Bytes.wrapByteBuffer(TOKEN_KEY.encodeCall(tuple)); + final var inputBytes = org.apache.tuweni.bytes.Bytes.wrapByteBuffer(TOKEN_KEY.encodeCall(tuple)); given(attempt.input()).willReturn(inputBytes); given(attempt.enhancement()).willReturn(enhancement); given(attempt.systemContractGasCalculator()).willReturn(gasCalculator); + given(attempt.configuration()).willReturn(HederaTestConfigBuilder.createConfig()); final var call = subject.callFrom(attempt); assertThat(call).isInstanceOf(TokenKeyCall.class); } + + @ParameterizedTest + @ValueSource(ints = {1, 2, 4, 8, 16, 32, 64, 128}) + void testTokenKey(final int keyType) { + final Token token = Token.newBuilder() + .adminKey(keyBuilder("adminKey")) + .kycKey(keyBuilder("kycKey")) + .freezeKey(keyBuilder("freezeKey")) + .wipeKey(keyBuilder("wipeKey")) + .supplyKey(keyBuilder("supplyKey")) + .feeScheduleKey(keyBuilder("feeScheduleKey")) + .pauseKey(keyBuilder("pauseKey")) + .metadataKey(keyBuilder("metadataKey")) + .build(); + + final Key result = subject.getTokenKey(token, keyType, true); + assertThat(result).isNotNull(); + } + + private Key keyBuilder(final String keyName) { + return Key.newBuilder().ed25519(Bytes.wrap(keyName.getBytes())).build(); + } } diff --git a/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/systemcontracts/hts/update/UpdateDecoderTest.java b/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/systemcontracts/hts/update/UpdateDecoderTest.java index 0b3226668e9a..a8f860f13ed1 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/systemcontracts/hts/update/UpdateDecoderTest.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/systemcontracts/hts/update/UpdateDecoderTest.java @@ -51,6 +51,7 @@ class UpdateDecoderTest { private final UpdateDecoder subject = new UpdateDecoder(); private final String newName = "NEW NAME"; + private final String metadata = "LionTigerBear"; private static final long EXPIRY_TIMESTAMP = Instant.now().plusSeconds(3600).toEpochMilli() / 1000; private static final long AUTO_RENEW_PERIOD = 8_000_000L; private final Tuple expiry = Tuple.of(EXPIRY_TIMESTAMP, OWNER_HEADLONG_ADDRESS, AUTO_RENEW_PERIOD); @@ -67,6 +68,21 @@ class UpdateDecoderTest { // Expiry expiry); + private final Tuple hederaTokenWithMetadata = Tuple.of( + newName, + "symbol", + OWNER_HEADLONG_ADDRESS, + "memo", + true, + 1000L, + false, + // TokenKey + new Tuple[] {}, + // Expiry + expiry, + // Metadata, + metadata.getBytes()); + @Nested class UpdateTokenInfoAndExpiry { @BeforeEach @@ -108,6 +124,18 @@ void updateV3Works() { assertEquals(tokenUpdate.name(), newName); } + @Test + void updateWithMetadataWorks() { + final var encoded = + Bytes.wrapByteBuffer(UpdateTranslator.TOKEN_UPDATE_INFO_FUNCTION_WITH_METADATA.encodeCallWithArgs( + FUNGIBLE_TOKEN_HEADLONG_ADDRESS, hederaTokenWithMetadata)); + given(attempt.input()).willReturn(encoded); + + final var body = subject.decodeTokenUpdateWithMetadata(attempt); + final var tokenUpdate = body.tokenUpdateOrThrow(); + assertEquals(tokenUpdate.metadata().asUtf8String(), metadata); + } + @Test void updateExpiryV1Works() { final var encoded = diff --git a/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/systemcontracts/hts/update/UpdateTranslatorTest.java b/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/systemcontracts/hts/update/UpdateTranslatorTest.java index 58da89422242..13d92969da66 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/systemcontracts/hts/update/UpdateTranslatorTest.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/systemcontracts/hts/update/UpdateTranslatorTest.java @@ -25,6 +25,7 @@ import static com.hedera.node.app.service.contract.impl.exec.systemcontracts.hts.update.UpdateTranslator.TOKEN_UPDATE_INFO_FUNCTION_V1; import static com.hedera.node.app.service.contract.impl.exec.systemcontracts.hts.update.UpdateTranslator.TOKEN_UPDATE_INFO_FUNCTION_V2; import static com.hedera.node.app.service.contract.impl.exec.systemcontracts.hts.update.UpdateTranslator.TOKEN_UPDATE_INFO_FUNCTION_V3; +import static com.hedera.node.app.service.contract.impl.exec.systemcontracts.hts.update.UpdateTranslator.TOKEN_UPDATE_INFO_FUNCTION_WITH_METADATA; import static com.hedera.node.app.service.contract.impl.test.TestHelpers.EXPLICITLY_IMMUTABLE_FUNGIBLE_TOKEN; import static com.hedera.node.app.service.contract.impl.test.TestHelpers.FUNGIBLE_TOKEN; import static com.hedera.node.app.service.contract.impl.test.TestHelpers.FUNGIBLE_TOKEN_ID; @@ -32,12 +33,14 @@ import static com.hedera.node.app.service.contract.impl.test.TestHelpers.NON_FUNGIBLE_TOKEN_HEADLONG_ADDRESS; import static com.hedera.node.app.service.contract.impl.test.TestHelpers.NON_SYSTEM_ACCOUNT_ID; import static com.hedera.node.app.service.contract.impl.test.exec.systemcontracts.CallAttemptHelpers.prepareHtsAttemptWithSelector; +import static com.hedera.node.app.service.contract.impl.test.exec.systemcontracts.CallAttemptHelpers.prepareHtsAttemptWithSelectorAndCustomConfig; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.lenient; import static org.mockito.Mockito.verifyNoInteractions; import com.esaulpaugh.headlong.abi.Tuple; @@ -55,6 +58,8 @@ import com.hedera.node.app.service.contract.impl.test.exec.systemcontracts.common.CallTestBase; import com.hedera.node.app.service.token.ReadableAccountStore; import com.hedera.node.app.service.token.ReadableTokenStore; +import com.hedera.node.config.data.ContractsConfig; +import com.swirlds.config.api.Configuration; import org.apache.tuweni.bytes.Bytes; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -85,6 +90,12 @@ class UpdateTranslatorTest extends CallTestBase { @Mock private ReadableAccountStore readableAccountStore; + @Mock + private ContractsConfig contractsConfig; + + @Mock + Configuration configuration; + private UpdateTranslator subject; private final UpdateDecoder decoder = new UpdateDecoder(); @@ -204,6 +215,21 @@ void matchesUpdateV3Test() { assertTrue(subject.matches(attempt)); } + @Test + void matchesUpdateMetadataTest() { + given(configuration.getConfigData(ContractsConfig.class)).willReturn(contractsConfig); + given(contractsConfig.metadataKeyAndFieldEnabled()).willReturn(true); + attempt = prepareHtsAttemptWithSelectorAndCustomConfig( + TOKEN_UPDATE_INFO_FUNCTION_WITH_METADATA, + subject, + enhancement, + addressIdConverter, + verificationStrategies, + gasCalculator, + configuration); + assertTrue(subject.matches(attempt)); + } + @Test void matchesFailsOnIncorrectSelector() { attempt = prepareHtsAttemptWithSelector( @@ -222,6 +248,11 @@ void callFromUpdateTest() { Bytes inputBytes = Bytes.wrapByteBuffer(TOKEN_UPDATE_INFO_FUNCTION_V1.encodeCall(tuple)); given(attempt.input()).willReturn(inputBytes); given(attempt.isSelector(TOKEN_UPDATE_INFO_FUNCTION_V1)).willReturn(true); + lenient() + .when(attempt.isSelector(TOKEN_UPDATE_INFO_FUNCTION_WITH_METADATA)) + .thenReturn(false); + lenient().when(attempt.isSelector(TOKEN_UPDATE_INFO_FUNCTION_V2)).thenReturn(false); + lenient().when(attempt.isSelector(TOKEN_UPDATE_INFO_FUNCTION_V3)).thenReturn(false); given(attempt.enhancement()).willReturn(mockEnhancement()); given(attempt.addressIdConverter()).willReturn(addressIdConverter); given(addressIdConverter.convertSender(any())).willReturn(NON_SYSTEM_ACCOUNT_ID); diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/dsl/operations/transactions/AuthorizeContractOperation.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/dsl/operations/transactions/AuthorizeContractOperation.java index fe954aa00472..5d485dcc06b3 100644 --- a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/dsl/operations/transactions/AuthorizeContractOperation.java +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/dsl/operations/transactions/AuthorizeContractOperation.java @@ -70,11 +70,11 @@ public AuthorizeContractOperation(@NonNull final SpecEntity target, @NonNull fin /** * Update this operation to also authorize a given key types besides the admin key. - * @param keyType an additional the key types to authorize + * @param keyTypes an additional the key types to authorize * @return this */ - public AuthorizeContractOperation alsoAuthorizing(@NonNull final TokenKeyType... keyType) { - extraTokenAuthorizations.addAll(Arrays.asList(keyType)); + public AuthorizeContractOperation alsoAuthorizing(@NonNull final TokenKeyType... keyTypes) { + extraTokenAuthorizations.addAll(Arrays.asList(keyTypes)); return this; } diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/dsl/operations/transactions/CallContractOperation.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/dsl/operations/transactions/CallContractOperation.java index 9aa9e592cf78..c67a81d95052 100644 --- a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/dsl/operations/transactions/CallContractOperation.java +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/dsl/operations/transactions/CallContractOperation.java @@ -26,6 +26,7 @@ import com.hedera.services.bdd.spec.dsl.entities.SpecContract; import com.hedera.services.bdd.spec.transactions.contract.HapiContractCall; import edu.umd.cs.findbugs.annotations.NonNull; +import java.util.function.Consumer; /** * Represents a call to a smart contract. @@ -40,6 +41,7 @@ public class CallContractOperation extends AbstractSpecTransaction resultObserver; public CallContractOperation( @NonNull final SpecContract target, @NonNull final String function, @NonNull final Object... parameters) { @@ -56,6 +58,7 @@ protected SpecOperation computeDelegate(@NonNull final HapiSpec spec) { target.name(), function, withSubstitutedTypes(spec.targetNetworkOrThrow(), parameters)) .sending(sendValue) .via(txnName) + .exposingResultTo(resultObserver) .gas(gas); maybeAssertions().ifPresent(a -> a.accept(op)); return op; @@ -86,6 +89,11 @@ public CallContractOperation via(final String txnName) { return this; } + public CallContractOperation exposingResultTo(final Consumer observer) { + this.resultObserver = observer; + return this; + } + @Override protected CallContractOperation self() { return this; diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/precompile/token/GetTokenKeyPrecompileTest.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/precompile/token/GetTokenKeyPrecompileTest.java index ca43bd679240..dc914afa6b42 100644 --- a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/precompile/token/GetTokenKeyPrecompileTest.java +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/precompile/token/GetTokenKeyPrecompileTest.java @@ -21,10 +21,12 @@ import static com.hedera.services.bdd.spec.HapiSpec.hapiTest; import static com.hedera.services.bdd.spec.assertions.ContractFnResultAsserts.isLiteralResult; import static com.hedera.services.bdd.spec.assertions.ContractFnResultAsserts.resultWith; +import static com.hedera.services.bdd.spec.dsl.entities.SpecTokenKey.ADMIN_KEY; import static com.hedera.services.bdd.spec.transactions.contract.HapiParserUtil.asHeadlongAddress; import static com.hedera.services.bdd.suites.contract.Utils.FunctionType.FUNCTION; import static com.hedera.services.bdd.suites.contract.Utils.getABIFor; import static com.hedera.services.bdd.suites.utils.contracts.precompile.TokenKeyType.FREEZE_KEY; +import static com.hedera.services.bdd.suites.utils.contracts.precompile.TokenKeyType.METADATA_KEY; import static com.hedera.services.bdd.suites.utils.contracts.precompile.TokenKeyType.SUPPLY_KEY; import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.CONTRACT_REVERT_EXECUTED; import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.INVALID_TOKEN_ID; @@ -37,6 +39,7 @@ import com.hedera.services.bdd.spec.dsl.annotations.NonFungibleToken; import com.hedera.services.bdd.spec.dsl.entities.SpecContract; import com.hedera.services.bdd.spec.dsl.entities.SpecNonFungibleToken; +import com.hedera.services.bdd.spec.dsl.entities.SpecTokenKey; import java.math.BigInteger; import java.util.stream.Stream; import org.junit.jupiter.api.DisplayName; @@ -53,7 +56,9 @@ public class GetTokenKeyPrecompileTest { @Contract(contract = "UpdateTokenInfoContract", creationGas = 4_000_000L) static SpecContract getTokenKeyContract; - @NonFungibleToken(numPreMints = 1) + @NonFungibleToken( + numPreMints = 1, + keys = {ADMIN_KEY, SpecTokenKey.SUPPLY_KEY, SpecTokenKey.METADATA_KEY}) static SpecNonFungibleToken nonFungibleToken; @HapiTest @@ -67,6 +72,17 @@ public Stream canGetSupplyKeyViaStaticCall() { isLiteralResult(new Object[] {keyTupleFor(token.supplyKeyOrThrow())})))))); } + @HapiTest + @DisplayName("can get a token's metadata key via static call") + public Stream canGetMetadataKeyViaStaticCall() { + return hapiTest(nonFungibleToken.doWith(token -> getTokenKeyContract + .staticCall("getKeyFromToken", nonFungibleToken, METADATA_KEY.asBigInteger()) + .andAssert(query -> query.has(resultWith() + .resultThruAbi( + getABIFor(FUNCTION, "getKeyFromToken", "UpdateTokenInfoContract"), + isLiteralResult(new Object[] {keyTupleFor(token.metadataKeyOrThrow())})))))); + } + @HapiTest @DisplayName("cannot get a nonsense key type") public Stream cannotGetNonsenseKeyType() { diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/precompile/token/TokenMetadataTest.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/precompile/token/TokenMetadataTest.java new file mode 100644 index 000000000000..8da2316af11c --- /dev/null +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/precompile/token/TokenMetadataTest.java @@ -0,0 +1,575 @@ +/* + * Copyright (C) 2024 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.services.bdd.suites.contract.precompile.token; + +import static com.hedera.services.bdd.junit.TestTags.SMART_CONTRACT; +import static com.hedera.services.bdd.spec.HapiSpec.hapiTest; +import static com.hedera.services.bdd.spec.assertions.ContractFnResultAsserts.resultWith; +import static com.hedera.services.bdd.spec.assertions.TransactionRecordAsserts.recordWith; +import static com.hedera.services.bdd.spec.dsl.entities.SpecTokenKey.ADMIN_KEY; +import static com.hedera.services.bdd.spec.dsl.entities.SpecTokenKey.METADATA_KEY; +import static com.hedera.services.bdd.spec.dsl.entities.SpecTokenKey.PAUSE_KEY; +import static com.hedera.services.bdd.spec.dsl.entities.SpecTokenKey.SUPPLY_KEY; +import static com.hedera.services.bdd.spec.utilops.CustomSpecAssert.allRunFor; +import static com.hedera.services.bdd.spec.utilops.UtilVerbs.childRecordsCheck; +import static com.hedera.services.bdd.spec.utilops.UtilVerbs.exposeTargetLedgerIdTo; +import static com.hedera.services.bdd.spec.utilops.UtilVerbs.withOpContext; +import static com.hedera.services.bdd.suites.HapiSuite.ONE_HBAR; +import static com.hedera.services.bdd.suites.HapiSuite.ONE_MILLION_HBARS; +import static com.hedera.services.bdd.suites.HapiSuite.THREE_MONTHS_IN_SECONDS; +import static com.hedera.services.bdd.suites.utils.contracts.precompile.HTSPrecompileResult.htsPrecompileResult; +import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.SUCCESS; +import static com.hederahashgraph.api.proto.java.TokenSupplyType.INFINITE_VALUE; +import static com.hederahashgraph.api.proto.java.TokenType.FUNGIBLE_COMMON; +import static com.hederahashgraph.api.proto.java.TokenType.NON_FUNGIBLE_UNIQUE; + +import com.esaulpaugh.headlong.abi.Address; +import com.google.protobuf.ByteString; +import com.hedera.node.app.hapi.utils.contracts.ParsingConstants.FunctionType; +import com.hedera.services.bdd.junit.HapiTest; +import com.hedera.services.bdd.junit.HapiTestLifecycle; +import com.hedera.services.bdd.junit.support.TestLifecycle; +import com.hedera.services.bdd.spec.HapiSpec; +import com.hedera.services.bdd.spec.dsl.annotations.Account; +import com.hedera.services.bdd.spec.dsl.annotations.Contract; +import com.hedera.services.bdd.spec.dsl.annotations.FungibleToken; +import com.hedera.services.bdd.spec.dsl.annotations.Key; +import com.hedera.services.bdd.spec.dsl.annotations.NonFungibleToken; +import com.hedera.services.bdd.spec.dsl.entities.SpecAccount; +import com.hedera.services.bdd.spec.dsl.entities.SpecContract; +import com.hedera.services.bdd.spec.dsl.entities.SpecFungibleToken; +import com.hedera.services.bdd.spec.dsl.entities.SpecKey; +import com.hedera.services.bdd.spec.dsl.entities.SpecNonFungibleToken; +import com.hedera.services.bdd.suites.utils.contracts.precompile.TokenKeyType; +import com.hederahashgraph.api.proto.java.CustomFee; +import com.hederahashgraph.api.proto.java.Duration; +import com.hederahashgraph.api.proto.java.FixedFee; +import com.hederahashgraph.api.proto.java.Timestamp; +import com.hederahashgraph.api.proto.java.TokenID; +import com.hederahashgraph.api.proto.java.TokenInfo; +import java.nio.charset.StandardCharsets; +import java.util.concurrent.atomic.AtomicReference; +import java.util.stream.Stream; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.DynamicTest; +import org.junit.jupiter.api.Tag; + +@Tag(SMART_CONTRACT) +@DisplayName("metadata tests") +@SuppressWarnings("java:S1192") +@HapiTestLifecycle +public class TokenMetadataTest { + + @Account(maxAutoAssociations = 10, tinybarBalance = ONE_MILLION_HBARS) + static SpecAccount alice; + + @Contract(contract = "CreateTokenVTwo", creationGas = 1_000_000L) + static SpecContract contractTarget; + + @Contract(contract = "TokenInfo", creationGas = 1_000_000L) + static SpecContract tokenInfoContract; + + @FungibleToken(name = "fungibleToken", initialSupply = 1_000L, maxSupply = 1_200L) + static SpecFungibleToken fungibleToken; + + @NonFungibleToken( + numPreMints = 5, + keys = {SUPPLY_KEY, PAUSE_KEY, ADMIN_KEY, METADATA_KEY}) + static SpecNonFungibleToken nft; + + @Key() + SpecKey key; + + @BeforeAll + static void beforeAll(final TestLifecycle testLifecycle) { + testLifecycle.doAdhoc( + alice.authorizeContract(contractTarget) + .alsoAuthorizing(TokenKeyType.SUPPLY_KEY, TokenKeyType.METADATA_KEY), + nft.authorizeContracts(contractTarget) + .alsoAuthorizing(TokenKeyType.SUPPLY_KEY, TokenKeyType.METADATA_KEY), + fungibleToken.authorizeContracts(contractTarget)); + } + + @HapiTest + public Stream testUpdateMetadata() { + return Stream.of(nft, fungibleToken) + .flatMap(token -> hapiTest( + contractTarget + .call("updateTokenMetadata", token, "randomMetaNew777") + .gas(1_000_000L) + .andAssert(txn -> txn.hasKnownStatus(SUCCESS)), + token.getInfo().andAssert(info -> info.hasMetadata("randomMetaNew777")))); + } + + @HapiTest + public Stream testUpdateTokenKeys() { + return hapiTest(contractTarget + .call("updateTokenKeys", nft, alice.getED25519KeyBytes(), contractTarget) + .gas(1_000_000L) + .payingWith(alice) + .andAssert(txn -> txn.hasKnownStatus(SUCCESS))); + } + + @HapiTest + final Stream createTokenV2HappyPath() { + final AtomicReference
newToken = new AtomicReference<>(); + final AtomicReference ledgerId = new AtomicReference<>(); + return hapiTest(withOpContext((spec, opLog) -> { + final var create = contractTarget + .call("createTokenWithMetadata") + .sending(2000 * ONE_HBAR) + .gas(1_000_000L) + .exposingResultTo(res -> newToken.set((Address) res[0])); + final var ledger = exposeTargetLedgerIdTo(ledgerId::set); + allRunFor(spec, create, ledger); + final var getInfo = tokenInfoContract + .call("getInformationForTokenV2", newToken.get()) + .gas(100_000L) + .andAssert(txn -> txn.hasKnownStatus(SUCCESS).via("getInfo").logged()); + final var childRecord = childRecordsCheck( + "getInfo", + SUCCESS, + recordWith() + .status(SUCCESS) + .contractCallResult(resultWith() + .contractCallResult(htsPrecompileResult() + .forFunction(FunctionType.HAPI_GET_TOKEN_INFO_V2) + .withStatus(SUCCESS) + .withTokenInfo(tokenInfoV2(spec, ledgerId, newToken) + .build())))); + allRunFor(spec, getInfo, childRecord); + })); + } + + @HapiTest + final Stream createTokenHappyPath() { + final AtomicReference
newToken = new AtomicReference<>(); + final AtomicReference ledgerId = new AtomicReference<>(); + return hapiTest(withOpContext((spec, opLog) -> { + final var create = contractTarget + .call("createToken") + .sending(2000 * ONE_HBAR) + .gas(1_000_000L) + .exposingResultTo(res -> newToken.set((Address) res[0])); + final var ledger = exposeTargetLedgerIdTo(ledgerId::set); + allRunFor(spec, create, ledger); + final var getInfo = tokenInfoContract + .call("getInformationForToken", newToken.get()) + .gas(100_000L) + .andAssert(txn -> txn.hasKnownStatus(SUCCESS).via("getInfo").logged()); + final var childRecord = childRecordsCheck( + "getInfo", + SUCCESS, + recordWith() + .status(SUCCESS) + .contractCallResult(resultWith() + .contractCallResult(htsPrecompileResult() + .forFunction(FunctionType.HAPI_GET_TOKEN_INFO) + .withStatus(SUCCESS) + .withTokenInfo(tokenInfo(spec, ledgerId, newToken) + .build())))); + // re-enable once system contracts versioning is done + // allRunFor(spec, getInfo, childRecord); + })); + } + + @HapiTest + final Stream createTokenV2WithKeyHappyPath() { + final AtomicReference
newToken = new AtomicReference<>(); + final AtomicReference ledgerId = new AtomicReference<>(); + return hapiTest(withOpContext((spec, opLog) -> { + final var create = contractTarget + .call("createTokenWithMetadataAndKey") + .sending(2000 * ONE_HBAR) + .gas(1_000_000L) + .exposingResultTo(res -> newToken.set((Address) res[0])); + final var ledger = exposeTargetLedgerIdTo(ledgerId::set); + allRunFor(spec, create, ledger); + final var getInfo = tokenInfoContract + .call("getInformationForTokenV2", newToken.get()) + .gas(100_000L) + .andAssert(txn -> txn.hasKnownStatus(SUCCESS).via("getInfo").logged()); + final var childRecord = childRecordsCheck( + "getInfo", + SUCCESS, + recordWith() + .status(SUCCESS) + .contractCallResult(resultWith() + .contractCallResult(htsPrecompileResult() + .forFunction(FunctionType.HAPI_GET_TOKEN_INFO_V2) + .withStatus(SUCCESS) + .withTokenInfo(tokenInfoV2(spec, ledgerId, newToken) + .setMetadataKey(metaKey(spec)) + .build())))); + allRunFor(spec, getInfo, childRecord); + })); + } + + @HapiTest + final Stream createTokenV2WithCustomFeesHappyPath() { + final AtomicReference
newToken = new AtomicReference<>(); + final AtomicReference ledgerId = new AtomicReference<>(); + return hapiTest(withOpContext((spec, opLog) -> { + final var create = contractTarget + .call("createTokenWithMetadataAndCustomFees") + .sending(2000 * ONE_HBAR) + .gas(1_000_000L) + .exposingResultTo(res -> newToken.set((Address) res[0])); + final var ledger = exposeTargetLedgerIdTo(ledgerId::set); + allRunFor(spec, create, ledger); + final var getInfo = tokenInfoContract + .call("getInformationForTokenV2", newToken.get()) + .gas(100_000L) + .andAssert(txn -> txn.hasKnownStatus(SUCCESS).via("getInfo").logged()); + final var childRecord = childRecordsCheck( + "getInfo", + SUCCESS, + recordWith() + .status(SUCCESS) + .contractCallResult(resultWith() + .contractCallResult(htsPrecompileResult() + .forFunction(FunctionType.HAPI_GET_TOKEN_INFO_V2) + .withStatus(SUCCESS) + .withTokenInfo(tokenInfoV2(spec, ledgerId, newToken) + .addCustomFees(customFee(spec)) + .build())))); + allRunFor(spec, getInfo, childRecord); + })); + } + + @HapiTest + final Stream createTokenWithCustomFeesHappyPath() { + final AtomicReference
newToken = new AtomicReference<>(); + final AtomicReference ledgerId = new AtomicReference<>(); + return hapiTest(withOpContext((spec, opLog) -> { + final var create = contractTarget + .call("createTokenWithCustomFees") + .sending(2000 * ONE_HBAR) + .gas(1_000_000L) + .exposingResultTo(res -> newToken.set((Address) res[0])); + final var ledger = exposeTargetLedgerIdTo(ledgerId::set); + allRunFor(spec, create, ledger); + final var getInfo = tokenInfoContract + .call("getInformationForToken", newToken.get()) + .gas(100_000L) + .andAssert(txn -> txn.hasKnownStatus(SUCCESS).via("getInfo").logged()); + final var childRecord = childRecordsCheck( + "getInfo", + SUCCESS, + recordWith() + .status(SUCCESS) + .contractCallResult(resultWith() + .contractCallResult(htsPrecompileResult() + .forFunction(FunctionType.HAPI_GET_TOKEN_INFO) + .withStatus(SUCCESS) + .withTokenInfo(tokenInfo(spec, ledgerId, newToken) + .addCustomFees(customFee(spec)) + .build())))); + // re-enable once system contracts versioning is done + // allRunFor(spec, getInfo, childRecord); + })); + } + + @HapiTest + final Stream createTokenV2WithKeyAndCustomFeesHappyPath() { + final AtomicReference
newToken = new AtomicReference<>(); + final AtomicReference ledgerId = new AtomicReference<>(); + return hapiTest(withOpContext((spec, opLog) -> { + final var create = contractTarget + .call("createTokenWithMetadataAndKeyAndCustomFees") + .sending(2000 * ONE_HBAR) + .gas(1_000_000L) + .exposingResultTo(res -> newToken.set((Address) res[0])); + final var ledger = exposeTargetLedgerIdTo(ledgerId::set); + allRunFor(spec, create, ledger); + final var getInfo = tokenInfoContract + .call("getInformationForTokenV2", newToken.get()) + .gas(100_000L) + .andAssert(txn -> txn.hasKnownStatus(SUCCESS).via("getInfo").logged()); + final var childRecord = childRecordsCheck( + "getInfo", + SUCCESS, + recordWith() + .status(SUCCESS) + .contractCallResult(resultWith() + .contractCallResult(htsPrecompileResult() + .forFunction(FunctionType.HAPI_GET_TOKEN_INFO_V2) + .withStatus(SUCCESS) + .withTokenInfo(tokenInfoV2(spec, ledgerId, newToken) + .addCustomFees(customFee(spec)) + .setMetadataKey(metaKey(spec)) + .build())))); + allRunFor(spec, getInfo, childRecord); + })); + } + + @HapiTest + final Stream createNftHappyPath() { + final AtomicReference
newToken = new AtomicReference<>(); + final AtomicReference ledgerId = new AtomicReference<>(); + return hapiTest(withOpContext((spec, opLog) -> { + final var create = contractTarget + .call("createNft") + .sending(2000 * ONE_HBAR) + .gas(1_000_000L) + .exposingResultTo(res -> newToken.set((Address) res[0])); + final var ledger = exposeTargetLedgerIdTo(ledgerId::set); + allRunFor(spec, create, ledger); + final var getInfo = tokenInfoContract + .call("getInformationForToken", newToken.get()) + .gas(100_000L) + .andAssert(txn -> txn.hasKnownStatus(SUCCESS).via("getInfo").logged()); + final var childRecord = childRecordsCheck( + "getInfo", + SUCCESS, + recordWith() + .status(SUCCESS) + .contractCallResult(resultWith() + .contractCallResult(htsPrecompileResult() + .forFunction(FunctionType.HAPI_GET_TOKEN_INFO) + .withStatus(SUCCESS) + .withTokenInfo(nftInfo(spec, ledgerId, newToken) + .build())))); + // re-enable once system contracts versioning is done + // allRunFor(spec, getInfo, childRecord); + })); + } + + @HapiTest + final Stream createNftWithMetaHappyPath() { + final AtomicReference
newToken = new AtomicReference<>(); + final AtomicReference ledgerId = new AtomicReference<>(); + return hapiTest(withOpContext((spec, opLog) -> { + final var create = contractTarget + .call("createNftWithMetadata") + .sending(2000 * ONE_HBAR) + .gas(1_000_000L) + .exposingResultTo(res -> newToken.set((Address) res[0])); + final var ledger = exposeTargetLedgerIdTo(ledgerId::set); + allRunFor(spec, create, ledger); + final var getInfo = tokenInfoContract + .call("getInformationForTokenV2", newToken.get()) + .gas(100_000L) + .andAssert(txn -> txn.hasKnownStatus(SUCCESS).via("getInfo").logged()); + final var childRecord = childRecordsCheck( + "getInfo", + SUCCESS, + recordWith() + .status(SUCCESS) + .contractCallResult(resultWith() + .contractCallResult(htsPrecompileResult() + .forFunction(FunctionType.HAPI_GET_TOKEN_INFO_V2) + .withStatus(SUCCESS) + .withTokenInfo(nftInfoV2(spec, ledgerId, newToken) + .build())))); + allRunFor(spec, getInfo, childRecord); + })); + } + + @HapiTest + final Stream createNftWithMetaAndKeyHappyPath() { + final AtomicReference
newToken = new AtomicReference<>(); + final AtomicReference ledgerId = new AtomicReference<>(); + return hapiTest(withOpContext((spec, opLog) -> { + final var create = contractTarget + .call("createNftWithMetaAndKey") + .sending(2000 * ONE_HBAR) + .gas(5_000_000L) + .exposingResultTo(res -> newToken.set((Address) res[0])); + final var ledger = exposeTargetLedgerIdTo(ledgerId::set); + allRunFor(spec, create, ledger); + final var getInfo = tokenInfoContract + .call("getInformationForTokenV2", newToken.get()) + .gas(100_000L) + .andAssert(txn -> txn.hasKnownStatus(SUCCESS).via("getInfo").logged()); + final var childRecord = childRecordsCheck( + "getInfo", + SUCCESS, + recordWith() + .status(SUCCESS) + .contractCallResult(resultWith() + .contractCallResult(htsPrecompileResult() + .forFunction(FunctionType.HAPI_GET_TOKEN_INFO_V2) + .withStatus(SUCCESS) + .withTokenInfo(nftInfoV2(spec, ledgerId, newToken) + .setMetadataKey(metaKey(spec)) + .build())))); + allRunFor(spec, getInfo, childRecord); + })); + } + + @HapiTest + final Stream createNftWithCustomFeesHappyPath() { + final AtomicReference
newToken = new AtomicReference<>(); + final AtomicReference ledgerId = new AtomicReference<>(); + return hapiTest(withOpContext((spec, opLog) -> { + final var create = contractTarget + .call("createNftWithCustomFees") + .sending(2000 * ONE_HBAR) + .gas(1_000_000L) + .exposingResultTo(res -> newToken.set((Address) res[0])); + final var ledger = exposeTargetLedgerIdTo(ledgerId::set); + allRunFor(spec, create, ledger); + final var getInfo = tokenInfoContract + .call("getInformationForToken", newToken.get()) + .gas(100_000L) + .andAssert(txn -> txn.hasKnownStatus(SUCCESS).via("getInfo").logged()); + final var childRecord = childRecordsCheck( + "getInfo", + SUCCESS, + recordWith() + .status(SUCCESS) + .contractCallResult(resultWith() + .contractCallResult(htsPrecompileResult() + .forFunction(FunctionType.HAPI_GET_TOKEN_INFO) + .withStatus(SUCCESS) + .withTokenInfo(nftInfo(spec, ledgerId, newToken) + .addCustomFees(customFee(spec)) + .build())))); + // re-enable once system contracts versioning is done + // allRunFor(spec, getInfo, childRecord); + })); + } + + @HapiTest + final Stream createNftWithMetaAndCustomFeesHappyPath() { + final AtomicReference
newToken = new AtomicReference<>(); + final AtomicReference ledgerId = new AtomicReference<>(); + return hapiTest(withOpContext((spec, opLog) -> { + final var create = contractTarget + .call("createNftWithMetadataAndCustomFees") + .sending(2000 * ONE_HBAR) + .gas(1_000_000L) + .exposingResultTo(res -> newToken.set((Address) res[0])); + final var ledger = exposeTargetLedgerIdTo(ledgerId::set); + allRunFor(spec, create, ledger); + final var getInfo = tokenInfoContract + .call("getInformationForTokenV2", newToken.get()) + .gas(100_000L) + .andAssert(txn -> txn.hasKnownStatus(SUCCESS).via("getInfo").logged()); + final var childRecord = childRecordsCheck( + "getInfo", + SUCCESS, + recordWith() + .status(SUCCESS) + .contractCallResult(resultWith() + .contractCallResult(htsPrecompileResult() + .forFunction(FunctionType.HAPI_GET_TOKEN_INFO_V2) + .withStatus(SUCCESS) + .withTokenInfo(nftInfoV2(spec, ledgerId, newToken) + .addCustomFees(customFee(spec)) + .build())))); + allRunFor(spec, getInfo, childRecord); + })); + } + + @HapiTest + final Stream createNftWithMetaAndKeyAndCustomFeesHappyPath() { + final AtomicReference
newToken = new AtomicReference<>(); + final AtomicReference ledgerId = new AtomicReference<>(); + return hapiTest(withOpContext((spec, opLog) -> { + final var create = contractTarget + .call("createNftWithMetaAndKeyAndCustomFees") + .sending(2000 * ONE_HBAR) + .gas(1_000_000L) + .exposingResultTo(res -> newToken.set((Address) res[0])); + final var ledger = exposeTargetLedgerIdTo(ledgerId::set); + allRunFor(spec, create, ledger); + final var getInfo = tokenInfoContract + .call("getInformationForTokenV2", newToken.get()) + .gas(100_000L) + .andAssert(txn -> txn.hasKnownStatus(SUCCESS).via("getInfo").logged()); + final var childRecord = childRecordsCheck( + "getInfo", + SUCCESS, + recordWith() + .status(SUCCESS) + .contractCallResult(resultWith() + .contractCallResult(htsPrecompileResult() + .forFunction(FunctionType.HAPI_GET_TOKEN_INFO_V2) + .withStatus(SUCCESS) + .withTokenInfo(nftInfoV2(spec, ledgerId, newToken) + .setMetadataKey(metaKey(spec)) + .addCustomFees(customFee(spec)) + .build())))); + allRunFor(spec, getInfo, childRecord); + })); + } + + private TokenInfo.Builder tokenInfo( + final HapiSpec spec, final AtomicReference ledgerId, final AtomicReference
newToken) { + return TokenInfo.newBuilder() + .setLedgerId(ledgerId.get()) + .setName("testToken") + .setTokenId(TokenID.newBuilder() + .setTokenNum(newToken.get().value().longValue()) + .build()) + .setSymbol("test") + .setTotalSupply(100L) + .setDecimals(4) + .setTokenType(FUNGIBLE_COMMON) + .setSupplyTypeValue(INFINITE_VALUE) + .setExpiry(Timestamp.newBuilder().setSeconds(-9223372036854775808L)) + .setAutoRenewPeriod(Duration.newBuilder().setSeconds(THREE_MONTHS_IN_SECONDS)) + .setTreasury(spec.registry().getAccountID(contractTarget.name())); + } + + private TokenInfo.Builder tokenInfoV2( + final HapiSpec spec, final AtomicReference ledgerId, final AtomicReference
newToken) { + return tokenInfo(spec, ledgerId, newToken) + .setMetadata(ByteString.copyFrom("testmeta".getBytes(StandardCharsets.UTF_8))); + } + + private TokenInfo.Builder nftInfo( + final HapiSpec spec, final AtomicReference ledgerId, final AtomicReference
newToken) { + return TokenInfo.newBuilder() + .setLedgerId(ledgerId.get()) + .setName("nft") + .setTokenId(TokenID.newBuilder() + .setTokenNum(newToken.get().value().longValue()) + .build()) + .setSymbol("nft") + .setTokenType(NON_FUNGIBLE_UNIQUE) + .setExpiry(Timestamp.newBuilder().setSeconds(-9223372036854775808L)) + .setSupplyKey(metaKey(spec)) + .setAutoRenewPeriod(Duration.newBuilder().setSeconds(THREE_MONTHS_IN_SECONDS)) + .setTreasury(spec.registry().getAccountID(contractTarget.name())); + } + + private TokenInfo.Builder nftInfoV2( + final HapiSpec spec, final AtomicReference ledgerId, final AtomicReference
newToken) { + return nftInfo(spec, ledgerId, newToken) + .setMetadata(ByteString.copyFrom("testmeta".getBytes(StandardCharsets.UTF_8))); + } + + private com.hederahashgraph.api.proto.java.Key metaKey(final HapiSpec spec) { + final var contractID = spec.registry().getContractId(contractTarget.name()); + return com.hederahashgraph.api.proto.java.Key.newBuilder() + .setContractID(contractID) + .build(); + } + + private CustomFee customFee(final HapiSpec spec) { + final var fixedFee = FixedFee.newBuilder().setAmount(10); + final var accountID = spec.registry().getAccountID(contractTarget.name()); + return CustomFee.newBuilder() + .setFixedFee(fixedFee) + .setFeeCollectorAccountId(accountID) + .build(); + } +} diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/utils/contracts/precompile/HTSPrecompileResult.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/utils/contracts/precompile/HTSPrecompileResult.java index 5a593836d1b1..770d8c5ca3b2 100644 --- a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/utils/contracts/precompile/HTSPrecompileResult.java +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/utils/contracts/precompile/HTSPrecompileResult.java @@ -22,8 +22,8 @@ import static com.hedera.node.app.hapi.utils.contracts.ParsingConstants.EXPIRY; import static com.hedera.node.app.hapi.utils.contracts.ParsingConstants.FIXED_FEE; import static com.hedera.node.app.hapi.utils.contracts.ParsingConstants.FRACTIONAL_FEE; -import static com.hedera.node.app.hapi.utils.contracts.ParsingConstants.HEDERA_TOKEN_V1; -import static com.hedera.node.app.hapi.utils.contracts.ParsingConstants.HEDERA_TOKEN_V4; +import static com.hedera.node.app.hapi.utils.contracts.ParsingConstants.HEDERA_TOKEN_V3; +import static com.hedera.node.app.hapi.utils.contracts.ParsingConstants.HEDERA_TOKEN_WITH_METADATA; import static com.hedera.node.app.hapi.utils.contracts.ParsingConstants.KEY_VALUE; import static com.hedera.node.app.hapi.utils.contracts.ParsingConstants.RESPONSE_STATUS_AT_BEGINNING; import static com.hedera.node.app.hapi.utils.contracts.ParsingConstants.ROYALTY_FEE; @@ -70,7 +70,7 @@ private HTSPrecompileResult() {} public static final String ROYALTY_FEE_REPLACED_ADDRESS = ROYALTY_FEE.replace(ADDRESS_TYPE, BYTES_32_TYPE); public static final String EXPIRY_REPLACED_ADDRESS = EXPIRY.replace(ADDRESS_TYPE, BYTES_32_TYPE); public static final String TOKEN_INFO_REPLACED_ADDRESS = "(" - + HEDERA_TOKEN_V1.replace(removeBrackets(ADDRESS), removeBrackets(BYTES32)) + + HEDERA_TOKEN_V3.replace(removeBrackets(ADDRESS), removeBrackets(BYTES32)) + ",int64,bool,bool,bool," + FIXED_FEE_REPLACED_ADDRESS + ARRAY_BRACKETS @@ -83,7 +83,7 @@ private HTSPrecompileResult() {} + ",string" + ")"; public static final String TOKEN_INFO_V2 = "(" - + HEDERA_TOKEN_V4.replace(removeBrackets(ADDRESS), removeBrackets(BYTES32)) + + HEDERA_TOKEN_WITH_METADATA.replace(removeBrackets(ADDRESS), removeBrackets(BYTES32)) + ",int64,bool,bool,bool," + FIXED_FEE_REPLACED_ADDRESS + ARRAY_BRACKETS diff --git a/hedera-node/test-clients/src/main/resources/contract/contracts/CreateTokenVTwo/CreateTokenVTwo.bin b/hedera-node/test-clients/src/main/resources/contract/contracts/CreateTokenVTwo/CreateTokenVTwo.bin new file mode 100644 index 000000000000..97930a4684d2 --- /dev/null +++ b/hedera-node/test-clients/src/main/resources/contract/contracts/CreateTokenVTwo/CreateTokenVTwo.bin @@ -0,0 +1 @@ +60806040523480156200001157600080fd5b5060018060008060078111156200002d576200002c62000235565b5b600781111562000042576200004162000235565b5b8152602001908152602001600020819055506002600160006001600781111562000071576200007062000235565b5b600781111562000086576200008562000235565b5b81526020019081526020016000208190555060046001600060026007811115620000b557620000b462000235565b5b6007811115620000ca57620000c962000235565b5b81526020019081526020016000208190555060086001600060036007811115620000f957620000f862000235565b5b60078111156200010e576200010d62000235565b5b815260200190815260200160002081905550601060016000600460078111156200013d576200013c62000235565b5b600781111562000152576200015162000235565b5b8152602001908152602001600020819055506020600160006005600781111562000181576200018062000235565b5b600781111562000196576200019562000235565b5b81526020019081526020016000208190555060406001600060066007811115620001c557620001c462000235565b5b6007811115620001da57620001d962000235565b5b81526020019081526020016000208190555060806001600060078081111562000208576200020762000235565b5b60078111156200021d576200021c62000235565b5b81526020019081526020016000208190555062000264565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b61446880620002746000396000f3fe6080604052600436106100fe5760003560e01c80634834271b116100955780637fad8c91116100645780637fad8c91146102c05780639b23d3d9146102de5780639cbf9e361461031b578063e3b9cd9014610339578063e487710514610357576100fe565b80634834271b1461021d578063593ea06b1461023b578063618dc65e14610259578063737aea7114610297576100fe565b80632248716b116100d15780632248716b146101a55780633a876110146101c35780633e312c86146101e15780633fc9dc1e146101ff576100fe565b806309a31b001461010357806310bbc17314610121578063122738051461014a57806315dacbea14610168575b600080fd5b61010b610375565b60405161011891906131d8565b60405180910390f35b34801561012d57600080fd5b5061014860048036038101906101439190613379565b610599565b005b6101526105cf565b60405161015f91906131d8565b60405180910390f35b34801561017457600080fd5b5061018f600480360381019061018a919061340b565b610749565b60405161019c919061348e565b60405180910390f35b6101ad610867565b6040516101ba91906131d8565b60405180910390f35b6101cb6109ee565b6040516101d891906131d8565b60405180910390f35b6101e9610ba7565b6040516101f691906131d8565b60405180910390f35b610207610d99565b60405161021491906131d8565b60405180910390f35b610225610f88565b60405161023291906131d8565b60405180910390f35b610243611145565b60405161025091906131d8565b60405180910390f35b34801561026557600080fd5b50610280600480360381019061027b919061354a565b61136d565b60405161028e92919061363e565b60405180910390f35b3480156102a357600080fd5b506102be60048036038101906102b9919061366e565b6114c9565b005b6102c8611576565b6040516102d591906131d8565b60405180910390f35b3480156102ea57600080fd5b506103056004803603810190610300919061340b565b61175b565b604051610312919061348e565b60405180910390f35b610323611879565b60405161033091906131d8565b60405180910390f35b6103416119c1565b60405161034e91906131d8565b60405180910390f35b61035f611c1b565b60405161036c91906131d8565b60405180910390f35b600061037f612ecf565b6040518060400160405280600381526020017f6e6674000000000000000000000000000000000000000000000000000000000081525081600001819052506040518060400160405280600381526020017f6e6674000000000000000000000000000000000000000000000000000000000081525081602001819052506040518060400160405280600881526020017f746573746d65746100000000000000000000000000000000000000000000000081525081610120018190525030816040019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff1681525050600167ffffffffffffffff81111561048d5761048c61324e565b5b6040519080825280602002602001820160405280156104c657816020015b6104b3612f45565b8152602001906001900390816104ab5790505b508160e0018190525060006104de6004600130611dce565b9050808260e001516000815181106104f9576104f86136dd565b5b60200260200101819052506000610511600a30611e05565b905060008067ffffffffffffffff81111561052f5761052e61324e565b5b60405190808252806020026020018201604052801561056857816020015b610555612f65565b81526020019060019003908161054d5790505b509050600080610579868585611e93565b91509150601660030b821461058d57600080fd5b80965050505050505090565b6105a1612ecf565b8181610120018190525060006105b78483612021565b9050601660030b81146105c957600080fd5b50505050565b60006105d9612fd2565b6040518060400160405280600381526020017f6e6674000000000000000000000000000000000000000000000000000000000081525081600001819052506040518060400160405280600381526020017f6e66740000000000000000000000000000000000000000000000000000000000815250816020018190525030816040019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff1681525050600167ffffffffffffffff8111156106a8576106a761324e565b5b6040519080825280602002602001820160405280156106e157816020015b6106ce612f45565b8152602001906001900390816106c65790505b508160e0018190525060006106f96004600130611dce565b9050808260e00151600081518110610714576107136136dd565b5b602002602001018190525060008061072b84612151565b91509150601660030b821461073f57600080fd5b8094505050505090565b600080600061016773ffffffffffffffffffffffffffffffffffffffff166315dacbea60e01b88888888604051602401610786949392919061371b565b604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050506040516107f0919061379c565b6000604051808303816000865af19150503d806000811461082d576040519150601f19603f3d011682016040523d82523d6000602084013e610832565b606091505b509150915081610843576015610858565b8080602001905181019061085791906137ec565b5b60030b92505050949350505050565b6000610871612ecf565b6040518060400160405280600981526020017f74657374546f6b656e000000000000000000000000000000000000000000000081525081600001819052506040518060400160405280600881526020017f746573746d6574610000000000000000000000000000000000000000000000008152508161012001819052506040518060400160405280600481526020017f7465737400000000000000000000000000000000000000000000000000000000815250816020018190525030816040019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff1681525050600067ffffffffffffffff81111561097f5761097e61324e565b5b6040519080825280602002602001820160405280156109b857816020015b6109a5612f45565b81526020019060019003908161099d5790505b508160e001819052506000806109d183606460046122d8565b91509150601660030b82146109e557600080fd5b80935050505090565b60006109f8612ecf565b6040518060400160405280600381526020017f6e6674000000000000000000000000000000000000000000000000000000000081525081600001819052506040518060400160405280600381526020017f6e6674000000000000000000000000000000000000000000000000000000000081525081602001819052506040518060400160405280600881526020017f746573746d65746100000000000000000000000000000000000000000000000081525081610120018190525030816040019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff1681525050600167ffffffffffffffff811115610b0657610b0561324e565b5b604051908082528060200260200182016040528015610b3f57816020015b610b2c612f45565b815260200190600190039081610b245790505b508160e001819052506000610b576004600130611dce565b9050808260e00151600081518110610b7257610b716136dd565b5b6020026020010181905250600080610b8984612466565b91509150601660030b8214610b9d57600080fd5b8094505050505090565b6000610bb1612ecf565b6040518060400160405280600981526020017f74657374546f6b656e000000000000000000000000000000000000000000000081525081600001819052506040518060400160405280600881526020017f746573746d6574610000000000000000000000000000000000000000000000008152508161012001819052506040518060400160405280600481526020017f7465737400000000000000000000000000000000000000000000000000000000815250816020018190525030816040019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff1681525050600067ffffffffffffffff811115610cbf57610cbe61324e565b5b604051908082528060200260200182016040528015610cf857816020015b610ce5612f45565b815260200190600190039081610cdd5790505b508160e001819052506000610d0e600a30611e05565b905060008067ffffffffffffffff811115610d2c57610d2b61324e565b5b604051908082528060200260200182016040528015610d6557816020015b610d52613041565b815260200190600190039081610d4a5790505b509050600080610d7a856064600887876125ed565b91509150601660030b8214610d8e57600080fd5b809550505050505090565b6000610da3612ecf565b6040518060400160405280600381526020017f6e6674000000000000000000000000000000000000000000000000000000000081525081600001819052506040518060400160405280600381526020017f6e6674000000000000000000000000000000000000000000000000000000000081525081602001819052506040518060400160405280600881526020017f746573746d65746100000000000000000000000000000000000000000000000081525081610120018190525030816040019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff1681525050600267ffffffffffffffff811115610eb157610eb061324e565b5b604051908082528060200260200182016040528015610eea57816020015b610ed7612f45565b815260200190600190039081610ecf5790505b508160e001819052506000610f026007600130611dce565b90506000610f136004600130611dce565b9050818360e00151600081518110610f2e57610f2d6136dd565b5b6020026020010181905250808360e00151600181518110610f5257610f516136dd565b5b6020026020010181905250600080610f6985612466565b91509150601660030b8214610f7d57600080fd5b809550505050505090565b6000610f92612ecf565b6040518060400160405280600981526020017f74657374546f6b656e000000000000000000000000000000000000000000000081525081600001819052506040518060400160405280600881526020017f746573746d6574610000000000000000000000000000000000000000000000008152508161012001819052506040518060400160405280600481526020017f7465737400000000000000000000000000000000000000000000000000000000815250816020018190525030816040019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff1681525050600167ffffffffffffffff8111156110a05761109f61324e565b5b6040519080825280602002602001820160405280156110d957816020015b6110c6612f45565b8152602001906001900390816110be5790505b508160e0018190525060006110f16007600130611dce565b9050808260e0015160008151811061110c5761110b6136dd565b5b602002602001018190525060008061112784606460046122d8565b91509150601660030b821461113b57600080fd5b8094505050505090565b600061114f612ecf565b6040518060400160405280600981526020017f74657374546f6b656e000000000000000000000000000000000000000000000081525081600001819052506040518060400160405280600881526020017f746573746d6574610000000000000000000000000000000000000000000000008152508161012001819052506040518060400160405280600481526020017f7465737400000000000000000000000000000000000000000000000000000000815250816020018190525030816040019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff1681525050600167ffffffffffffffff81111561125d5761125c61324e565b5b60405190808252806020026020018201604052801561129657816020015b611283612f45565b81526020019060019003908161127b5790505b508160e0018190525060006112ae6007600130611dce565b9050808260e001516000815181106112c9576112c86136dd565b5b602002602001018190525060006112e1600a30611e05565b905060008067ffffffffffffffff8111156112ff576112fe61324e565b5b60405190808252806020026020018201604052801561133857816020015b611325613041565b81526020019060019003908161131d5790505b50905060008061134d866064600887876125ed565b91509150601660030b821461136157600080fd5b80965050505050505090565b6000606060008061016773ffffffffffffffffffffffffffffffffffffffff1663618dc65e60e01b87876040516024016113a8929190613819565b604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050604051611412919061379c565b6000604051808303816000865af19150503d806000811461144f576040519150601f19603f3d011682016040523d82523d6000602084013e611454565b606091505b50915091507f4af4780e06fe8cb9df64b0794fa6f01399af979175bb988e35e0e57e594567bc828260405161148a929190613864565b60405180910390a1816114ae576015604051806020016040528060008152506114b2565b6016815b8160030b9150809450819550505050509250929050565b6000600167ffffffffffffffff8111156114e6576114e561324e565b5b60405190808252806020026020018201604052801561151f57816020015b61150c612f45565b8152602001906001900390816115045790505b50905061152f6007600184611dce565b81600081518110611543576115426136dd565b5b6020026020010181905250600061155a8583612781565b60070b9050601660030b811461156f57600080fd5b5050505050565b6000611580612fd2565b6040518060400160405280600381526020017f6e6674000000000000000000000000000000000000000000000000000000000081525081600001819052506040518060400160405280600381526020017f6e66740000000000000000000000000000000000000000000000000000000000815250816020018190525030816040019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff1681525050600167ffffffffffffffff81111561164f5761164e61324e565b5b60405190808252806020026020018201604052801561168857816020015b611675612f45565b81526020019060019003908161166d5790505b508160e0018190525060006116a06004600130611dce565b9050808260e001516000815181106116bb576116ba6136dd565b5b602002602001018190525060006116d3600a30611e05565b905060008067ffffffffffffffff8111156116f1576116f061324e565b5b60405190808252806020026020018201604052801561172a57816020015b611717612f65565b81526020019060019003908161170f5790505b50905060008061173b868585612899565b91509150601660030b821461174f57600080fd5b80965050505050505090565b600080600061016773ffffffffffffffffffffffffffffffffffffffff16639b23d3d960e01b88888888604051602401611798949392919061371b565b604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050604051611802919061379c565b6000604051808303816000865af19150503d806000811461183f576040519150601f19603f3d011682016040523d82523d6000602084013e611844565b606091505b50915091508161185557601561186a565b8080602001905181019061186991906137ec565b5b60030b92505050949350505050565b6000611883612fd2565b6040518060400160405280600981526020017f74657374546f6b656e000000000000000000000000000000000000000000000081525081600001819052506040518060400160405280600481526020017f7465737400000000000000000000000000000000000000000000000000000000815250816020018190525030816040019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff1681525050600067ffffffffffffffff8111156119525761195161324e565b5b60405190808252806020026020018201604052801561198b57816020015b611978612f45565b8152602001906001900390816119705790505b508160e001819052506000806119a48360646004612a27565b91509150601660030b82146119b857600080fd5b80935050505090565b60006119cb612ecf565b6040518060400160405280600381526020017f6e6674000000000000000000000000000000000000000000000000000000000081525081600001819052506040518060400160405280600381526020017f6e6674000000000000000000000000000000000000000000000000000000000081525081602001819052506040518060400160405280600881526020017f746573746d65746100000000000000000000000000000000000000000000000081525081610120018190525030816040019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff1681525050600267ffffffffffffffff811115611ad957611ad861324e565b5b604051908082528060200260200182016040528015611b1257816020015b611aff612f45565b815260200190600190039081611af75790505b508160e001819052506000611b2a6007600130611dce565b90506000611b3b6004600130611dce565b9050818360e00151600081518110611b5657611b556136dd565b5b6020026020010181905250808360e00151600181518110611b7a57611b796136dd565b5b60200260200101819052506000611b92600a30611e05565b905060008067ffffffffffffffff811115611bb057611baf61324e565b5b604051908082528060200260200182016040528015611be957816020015b611bd6612f65565b815260200190600190039081611bce5790505b509050600080611bfa878585611e93565b91509150601660030b8214611c0e57600080fd5b8097505050505050505090565b6000611c25612fd2565b6040518060400160405280600981526020017f74657374546f6b656e000000000000000000000000000000000000000000000081525081600001819052506040518060400160405280600481526020017f7465737400000000000000000000000000000000000000000000000000000000815250816020018190525030816040019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff1681525050600067ffffffffffffffff811115611cf457611cf361324e565b5b604051908082528060200260200182016040528015611d2d57816020015b611d1a612f45565b815260200190600190039081611d125790505b508160e001819052506000611d43600a30611e05565b905060008067ffffffffffffffff811115611d6157611d6061324e565b5b604051908082528060200260200182016040528015611d9a57816020015b611d87613041565b815260200190600190039081611d7f5790505b509050600080611daf85606460088787612bb5565b91509150601660030b8214611dc357600080fd5b809550505050505090565b611dd6612f45565b6040518060400160405280611dea86612d49565b8152602001611df98585612d8a565b81525090509392505050565b6060600167ffffffffffffffff811115611e2257611e2161324e565b5b604051908082528060200260200182016040528015611e5b57816020015b611e4861309b565b815260200190600190039081611e405790505b5090506000611e6a8484612e66565b90508082600081518110611e8157611e806136dd565b5b60200260200101819052505092915050565b6000808460008161010001516000015160070b148015611ebf575060008161010001516040015160070b145b15611ee2576276a70060030b8161010001516040019060070b908160070b815250505b60008061016773ffffffffffffffffffffffffffffffffffffffff1634898989604051602401611f1493929190613e64565b6040516020818303038152906040527fc5bc16bc000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050604051611f9e919061379c565b60006040518083038185875af1925050503d8060008114611fdb576040519150601f19603f3d011682016040523d82523d6000602084013e611fe0565b606091505b509150915081611ff35760156000612008565b808060200190518101906120079190613eee565b5b8160030b91508095508196505050505050935093915050565b600080600061016773ffffffffffffffffffffffffffffffffffffffff168585604051602401612052929190613f2e565b6040516020818303038152906040527f54c832a5000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050506040516120dc919061379c565b6000604051808303816000865af19150503d8060008114612119576040519150601f19603f3d011682016040523d82523d6000602084013e61211e565b606091505b50915091508161212f576015612144565b8080602001905181019061214391906137ec565b5b60030b9250505092915050565b6000808260008161010001516000015160070b14801561217d575060008161010001516040015160070b145b156121a0576276a70060030b8161010001516040019060070b908160070b815250505b60008061016773ffffffffffffffffffffffffffffffffffffffff1634876040516024016121ce9190614038565b6040516020818303038152906040527fea83f293000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050604051612258919061379c565b60006040518083038185875af1925050503d8060008114612295576040519150601f19603f3d011682016040523d82523d6000602084013e61229a565b606091505b5091509150816122ad57601560006122c2565b808060200190518101906122c19190613eee565b5b8160030b91508095508196505050505050915091565b6000808460008161010001516000015160070b148015612304575060008161010001516040015160070b145b15612327576276a70060030b8161010001516040019060070b908160070b815250505b60008061016773ffffffffffffffffffffffffffffffffffffffff163489898960405160240161235993929190614069565b6040516020818303038152906040527f7cb8323a000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050506040516123e3919061379c565b60006040518083038185875af1925050503d8060008114612420576040519150601f19603f3d011682016040523d82523d6000602084013e612425565b606091505b509150915081612438576015600061244d565b8080602001905181019061244c9190613eee565b5b8160030b91508095508196505050505050935093915050565b6000808260008161010001516000015160070b148015612492575060008161010001516040015160070b145b156124b5576276a70060030b8161010001516040019060070b908160070b815250505b60008061016773ffffffffffffffffffffffffffffffffffffffff1634876040516024016124e391906140a7565b6040516020818303038152906040527fad7f8f0b000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838183161783525050505060405161256d919061379c565b60006040518083038185875af1925050503d80600081146125aa576040519150601f19603f3d011682016040523d82523d6000602084013e6125af565b606091505b5091509150816125c257601560006125d7565b808060200190518101906125d69190613eee565b5b8160030b91508095508196505050505050915091565b6000808660008161010001516000015160070b148015612619575060008161010001516040015160070b145b1561263c576276a70060030b8161010001516040019060070b908160070b815250505b60008061016773ffffffffffffffffffffffffffffffffffffffff16348b8b8b8b8b6040516024016126729594939291906141f3565b6040516020818303038152906040527f5ac3e67a000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050506040516126fc919061379c565b60006040518083038185875af1925050503d8060008114612739576040519150601f19603f3d011682016040523d82523d6000602084013e61273e565b606091505b5091509150816127515760156000612766565b808060200190518101906127659190613eee565b5b8160030b915080955081965050505050509550959350505050565b600080600061016773ffffffffffffffffffffffffffffffffffffffff16636fc3cbaf60e01b86866040516024016127ba9291906142e1565b604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050604051612824919061379c565b6000604051808303816000865af19150503d8060008114612861576040519150601f19603f3d011682016040523d82523d6000602084013e612866565b606091505b50915091508161287757601561288c565b8080602001905181019061288b91906137ec565b5b60030b9250505092915050565b6000808460008161010001516000015160070b1480156128c5575060008161010001516040015160070b145b156128e8576276a70060030b8161010001516040019060070b908160070b815250505b60008061016773ffffffffffffffffffffffffffffffffffffffff163489898960405160240161291a93929190614311565b6040516020818303038152906040527fabb54eb5000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050506040516129a4919061379c565b60006040518083038185875af1925050503d80600081146129e1576040519150601f19603f3d011682016040523d82523d6000602084013e6129e6565b606091505b5091509150816129f95760156000612a0e565b80806020019051810190612a0d9190613eee565b5b8160030b91508095508196505050505050935093915050565b6000808460008161010001516000015160070b148015612a53575060008161010001516040015160070b145b15612a76576276a70060030b8161010001516040019060070b908160070b815250505b60008061016773ffffffffffffffffffffffffffffffffffffffff1634898989604051602401612aa89392919061435d565b6040516020818303038152906040527f0fb65bf3000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050604051612b32919061379c565b60006040518083038185875af1925050503d8060008114612b6f576040519150601f19603f3d011682016040523d82523d6000602084013e612b74565b606091505b509150915081612b875760156000612b9c565b80806020019051810190612b9b9190613eee565b5b8160030b91508095508196505050505050935093915050565b6000808660008161010001516000015160070b148015612be1575060008161010001516040015160070b145b15612c04576276a70060030b8161010001516040019060070b908160070b815250505b60008061016773ffffffffffffffffffffffffffffffffffffffff16348b8b8b8b8b604051602401612c3a95949392919061439b565b6040516020818303038152906040527f2af0c59a000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050604051612cc4919061379c565b60006040518083038185875af1925050503d8060008114612d01576040519150601f19603f3d011682016040523d82523d6000602084013e612d06565b606091505b509150915081612d195760156000612d2e565b80806020019051810190612d2d9190613eee565b5b8160030b915080955081965050505050509550959350505050565b600060016000836007811115612d6257612d61614403565b5b6007811115612d7457612d73614403565b5b8152602001908152602001600020549050919050565b612d926130fd565b60016004811115612da657612da5614403565b5b836004811115612db957612db8614403565b5b03612dfb5781816020019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff1681525050612e60565b600480811115612e0e57612e0d614403565b5b836004811115612e2157612e20614403565b5b03612e5f5781816080019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff16815250505b5b92915050565b612e6e61309b565b82816000019060070b908160070b81525050600181604001901515908115158152505081816080019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff168152505092915050565b6040518061014001604052806060815260200160608152602001600073ffffffffffffffffffffffffffffffffffffffff16815260200160608152602001600015158152602001600060070b815260200160001515815260200160608152602001612f3861315a565b8152602001606081525090565b604051806040016040528060008152602001612f5f6130fd565b81525090565b6040518060c00160405280600060070b8152602001600060070b8152602001600060070b8152602001600073ffffffffffffffffffffffffffffffffffffffff168152602001600015158152602001600073ffffffffffffffffffffffffffffffffffffffff1681525090565b6040518061012001604052806060815260200160608152602001600073ffffffffffffffffffffffffffffffffffffffff16815260200160608152602001600015158152602001600060070b81526020016000151581526020016060815260200161303b61315a565b81525090565b6040518060c00160405280600060070b8152602001600060070b8152602001600060070b8152602001600060070b8152602001600015158152602001600073ffffffffffffffffffffffffffffffffffffffff1681525090565b6040518060a00160405280600060070b8152602001600073ffffffffffffffffffffffffffffffffffffffff168152602001600015158152602001600015158152602001600073ffffffffffffffffffffffffffffffffffffffff1681525090565b6040518060a00160405280600015158152602001600073ffffffffffffffffffffffffffffffffffffffff1681526020016060815260200160608152602001600073ffffffffffffffffffffffffffffffffffffffff1681525090565b6040518060600160405280600060070b8152602001600073ffffffffffffffffffffffffffffffffffffffff168152602001600060070b81525090565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006131c282613197565b9050919050565b6131d2816131b7565b82525050565b60006020820190506131ed60008301846131c9565b92915050565b6000604051905090565b600080fd5b600080fd5b613210816131b7565b811461321b57600080fd5b50565b60008135905061322d81613207565b92915050565b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6132868261323d565b810181811067ffffffffffffffff821117156132a5576132a461324e565b5b80604052505050565b60006132b86131f3565b90506132c4828261327d565b919050565b600067ffffffffffffffff8211156132e4576132e361324e565b5b6132ed8261323d565b9050602081019050919050565b82818337600083830152505050565b600061331c613317846132c9565b6132ae565b90508281526020810184848401111561333857613337613238565b5b6133438482856132fa565b509392505050565b600082601f8301126133605761335f613233565b5b8135613370848260208601613309565b91505092915050565b600080604083850312156133905761338f6131fd565b5b600061339e8582860161321e565b925050602083013567ffffffffffffffff8111156133bf576133be613202565b5b6133cb8582860161334b565b9150509250929050565b6000819050919050565b6133e8816133d5565b81146133f357600080fd5b50565b600081359050613405816133df565b92915050565b60008060008060808587031215613425576134246131fd565b5b60006134338782880161321e565b94505060206134448782880161321e565b93505060406134558782880161321e565b9250506060613466878288016133f6565b91505092959194509250565b60008160070b9050919050565b61348881613472565b82525050565b60006020820190506134a3600083018461347f565b92915050565b600067ffffffffffffffff8211156134c4576134c361324e565b5b6134cd8261323d565b9050602081019050919050565b60006134ed6134e8846134a9565b6132ae565b90508281526020810184848401111561350957613508613238565b5b6135148482856132fa565b509392505050565b600082601f83011261353157613530613233565b5b81356135418482602086016134da565b91505092915050565b60008060408385031215613561576135606131fd565b5b600061356f8582860161321e565b925050602083013567ffffffffffffffff8111156135905761358f613202565b5b61359c8582860161351c565b9150509250929050565b6000819050919050565b6135b9816135a6565b82525050565b600081519050919050565b600082825260208201905092915050565b60005b838110156135f95780820151818401526020810190506135de565b60008484015250505050565b6000613610826135bf565b61361a81856135ca565b935061362a8185602086016135db565b6136338161323d565b840191505092915050565b600060408201905061365360008301856135b0565b81810360208301526136658184613605565b90509392505050565b600080600060608486031215613687576136866131fd565b5b60006136958682870161321e565b935050602084013567ffffffffffffffff8111156136b6576136b5613202565b5b6136c28682870161351c565b92505060406136d38682870161321e565b9150509250925092565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b613715816133d5565b82525050565b600060808201905061373060008301876131c9565b61373d60208301866131c9565b61374a60408301856131c9565b613757606083018461370c565b95945050505050565b600081905092915050565b6000613776826135bf565b6137808185613760565b93506137908185602086016135db565b80840191505092915050565b60006137a8828461376b565b915081905092915050565b60008160030b9050919050565b6137c9816137b3565b81146137d457600080fd5b50565b6000815190506137e6816137c0565b92915050565b600060208284031215613802576138016131fd565b5b6000613810848285016137d7565b91505092915050565b600060408201905061382e60008301856131c9565b81810360208301526138408184613605565b90509392505050565b60008115159050919050565b61385e81613849565b82525050565b60006040820190506138796000830185613855565b818103602083015261388b8184613605565b90509392505050565b600081519050919050565b600082825260208201905092915050565b60006138bb82613894565b6138c5818561389f565b93506138d58185602086016135db565b6138de8161323d565b840191505092915050565b6138f2816131b7565b82525050565b61390181613849565b82525050565b61391081613472565b82525050565b600081519050919050565b600082825260208201905092915050565b6000819050602082019050919050565b61394b816133d5565b82525050565b600082825260208201905092915050565b600061396d826135bf565b6139778185613951565b93506139878185602086016135db565b6139908161323d565b840191505092915050565b600060a0830160008301516139b360008601826138f8565b5060208301516139c660208601826138e9565b50604083015184820360408601526139de8282613962565b915050606083015184820360608601526139f88282613962565b9150506080830151613a0d60808601826138e9565b508091505092915050565b6000604083016000830151613a306000860182613942565b5060208301518482036020860152613a48828261399b565b9150508091505092915050565b6000613a618383613a18565b905092915050565b6000602082019050919050565b6000613a8182613916565b613a8b8185613921565b935083602082028501613a9d85613932565b8060005b85811015613ad95784840389528151613aba8582613a55565b9450613ac583613a69565b925060208a01995050600181019050613aa1565b50829750879550505050505092915050565b606082016000820151613b016000850182613907565b506020820151613b1460208501826138e9565b506040820151613b276040850182613907565b50505050565b6000610180830160008301518482036000860152613b4b82826138b0565b91505060208301518482036020860152613b6582826138b0565b9150506040830151613b7a60408601826138e9565b5060608301518482036060860152613b9282826138b0565b9150506080830151613ba760808601826138f8565b5060a0830151613bba60a0860182613907565b5060c0830151613bcd60c08601826138f8565b5060e083015184820360e0860152613be58282613a76565b915050610100830151613bfc610100860182613aeb565b50610120830151848203610160860152613c168282613962565b9150508091505092915050565b600081519050919050565b600082825260208201905092915050565b6000819050602082019050919050565b60a082016000820151613c656000850182613907565b506020820151613c7860208501826138e9565b506040820151613c8b60408501826138f8565b506060820151613c9e60608501826138f8565b506080820151613cb160808501826138e9565b50505050565b6000613cc38383613c4f565b60a08301905092915050565b6000602082019050919050565b6000613ce782613c23565b613cf18185613c2e565b9350613cfc83613c3f565b8060005b83811015613d2d578151613d148882613cb7565b9750613d1f83613ccf565b925050600181019050613d00565b5085935050505092915050565b600081519050919050565b600082825260208201905092915050565b6000819050602082019050919050565b60c082016000820151613d7c6000850182613907565b506020820151613d8f6020850182613907565b506040820151613da26040850182613907565b506060820151613db560608501826138e9565b506080820151613dc860808501826138f8565b5060a0820151613ddb60a08501826138e9565b50505050565b6000613ded8383613d66565b60c08301905092915050565b6000602082019050919050565b6000613e1182613d3a565b613e1b8185613d45565b9350613e2683613d56565b8060005b83811015613e57578151613e3e8882613de1565b9750613e4983613df9565b925050600181019050613e2a565b5085935050505092915050565b60006060820190508181036000830152613e7e8186613b2d565b90508181036020830152613e928185613cdc565b90508181036040830152613ea68184613e06565b9050949350505050565b6000613ebb82613197565b9050919050565b613ecb81613eb0565b8114613ed657600080fd5b50565b600081519050613ee881613ec2565b92915050565b60008060408385031215613f0557613f046131fd565b5b6000613f13858286016137d7565b9250506020613f2485828601613ed9565b9150509250929050565b6000604082019050613f4360008301856131c9565b8181036020830152613f558184613b2d565b90509392505050565b6000610160830160008301518482036000860152613f7c82826138b0565b91505060208301518482036020860152613f9682826138b0565b9150506040830151613fab60408601826138e9565b5060608301518482036060860152613fc382826138b0565b9150506080830151613fd860808601826138f8565b5060a0830151613feb60a0860182613907565b5060c0830151613ffe60c08601826138f8565b5060e083015184820360e08601526140168282613a76565b91505061010083015161402d610100860182613aeb565b508091505092915050565b600060208201905081810360008301526140528184613f5e565b905092915050565b614063816137b3565b82525050565b600060608201905081810360008301526140838186613b2d565b9050614092602083018561347f565b61409f604083018461405a565b949350505050565b600060208201905081810360008301526140c18184613b2d565b905092915050565b600081519050919050565b600082825260208201905092915050565b6000819050602082019050919050565b60c08201600082015161410b6000850182613907565b50602082015161411e6020850182613907565b5060408201516141316040850182613907565b5060608201516141446060850182613907565b50608082015161415760808501826138f8565b5060a082015161416a60a08501826138e9565b50505050565b600061417c83836140f5565b60c08301905092915050565b6000602082019050919050565b60006141a0826140c9565b6141aa81856140d4565b93506141b5836140e5565b8060005b838110156141e65781516141cd8882614170565b97506141d883614188565b9250506001810190506141b9565b5085935050505092915050565b600060a082019050818103600083015261420d8188613b2d565b905061421c602083018761347f565b614229604083018661405a565b818103606083015261423b8185613cdc565b9050818103608083015261424f8184614195565b90509695505050505050565b600082825260208201905092915050565b600061427782613916565b614281818561425b565b93508360208202850161429385613932565b8060005b858110156142cf57848403895281516142b08582613a55565b94506142bb83613a69565b925060208a01995050600181019050614297565b50829750879550505050505092915050565b60006040820190506142f660008301856131c9565b8181036020830152614308818461426c565b90509392505050565b6000606082019050818103600083015261432b8186613f5e565b9050818103602083015261433f8185613cdc565b905081810360408301526143538184613e06565b9050949350505050565b600060608201905081810360008301526143778186613f5e565b9050614386602083018561347f565b614393604083018461405a565b949350505050565b600060a08201905081810360008301526143b58188613f5e565b90506143c4602083018761347f565b6143d1604083018661405a565b81810360608301526143e38185613cdc565b905081810360808301526143f78184614195565b90509695505050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fdfea2646970667358221220838b6cc048d27d4625ebc236f75da635dce381dba05019462bd075b4e427279564736f6c63430008120033 \ No newline at end of file diff --git a/hedera-node/test-clients/src/main/resources/contract/contracts/CreateTokenVTwo/CreateTokenVTwo.json b/hedera-node/test-clients/src/main/resources/contract/contracts/CreateTokenVTwo/CreateTokenVTwo.json new file mode 100644 index 000000000000..9a773418dc56 --- /dev/null +++ b/hedera-node/test-clients/src/main/resources/contract/contracts/CreateTokenVTwo/CreateTokenVTwo.json @@ -0,0 +1,315 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "bool", + "name": "", + "type": "bool" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "name": "CallResponseEvent", + "type": "event" + }, + { + "inputs": [], + "name": "createNft", + "outputs": [ + { + "internalType": "address", + "name": "createdAddress", + "type": "address" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "createNftWithCustomFees", + "outputs": [ + { + "internalType": "address", + "name": "createdAddress", + "type": "address" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "createNftWithMetaAndKey", + "outputs": [ + { + "internalType": "address", + "name": "createdAddress", + "type": "address" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "createNftWithMetaAndKeyAndCustomFees", + "outputs": [ + { + "internalType": "address", + "name": "createdAddress", + "type": "address" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "createNftWithMetadata", + "outputs": [ + { + "internalType": "address", + "name": "createdAddress", + "type": "address" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "createNftWithMetadataAndCustomFees", + "outputs": [ + { + "internalType": "address", + "name": "createdAddress", + "type": "address" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "createToken", + "outputs": [ + { + "internalType": "address", + "name": "createdAddress", + "type": "address" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "createTokenWithCustomFees", + "outputs": [ + { + "internalType": "address", + "name": "createdAddress", + "type": "address" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "createTokenWithMetadata", + "outputs": [ + { + "internalType": "address", + "name": "createdAddress", + "type": "address" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "createTokenWithMetadataAndCustomFees", + "outputs": [ + { + "internalType": "address", + "name": "createdAddress", + "type": "address" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "createTokenWithMetadataAndKey", + "outputs": [ + { + "internalType": "address", + "name": "createdAddress", + "type": "address" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "createTokenWithMetadataAndKeyAndCustomFees", + "outputs": [ + { + "internalType": "address", + "name": "createdAddress", + "type": "address" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "bytes", + "name": "encodedFunctionSelector", + "type": "bytes" + } + ], + "name": "redirectForToken", + "outputs": [ + { + "internalType": "int256", + "name": "responseCode", + "type": "int256" + }, + { + "internalType": "bytes", + "name": "response", + "type": "bytes" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [ + { + "internalType": "int64", + "name": "responseCode", + "type": "int64" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "serialNumber", + "type": "uint256" + } + ], + "name": "transferFromNFT", + "outputs": [ + { + "internalType": "int64", + "name": "responseCode", + "type": "int64" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "bytes", + "name": "ed25519", + "type": "bytes" + }, + { + "internalType": "address", + "name": "contractID", + "type": "address" + } + ], + "name": "updateTokenKeys", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "string", + "name": "metadata", + "type": "string" + } + ], + "name": "updateTokenMetadata", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] \ No newline at end of file diff --git a/hedera-node/test-clients/src/main/resources/contract/contracts/CreateTokenVTwo/CreateTokenVTwo.sol b/hedera-node/test-clients/src/main/resources/contract/contracts/CreateTokenVTwo/CreateTokenVTwo.sol new file mode 100644 index 000000000000..ff0f0e3582e8 --- /dev/null +++ b/hedera-node/test-clients/src/main/resources/contract/contracts/CreateTokenVTwo/CreateTokenVTwo.sol @@ -0,0 +1,251 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.5.0 <0.9.0; +pragma experimental ABIEncoderV2; + +import "./KeyHelper.sol"; +import "./HederaResponseCodes.sol"; +import "./HederaTokenService.sol"; +import "./FeeHelper.sol"; + +contract CreateTokenVTwo is HederaTokenService, KeyHelper, FeeHelper { + function createTokenWithMetadata() public payable returns (address createdAddress) { + IHederaTokenService.HederaTokenV2 memory token; + token.name = "testToken"; + token.metadata = bytes("testmeta"); + token.symbol = "test"; + token.treasury = address(this); + token.tokenKeys = new IHederaTokenService.TokenKey[](0); + + (int responseCode, address tokenAddress) = HederaTokenService.createFungibleToken(token, 100, 4); + + if (responseCode != HederaResponseCodes.SUCCESS) { + revert(); + } + + createdAddress = tokenAddress; + } + + function createToken() public payable returns (address createdAddress) { + IHederaTokenService.HederaToken memory token; + token.name = "testToken"; + token.symbol = "test"; + token.treasury = address(this); + token.tokenKeys = new IHederaTokenService.TokenKey[](0); + + (int responseCode, address tokenAddress) = HederaTokenService.createFungibleToken(token, 100, 4); + + if (responseCode != HederaResponseCodes.SUCCESS) { + revert(); + } + + createdAddress = tokenAddress; + } + + function createTokenWithMetadataAndKey() public payable returns (address createdAddress) { + IHederaTokenService.HederaTokenV2 memory token; + token.name = "testToken"; + token.metadata = bytes("testmeta"); + token.symbol = "test"; + token.treasury = address(this); + token.tokenKeys = new IHederaTokenService.TokenKey[](1); + IHederaTokenService.TokenKey memory tokenKey = super.getSingleKey(KeyType.METADATA, KeyValueType.CONTRACT_ID, address(this)); + token.tokenKeys[0] = tokenKey; + (int responseCode, address tokenAddress) = HederaTokenService.createFungibleToken(token, 100, 4); + + if (responseCode != HederaResponseCodes.SUCCESS) { + revert(); + } + + createdAddress = tokenAddress; + } + + function createTokenWithMetadataAndCustomFees() public payable returns (address createdAddress) { + IHederaTokenService.HederaTokenV2 memory token; + token.name = "testToken"; + token.metadata = bytes("testmeta"); + token.symbol = "test"; + token.treasury = address(this); + token.tokenKeys = new IHederaTokenService.TokenKey[](0); + IHederaTokenService.FixedFee[] memory fixedFees = createSingleFixedFeeForHbars(10, address(this)); + IHederaTokenService.FractionalFee[] memory fractionalFees = new IHederaTokenService.FractionalFee[](0); + (int responseCode, address tokenAddress) = HederaTokenService.createFungibleTokenWithCustomFees(token, 100, 8, fixedFees, fractionalFees); + + if (responseCode != HederaResponseCodes.SUCCESS) { + revert(); + } + + createdAddress = tokenAddress; + } + + function createTokenWithCustomFees() public payable returns (address createdAddress) { + IHederaTokenService.HederaToken memory token; + token.name = "testToken"; + token.symbol = "test"; + token.treasury = address(this); + token.tokenKeys = new IHederaTokenService.TokenKey[](0); + + IHederaTokenService.FixedFee[] memory fixedFees = createSingleFixedFeeForHbars(10, address(this)); + IHederaTokenService.FractionalFee[] memory fractionalFees = new IHederaTokenService.FractionalFee[](0); + (int responseCode, address tokenAddress) = HederaTokenService.createFungibleTokenWithCustomFees(token, 100, 8, fixedFees, fractionalFees); + + if (responseCode != HederaResponseCodes.SUCCESS) { + revert(); + } + + createdAddress = tokenAddress; + } + + function createTokenWithMetadataAndKeyAndCustomFees() public payable returns (address createdAddress) { + IHederaTokenService.HederaTokenV2 memory token; + token.name = "testToken"; + token.metadata = bytes("testmeta"); + token.symbol = "test"; + token.treasury = address(this); + token.tokenKeys = new IHederaTokenService.TokenKey[](1); + IHederaTokenService.TokenKey memory tokenKey = super.getSingleKey(KeyType.METADATA, KeyValueType.CONTRACT_ID, address(this)); + token.tokenKeys[0] = tokenKey; + IHederaTokenService.FixedFee[] memory fixedFees = createSingleFixedFeeForHbars(10, address(this)); + IHederaTokenService.FractionalFee[] memory fractionalFees = new IHederaTokenService.FractionalFee[](0); + (int responseCode, address tokenAddress) = HederaTokenService.createFungibleTokenWithCustomFees(token, 100, 8, fixedFees, fractionalFees); + + + if (responseCode != HederaResponseCodes.SUCCESS) { + revert(); + } + + createdAddress = tokenAddress; + } + + + function createNft() public payable returns (address createdAddress) { + IHederaTokenService.HederaToken memory token; + token.name = "nft"; + token.symbol = "nft"; + token.treasury = address(this); + token.tokenKeys = new IHederaTokenService.TokenKey[](1); + IHederaTokenService.TokenKey memory tokenSupplyKey = super.getSingleKey(KeyType.SUPPLY, KeyValueType.CONTRACT_ID, address(this)); + token.tokenKeys[0] = tokenSupplyKey; + (int responseCode, address tokenAddress) = HederaTokenService.createNonFungibleToken(token); + if (responseCode != HederaResponseCodes.SUCCESS) { + revert(); + } + + createdAddress = tokenAddress; + } + + + function createNftWithMetadata() public payable returns (address createdAddress) { + IHederaTokenService.HederaTokenV2 memory token; + token.name = "nft"; + token.symbol = "nft"; + token.metadata = bytes("testmeta"); + token.treasury = address(this); + token.tokenKeys = new IHederaTokenService.TokenKey[](1); + IHederaTokenService.TokenKey memory tokenSupplyKey = super.getSingleKey(KeyType.SUPPLY, KeyValueType.CONTRACT_ID, address(this)); + token.tokenKeys[0] = tokenSupplyKey; + (int responseCode, address tokenAddress) = HederaTokenService.createNonFungibleToken(token); + if (responseCode != HederaResponseCodes.SUCCESS) { + revert(); + } + + createdAddress = tokenAddress; + } + + + function createNftWithMetaAndKey() public payable returns (address createdAddress) { + IHederaTokenService.HederaTokenV2 memory token; + token.name = "nft"; + token.symbol = "nft"; + token.metadata = bytes("testmeta"); + token.treasury = address(this); + token.tokenKeys = new IHederaTokenService.TokenKey[](2); + IHederaTokenService.TokenKey memory tokenKey = super.getSingleKey(KeyType.METADATA, KeyValueType.CONTRACT_ID, address(this)); + IHederaTokenService.TokenKey memory tokenSupplyKey = super.getSingleKey(KeyType.SUPPLY, KeyValueType.CONTRACT_ID, address(this)); + token.tokenKeys[0] = tokenKey; + token.tokenKeys[1] = tokenSupplyKey; + (int responseCode, address tokenAddress) = HederaTokenService.createNonFungibleToken(token); + if (responseCode != HederaResponseCodes.SUCCESS) { + revert(); + } + + createdAddress = tokenAddress; + } + + function createNftWithCustomFees() public payable returns (address createdAddress) { + IHederaTokenService.HederaToken memory token; + token.name = "nft"; + token.symbol = "nft"; + token.treasury = address(this); + token.tokenKeys = new IHederaTokenService.TokenKey[](1); + IHederaTokenService.TokenKey memory tokenSupplyKey = super.getSingleKey(KeyType.SUPPLY, KeyValueType.CONTRACT_ID, address(this)); + token.tokenKeys[0] = tokenSupplyKey; + IHederaTokenService.FixedFee[] memory fixedFees = createSingleFixedFeeForHbars(10, address(this)); + IHederaTokenService.RoyaltyFee[] memory royaltyFees = new IHederaTokenService.RoyaltyFee[](0); + (int responseCode, address tokenAddress) = HederaTokenService.createNonFungibleTokenWithCustomFees(token, fixedFees, royaltyFees); + if (responseCode != HederaResponseCodes.SUCCESS) { + revert(); + } + + createdAddress = tokenAddress; + } + + + function createNftWithMetadataAndCustomFees() public payable returns (address createdAddress) { + IHederaTokenService.HederaTokenV2 memory token; + token.name = "nft"; + token.symbol = "nft"; + token.metadata = bytes("testmeta"); + token.treasury = address(this); + token.tokenKeys = new IHederaTokenService.TokenKey[](1); + IHederaTokenService.TokenKey memory tokenSupplyKey = super.getSingleKey(KeyType.SUPPLY, KeyValueType.CONTRACT_ID, address(this)); + token.tokenKeys[0] = tokenSupplyKey; + IHederaTokenService.FixedFee[] memory fixedFees = createSingleFixedFeeForHbars(10, address(this)); + IHederaTokenService.RoyaltyFee[] memory royaltyFees = new IHederaTokenService.RoyaltyFee[](0); + (int responseCode, address tokenAddress) = HederaTokenService.createNonFungibleTokenWithCustomFees(token, fixedFees, royaltyFees); + if (responseCode != HederaResponseCodes.SUCCESS) { + revert(); + } + + createdAddress = tokenAddress; + } + + function createNftWithMetaAndKeyAndCustomFees() public payable returns (address createdAddress) { + IHederaTokenService.HederaTokenV2 memory token; + token.name = "nft"; + token.symbol = "nft"; + token.metadata = bytes("testmeta"); + token.treasury = address(this); + token.tokenKeys = new IHederaTokenService.TokenKey[](2); + IHederaTokenService.TokenKey memory tokenMetaKey = super.getSingleKey(KeyType.METADATA, KeyValueType.CONTRACT_ID, address(this)); + IHederaTokenService.TokenKey memory tokenSupplyKey = super.getSingleKey(KeyType.SUPPLY, KeyValueType.CONTRACT_ID, address(this)); + token.tokenKeys[0] = tokenMetaKey; + token.tokenKeys[1] = tokenSupplyKey; + IHederaTokenService.FixedFee[] memory fixedFees = createSingleFixedFeeForHbars(10, address(this)); + IHederaTokenService.RoyaltyFee[] memory royaltyFees = new IHederaTokenService.RoyaltyFee[](0); + (int responseCode, address tokenAddress) = HederaTokenService.createNonFungibleTokenWithCustomFees(token, fixedFees, royaltyFees); + if (responseCode != HederaResponseCodes.SUCCESS) { + revert(); + } + + createdAddress = tokenAddress; + } + + function updateTokenMetadata(address token, string memory metadata) public { + IHederaTokenService.HederaTokenV2 memory tokenInfo; + tokenInfo.metadata = bytes(metadata); + + (int256 responseCode) = HederaTokenService.updateTokenInfo(token, tokenInfo); + + if (responseCode != HederaResponseCodes.SUCCESS) { + revert(); + } + } + + function updateTokenKeys(address token, bytes memory ed25519, address contractID) public { + IHederaTokenService.TokenKey[] memory keys = new IHederaTokenService.TokenKey[](1); + keys[0] = getSingleKey(KeyType.METADATA, KeyValueType.CONTRACT_ID, contractID); //metadata 7 + + (int256 responseCode) = HederaTokenService.updateTokenKeys(token, keys); + require(responseCode == HederaResponseCodes.SUCCESS); + } +}