API Reference

Timelock API Reference

This page documents the public API of openzeppelin_timelock for OpenZeppelin Contracts for Sui v1.x.

use openzeppelin_timelock::timelock;

Hash-keyed operation timelock with typed, on-chain operation parameters and a typed hot-potato execution ticket. It is a Sui-native take on OpenZeppelin's TimelockController. A proposer schedules an operation with its typed params stored on-chain under a deterministic, off-chain reproducible id. After the per-op delay elapses and before the grace window closes (the ready window is half-open, [ready_at_ms, expires_at_ms), and both bounds are locked at schedule time), an executor executes it, minting an ExecutionTicket<Action, Params>, a no-ability hot potato that must be consumed by consume in the same PTB. Done is sticky, so operations are non-replayable.

Authorization comes from a hard dependency on openzeppelin_access: every gated entry takes &Auth<Role> and asserts Role matches one of the four role types bound into the Timelock at creation (proposer_role / executor_role / canceller_role / admin_role). Role types are immutable; membership is managed in the consumer's AccessControl. Configuration (min_delay_ms, grace_period_ms, open_executor) is mutated only through the admin-gated schedule_* / execute_* pairs, so every configuration change is itself timelocked.

The raw entries (schedule / execute / execute_open / cancel) do NOT bind the call to a specific Timelock id. A consumer using them must pin the canonical timelock id itself, or an actor holding the roles can route a self-created zero-delay timelock through the consumer. Prefer the *_with entries, which take a stored OperationCap that enforces the canonical-timelock binding structurally. Action witness types must be drop-only, one per consume function, and their construction must never leak across the package boundary.

Types

Functions

Events

Errors

Types

struct Timelock

struct

#

The per-protocol timelock registry. Shared object.

key-only (no store): the only legal disposition is sharing, so a Timelock is always a shared object and can never be wrapped or address-owned. It stores the configuration (min_delay_ms, grace_period_ms, open_executor), the four bound role types, and a table mapping operation ids (keccak256, 32 bytes) to per-op state; absence of an id means Unset. Typed operation params are stored as dynamic fields directly on its id, keyed by the operation id.

enum OpTimestamp

enum

#

Stored per-operation state: Pending { ready_at_ms, expires_at_ms, predecessor, action } or Done.

Timestamps and predecessor are locked at schedule time, so later min_delay_ms / grace_period_ms changes never move an existing op. action records the scheduled Action type so execute can re-check it (the id commits the Action, but execute takes the id directly).

struct ExecutionTicket<phantom Action, Params>

struct

#

Deferred authorization for one typed operation, carrying the scheduled params.

Hot potato with NO abilities, so it must be consumed in the same transaction it is minted. Minted by execute / execute_open (or in-module for self-admin ops); destroyed only by consume (or in-module for self-admin ops).

struct OperationCap<phantom Action, phantom Params>

struct

#

A stored handle that binds an operation kind (Action, Params) to ONE Timelock instance. Carries NO authority on its own; combine it with &Auth<Role> for role authorization.

Mint one per operation kind at consumer init via new_operation_cap (against your canonical timelock) and store it inside the object you protect; the *_with entries then enforce the canonical-timelock binding for you, so you never hand-write the object::id assert. store-only: it cannot be copied or dropped, so it lives in your protected object.

struct IdInput

struct

#

BCS preimage of every operation id, with fields action, payload_digest, predecessor, salt, timelock_id (in declaration order).

Including timelock_id makes identical inputs hash to different ids across Timelock instances.

enum OperationState

enum

#

Observable operation state, computed from the stored OpTimestamp plus the Clock: Unset, Waiting { ready_at_ms, expires_at_ms }, Ready { ready_at_ms, expires_at_ms }, Expired { ready_at_ms, expires_at_ms }, or Done.

struct UpdateMinDelayWitness

struct

#

Module-private marker witness (the Action) for the self-administered min_delay_ms pipeline. Only this module can construct it, so only its own execute path can mint/redeem the corresponding ExecutionTicket<UpdateMinDelayWitness, _>.

struct UpdateGracePeriodWitness

struct

#

Module-private marker witness (the Action) for the self-administered grace_period_ms pipeline. Only this module can construct it, so only its own execute path can mint/redeem the corresponding ExecutionTicket<UpdateGracePeriodWitness, _>.

struct SetOpenExecutorWitness

