# Build a DeCharge Machine

Before proceeding, ensure you have cloned the [DePHY vending machine examples](https://github.com/dephy-io/dephy-vending_machine-examples) repository:

```bash
git clone https://github.com/dephy-io/dephy-vending_machine-examples.git
```

The DeCharge machine demonstrates a **continuous charging model**, where a device operates over time and charges users incrementally. Its messaging network leverages Nostr for event-driven communication, with Solana handling payments via `lock` and `settle`.

An online demo of these examples is available at: <https://dephy-vending-machine-examples.pages.dev/examples>

## How to Run Locally

1. [Run DePHY messaging network](https://github.com/dephy-io/dephy-messaging-network-self-hosted/tree/main/dephy-messaging-network)
2. Run DePHY vending machine workers by: `docker compose up`
3. [Deploy the Solana program and run the dApp](https://github.com/dephy-io/dephy-vending_machine-examples/blob/main/balance-payment/README.md)

* The `docker compose` setup and the App demo application integrate both **DeCharge** and **Gacha** use cases.
* Ensure all dependencies are installed before running the application.

## Messaging

* **Message Definition**: Messages are defined as `DephyDechargeMessage` (in `message.rs`), with two variants:

  * `Request`: Initiates a state change (e.g., user requests the machine to start).
  * `Status`: Confirms a state update (e.g., machine is now Working).

  ```rust
  pub enum DephyDechargeMessage {
      Request { 
          to_status: DephyDechargeStatus, 
          reason: DephyDechargeStatusReason, 
          initial_request: EventId, 
          payload: String, 
      },
      Status { 
          status: DephyDechargeStatus, 
          reason: DephyDechargeStatusReason, 
          initial_request: EventId, 
          payload: String, 
      },
  }
  ```
* **Mention Tag (Machine Pubkey)**: The `p` tag identifies the target machine using its Nostr public key. For example, `PublicKey::parse("machine_pubkey")` specifies which device receives the message.
* **Session Tag**: The `s` tag scopes events to a specific session (e.g., "`dephy-decharge-controller`"), ensuring messages are isolated to the current context.
* **Publishing and Subscription**: The `RelayClient` publishes messages to a Nostr relay and subscribes to events using filters:

  ```rust
  let filter = Filter::new()
      .kind(EVENT_KIND) // Custom kind 1573
      .since(started_at)
      .custom_tag(SESSION_TAG, ["dephy-decharge-controller"])
      .custom_tag(MENTION_TAG, [machine_pubkey.to_hex()]);
  let sub_id = relay_client.subscribe(started_at, [machine_pubkey]).await?;
  ```

  * **Filter**: Retrieves events since `started_at`, scoped to the session and machine pubkey.
  * **Sender**: Typically a user or admin via the dApp or CLI.
  * **Receiver**: The DeCharge node controller handling the machine.
* **Message Handling**: The MessageHandler processes events, updating machine states and coordinating with Solana.

## Building Controller

* **Node**: Listens for user `Request` events, verifies eligibility with `check_eligible`, and updates machine state (e.g., `Available` to `Working`). It notifies the server via `Status` events.
* **Server**: Monitors `Status` events, locks funds with `lock` when the machine starts, and settles the final amount with `settle` when it stops. This split ensures state management and payment processing are separate but coordinated.

## Solana Interaction

* **Controller**: Initiates state change and verifies eligibility

```rust
// Before: Received Request event to start machine
if dephy_balance_payment_sdk::check_eligible(
    &self.solana_rpc_url,
    parsed_payload.namespace_id,
    &parsed_payload.user,
    parsed_payload.nonce,
    PREPAID_AMOUNT,
    &parsed_payload.recover_info,
).await? {
    // After: Send Status event to set machine to Working
    self.client.send_event(mention, &DephyDechargeMessage::Status { ... }).await?;
}
```

* **Server**: Locks and settles funds

  ```rust
  // When machine starts (Working status)
  // Before: Received Status event indicating machine is Working
  if let Err(e) = dephy_balance_payment_sdk::lock(
      &self.solana_rpc_url,
      &self.solana_keypair_path,
      parsed_payload.namespace_id,
      &parsed_payload.user,
      PREPAID_AMOUNT,
      &parsed_payload.recover_info,
  ).await {
      // After: Send Request event to revert to Available if lock fails
      self.client.send_event(mention, &DephyDechargeMessage::Request { to_status: DephyDechargeStatus::Available, ... }).await?;
  }

  // When machine stops (Available status after 60s)
  // Before: Received Status event indicating machine is Available
  if let Err(e) = dephy_balance_payment_sdk::settle(
      &self.solana_rpc_url,
      &self.solana_keypair_path,
      &parsed_payload.user,
      parsed_payload.nonce,
      TRANSFER_AMOUNT,
  ).await {
      tracing::error!("Settle failed: {:?}", e);
  }
  // After: No event sent; balance is settled
  ```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.dephy.io/tutorials/build-a-decharge-machine.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
