# Build a Gacha Machine

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

* **Message Definition**: Same as DeCharge (`XephyDechargeMessage`), but typically only processes `Request` to `Working` and `Status` back to `Available`.
* **Mention Tag (Machine Pubkey)**: Identifies the GaCha machine (e.g., `PublicKey::parse("gacha_machine_pubkey")`).
* **Session Tag**: Scopes events (e.g., `"xephy-gacha-controller"`).
* **Publishing and Subscription**

  Copy

  ```
  let filter = Filter::new()
      .kind(EVENT_KIND)
      .since(Timestamp::now())
      .custom_tag(SESSION_TAG, ["gacha_session"])
      .custom_tag(MENTION_TAG, [gacha_machine_pubkey.to_hex()]);
  let sub_id = relay_client.subscribe(Timestamp::now(), [gacha_machine_pubkey]).await?;
  ```

  * **Sender**: User requesting a dispense.
  * **Receiver**: Gacha node controller.
* **Message Handling**: Processes a single transaction and reverts state.

### Building Controller <a href="#building-controller" id="building-controller"></a>

* **Node**: Unlike DeCharge, Gacha’s simpler transaction (one payment, one action) doesn’t require a separate server. The node handles both state and payment in a single step using `pay`, making it lightweight.

### Solana Interaction <a href="#solana-interaction" id="solana-interaction"></a>

Uses `pay` for a one-time transaction:

Copy

```
// Before: Received Request event to start machine
if let Err(e) = xephy_balance_payment_sdk::pay(
    &self.solana_rpc_url,
    &self.solana_keypair_path,
    parsed_payload.namespace_id,
    &parsed_payload.user,
    PAY_AMOUNT,
    &parsed_payload.recover_info,
)
.await
{
    tracing::error!("Failed to pay, error: {:?} skip event: {:?}", e, event);
    return Ok(());
};

self.client
    .send_event(mention, &XephyGachaMessage::Status {
        status: *to_status,
        reason: *reason,
        initial_request: event.id,
        payload: serde_json::to_string(&XephyGachaMessageStatusPayload {
            namespace_id: parsed_payload.namespace_id,
            user: parsed_payload.user.clone(),
            nonce: parsed_payload.nonce,
            recover_info: parsed_payload.recover_info.clone(),
        })?,
    })
    .await?;
```