struct

#

Module-private marker witness (the Action) for the self-administered open_executor pipeline. Only this module can construct it, so only its own execute path can mint/redeem the corresponding ExecutionTicket<SetOpenExecutorWitness, _>.

Functions

new<ProposerRole, ExecutorRole, CancellerRole, AdminRole>(min_delay_ms: u64, grace_period_ms: u64, ctx: &mut TxContext) -> Timelock

public

#

Mints a fresh Timelock bound to the four consumer role types. min_delay_ms is the floor on every operation's delay (may be 0); grace_period_ms is the window, after an op becomes ready, during which it stays executable.

Returns the unshared Timelock; the caller must dispose of it via share (or use new_shared).

Aborts with EInvalidConfig if min_delay_ms > MAX_DELAY_MS, or if grace_period_ms is zero or greater than MAX_DELAY_MS.

Emits a TimelockCreated event.

new_shared<ProposerRole, ExecutorRole, CancellerRole, AdminRole>(min_delay_ms: u64, grace_period_ms: u64, ctx: &mut TxContext) -> ID

public

#

Convenience constructor that shares the Timelock and returns its ID.

Aborts with EInvalidConfig on the same config bounds as new.

Emits a TimelockCreated event.

share(self: Timelock)

public

#

Shares a Timelock. The only legal way to dispose of a value returned by new.

hash_operation<Action>(timelock_id: ID, payload_digest: vector<u8>, predecessor: vector<u8>, salt: vector<u8>) -> vector<u8>

public

#

Pure operation-id derivation from a payload digest. Off-chain tooling computes payload_digest = keccak256(bcs(params)) and reproduces the id from there. Returns the 32-byte operation id.

The id is keccak256(DOMAIN_TAG || bcs(IdInput)), byte-exact:

  • DOMAIN_TAG is the ASCII string OZ_Timelock_1_Sui (readable via domain_tag). It is not length-prefixed: the preimage starts with its raw 17 bytes.
  • IdInput fields are BCS-encoded in declaration order: action, payload_digest, predecessor, salt, timelock_id.
  • action is type_name::with_original_ids<Action>(): the fully-qualified type string (<address-hex>::<module>::<name>, 64 lowercase hex chars, no 0x prefix, type arguments included), resolved with the ORIGINAL package address so ids stay stable across package upgrades. Its BCS is a ULEB128 length prefix followed by those ASCII bytes.
  • payload_digest, predecessor, and salt are ULEB128-length-prefixed byte vectors (an empty predecessor encodes as the single byte 0x00); timelock_id is the raw 32 address bytes, unprefixed.

Cross-check any off-chain implementation against this function before relying on predicted ids.

schedule<Role, Action, Params: store + drop>(self: &mut Timelock, _proposer_auth: &Auth<Role>, params: Params, predecessor: vector<u8>, salt: vector<u8>, delay_ms: u64, clock: &Clock, ctx: &mut TxContext) -> vector<u8>

public

#

Schedules an operation, storing its typed params on-chain. Caller must hold the proposer role. predecessor is the id of an op that must be Done before this one (or empty for none); salt disambiguates otherwise-identical operations; delay_ms must be at least min_delay_ms. Returns the operation id (pass to execute / cancel). This raw entry does not bind the Timelock id. Prefer schedule_with.

Aborts with EWrongRole if Role is not the bound proposer_role.

Aborts with EDelayTooShort if delay_ms < min_delay_ms.

Aborts with EScheduleOverflow if now + delay_ms (or that sum plus grace_period_ms) overflows u64.

Aborts with EInvalidPredecessor if predecessor is non-empty and not a 32-byte id.

Aborts with EPredecessorIsSelf if predecessor equals the computed id (defense in depth; unreachable in practice because the id hashes over predecessor).

Aborts with EOperationAlreadyExists if the id is already scheduled.

Emits an OperationScheduled event.

execute<Role, Action, Params: store + drop>(self: &mut Timelock, _executor_auth: &Auth<Role>, id: vector<u8>, clock: &Clock, ctx: &mut TxContext) -> ExecutionTicket<Action, Params>

public

#

Executes a ready operation by id. Caller must hold the executor role. Marks the op Done (sticky), removes the stored params, and returns an ExecutionTicket<Action, Params> carrying them: a no-ability hot potato that must be consumed by consume in the same PTB. This raw entry does not bind the Timelock id. Prefer execute_with.

