# Transaction flow (Deposits & Withdrawals)

This doc explains how **transaction status fields** are used in HollaEx.

### Concepts and balances

HollaEx effectively treats funds in a few “buckets”:

* **Available balance**: spendable/tradable/withdrawable (depending on your rules).
* **Locked amount**: reserved so the user can’t double-spend while a withdrawal is pending.
* **Final balance deduction**: when the withdrawal is confirmed/settled.

Withdrawals typically move funds like:

**Available → Locked → Deducted (final)**

***

### Deposits: what fields do

#### Deposit `status`

* `status: false` → **does nothing** (no balance change).
* `status: true` → **adds balance** (credits user funds).

#### Other deposit fields

Other fields may exist on the deposit record, but **do not trigger special behavior by default**. They are still useful as:

* metadata for audits,
* integration-specific routing,
* custom flow extensions in a plugin.

***

### Withdrawals: lifecycle and field meanings

A withdrawal record contains flags like:

```json
{
  "type": "withdrawal",
  "status": false,
  "dismissed": false,
  "rejected": false,
  "processing": false,
  "waiting": false,
  "onhold": false
}
```

#### Core behavior at creation time

When a withdrawal is **created**:

* It **deducts from available** immediately
* and **locks the amount** (reserves it).

So the user can’t use the same funds again while the withdrawal is pending.

***

### What each withdrawal field means

#### `status` (finalization switch)

* `status: false` → withdrawal is **not finalized**.
* `status: true` → withdrawal is **finalized** and the system **deducts the balance** (i.e., completes the accounting outcome).

In other words:

* Creation locks funds.
* `status=true` completes the “final deduction” step.

#### `rejected` and `dismissed` (unlock paths when not finalized)

If `status: false` and **either**:

* `rejected: true`, or
* `dismissed: true`

then the system should **unlock** the locked funds (release them back to available), because the withdrawal won’t be finalized.

“Updating `rejected` or `dismissed` to true when `status` is false will unlock the locked balance.”

`dismissed` is generally used when admin dismissed the transaction and `rejected` is used when the system rejects the transaction.

#### `processing` and `waiting` (worker/cron safety flags)

* They **do not change balances**
* They exist to **prevent race conditions** (e.g., cron job double-picking the same withdrawal)
* They can be used however you want as “in-progress” markers.

Typical intention:

* `processing: true` → a worker has claimed it and is actively handling it now.
* `waiting: true` → it’s queued/backing off/awaiting external confirmation (or simply “don’t pick me again yet”).

#### `onhold` (manual pause / admin gate)

* Does not directly change balances by itself.
* Used to prevent automated processing until an admin clears it.
* Once you clean `onhold: false` the transaction would go back to its original course.
* Practically: if `onhold: true`, the cron/plugin should skip it.
* Note that when you set `onhold: false` you should not update other flags in the same request. Setting `onhold` to `true` and `false` should be done independently.

***

### Withdrawal state transitions

#### 1) New withdrawal (eligible for processing)

* Funds: **Available decreases**, **Locked increases**
* Flags commonly look like:
  * `status=false`
  * `processing=false`
  * `waiting=false`
  * `onhold=false`
  * `rejected=false`
  * `dismissed=false`

#### 2) Claimed by cron/plugin

* Funds: unchanged
* Flags:
  * set `processing=true` (or `waiting=true` depending on your approach)

#### 3) Success path (completed)

* Set `status=true`
* Funds: **final deduction happens** (the withdrawal is accounted as completed)

#### 4) Failure path (not completed, funds returned)

* Keep `status=false`
* Set `rejected=true` **or** `dismissed=true`
* Funds: **locked amount is unlocked** back to available

***

### Retry behavior: “Resend withdrawal”

When you click **Resend withdrawal**:

* it **sets all values to `false`**,
* so the withdrawal returns to the “fresh / eligible” state,
* and it will **re-enter the plugin cron processing flow**.

So it effectively:

* clears `processing`, `waiting`, `onhold`, `rejected`, `dismissed`, and keeps `status=false`,
* making it pickable again by the worker.

This is a “reset and retry” mechanism.

***

### Admin console: validation + retry logic

Even without code-level specifics, the admin console typically implements guardrails like:

#### Validation (what should be checked before processing)

Common checks before a worker/admin action finalizes:

* Withdrawal still exists and is consistent (amount, asset, destination).
* Sufficient locked amount exists (i.e., it was reserved).
* Not already finalized (`status !== true`).
* Not blocked by flags (e.g., `onhold === true`, or `processing === true` depending on the action).

#### Retry logic (what “resend” achieves)

* Clears flags that prevent picking (`processing/waiting/onhold`)
* Leaves it in a clean `status=false` state for cron/plugin to attempt again

#### Preventing double processing (race condition control)

The reason `processing`/`waiting` exist is to ensure:

* two cron runs don’t send the same withdrawal twice,
* an admin action doesn’t collide with a worker action.

***

### Practical “rules of thumb”

* **Deposits:** only `status=true` credits balance. `status=false` does nothing.
* **Withdrawals:**
  * Creation: **locks** funds (reduces available, increases locked).
  * `status=true`: **finalizes** and deducts balance.
  * `status=false` + (`rejected=true` or `dismissed=true`): **unlocks** funds.
  * `processing/waiting`: **no balance effects**, just worker flags.
  * `onhold`: **pause flag** (usually skip in cron).
* **Resend withdrawal:** sets everything to `false` → makes it eligible for cron/plugin again.
