# Signing Method B — Typed Data

Four endpoints use Method B instead of Method A. These are the high-sensitivity account-relationship operations where having the wallet display **human-readable** fields at signing time matters more than reusing a generic envelope.

| Endpoint                         | Action             |
| -------------------------------- | ------------------ |
| `POST /v1/account/approve-agent` | Approve Agent Key  |
| `POST /v1/account/revoke-agent`  | Revoke Agent Key   |
| `POST /v1/account/renew-agent`   | Renew Agent Key    |
| `POST /v1/account/create-sub`    | Create sub-account |

All other write endpoints use [Signing Method A](broken://pages/d2130f996d95713bdd27db8c056dc20bfb86db5d).

{% hint style="info" %}
**Design motivation:** Account-relationship operations (Agent Key approval/revocation, sub-account management, etc.) are high-security-sensitive operations. With Typed Data direct signing, the wallet signing prompt directly displays business fields (e.g. `agentAddress`, `validDays`), so users don't have to trust the opaque `actionHash` hash value — significantly improving signature security and readability.
{% endhint %}

## Difference From Method A

| Aspect                 | Method A                                               | Method B                                        |
| ---------------------- | ------------------------------------------------------ | ----------------------------------------------- |
| Outer struct           | `Agent(sender, actionHash, nonce, expiresAfter)`       | Operation-specific struct (e.g. `ApproveAgent`) |
| `actionHash` step      | Required                                               | **Not used**                                    |
| Action tag             | Required (single byte prefix)                          | **Not used**                                    |
| Wallet display         | Opaque hash                                            | Readable business fields                        |
| EIP-712 Domain         | Same                                                   | Same                                            |
| `signing_hash` formula | `keccak256("\x19\x01" ‖ domainSeparator ‖ structHash)` | Identical                                       |
| `tx_hash`              | `= signing_hash`                                       | `= signing_hash`                                |

The Domain is the same as Method A:

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

## Typed Data Definitions

### ApproveAgent

```
ApproveAgent(address sender, address agentAddress, address authorizedAddress,
             uint32 validDays, string label, uint64 nonce, uint64 expiresAfter)
```

| Field               | Type      | From request                            |
| ------------------- | --------- | --------------------------------------- |
| `sender`            | `address` | Signer's own address (= HTTP `address`) |
| `agentAddress`      | `address` | `agent_address`                         |
| `authorizedAddress` | `address` | `authorized_address`                    |
| `validDays`         | `uint32`  | `valid_days`                            |
| `label`             | `string`  | `label`                                 |
| `nonce`             | `uint64`  | `nonce`                                 |
| `expiresAfter`      | `uint64`  | `expires_after`                         |

### RevokeAgent

```
RevokeAgent(address sender, address agentAddress, uint64 nonce, uint64 expiresAfter)
```

### RenewAgent

```
RenewAgent(address sender, address agentAddress, uint32 validDays, uint64 nonce, uint64 expiresAfter)
```

### CreateSubAccount

```
CreateSubAccount(address sender, string label, uint64 nonce, uint64 expiresAfter)
```

## EIP-712 Type Encoding

| Solidity type | Encoded as                                                  |
| ------------- | ----------------------------------------------------------- |
| `address`     | 20 bytes right-aligned in a 32-byte slot (left-zero-padded) |
| `uint32`      | 4 bytes big-endian, right-aligned in a 32-byte slot         |
| `uint64`      | 8 bytes big-endian, right-aligned in a 32-byte slot         |
| `string`      | `keccak256(utf8_bytes(value))` (32 bytes)                   |

### `structHash` Formula

```
typeHash      = keccak256(encodeType_string)
structHash    = keccak256(typeHash ‖ encoded_field_1 ‖ encoded_field_2 ‖ …)
signing_hash  = keccak256("\x19\x01" ‖ domainSeparator ‖ structHash)
```

Field order **must match** the declaration order in the `encodeType` string.

## Full Example — Approve Agent Key

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

domain = {"name": "UniX", "version": "1", "chainId": 1}
types = {"ApproveAgent": [
    {"name": "sender", "type": "address"},
    {"name": "agentAddress", "type": "address"},
    {"name": "authorizedAddress", "type": "address"},
    {"name": "validDays", "type": "uint32"},
    {"name": "label", "type": "string"},
    {"name": "nonce", "type": "uint64"},
    {"name": "expiresAfter", "type": "uint64"},
]}

nonce = int(time.time() * 1000)
message = {
    "sender": "0xYOUR_ADDRESS",
    "agentAddress": "0xAGENT_ADDRESS",
    "authorizedAddress": "0xYOUR_ADDRESS",
    "validDays": 30,
    "label": "mm-bot-prod",
    "nonce": nonce,
    "expiresAfter": nonce + 600_000,
}
signed = Account.sign_message(
    encode_typed_data(domain, types, "ApproveAgent", message),
    private_key="0xYOUR_PRIVATE_KEY",
)

resp = requests.post("http://10.34.8.77:8481/v1/account/approve-agent", json={
    "agent_address": "0xAGENT_ADDRESS",
    "authorized_address": "0xYOUR_ADDRESS",
    "valid_days": 30,
    "label": "mm-bot-prod",
    "address": "0xYOUR_ADDRESS",
    "nonce": nonce,
    "expires_after": nonce + 600_000,
    "signature": {"r": hex(signed.r), "s": hex(signed.s), "v": signed.v},
})
print(resp.json())
```

{% hint style="info" %}
Method B requires **no** canonical JSON serialization, **no** action tag, **no** `actionHash`. Construct the typed data, sign, send.
{% endhint %}


---

# 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.lynxtrade.world/authentication/signing-method-b-typed-data.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.