Aborts with EWrongRole if Role is not the bound executor_role.

Aborts with EOperationUnset if no operation with this id exists.

Aborts with EOperationAlreadyDone if the operation has already been executed.

Aborts with EDelayNotElapsed if the operation's delay has not elapsed yet.

Aborts with EOperationExpired if the operation's grace window has closed.

Aborts with EPredecessorUnset if the operation names a predecessor that is not in the timelock.

Aborts with EPredecessorNotDone if the operation names a predecessor that is not yet executed.

Aborts with EWrongAction if Action does not match the type the operation was scheduled with.

Aborts with EWrongParams if Params does not match the type the operation was scheduled with.

Emits an OperationExecuted event.

execute_open<Action, Params: store + drop>(self: &mut Timelock, id: vector<u8>, clock: &Clock, ctx: &mut TxContext) -> ExecutionTicket<Action, Params>

public

#

Executes a ready operation in open-executor mode (no Auth required). Open mode lifts only the executor-role gate on minting the ticket; consumption stays witness-gated, so a caller who cannot construct Action cannot consume the returned ticket. Since it has no abilities, their transaction aborts and reverts the state change atomically.

Aborts with EOpenExecutorDisabled if open_executor is false.

Aborts on the same operation-state conditions as execute.

Emits an OperationExecuted event.

consume<Action: drop, Params>(self: &Timelock, ticket: ExecutionTicket<Action, Params>, _witness: Action) -> (vector<u8>, Params)

public

#

Redeems an execution ticket. Two gates fire: timelock-binding and witness-by-value (Action is consumed, so only a module that can construct Action can call this). Returns (op_id, params), the operation id and the exact typed params committed at schedule time; there is no payload to re-supply or mismatch.

Aborts with EWrongTimelock if the ticket was minted by a different Timelock.

cancel<Role, Params: store + drop>(self: &mut Timelock, _canceller_auth: &Auth<Role>, id: vector<u8>, ctx: &mut TxContext)

public

#

Cancels a scheduled operation by id, dropping its stored params. Caller must hold the canceller role. Allowed on Waiting / Ready / Expired operations; not on Done. The operation's Params type must be named so the stored params can be cleaned up. This raw entry does not bind the Timelock id. Prefer cancel_with.

Aborts with EWrongRole if Role is not the bound canceller_role.

Aborts with EOperationUnset if no such operation exists.

Aborts with EOperationAlreadyDone if the operation was already executed.

Aborts with EWrongParams if Params does not match the type the operation was scheduled with.

Emits an OperationCancelled event.

new_operation_cap<Action, Params>(self: &Timelock) -> OperationCap<Action, Params>

public

#

Mints an OperationCap binding (Action, Params) to this Timelock. Permissionless and authority-free: call it once at consumer init against your canonical timelock and store the result in the object you protect.

operation_cap_timelock_id<Action, Params>(cap: &OperationCap<Action, Params>) -> ID

public

#

Returns the Timelock id an OperationCap is bound to.

destroy_operation_cap<Action, Params>(cap: OperationCap<Action, Params>)

public

#

Destroys an OperationCap, e.g. when decommissioning the object that stored it. An OperationCap has store but not drop, so it cannot be discarded implicitly; this is the explicit disposal. The cap carries no authority; a fresh one is always mintable via new_operation_cap.

schedule_with<Role, Action, Params: store + drop>(self: &mut Timelock, cap: &OperationCap<Action, Params>, proposer_auth: &Auth<Role>, params: Params, predecessor: vector<u8>, salt: vector<u8>, delay_ms: u64, clock: &Clock, ctx: &mut TxContext) -> vector<u8>

public

#

Like schedule, but the OperationCap enforces the canonical-timelock binding, and Action / Params infer from the cap (zero explicit type args at the call site). Recommended over the raw entry. Returns the operation id.

Aborts with EWrongTimelock if cap is not bound to self; plus the same aborts as schedule.

Emits an OperationScheduled event.

execute_with<Role, Action, Params: store + drop>(self: &mut Timelock, cap: &OperationCap<Action, Params>, executor_auth: &Auth<Role>, id: vector<u8>, clock: &Clock, ctx: &mut TxContext) -> ExecutionTicket<Action, Params>

public

#

