Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
b6e0e2b
feat(1674): adr for scheduled transaction
mmyslblocky Mar 30, 2026
16e0e26
Merge branch 'feat/1674-spike-schedule-transaction-adr' of github.com…
mmyslblocky Mar 30, 2026
d2a3f8d
Merge branch 'main' of github.com:hiero-ledger/hiero-cli into feat/17…
mmyslblocky Mar 31, 2026
eefb3d2
feat(1739): added scheduled transaction
mmyslblocky Mar 31, 2026
aab9f20
feat(1739): fix
mmyslblocky Mar 31, 2026
b656c5f
feat(1739): added mocks to test
mmyslblocky Mar 31, 2026
5579a68
feat(1739): schedule transaction implementation
mmyslblocky Apr 1, 2026
15d6a0b
feat(1739): PR remarks
mmyslblocky Apr 2, 2026
88aca2d
feat(1748): change in string literals
mmyslblocky Apr 7, 2026
9bd7a92
feat(1739): resolve merge conflicts
mmyslblocky Apr 7, 2026
d45e76c
feat(1742): added hook for state persist for account create
mmyslblocky Apr 8, 2026
de3c356
feat(1742): added schedule account update state hook
mmyslblocky Apr 8, 2026
6e28f6d
feat(1742): added unit tests
mmyslblocky Apr 8, 2026
a2c73c7
feat(1742): added unit tests and docs
mmyslblocky Apr 8, 2026
1526df5
feat(1742): review remarks
mmyslblocky Apr 8, 2026
d1d26c2
feat(1742): resolved merge conflicts
mmyslblocky Apr 9, 2026
c68f76b
Merge branch 'feat/1774-schedule-transaction-docs-update-missing-test…
mmyslblocky Apr 9, 2026
b748464
feat(1742): change the sign command implementation inside the schedul…
mmyslblocky Apr 10, 2026
19e0317
docs: ADR-012-unified-hook-architecture
piotrswierzy Apr 10, 2026
c73d1c7
Merge branch 'feat/1742-implement-state-hooks' of github-blockydevs:h…
piotrswierzy Apr 10, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 20 additions & 7 deletions docs/adr/ADR-011-schedule-transaction-plugin.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

- Status: Proposed
- Date: 2026-03-26
- Related: `src/plugins/schedule/*`, `src/core/services/schedule-transaction/*`, `src/core/services/mirrornode/*`, `src/core/commands/command.ts`, `src/core/hooks/abstract-hook.ts`, `docs/adr/ADR-001-plugin-architecture.md`, `docs/adr/ADR-009-class-based-handler-and-hook-architecture.md`, `docs/adr/ADR-010-batch-transaction-plugin.md`
- Related: `src/plugins/schedule/*`, `src/plugins/account/hooks/schedule-create/*`, `src/plugins/account/hooks/schedule-update/*`, `src/core/services/schedule-transaction/*`, `src/core/services/mirrornode/*`, `src/core/commands/command.ts`, `src/core/hooks/abstract-hook.ts`, `docs/adr/ADR-001-plugin-architecture.md`, `docs/adr/ADR-009-class-based-handler-and-hook-architecture.md`, `docs/adr/ADR-010-batch-transaction-plugin.md`

## Context

Expand Down Expand Up @@ -542,6 +542,8 @@ When `--name` is provided:
- If a state record exists, verify updates `scheduled` and `executed` flags in local state.
- If no state record exists but a name is given, verify **creates** a new state record from the Mirror Node response, resolving admin and payer keys.

**Verify hooks (domain state):** The `schedule verify` command lists `registeredHooks` such as `account-create-schedule-state` and `account-update-schedule-state`. When an existing record was not yet marked executed and Mirror reports `executed_timestamp`, verify saves the updated record and invokes `AbstractHook.customHandlerHook` so account plugins can persist state: they call `api.mirror.getTransactionRecord(formatTransactionIdToDashFormat(innerTransactionId))` and use the Mirror row where `scheduled === true` (with `entity_id` / `result`) to validate the inner transaction.

