# Build a XeCharge Machine

### Messaging <a href="#messaging" id="messaging"></a>

* **Message Definition**: Messages are defined as `XephyDechargeMessage` (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).

  Copy

  ```
  pub enum XephyDechargeMessage {
      Request { 
          to_status: XephyDechargeStatus, 
          reason: XephyDechargeStatusReason, 
          initial_request: EventId, 
          payload: String, 
      },
      Status { 
          status: XephyDechargeStatus, 
          reason: XephyDechargeStatusReason, 
          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., "`xephy-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:

  Copy

  ```
  let filter = Filter::new()
      .kind(EVENT_KIND) // Custom kind 1573
      .since(started_at)
      .custom_tag(SESSION_TAG, ["xephy-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 <a href="#building-controller" id="building-controller"></a>

* **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 <a href="#solana-interaction" id="solana-interaction"></a>

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

Copy

```
// Before: Received Request event to start machine
if xephy_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, &XephyDechargeMessage::Status { ... }).await?;
}
```

* **Server**: Locks and settles funds

  Copy

  ```
  // When machine starts (Working status)
  // Before: Received Status event indicating machine is Working
  if let Err(e) = xephy_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, &XephyDechargeMessage::Request { to_status: XephyDechargeStatus::Available, ... }).await?;
  }

  // When machine stops (Available status after 60s)
  // Before: Received Status event indicating machine is Available
  if let Err(e) = xephy_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
  ```

  <br>