Like execute, but the OperationCap enforces the canonical-timelock binding. Recommended over the raw entry. Returns an ExecutionTicket<Action, Params> that must be consumed in the same PTB.

Aborts with EWrongTimelock if cap is not bound to self; plus the same aborts as execute.

Emits an OperationExecuted event.

execute_open_with<Action, Params: store + drop>(self: &mut Timelock, cap: &OperationCap<Action, Params>, id: vector<u8>, clock: &Clock, ctx: &mut TxContext) -> ExecutionTicket<Action, Params>

public

#

Like execute_open, but the OperationCap enforces the canonical-timelock binding. Returns an ExecutionTicket<Action, Params> that must be consumed in the same PTB.

Aborts with EWrongTimelock if cap is not bound to self; plus the same aborts as execute_open.

Emits an OperationExecuted event.

cancel_with<Role, Action, Params: store + drop>(self: &mut Timelock, cap: &OperationCap<Action, Params>, canceller_auth: &Auth<Role>, id: vector<u8>, ctx: &mut TxContext)

public

#

Like cancel, but the OperationCap enforces the canonical-timelock binding. Recommended over the raw entry.

Aborts with EWrongTimelock if cap is not bound to self; plus the same aborts as cancel.

Emits an OperationCancelled event.

schedule_update_min_delay<Role>(self: &mut Timelock, _admin_auth: &Auth<Role>, new_min_delay_ms: u64, predecessor: vector<u8>, salt: vector<u8>, delay_ms: u64, clock: &Clock, ctx: &mut TxContext) -> vector<u8>

public

#