```ts
export class ScheduleVerifyCommand implements Command {
async execute(args: CommandHandlerArgs): Promise<CommandResult> {
Expand All @@ -562,13 +564,22 @@ export class ScheduleVerifyCommand implements Command {
// Query Mirror Node REST API
const scheduleResponse = await api.mirror.getScheduled(resolvedScheduleId);

// Update or create local state record
if (scheduleRecord) {
stateHelper.saveScheduled(composeKey(network, scheduleRecord.name), {
// Update or create local state record; run registered verify hooks when execution first completes
if (scheduleRecord && !scheduleRecord.executed) {
const updatedScheduledRecord = {
...scheduleRecord,
scheduled: true,
executed: !!scheduleResponse.executed_timestamp,
});
};
stateHelper.saveScheduled(
composeKey(network, scheduleRecord.name),
updatedScheduledRecord,
);
if (updatedScheduledRecord.executed && updatedScheduledRecord.command) {
await this.customHandlerHook(args, {
customHandlerParams: { scheduledData: updatedScheduledRecord },
});
}
} else if (scheduleName) {
// Import schedule from Mirror Node into local state
// Resolves payer and admin key references from the response
Expand Down Expand Up @@ -636,6 +647,8 @@ The `verify` command queries the Hedera Mirror Node REST API rather than the SDK

**Types:** `ScheduleInfo` and `ScheduleSignatureInfo` in `src/core/services/mirrornode/types.ts`.

**Transaction records:** Domain hooks that need the inner transaction after execution use `getTransactionRecord(transactionId, nonce?)` (`GET /api/v1/transactions/{transactionId}`). Pass the inner transaction id in the **dash** path form (see `formatTransactionIdToDashFormat` in core utils), not the SDK `@` form.

```ts
export interface ScheduleInfo {
admin_key?: MirrorNodeTokenKey | null;
Expand Down Expand Up @@ -816,7 +829,7 @@ flowchart TD
2. Include `'scheduled'` in their `registeredHooks` array. The `--scheduled` option is then injected automatically.
3. Expose `keyRefIds` on their normalized params (`BaseNormalizedParams`) so the hook can append admin/payer key references.
- The `ScheduleTransactionService` is wired into `CoreApi` as `api.schedule`, alongside existing services.
- `HederaMirrornodeService` gains a `getScheduled` method querying `GET /api/v1/schedules/{scheduleId}`, with `ScheduleInfoSchema` / `ScheduleSignatureInfoSchema` Zod schemas for response validation.
- `HederaMirrornodeService` gains a `getScheduled` method querying `GET /api/v1/schedules/{scheduleId}`, with `ScheduleInfoSchema` / `ScheduleSignatureInfoSchema` Zod schemas for response validation. Account schedule verify hooks additionally use `getTransactionRecord` for the inner transaction id after execution.
- `TransactionResult` in `src/core/types/shared.types.ts` includes an optional `scheduleId` field, and `receipt-mapper.ts` populates it from the SDK receipt.
- The `verify` command should be used to check the status of scheduled transactions with `waitForExpiry: true`, since execution is deferred and no automatic notification is provided.
- Zod `.transform()` schemas (`KeySchema`, `ScheduleReferenceObjectSchema`) produce parsed objects — handlers must pass the parsed value directly to service methods rather than double-parsing.
Expand All @@ -831,7 +844,7 @@ flowchart TD
- `executeTransaction`: verify `TransactionError` on failed submission.
- `outputPreparation`: verify output includes optional `name` only when resolved from alias.
- **Unit: ScheduleDeleteCommand phases.** Test the `execute()` override: state-only delete when `scheduled === false` or `executed === true`; on-chain delete path (via `super.execute()`) otherwise. Test `outputPreparation` removes local state entry after successful on-chain delete.
- **Unit: ScheduleVerifyCommand.** Mock `api.mirror.getScheduled()` response and verify that the output includes correct `executedAt`, `deleted` (boolean), `waitForExpiry`, `scheduleMemo`, `expirationTime`, and `payerAccountId` values. Verify state is updated when `--name` is provided. Verify new state record is created when a name is given but no existing state record exists.
- **Unit: ScheduleVerifyCommand.** Mock `api.mirror.getScheduled()` response and verify that the output includes correct `executedAt`, `deleted` (boolean), `waitForExpiry`, `scheduleMemo`, `expirationTime`, and `payerAccountId` values. Verify state is updated when `--name` is provided. Verify new state record is created when a name is given but no existing state record exists. When testing hook integration, mock `getTransactionRecord` with a `transactions` entry where `scheduled: true` for account schedule-state hooks.
- **Unit: ScheduledHook.** Invoke `preSignTransactionHook` with mock args containing `--scheduled` flag. Assert that the hook builds, signs, and executes the `ScheduleCreateTransaction`, persists `scheduleId` to state, and returns `breakFlow: true` with `ScheduledOutputSchema`. Invoke without `--scheduled` flag and assert `breakFlow: false`.
- **Unit: ScheduleHelper.** Test `resolveScheduleIdByEntityReference` for alias resolution (state lookup), entity ID resolution (Mirror Node query), and EVM address rejection. Test `resolveScheduleIdFromArgs` for error when both/neither args provided.
- **Unit: ScheduleTransactionService.** Verify that `buildScheduleCreateTransaction` correctly sets `scheduledTransaction`, `waitForExpiry`, and optional fields (payer, admin key, memo, expiration). Verify `buildScheduleSignTransaction` and `buildScheduleDeleteTransaction` set the correct `scheduleId`.
Expand Down
Loading
Loading