Account Structure

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

Account ID

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

1

Converting the wallet address to bytes

2

Converting the keccak256 hash of the `brokerId` string to bytes

3

ABI encoding (1) and (2)

4

Converting back to Hex String, the keccak256 hash of the result of (3)

export function getAccountId(userAddress, brokerId) {
  const abicoder = AbiCoder.defaultAbiCoder();
  return keccak256(
    abicoder.encode(
      ['address', 'bytes32'],
      [userAddress, solidityPackedKeccak256(['string'], [brokerId])]
    )
  );
}

Account Registration

Follow the following steps to register an account on Orderly Network:

1

Choose a valid chain/broker to register the account on

List of chains can be found here and supported brokers through Get list of brokers API.

2

Check if the account is already registered

Can be checked via Get Account API.

3

Obtain a registration nonce

Get a nonce from Get Registration Nonce API.

4

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.

{
    "brokerId": "woofi_dex",
    "chainId": 80001,
    "timestamp": 1685973017064,
    "registrationNonce": "194528949540"
}

where:

NameTypeRequiredDescription
brokerIdstringYBroker ID
chainIdintYChain ID of registering chain (within those that are supported by the Network)
timestamptimestampYtimestamp in UNIX milliseconds
registrationNoncestringYValid nonce from Get Registration Nonce
5

Register account

Send all the necessary information via Register account API.

Full example

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-evm.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);
   }
}