Actions

Operations executed on objects through the Dual Event Bus.

What are Actions?

Actions are the primary way to interact with objects on the Dual network. Every state change, emitting a new object, transferring ownership, updating properties, redeeming a token, is modeled as an action. Actions are executed through the Event Bus, which validates, signs, sequences, and records them.

This design ensures that every mutation is auditable, ordered, and cryptographically signed. There are no "backdoor" writes, all state changes flow through the Event Bus.

Action Types

Before you can execute an action, you need to register an action type that defines its behavior. Action types are scoped to a template and specify what the action does, what parameters it requires, and what validation rules apply:

Code
POST /ebus/action-types
{
"name": "io.acme.product.transfer",
"template": "io.acme.product.v1",
"description": "Transfer product ownership to another wallet",
"parameters": {
"to": { "type": "string", "required": true }
}
}

Common action types include:

  • Emit, Create a new object instance from a template.
  • Transfer, Move ownership from one wallet to another.
  • Update, Modify object properties (e.g. change status, increment a counter).
  • Redeem, Mark an object as used/consumed (e.g. a ticket being scanned).
  • Burn, Permanently destroy an object, removing it from circulation.

You can define any custom action types your application needs. List registered types with GET /ebus/action-types.

Executing Actions

Execute an action by posting to POST /ebus/actions:

Code
POST /ebus/actions
{
"action_type": "io.acme.product.transfer",
"object_id": "obj_xyz",
"payload": {
"to": "w_bob"
}
}

The Event Bus validates the action against the action type's rules, verifies the caller's permissions (e.g. only the current owner can transfer), signs it with an EIP-712 signature, and adds it to the processing queue.

Action Lifecycle

Each action moves through a defined lifecycle:

  • Pending, The action has been received and queued for processing.
  • Processing, The Event Bus is validating and executing the action.
  • Confirmed, The action succeeded and state has been updated. The action is included in the next Sequencer batch.
  • Failed, Validation failed or a precondition was not met. The action is recorded but no state change occurs.

Check an action's status with GET /ebus/actions/{actionId}.

Batch Execution

For scenarios where multiple actions must succeed or fail together, use batch execution:

Code
POST /ebus/actions/batch
{
"actions": [
{
"action_type": "io.acme.product.transfer",
"object_id": "obj_001",
"payload": { "to": "w_bob" }
},
{
"action_type": "io.acme.product.transfer",
"object_id": "obj_002",
"payload": { "to": "w_bob" }
}
]
}

Batches are atomic, if any action in the batch fails validation, the entire batch is rolled back. This is essential for operations like "transfer a set of items" where partial success would leave the system in an inconsistent state.

Cryptographic Signing

Every action is signed using EIP-712 typed data signatures. This provides:

  • Non-repudiation, The signer cannot deny they initiated the action.
  • Tamper detection, Any modification to the action payload invalidates the signature.
  • Chain integrity, Actions are linked via hash chain, so reordering or omitting actions is detectable.

These signatures are verified by the Sequencer when building batches and by the smart contracts if a fraud proof is submitted.

Webhooks for Action Events

Register webhooks to receive real-time notifications when actions are executed. This enables reactive architectures, for example, sending an email when a product is transferred, or updating an inventory system when an item is redeemed.

Relationship to Other Concepts

  • Objects, Actions operate on objects. Every object state change is an action in its activity log.
  • Templates, Action types are registered against templates. A "transfer" action type for product tokens is separate from a "transfer" action type for tickets.
  • Sequencer, Confirmed actions are collected by the Sequencer, batched, fingerprinted, and anchored on-chain.
  • Wallets, The executing wallet must have the appropriate permissions for the action type being invoked.