> ## Documentation Index
> Fetch the complete documentation index at: https://orderly.network/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Accounts

> Learn how to register user accounts, manage account states, and map wallets to builders on Orderly.

## Account Structure

A wallet owner can register an account on Orderly for each builder that is supported. Each wallet owner could thus have multiple accounts (that are not interlinked with each other) registered with Orderly.

### Account ID

Each account is identified by an account id, which can be calculated by

<Steps>
  <Step title="Converting the wallet address to bytes" />

  <Step title="Converting the keccak256 hash of the `brokerId` string to bytes" />

  <Step title="ABI encoding (1) and (2)" />

  <Step title="Converting back to Hex String, the keccak256 hash of the result of (3)">
    <CodeGroup>
      ```ts TypeScript theme={null}
      export function getAccountId(userAddress, brokerId) {
        const abicoder = AbiCoder.defaultAbiCoder();
        return keccak256(
          abicoder.encode(
            ['address', 'bytes32'],
            [userAddress, solidityPackedKeccak256(['string'], [brokerId])]
          )
        );
      }
      ```
    </CodeGroup>
  </Step>
</Steps>

## Account Registration

To register an account on Orderly:

<Steps>
  <Step title="Choose a valid chain/builder to register the account on">
    List of chains can be found [here](/build-on-omnichain/addresses) and supported builders through [Get list of builders API](/build-on-omnichain/restful-api/public/get-builder-list).
  </Step>

  <Step title="Check if the wallet is already registered or account exists">
    Check if the wallet is already registered via [this](/build-on-omnichain/restful-api/public/check-if-wallet-is-registered) API or if account already exists via [this](/build-on-omnichain/restful-api/public/check-if-account-exists) API.
  </Step>

  <Step title="Obtain a registration nonce">
    Get a nonce from [Get Registration Nonce API](/build-on-omnichain/restful-api/public/get-registration-nonce).

    <Accordion title="Example code">
      <CodeGroup>
        ```Java Java theme={null}
        String baseUrl = "https://testnet-api.orderly.org";

        OkHttpClient client = new OkHttpClient();

        Request nonceReq = new Request.Builder()
              .url(baseUrl + "/v1/registration_nonce")
              .build();
        String nonceRes;
        try (Response response = client.newCall(nonceReq).execute()) {
            nonceRes = response.body().string();
        }
        JSONObject nonceObj = new JSONObject(nonceRes);

        String registrationNonce = nonceObj.getJSONObject("data").getString("registration_nonce");
        ```

        ```py Python theme={null}
        base_url = "https://testnet-api.orderly.org"

        res = requests.get("%s/v1/registration_nonce" % base_url)
        response = json.loads(res.text)
        registration_nonce = response["data"]["registration_nonce"]
        ```

        ```ts TypeScript theme={null}
        const BASE_URL = "https://testnet-api.orderly.org";

        const nonceRes = await fetch(`${BASE_URL}/v1/registration_nonce`);
        const nonceJson = await nonceRes.json();
        const registrationNonce = nonceJson.data.registration_nonce as string;
        ```
      </CodeGroup>
    </Accordion>
  </Step>

  <Step title="Obtain a signature from EIP-712">
    Sign a message from the wallet in the following format using the EIP-712 standard and obtain the signature.

    ```json theme={null}
    {
      "brokerId": "woofi_dex",
      "chainId": 80001,
      "timestamp": 1685973017064,
      "registrationNonce": "194528949540"
    }
    ```

    where:

    | **Name**          | **Type**  | **Required** | **Description**                                                                                          |
    | ----------------- | --------- | ------------ | -------------------------------------------------------------------------------------------------------- |
    | brokerId          | string    | Y            | Builder ID                                                                                               |
    | chainId           | int       | Y            | Chain ID of registering chain (within those that are supported by the Network)                           |
    | timestamp         | timestamp | Y            | timestamp in UNIX milliseconds                                                                           |
    | registrationNonce | string    | Y            | Valid nonce from Get [Registration Nonce](/build-on-omnichain/restful-api/public/get-registration-nonce) |

    <Accordion title="Example code">
      <CodeGroup>
        ```Java Java theme={null}
        JSONObject MESSAGE_TYPES = new JSONObject("""
          {
            "EIP712Domain": [
                {"name": "name", "type": "string"},
                {"name": "version", "type": "string"},
                {"name": "chainId", "type": "uint256"},
                {"name": "verifyingContract", "type": "address"},
            ],
            "Registration": [
                {"name": "brokerId", "type": "string"},
                {"name": "chainId", "type": "uint256"},
                {"name": "timestamp", "type": "uint64"},
                {"name": "registrationNonce", "type": "uint256"},
            ],
          }""");

        JSONObject OFF_CHAIN_DOMAIN = new JSONObject("""
          {
            "name": "Orderly",
            "version": "1",
            "chainId": 421614,
            "verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC",
          }""");

        String brokerId = "woofi_dex";
        int chainId = 421614;

        Dotenv dotenv = Dotenv.load();
        String pk = dotenv.get("PRIVATE_KEY");
        Credentials credentials = Credentials.create(ECKeyPair.create(Hex.decodeHex(pk)));

        JSONObject registerMessage = new JSONObject();
        registerMessage.put("brokerId", brokerId);
        registerMessage.put("chainId", chainId);
        registerMessage.put("timestamp", Instant.now().toEpochMilli());
        registerMessage.put("registrationNonce", registrationNonce);

        JSONObject jsonObject = new JSONObject();
        jsonObject.put("types", RegisterExample.MESSAGE_TYPES);
        jsonObject.put("primaryType", "Registration");
        jsonObject.put("domain", RegisterExample.OFF_CHAIN_DOMAIN);
        jsonObject.put("message", registerMessage);

        Sign.SignatureData signature = Sign.signTypedData(jsonObject.toString(), credentials.getEcKeyPair());
        ```

        ```py Python theme={null}
        MESSAGE_TYPES = {
            "EIP712Domain": [
                {"name": "name", "type": "string"},
                {"name": "version", "type": "string"},
                {"name": "chainId", "type": "uint256"},
                {"name": "verifyingContract", "type": "address"},
            ],
            "Registration": [
                {"name": "brokerId", "type": "string"},
                {"name": "chainId", "type": "uint256"},
                {"name": "timestamp", "type": "uint64"},
                {"name": "registrationNonce", "type": "uint256"},
            ],
        }

        OFF_CHAIN_DOMAIN = {
            "name": "Orderly",
            "version": "1",
            "chainId": 421614,
            "verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC",
        }

        broker_id = "woofi_dex"
        chain_id = 421614

        account: Account = Account.from_key(os.environ.get("PRIVATE_KEY"))

        d = datetime.utcnow()
        epoch = datetime(1970, 1, 1)
        timestamp = math.trunc((d - epoch).total_seconds() * 1_000)

        register_message = {
            "brokerId": broker_id,
            "chainId": chain_id,
            "timestamp": timestamp,
            "registrationNonce": registration_nonce,
        }

        encoded_data = messages.encode_typed_data(
            domain_data=OFF_CHAIN_DOMAIN,
            message_types={"Registration": MESSAGE_TYPES["Registration"]},
            message_data=register_message,
        )
        signed_message = account.sign_message(encoded_data)
        ```

        ```ts TypeScript theme={null}
        const MESSAGE_TYPES = {
          EIP712Domain: [
            { name: "name", type: "string" },
            { name: "version", type: "string" },
            { name: "chainId", type: "uint256" },
            { name: "verifyingContract", type: "address" }
          ],
          Registration: [
            { name: "brokerId", type: "string" },
            { name: "chainId", type: "uint256" },
            { name: "timestamp", type: "uint64" },
            { name: "registrationNonce", type: "uint256" }
          ]
        };

        const OFF_CHAIN_DOMAIN = {
          name: "Orderly",
          version: "1",
          chainId: 421614,
          verifyingContract: "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC"
        };

        const BASE_URL = "https://testnet-api.orderly.org";
        const BROKER_ID = "woofi_dex";
        const CHAIN_ID = 421614;

        const wallet = new ethers.Wallet(process.env.PRIVATE_KEY!);

        const registerMessage = {
          brokerId: BROKER_ID,
          chainId: CHAIN_ID,
          timestamp: Date.now(),
          registrationNonce
        };

        const signature = await wallet.signTypedData(
          OFF_CHAIN_DOMAIN,
          {
            Registration: MESSAGE_TYPES.Registration
          },
          registerMessage
        );
        ```
      </CodeGroup>
    </Accordion>
  </Step>

  <Step title="Register account">
    Send all the necessary information via [Register account API](/build-on-omnichain/restful-api/public/register-account).

    <Accordion title="Example code">
      <CodeGroup>
        ```Java Java theme={null}
        String baseUrl = "https://testnet-api.orderly.org";

        JSONObject jsonBody = new JSONObject();
        jsonBody.put("message", registerMessage);
        jsonBody.put("signature", RegisterExample.signatureToHashString(signature));
        jsonBody.put("userAddress", credentials.getAddress());
        RequestBody body = RequestBody.create(jsonBody.toString(), MediaType.get("application/json"));
        Request registerReq = new Request.Builder()
              .url(baseUrl + "/v1/register_account")
              .post(body)
              .build();
        String registerRes;
        try (Response response = client.newCall(registerReq).execute()) {
            registerRes = response.body().string();
        }
        JSONObject registerObj = new JSONObject(registerRes);

        String orderlyAccountId = registerObj.getJSONObject("data").getString("account_id");
        System.out.println("orderlyAccountId: " + orderlyAccountId);

        // ...

        public static String signatureToHashString(Sign.SignatureData signature) {
            byte[] retval = new byte[65];
            System.arraycopy(signature.getR(), 0, retval, 0, 32);
            System.arraycopy(signature.getS(), 0, retval, 32, 32);
            System.arraycopy(signature.getV(), 0, retval, 64, 1);
            return Numeric.toHexString(retval);
        }
        ```

        ```py Python theme={null}
        base_url = "https://testnet-api.orderly.org"

        res = requests.post(
            "%s/v1/register_account" % base_url,
            headers={"Content-Type": "application/json"},
            json={
                "message": register_message,
                "signature": signed_message.signature.hex(),
                "userAddress": account.address,
            },
        )
        response = json.loads(res.text)

        orderly_account_id = response["data"]["account_id"]
        print("orderly_account_id:", orderly_account_id)
        ```

        ```ts TypeScript theme={null}
        const BASE_URL = "https://testnet-api.orderly.org";

        const registerRes = await fetch(`${BASE_URL}/v1/register_account`, {
          method: "POST",
          headers: {
            "Content-Type": "application/json"
          },
          body: JSON.stringify({
            message: registerMessage,
            signature,
            userAddress: await wallet.getAddress()
          })
        });
        const registerJson = await registerRes.json();
        if (!registerJson.success) {
          throw new Error(registerJson.message);
        }
        const orderlyAccountId = registerJson.data.account_id;
        console.log("orderlyAccountId", orderlyAccountId);
        ```
      </CodeGroup>
    </Accordion>
  </Step>
