> For the complete documentation index, see [llms.txt](https://docs.unix.market/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.unix.market/authentication/signing-method-a-actionhash.md).

# Signing Method A — ActionHash

Method A is used for trading, trading-related account settings, transfer, and withdrawal writes. Method B is used for Agent management and sub-account creation.

Every write request submits business parameters, shared public parameters, and an ECDSA signature. The Gateway/Node rebuilds the same `signing_hash`, recovers the signer from `signature`, then checks nonce, expiration, and account permissions.

Method A has two layers:

1. Business parameters are serialized as canonical JSON and hashed into `actionHash`.
2. `actionHash` is wrapped in an EIP-712 `Agent` struct with `signerAddress`, optional `targetAddress`, `nonce`, and `expiresAfter`.

## Method A Endpoints

| Endpoint                                 | Action                 |
| ---------------------------------------- | ---------------------- |
| `POST /v1/trade/orders`                  | Place order            |
| `POST /v1/trade/orders/modify`           | Modify order           |
| `POST /v1/trade/orders/cancel`           | Cancel order           |
| `POST /v1/trade/orders/cancel-all`       | Cancel all orders      |
| `POST /v1/trade/orders/chase`            | Chase order            |
| `POST /v1/trade/orders/batch`            | Batch place orders     |
| `POST /v1/trade/orders/batch/modify`     | Batch modify orders    |
| `POST /v1/trade/orders/batch/cancel`     | Batch cancel orders    |
| `POST /v1/trade/orders/cancel-all-after` | Cancel All After       |
| `POST /v1/account/position-mode`         | Set position mode      |
| `POST /v1/account/auto-borrow`           | Set auto-borrow        |
| `POST /v1/account/coin-leverage`         | Set coin leverage      |
| `POST /v1/account/leverage`              | Set contract leverage  |
| `POST /v1/account/isolated-margin`       | Adjust isolated margin |
| `POST /v1/account/transfer`              | Internal transfer      |
| `POST /v1/account/withdraw`              | Withdrawal             |

## EIP-712 Domain

Method A and Method B use the same domain:

```json
{
  "name": "UniX",
  "version": "1",
  "chainId": 1,
  "verifyingContract": "0x0000000000000000000000000000000000000000"
}
```

Domain type:

```
EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)
```

{% stepper %}
{% step %}

## Step 1 — Canonical JSON

Build `canonical_json` from the endpoint's business parameters:

1. Start from the request body.
2. Remove all public signing fields: `signer_address`, `target_address`, `nonce`, `expires_after`, and `signature`.
3. Remove optional fields that were not supplied or are `null`.
4. Sort object keys alphabetically. Nested objects use the same rule.
5. Serialize as compact JSON with no spaces, newlines, or indentation. This string is `canonical_json`.
6. UTF-8 encode `canonical_json` when hashing. The resulting bytes are `canonical_json_bytes`.

Example business parameters for a limit order:

```json
{
  "symbol_id": 100001,
  "is_buy": true,
  "order_type": "limit",
  "time_in_force": "gtc",
  "quantity": "1.0",
  "price": "67500.00",
  "position_side": "both",
  "margin_mode": "cross"
}
```

Canonical JSON:

```json
{"is_buy":true,"margin_mode":"cross","order_type":"limit","position_side":"both","price":"67500.00","quantity":"1.0","symbol_id":100001,"time_in_force":"gtc"}
```

{% endstep %}

{% step %}

## Step 2 — Compute `actionHash`

```
actionHash = keccak256(concat(uint8(tag), canonical_json))
```

Use the canonical\_json generated in Step 1, and use the tag value from the table below.

<table><thead><tr><th width="229.2265625">Tag</th><th>Action</th><th>Endpoint</th></tr></thead><tbody><tr><td>7</td><td>PlaceOrder</td><td><code>POST /v1/trade/orders</code></td></tr><tr><td>8</td><td>CancelOrder</td><td><code>POST /v1/trade/orders/cancel</code></td></tr><tr><td>9</td><td>CancelAll</td><td><code>POST /v1/trade/orders/cancel-all</code></td></tr><tr><td>10</td><td>SetPositionMode</td><td><code>POST /v1/account/position-mode</code></td></tr><tr><td>11</td><td>SetLeverage</td><td><code>POST /v1/account/leverage</code></td></tr><tr><td>12</td><td>ModifyOrder</td><td><code>POST /v1/trade/orders/modify</code></td></tr><tr><td>13</td><td>ChaseOrder</td><td><code>POST /v1/trade/orders/chase</code></td></tr><tr><td>15</td><td>UpdateMargin</td><td><code>POST /v1/account/isolated-margin</code></td></tr><tr><td>16</td><td>BatchCancel</td><td><code>POST /v1/trade/orders/batch/cancel</code></td></tr><tr><td>17</td><td>BatchOrder</td><td><code>POST /v1/trade/orders/batch</code></td></tr><tr><td>18</td><td>BatchModify</td><td><code>POST /v1/trade/orders/batch/modify</code></td></tr></tbody></table>
{% endstep %}

{% step %}

## Step 3 — Build The `Agent` Struct

Without `target_address`:

```
Agent(address signerAddress,bytes32 actionHash,uint64 nonce,uint64 expiresAfter)
```

With `target_address`:

```
Agent(address signerAddress,address targetAddress,bytes32 actionHash,uint64 nonce,uint64 expiresAfter)
```

| EIP-712 field   | Type      | Source                                                                         |
| --------------- | --------- | ------------------------------------------------------------------------------ |
| `signerAddress` | `address` | HTTP `signer_address`                                                          |
| `targetAddress` | `address` | HTTP `target_address`; only present when the request includes `target_address` |
| `actionHash`    | `bytes32` | Take from step 2                                                               |
| `nonce`         | `uint64`  | HTTP `nonce`                                                                   |
| `expiresAfter`  | `uint64`  | HTTP `expires_after`                                                           |
| {% endstep %}   |           |                                                                                |

{% step %}

## Step 4 — Compute `signing_hash`

`structHash` follows standard EIP-712 struct encoding:

```
typeHash = keccak256(encodeType)
structHash = keccak256(concat(typeHash, encoded_field_1, encoded_field_2, ...))
signing_hash = keccak256(concat("\x19\x01", domainSeparator, structHash))
```

Without `target_address`:

```
encodeType = "Agent(address signerAddress,bytes32 actionHash,uint64 nonce,uint64 expiresAfter)"
typeHash = keccak256(encodeType)

structHash = keccak256(concat(
  typeHash,
  pad32(signerAddress),
  actionHash,
  pad32(nonce),
  pad32(expiresAfter)
))
```

With `target_address`:

```
encodeType = "Agent(address signerAddress,address targetAddress,bytes32 actionHash,uint64 nonce,uint64 expiresAfter)"
typeHash = keccak256(encodeType)

structHash = keccak256(concat(
  typeHash,
  pad32(signerAddress),
  pad32(targetAddress),
  actionHash,
  pad32(nonce),
  pad32(expiresAfter)
))
```

{% endstep %}
{% endstepper %}

## Example — Place A Limit Order

```python
import json, time, requests
from web3 import Web3
from eth_account import Account
from eth_account.messages import encode_typed_data

action = {
    "symbol_id": 100001,
    "is_buy": True,
    "order_type": "limit",
    "time_in_force": "gtc",
    "quantity": "1.0",
    "price": "67500.00",
    "position_side": "both",
    "margin_mode": "cross",
}

canonical_json = json.dumps(action, sort_keys=True, separators=(",", ":"))
canonical_json_bytes = canonical_json.encode("utf-8")
action_hash = Web3.keccak(bytes([7]) + canonical_json_bytes)

nonce = int(time.time() * 1000)
expires_after = nonce + 600_000

domain = {
    "name": "UniX",
    "version": "1",
    "chainId": 1,
    "verifyingContract": "0x0000000000000000000000000000000000000000",
}
types = {"Agent": [
    {"name": "signerAddress", "type": "address"},
    {"name": "actionHash", "type": "bytes32"},
    {"name": "nonce", "type": "uint64"},
    {"name": "expiresAfter", "type": "uint64"},
]}
message = {
    "signerAddress": "0xYOUR_ADDRESS",
    "actionHash": action_hash,
    "nonce": nonce,
    "expiresAfter": expires_after,
}

signed = Account.sign_message(
    encode_typed_data(domain, types, "Agent", message),
    private_key="0xYOUR_PRIVATE_KEY",
)

resp = requests.post("https://api.unixtrade.pro/v1/trade/orders", json={
    **action,
    "signer_address": "0xYOUR_ADDRESS",
    "nonce": nonce,
    "expires_after": expires_after,
    "signature": {"r": hex(signed.r), "s": hex(signed.s), "v": signed.v},
})
print(resp.json())
```


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## 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.unix.market/authentication/signing-method-a-actionhash.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.
