Skip to content

Commit 7e0bd18

Browse files
authored
feat(1644): contract delete command expansion (#1693)
Signed-off-by: rozekmichal <michal.rozek@blockydevs.com>
1 parent 94cf56a commit 7e0bd18

File tree

17 files changed

+807
-86
lines changed

17 files changed

+807
-86
lines changed

skills/hiero-cli/references/contract.md

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -73,16 +73,24 @@ hcli contract import --contract 0xAbCd1234... --alias myAlias
7373

7474
### `hcli contract delete`
7575

76-
Remove a contract from local state (does NOT delete from the Hedera network).
76+
**Default:** submits `ContractDeleteTransaction` on Hedera, then removes the contract from local CLI state. **With `--state-only`:** only removes it from local CLI state (no network transaction). Hedera does not allow deleting a contract on the network if it has no admin key; in that case use `--state-only` for local cleanup only. When the contract has an admin key, pass `--admin-key` (or keep a stored admin reference from `contract create --admin-key`) if signing material is not already in state.
7777

78-
| Option | Short | Type | Required | Default | Description |
79-
| ------------ | ----- | ------ | -------- | ------- | -------------------------------- |
80-
| `--contract` | `-c` | string | **yes** || Contract ID (`0.0.xxx`) or alias |
78+
⚠️ Requires confirmation unless using `--confirm` / script mode.
79+
80+
| Option | Short | Type | Required | Default | Description |
81+
| ------------------------ | ----- | ---------- | -------- | ------- | ------------------------------------------------------------------------------------------------------------------------- |
82+
| `--contract` | `-c` | string | **yes** || Contract ID (`0.0.xxx`) or alias; for network delete, state entry optional if mirror can resolve ID |
83+
| `--state-only` | `-s` | boolean | no | `false` | Remove only from local CLI state; do not submit a network delete |
84+
| `--transfer-id` | `-t` | string | no† || Account receiving remaining HBAR (ID or alias). †One of `-t` or `-r` required for network delete. Not with `--state-only` |
85+
| `--transfer-contract-id` | `-r` | string | no† || Contract receiving remaining HBAR. †One of `-t` or `-r` required for network delete. Not with `--state-only` |
86+
| `--admin-key` | `-a` | repeatable | no || Admin key if not stored in state (same as create). Not with `--state-only` |
87+
| `--key-manager` | `-k` | string | no | config | Key manager when resolving `--admin-key` |
8188

8289
**Example:**
8390

8491
```
85-
hcli contract delete --contract myErc20
92+
hcli contract delete --contract myErc20 --transfer-id 0.0.1234 --confirm
93+
hcli contract delete --contract 0.0.123456 --state-only --confirm
8694
```
8795

88-
**Output:** `{ contractId, deleted }`
96+
**Output:** `{ deletedContract, network, removedAliases?, transactionId?, stateOnly? }`

src/__tests__/mocks/fixtures.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ export const MOCK_PUBLIC_KEY =
2727
'0000000000000000000000000000000000000000000000000000000000000000';
2828

2929
export const MOCK_CONTRACT_ID = '0.0.1234';
30+
export const MOCK_CONTRACT_ID_UNKNOWN = '0.0.9999';
3031
export const MOCK_ACCOUNT_ID = '0.0.5678';
3132
export const MOCK_ACCOUNT_ID_ALT = '0.0.5679';
3233
export const MOCK_TOPIC_ID = '0.0.7777';

src/__tests__/mocks/mocks.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -492,6 +492,8 @@ const makePluginManagementServiceMock = (): PluginManagementService =>
492492
const makeContractTransactionServiceMock = (): ContractTransactionService =>
493493
({
494494
contractCreateFlowTransaction: jest.fn(),
495+
contractExecuteTransaction: jest.fn(),
496+
deleteContract: jest.fn(),
495497
}) as unknown as ContractTransactionService;
496498

497499
const makeContractVerifierServiceMock = (): ContractVerifierService =>

src/core/services/contract-transaction/contract-transaction-service.interface.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import type {
22
ContractCreateFlowParams,
33
ContractCreateFlowResult,
4+
ContractDeleteResult,
45
ContractExecuteParams,
56
ContractExecuteResult,
7+
DeleteContractParams,
68
} from '@/core/services/contract-transaction/types';
79

810
export interface ContractTransactionService {
@@ -18,4 +20,8 @@ export interface ContractTransactionService {
1820
contractExecuteTransaction(
1921
params: ContractExecuteParams,
2022
): ContractExecuteResult;
23+
/**
24+
* Build a ContractDeleteTransaction (sign with contract admin key + operator)
25+
*/
26+
deleteContract(params: DeleteContractParams): ContractDeleteResult;
2127
}

src/core/services/contract-transaction/contract-transaction-service.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,23 @@ import type { ContractTransactionService } from '@/core/services/contract-transa
22
import type {
33
ContractCreateFlowParams,
44
ContractCreateFlowResult,
5+
ContractDeleteResult,
56
ContractExecuteParams,
67
ContractExecuteResult,
8+
DeleteContractParams,
79
} from '@/core/services/contract-transaction/types';
810

911
import {
12+
AccountId,
1013
ContractCreateFlow,
14+
ContractDeleteTransaction,
1115
ContractExecuteTransaction,
1216
ContractId,
1317
} from '@hashgraph/sdk';
1418
import { ethers, getBytes } from 'ethers';
1519

20+
import { ValidationError } from '@/core/errors';
21+
1622
export class ContractTransactionServiceImpl implements ContractTransactionService {
1723
contractCreateFlowTransaction(
1824
params: ContractCreateFlowParams,
@@ -53,4 +59,32 @@ export class ContractTransactionServiceImpl implements ContractTransactionServic
5359
transaction: contractExecuteTx,
5460
};
5561
}
62+
63+
deleteContract(params: DeleteContractParams): ContractDeleteResult {
64+
try {
65+
const transaction = new ContractDeleteTransaction().setContractId(
66+
ContractId.fromString(params.contractId),
67+
);
68+
if (params.transferAccountId) {
69+
transaction.setTransferAccountId(
70+
AccountId.fromString(params.transferAccountId),
71+
);
72+
}
73+
if (params.transferContractId) {
74+
transaction.setTransferContractId(
75+
ContractId.fromString(params.transferContractId),
76+
);
77+
}
78+
return { transaction };
79+
} catch (error) {
80+
throw new ValidationError('Invalid contract delete parameters', {
81+
context: {
82+
contractId: params.contractId,
83+
transferAccountId: params.transferAccountId,
84+
transferContractId: params.transferContractId,
85+
},
86+
cause: error,
87+
});
88+
}
89+
}
5690
}

src/core/services/contract-transaction/types.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import type {
22
ContractCreateFlow,
3+
ContractDeleteTransaction,
34
ContractExecuteTransaction,
45
ContractFunctionParameters,
56
Key,
@@ -28,3 +29,13 @@ export interface ContractExecuteParams {
2829
export interface ContractExecuteResult {
2930
transaction: ContractExecuteTransaction;
3031
}
32+
33+
export interface DeleteContractParams {
34+
contractId: string;
35+
transferAccountId?: string;
36+
transferContractId?: string;
37+
}
38+
39+
export interface ContractDeleteResult {
40+
transaction: ContractDeleteTransaction;
41+
}

src/plugins/contract/README.md

Lines changed: 42 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -16,23 +16,19 @@ This plugin follows the plugin architecture principles:
1616

1717
```
1818
src/plugins/contract/
19-
├── manifest.ts # Plugin manifest with command definitions
20-
├── schema.ts # Contract data schema with Zod validation
19+
├── manifest.ts
20+
├── schema.ts
21+
├── contract-helper.ts # Local state cleanup after delete
2122
├── commands/
2223
│ ├── create/
23-
│ │ ├── handler.ts # Contract creation handler
24-
│ │ ├── input.ts # Input schema and validation
25-
│ │ ├── output.ts # Output schema and template
26-
│ │ └── index.ts # Command exports
27-
│ └── list/
28-
│ ├── handler.ts # List contracts handler
29-
│ ├── output.ts # Output schema and template
30-
│ └── index.ts # Command exports
24+
│ ├── import/
25+
│ ├── list/
26+
│ └── delete/
3127
├── utils/
32-
│ └── contract-file-helpers.ts # Contract file reading utilities
33-
├── zustand-state-helper.ts # State management helper
34-
├── __tests__/unit/ # Unit tests
35-
└── index.ts # Plugin exports
28+
│ └── contract-file-helpers.ts
29+
├── zustand-state-helper.ts
30+
├── __tests__/unit/
31+
└── index.ts
3632
```
3733

3834
## 🚀 Commands
@@ -95,6 +91,16 @@ hcli contract create --name my-nft --default erc721
9591
hcli contract create --name my-token --default erc20 -c "CustomToken" -c "CTK" -c "500000"
9692
```
9793

94+
### Contract Import
95+
96+
Imports an existing contract from Hedera (by contract ID or EVM address) into local state.
97+
98+
**Main options:** `--contract` (`-c`, required), optional `--name`, `--alias`, `--verified`.
99+
100+
```bash
101+
hcli contract import --contract 0.0.123456 --name myContract
102+
```
103+
98104
### Contract List
99105

100106
Lists all deployed contracts stored in the state across all networks.
@@ -103,17 +109,29 @@ Lists all deployed contracts stored in the state across all networks.
103109
hcli contract list
104110
```
105111

112+
### Contract Delete
113+
114+
**Default:** submits `ContractDeleteTransaction` on Hedera, then removes the contract from local CLI state. **With `--state-only`:** only removes from local CLI state (no network transaction).
115+
116+
When deleting on Hedera (default), pass **`--transfer-id` (`-t`)** or **`--transfer-contract-id` (`-r`)** so remaining HBAR has a destination (avoids `OBTAINER_REQUIRED` from the network). Use `--admin-key` when signing material for the contract admin key is not already in state (same formats as create). Contracts **without** an admin key on Hedera cannot be deleted on the network—use `--state-only` to drop local CLI state only. If the contract is not in local state, the CLI loads contract info from the mirror node.
117+
118+
```bash
119+
hcli contract delete --contract myAlias --transfer-id 0.0.5678
120+
hcli contract delete --contract 0.0.123456 --state-only
121+
```
122+
106123
## 🔧 Core API Integration
107124

108125
The plugin uses the Core API services:
109126

110-
- `api.contract` - Contract deployment transaction creation
127+
- `api.contract` - Contract deployment and `ContractDeleteTransaction` construction
111128
- `api.contractCompiler` - Solidity compilation
112129
- `api.contractVerifier` - HashScan verification
113-
- `api.txExecution` - Transaction signing and execution
130+
- `api.txSign` / `api.txExecute` - Signing and execution (including contract create flow)
114131
- `api.state` - Namespaced state management
115132
- `api.network` - Network information
116133
- `api.alias` - Name registration and resolution
134+
- `api.mirror` - Contract info when deleting on network without a local state entry
117135
- `api.config` - Configuration (key manager default)
118136
- `api.keyResolver` - Key resolution for admin key
119137
- `api.logger` - Logging
@@ -131,7 +149,9 @@ interface CommandResult {
131149
**Output schemas:**
132150

133151
- **Create**: `contractId`, `contractName`, `contractEvmAddress`, `alias`, `network`, `transactionId`, `adminPublicKey`
152+
- **Import**: `contractId`, `contractName`, `contractEvmAddress`, `alias`, `network`, `memo`, `verified`
134153
- **List**: `contracts` (array with `contractId`, `contractName`, `contractEvmAddress`, `alias`, `adminPublicKey`, `network`), `totalCount`
154+
- **Delete**: `deletedContract`, `network`, optional `removedAliases`, `transactionId`, `stateOnly`
135155

136156
Human-readable output uses Handlebars templates with HashScan links for contract and transaction IDs.
137157

@@ -142,12 +162,14 @@ Contract data is stored in the `contract-contracts` namespace with the following
142162
```typescript
143163
interface ContractData {
144164
contractId: string; // Hedera contract ID (0.0.xxxxx)
145-
contractName: string; // Contract name from Solidity source
146-
contractEvmAddress: string; // Deployed EVM address
147-
adminPublicKey?: string; // Optional admin public key
165+
alias?: string; // Local alias for `--contract` lookups (optional)
166+
contractName?: string; // Human-readable name (e.g. from Solidity or import)
167+
contractEvmAddress: string; // Deployed contract EVM address
168+
adminPublicKey?: string; // Optional admin public key (from network / create)
169+
adminKeyRefId?: string; // KMS key ref for admin when set at create (for signing delete on network)
148170
network: SupportedNetwork; // Network
149171
memo?: string; // Optional memo (max 100 chars)
150-
verified?: boolean; // Contract verification status
172+
verified?: boolean; // Contract verification status (e.g. HashScan)
151173
}
152174
```
153175

0 commit comments

Comments
 (0)