</Steps>

### Full example

<CodeGroup>
  ```Java Java theme={null}
  import java.time.Instant;

  import org.apache.commons.codec.binary.Hex;
  import org.json.JSONObject;
  import org.web3j.crypto.*;

  import io.github.cdimascio.dotenv.Dotenv;
  import okhttp3.MediaType;
  import okhttp3.OkHttpClient;
  import okhttp3.Request;
  import okhttp3.RequestBody;
  import okhttp3.Response;

  public class RegisterExample {
  public static JSONObject MESSAGE_TYPES = new JSONObject("""
  {
  "EIP712Domain": [
  {"name": "name", "type": "string"},
  {"name": "version", "type": "string"},
  {"name": "chainId", "type": "uint256"},
  {"name": "verifyingContract", "type": "address"},
  ],
  "Registration": [
  {"name": "brokerId", "type": "string"},
  {"name": "chainId", "type": "uint256"},
  {"name": "timestamp", "type": "uint64"},
  {"name": "registrationNonce", "type": "uint256"},
  ],
  }""");

  public static JSONObject OFF_CHAIN_DOMAIN = new JSONObject("""
  {
  "name": "Orderly",
  "version": "1",
  "chainId": 421614,
  "verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC",
  }""");

  public static void main(String[] args) throws Exception {
  String baseUrl = "https://testnet-api.orderly.org";
  String brokerId = "woofi_dex";
  int chainId = 421614;

        Dotenv dotenv = Dotenv.load();
        String pk = dotenv.get("PRIVATE_KEY");
        Credentials credentials = Credentials.create(ECKeyPair.create(Hex.decodeHex(pk)));
        OkHttpClient client = new OkHttpClient();

        Request nonceReq = new Request.Builder()
              .url(baseUrl + "/v1/registration_nonce")
              .build();
        String nonceRes;
        try (Response response = client.newCall(nonceReq).execute()) {
           nonceRes = response.body().string();
        }
        JSONObject nonceObj = new JSONObject(nonceRes);

        String registrationNonce = nonceObj.getJSONObject("data").getString("registration_nonce");

        JSONObject registerMessage = new JSONObject();
        registerMessage.put("brokerId", brokerId);
        registerMessage.put("chainId", chainId);
        registerMessage.put("timestamp", Instant.now().toEpochMilli());
        registerMessage.put("registrationNonce", registrationNonce);

        JSONObject jsonObject = new JSONObject();
        jsonObject.put("types", RegisterExample.MESSAGE_TYPES);
        jsonObject.put("primaryType", "Registration");
        jsonObject.put("domain", RegisterExample.OFF_CHAIN_DOMAIN);
        jsonObject.put("message", registerMessage);

        Sign.SignatureData signature = Sign.signTypedData(jsonObject.toString(), credentials.getEcKeyPair());

        JSONObject jsonBody = new JSONObject();
        jsonBody.put("message", registerMessage);
        jsonBody.put("signature", RegisterExample.signatureToHashString(signature));
        jsonBody.put("userAddress", credentials.getAddress());
        RequestBody body = RequestBody.create(jsonBody.toString(), MediaType.get("application/json"));
        Request registerReq = new Request.Builder()
              .url(baseUrl + "/v1/register_account")
              .post(body)
              .build();
        String registerRes;
        try (Response response = client.newCall(registerReq).execute()) {
           registerRes = response.body().string();
        }
        JSONObject registerObj = new JSONObject(registerRes);

        String orderlyAccountId = registerObj.getJSONObject("data").getString("account_id");
        System.out.println("orderlyAccountId: " + orderlyAccountId);

  }

  public static String signatureToHashString(Sign.SignatureData signature) {
  byte[] retval = new byte[65];
  System.arraycopy(signature.getR(), 0, retval, 0, 32);
  System.arraycopy(signature.getS(), 0, retval, 32, 32);
  System.arraycopy(signature.getV(), 0, retval, 64, 1);
  return Numeric.toHexString(retval);
  }
  }

  ```

  ```py Python theme={null}
  from datetime import datetime
  import json
  import math
  import os
  import requests

  from eth_account import Account, messages

  MESSAGE_TYPES = {
      "EIP712Domain": [
          {"name": "name", "type": "string"},
          {"name": "version", "type": "string"},
          {"name": "chainId", "type": "uint256"},
          {"name": "verifyingContract", "type": "address"},
      ],
      "Registration": [
          {"name": "brokerId", "type": "string"},
          {"name": "chainId", "type": "uint256"},
          {"name": "timestamp", "type": "uint64"},
          {"name": "registrationNonce", "type": "uint256"},
      ],
  }

  OFF_CHAIN_DOMAIN = {
      "name": "Orderly",
      "version": "1",
      "chainId": 421614,
      "verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC",
  }

  base_url = "https://testnet-api.orderly.org"
  broker_id = "woofi_dex"
  chain_id = 421614

  account: Account = Account.from_key(os.environ.get("PRIVATE_KEY"))

  res = requests.get("%s/v1/registration_nonce" % base_url)
  response = json.loads(res.text)
  registration_nonce = response["data"]["registration_nonce"]

  d = datetime.utcnow()
  epoch = datetime(1970, 1, 1)
  timestamp = math.trunc((d - epoch).total_seconds() * 1_000)

  register_message = {
      "brokerId": broker_id,
      "chainId": chain_id,
      "timestamp": timestamp,
      "registrationNonce": registration_nonce,
  }

  encoded_data = messages.encode_typed_data(
      domain_data=OFF_CHAIN_DOMAIN,
      message_types={"Registration": MESSAGE_TYPES["Registration"]},
      message_data=register_message,
  )
  signed_message = account.sign_message(encoded_data)

  res = requests.post(
      "%s/v1/register_account" % base_url,
      headers={"Content-Type": "application/json"},
      json={
          "message": register_message,
          "signature": signed_message.signature.hex(),
          "userAddress": account.address,
      },
  )
  response = json.loads(res.text)

  orderly_account_id = response["data"]["account_id"]
  print("orderly_account_id:", orderly_account_id)
  ```

  ```ts TypeScript theme={null}
  import { config } from "dotenv";
  import { ethers } from "ethers";

  const MESSAGE_TYPES = {
    EIP712Domain: [
      { name: "name", type: "string" },
      { name: "version", type: "string" },
      { name: "chainId", type: "uint256" },
      { name: "verifyingContract", type: "address" }
    ],
    Registration: [
      { name: "brokerId", type: "string" },
      { name: "chainId", type: "uint256" },
      { name: "timestamp", type: "uint64" },
      { name: "registrationNonce", type: "uint256" }
    ]
  };

  const OFF_CHAIN_DOMAIN = {
    name: "Orderly",
    version: "1",
    chainId: 421614,
    verifyingContract: "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC"
  };

  const BASE_URL = "https://testnet-api.orderly.org";
  const BROKER_ID = "woofi_dex";
  const CHAIN_ID = 421614;

  config();

  async function registerAccount(): Promise<void> {
    const wallet = new ethers.Wallet(process.env.PRIVATE_KEY!);

    const nonceRes = await fetch(`${BASE_URL}/v1/registration_nonce`);
    const nonceJson = await nonceRes.json();
    const registrationNonce = nonceJson.data.registration_nonce as string;

    const registerMessage = {
      brokerId: BROKER_ID,
      chainId: CHAIN_ID,
      timestamp: Date.now(),
      registrationNonce
    };

    const signature = await wallet.signTypedData(
      OFF_CHAIN_DOMAIN,
      {
        Registration: MESSAGE_TYPES.Registration
      },
      registerMessage
    );

    const registerRes = await fetch(`${BASE_URL}/v1/register_account`, {
      method: "POST",
      headers: {
        "Content-Type": "application/json"
      },
      body: JSON.stringify({
        message: registerMessage,
        signature,
        userAddress: await wallet.getAddress()
      })
    });
    const registerJson = await registerRes.json();
    if (!registerJson.success) {
      throw new Error(registerJson.message);
    }
    const orderlyAccountId = registerJson.data.account_id;
    console.log("orderlyAccountId", orderlyAccountId);
  }

  registerAccount();
  ```
</CodeGroup>
