diff --git a/hedera-node/hedera-app/src/xtest/java/contract/DeleteXTest.java b/hedera-node/hedera-app/src/xtest/java/contract/DeleteXTest.java
index 0426b1001b51..a2d2c7f78c50 100644
--- a/hedera-node/hedera-app/src/xtest/java/contract/DeleteXTest.java
+++ b/hedera-node/hedera-app/src/xtest/java/contract/DeleteXTest.java
@@ -16,10 +16,21 @@
package contract;
+import static com.hedera.hapi.node.base.ResponseCodeEnum.INVALID_FULL_PREFIX_SIGNATURE_FOR_PRECOMPILE;
import static com.hedera.hapi.node.base.ResponseCodeEnum.INVALID_TOKEN_ID;
import static com.hedera.hapi.node.base.ResponseCodeEnum.TOKEN_IS_IMMUTABLE;
import static com.hedera.hapi.node.base.ResponseCodeEnum.TOKEN_WAS_DELETED;
import static com.hedera.node.app.service.contract.impl.utils.ConversionUtils.asHeadlongAddress;
+import static contract.AssociationsXTestConstants.A_TOKEN_ADDRESS;
+import static contract.AssociationsXTestConstants.A_TOKEN_ID;
+import static contract.AssociationsXTestConstants.B_TOKEN_ADDRESS;
+import static contract.AssociationsXTestConstants.B_TOKEN_ID;
+import static contract.AssociationsXTestConstants.C_TOKEN_ADDRESS;
+import static contract.AssociationsXTestConstants.C_TOKEN_ID;
+import static contract.AssociationsXTestConstants.D_TOKEN_ADDRESS;
+import static contract.AssociationsXTestConstants.D_TOKEN_ID;
+import static contract.HtsErc721TransferXTestConstants.UNAUTHORIZED_SPENDER_ID;
+import static contract.XTestConstants.AN_ED25519_KEY;
import static contract.XTestConstants.ERC20_TOKEN_ADDRESS;
import static contract.XTestConstants.ERC20_TOKEN_ID;
import static contract.XTestConstants.ERC721_TOKEN_ADDRESS;
@@ -50,6 +61,20 @@
import java.util.Map;
import org.apache.tuweni.bytes.Bytes;
+/**
+ * Exercises delete on a fungible and non-fungible token via the following steps relative to an {@code OWNER} account:
+ *
+ * - Deletes {@code ERC20_TOKEN} via DELETE operation
+ * - Deletes {@code ERC721_TOKEN} via DELETE operation
+ * - Freezes a deleted {@code ERC20_TOKEN}. This should fail with TOKEN_WAS_DELETED
+ * - Freezes a deleted {@code ERC721_TOKEN}. This should fail with TOKEN_WAS_DELETED
+ * - Deletes {@code ERC20_TOKEN} without admin key. This should fail with TOKEN_IS_IMMUTABLE
+ * - Deletes {@code ERC721_TOKEN} without admin key. This should fail with TOKEN_IS_IMMUTABLE
+ * - Deletes {@code ERC20_TOKEN} with wrong admin key. This should fail with INVALID_FULL_PREFIX_SIGNATURE_FOR_PRECOMPILE
+ * - Deletes {@code ERC721_TOKEN} with wrong admin key. This should fail with INVALID_FULL_PREFIX_SIGNATURE_FOR_PRECOMPILE
+ * - Deletes token with invalid token address via DELETE operation. This should fail with INVALID_TOKEN_ID
+ *
+ */
public class DeleteXTest extends AbstractContractXTest {
@Override
protected void doScenarioOperations() {
@@ -61,6 +86,14 @@ protected void doScenarioOperations() {
.array()),
assertSuccess());
+ // Successfully delete token
+ runHtsCallAndExpectOnSuccess(
+ SENDER_BESU_ADDRESS,
+ Bytes.wrap(DeleteTranslator.DELETE_TOKEN
+ .encodeCallWithArgs(ERC721_TOKEN_ADDRESS)
+ .array()),
+ assertSuccess());
+
// Try to freeze deleted token
runHtsCallAndExpectOnSuccess(
SENDER_BESU_ADDRESS,
@@ -70,15 +103,55 @@ protected void doScenarioOperations() {
output -> assertEquals(
Bytes.wrap(ReturnTypes.encodedRc(TOKEN_WAS_DELETED).array()), output));
+ // Try to freeze deleted token
+ runHtsCallAndExpectOnSuccess(
+ SENDER_BESU_ADDRESS,
+ Bytes.wrap(FreezeUnfreezeTranslator.FREEZE
+ .encodeCallWithArgs(ERC721_TOKEN_ADDRESS, asHeadlongAddress(SENDER_ADDRESS.toByteArray()))
+ .array()),
+ output -> assertEquals(
+ Bytes.wrap(ReturnTypes.encodedRc(TOKEN_WAS_DELETED).array()), output));
+
// Fail if token has no admin key
runHtsCallAndExpectOnSuccess(
SENDER_BESU_ADDRESS,
Bytes.wrap(DeleteTranslator.DELETE_TOKEN
- .encodeCallWithArgs(ERC721_TOKEN_ADDRESS)
+ .encodeCallWithArgs(A_TOKEN_ADDRESS)
+ .array()),
+ output -> assertEquals(
+ Bytes.wrap(ReturnTypes.encodedRc(TOKEN_IS_IMMUTABLE).array()), output));
+
+ // Fail if token has no admin key
+ runHtsCallAndExpectOnSuccess(
+ SENDER_BESU_ADDRESS,
+ Bytes.wrap(DeleteTranslator.DELETE_TOKEN
+ .encodeCallWithArgs(B_TOKEN_ADDRESS)
.array()),
output -> assertEquals(
Bytes.wrap(ReturnTypes.encodedRc(TOKEN_IS_IMMUTABLE).array()), output));
+ // Fail if token has wrong admin key
+ runHtsCallAndExpectOnSuccess(
+ SENDER_BESU_ADDRESS,
+ Bytes.wrap(DeleteTranslator.DELETE_TOKEN
+ .encodeCallWithArgs(C_TOKEN_ADDRESS)
+ .array()),
+ output -> assertEquals(
+ Bytes.wrap(ReturnTypes.encodedRc(INVALID_FULL_PREFIX_SIGNATURE_FOR_PRECOMPILE)
+ .array()),
+ output));
+
+ // Fail if token has wrong admin key
+ runHtsCallAndExpectOnSuccess(
+ SENDER_BESU_ADDRESS,
+ Bytes.wrap(DeleteTranslator.DELETE_TOKEN
+ .encodeCallWithArgs(D_TOKEN_ADDRESS)
+ .array()),
+ output -> assertEquals(
+ Bytes.wrap(ReturnTypes.encodedRc(INVALID_FULL_PREFIX_SIGNATURE_FOR_PRECOMPILE)
+ .array()),
+ output));
+
// should fail when token has invalid address
runHtsCallAndExpectOnSuccess(
SENDER_BESU_ADDRESS,
@@ -122,8 +195,40 @@ protected Map initialTokens() {
Token.newBuilder()
.tokenId(ERC721_TOKEN_ID)
.treasuryAccountId(SENDER_ID)
+ .tokenType(TokenType.NON_FUNGIBLE_UNIQUE)
+ .adminKey(SENDER_CONTRACT_ID_KEY)
+ .freezeKey(SENDER_CONTRACT_ID_KEY)
+ .build());
+ tokens.put(
+ A_TOKEN_ID,
+ Token.newBuilder()
+ .tokenId(A_TOKEN_ID)
+ .treasuryAccountId(SENDER_ID)
.tokenType(TokenType.FUNGIBLE_COMMON)
.build());
+ tokens.put(
+ B_TOKEN_ID,
+ Token.newBuilder()
+ .tokenId(B_TOKEN_ID)
+ .treasuryAccountId(SENDER_ID)
+ .tokenType(TokenType.NON_FUNGIBLE_UNIQUE)
+ .build());
+ tokens.put(
+ C_TOKEN_ID,
+ Token.newBuilder()
+ .tokenId(C_TOKEN_ID)
+ .treasuryAccountId(UNAUTHORIZED_SPENDER_ID)
+ .tokenType(TokenType.FUNGIBLE_COMMON)
+ .adminKey(AN_ED25519_KEY)
+ .build());
+ tokens.put(
+ D_TOKEN_ID,
+ Token.newBuilder()
+ .tokenId(D_TOKEN_ID)
+ .treasuryAccountId(UNAUTHORIZED_SPENDER_ID)
+ .tokenType(TokenType.NON_FUNGIBLE_UNIQUE)
+ .adminKey(AN_ED25519_KEY)
+ .build());
return tokens;
}