Schedules a min_delay_ms change (stored as the operation's params). Admin-gated. Returns the operation id (pass to execute_update_min_delay).

Aborts with EWrongRole if Role is not the bound admin_role.

Aborts with EInvalidConfig if new_min_delay_ms > MAX_DELAY_MS.

Aborts on the scheduling conditions of schedule (EDelayTooShort, EScheduleOverflow, EInvalidPredecessor, EPredecessorIsSelf (unreachable in practice), EOperationAlreadyExists).

Emits an OperationScheduled event.

execute_update_min_delay<Role>(self: &mut Timelock, _admin_auth: &Auth<Role>, id: vector<u8>, clock: &Clock, ctx: &mut TxContext)

public

#

Executes a scheduled min_delay_ms change by id. Admin-gated. Applies the stored value; the ticket is minted and consumed in-module (the marker witness is only constructible here).

Aborts with EWrongRole if Role is not the bound admin_role.

Aborts on the same conditions as execute, apart from its role check.

Aborts with EInvalidConfig if the stored new_min_delay_ms exceeds MAX_DELAY_MS. The bound is re-asserted at apply time because the op can be staged through the generic schedule.

Emits an OperationExecuted event, and a MinDelayChanged event when the configured value actually changes (a no-op update emits no MinDelayChanged).

schedule_update_grace_period<Role>(self: &mut Timelock, _admin_auth: &Auth<Role>, new_grace_period_ms: u64, predecessor: vector<u8>, salt: vector<u8>, delay_ms: u64, clock: &Clock, ctx: &mut TxContext) -> vector<u8>

public

#

Schedules a grace_period_ms change. Admin-gated. Returns the operation id (pass to execute_update_grace_period).

Aborts with EWrongRole if Role is not the bound admin_role.

Aborts with EInvalidConfig if new_grace_period_ms is zero or greater than MAX_DELAY_MS.

Aborts on the scheduling conditions of schedule (EDelayTooShort, EScheduleOverflow, EInvalidPredecessor, EPredecessorIsSelf (unreachable in practice), EOperationAlreadyExists).

Emits an OperationScheduled event.

execute_update_grace_period<Role>(self: &mut Timelock, _admin_auth: &Auth<Role>, id: vector<u8>, clock: &Clock, ctx: &mut TxContext)

public

#

Executes a scheduled grace_period_ms change by id. Admin-gated. Applies the stored value.

Aborts with EWrongRole if Role is not the bound admin_role.

Aborts on the same conditions as execute, apart from its role check.

Aborts with EInvalidConfig if the stored new_grace_period_ms is zero or exceeds MAX_DELAY_MS. The bound is re-asserted at apply time because the op can be staged through the generic schedule (a zero grace period would brick the timelock with an empty ready window).

Emits an OperationExecuted event, and a GracePeriodChanged event when the configured value actually changes (a no-op update emits no GracePeriodChanged).

schedule_set_open_executor<Role>(self: &mut Timelock, _admin_auth: &Auth<Role>, value: bool, predecessor: vector<u8>, salt: vector<u8>, delay_ms: u64, clock: &Clock, ctx: &mut TxContext) -> vector<u8>

public

#

Schedules an open_executor toggle. Admin-gated. value is the setting applied when the scheduled op executes. Returns the operation id (pass to execute_set_open_executor).

Aborts with EWrongRole if Role is not the bound admin_role.

Aborts on the scheduling conditions of schedule (EDelayTooShort, EScheduleOverflow, EInvalidPredecessor, EPredecessorIsSelf (unreachable in practice), EOperationAlreadyExists).

Emits an OperationScheduled event.

execute_set_open_executor<Role>(self: &mut Timelock, _admin_auth: &Auth<Role>, id: vector<u8>, clock: &Clock, ctx: &mut TxContext)

public

#

Executes a scheduled open_executor toggle by id. Admin-gated.

Aborts with EWrongRole if Role is not the bound admin_role.

Aborts on the same conditions as execute, apart from its role check.

Emits an OperationExecuted event, and an OpenExecutorChanged event when the setting actually toggles (re-applying the current setting emits no OpenExecutorChanged).

min_delay_ms(self: &Timelock) -> u64

public

#

Returns the configured floor on every operation's delay.

grace_period_ms(self: &Timelock) -> u64

public

#

Returns the configured window, after an op becomes ready, during which it stays executable.

is_open_executor(self: &Timelock) -> bool

public

#

Returns whether open-executor mode is enabled (anyone may call execute_open).

max_delay_ms() -> u64

public

#

Returns the upper bound on the configured min_delay_ms and grace_period_ms (60 days in milliseconds). Does NOT bound the per-call delay_ms.

domain_tag() -> vector<u8>

public

#

Returns the domain separation tag prefixed to every operation-id preimage: the ASCII string OZ_Timelock_1_Sui. OZ_Timelock names the primitive, 1 versions the preimage format, Sui pins the chain. Locked at publication.

proposer_role(self: &Timelock) -> TypeName

public

#

Returns the role type bound for scheduling.

executor_role(self: &Timelock) -> TypeName

public

#

Returns the role type bound for executing.

canceller_role(self: &Timelock) -> TypeName

public

#

Returns the role type bound for cancelling.

admin_role(self: &Timelock) -> TypeName

public

#

Returns the role type bound for the self-administered configuration pipeline.

is_operation(self: &Timelock, id: vector<u8>) -> bool

public

#

Returns true if an operation with this id exists in the timelock (any state but Unset: Waiting, Ready, Expired, or Done); false otherwise.

is_operation_pending(self: &Timelock, id: vector<u8>, clock: &Clock) -> bool

public

#

Returns true if the operation is on the execution track: Waiting or Ready. Expired, Done, and Unset operations return false.

Narrower than isOperationPending in OpenZeppelin's Solidity TimelockController, where operations never expire and pending doubles as the cancellability check. Here an Expired operation is no longer pending but is still cancellable. To gate cancellation or cleanup, use is_operation(id) && !is_operation_done(id) (or match on operation_state).

is_operation_ready(self: &Timelock, id: vector<u8>, clock: &Clock) -> bool

public

#

Returns true if the operation is Ready: its delay has elapsed and its grace window is still open, so it can be executed now (predecessor permitting); false otherwise.

is_operation_expired(self: &Timelock, id: vector<u8>, clock: &Clock) -> bool

public

#

Returns true if the operation is Expired: its grace window has closed, so it can no longer be executed and can only be cancelled; false otherwise.

is_operation_done(self: &Timelock, id: vector<u8>) -> bool

public

#

Returns true if the operation has been executed (Done); false otherwise.

operation_state(self: &Timelock, id: vector<u8>, clock: &Clock) -> OperationState

public

#

Returns the observable OperationState of an operation at the current clock time: Unset, Waiting, Ready, Expired, or Done.

operation_params<Params: store>(self: &Timelock, id: vector<u8>) -> &Params

public

#

Borrows the typed params of a scheduled, not-yet-executed operation (Waiting, Ready, or Expired) for off-chain inspection and UIs.

Aborts with sui::dynamic_field::EFieldDoesNotExist if the id has no stored params (Unset or already Done).

Aborts with sui::dynamic_field::EFieldTypeMismatch if Params does not match the type the operation was scheduled with.

Events

TimelockCreated(timelock_id: ID, min_delay_ms: u64, grace_period_ms: u64, proposer_role: TypeName, executor_role: TypeName, canceller_role: TypeName, admin_role: TypeName)

event

#

Emitted by new when a Timelock is created, recording its initial config and the four bound role types.

OperationScheduled(id: vector<u8>, action: TypeName, payload_digest: vector<u8>, predecessor: vector<u8>, salt: vector<u8>, ready_at_ms: u64, expires_at_ms: u64, proposer: address)

event

#

Emitted when an operation is committed by schedule or by the self-administered schedule_update_min_delay / schedule_update_grace_period / schedule_set_open_executor (whose proposer is an admin-role holder). payload_digest is keccak256(bcs(params)); the params themselves are not emitted.

OperationExecuted(id: vector<u8>, action: TypeName, executor: address)

event

#

Emitted when an operation is executed and its ticket minted by execute / execute_open or by the self-administered execute_update_min_delay / execute_update_grace_period / execute_set_open_executor, which emit it alongside the corresponding *Changed event in the same call.

OperationCancelled(id: vector<u8>, action: TypeName, canceller: address)

event

#

Emitted by cancel / cancel_with when a pending (Waiting / Ready / Expired) operation is cancelled. action is the scheduled Action type of the cancelled operation.

MinDelayChanged(previous_ms: u64, new_ms: u64)

event

#

Emitted by execute_update_min_delay when the configured min_delay_ms actually changes; a no-op update (applying the already-configured value) emits no event.

GracePeriodChanged(previous_ms: u64, new_ms: u64)

event

#

Emitted by execute_update_grace_period when the configured grace_period_ms actually changes; a no-op update (applying the already-configured value) emits no event.

OpenExecutorChanged(previous: bool, new: bool)

event

#

Emitted by execute_set_open_executor when open-executor mode actually toggles; re-applying the current setting emits no event.

Errors

EWrongRole

error

#

Raised when the &Auth<Role> role type does not match the role bound for this action.

EInvalidConfig

error

#

Raised when min_delay_ms or grace_period_ms is outside the permitted bounds.

EDelayTooShort

error

#

Raised when schedule is called with delay_ms below the configured min_delay_ms.

EOperationAlreadyExists

error

#

Raised when an operation with this id is already scheduled.

EOperationUnset

error

#

Raised when no operation with this id exists in the timelock.

EOperationAlreadyDone

error

#

Raised when the operation has already been executed.

EDelayNotElapsed

error

#

Raised when the operation's delay has not elapsed yet.

EOperationExpired

error

#

Raised when the operation's grace window has closed; it can no longer be executed.

EPredecessorNotDone

error

#

Raised when the named predecessor is scheduled but not yet executed.

EPredecessorUnset

error

#

Raised when the named predecessor is not present in the timelock.

EPredecessorIsSelf

error

#

Raised when an operation names itself as its predecessor. Defense in depth: the id is the keccak256 of a preimage that includes predecessor, so a match would require a hash fixed point and is unreachable in practice.

EOpenExecutorDisabled

error

#

Raised when execute_open is called while open-executor mode is disabled.

EWrongTimelock

error

#

Raised when the execution ticket (or the OperationCap on the *_with path) does not belong to this timelock.

EScheduleOverflow

error

#

Raised when scheduling this operation would overflow the u64 deadline arithmetic (now + delay_ms, or that sum plus grace_period_ms).

EWrongParams

error

#

Raised when the supplied Params type does not match the type the operation was scheduled with. Reachable on either path: the raw &Auth path takes Params explicitly, and the *_with (cap) path pins it via the OperationCap. Supplying a cap for a different (Action, Params) than the op was scheduled with still mismatches.

EWrongAction

error

#

Raised when the supplied Action type does not match the type the operation was scheduled with. The Action is hashed into the operation id, but execute takes the id directly, so the binding is re-checked at execute time against the stored Action.

EInvalidPredecessor

error

#

Raised when predecessor is non-empty but not a 32-byte operation id, so it could never match a scheduled op. Rejected at schedule time rather than leaving a silently un-executable op.

On this page