API Reference

1. What

EIP712 signing is the system's primary method for authentication, it allows a user to sign a message with their private key, providing a signature, this signature is then validated by the off-chain systems AND smart contracts against the message to make sure the signer is either the account being interacted with or authorised to interact with the specified account. These signatures in combination with the on-chain smart contracts allow for a self-custodial experience for users and make it impossible for other users and the off-chain system to touch a user's funds without authorisation (so long as a user's private key is not compromised).

For more details on EIP712 signing and its safety read the following: https://eips.ethereum.org/EIPS/eip-712

Domain Seperator

The domain separator must be attached to every EIP712 message, the information in this struct is static for each chain. For the rysk EIP712Domain:

  • The name parameter is "rysk"
  • The version is "0.0.0",
  • The chainId is 421614 for ARBITRUM SEPOLIA TESTNET and 42161 for ARBITRUM Mainnet
  • The verifyingContract This is the OrderDispatch contract. (0x6644D5B09EBae015fE4e3a87Eff1A07d33558E59)
{
  EIP712Domain: [
    {
      name: 'name', // rysk
      type: 'string',
    },
    {
      name: 'version', // 0.0.0
      type: 'string',
    },
    {
      name: 'chainId', // 421614 - ARBITRUM testnet || 42161 - ARBITRUM mainnet
      type: 'uint256',
    },
    {
      name: 'verifyingContract', // OrderDispatch 0x6644D5B09EBae015fE4e3a87Eff1A07d33558E59
      type: 'address',
    },
  ],
}

EIP712 Types (Messages)

The messages that can be signed are provided below, descriptions of each element are provided.

{
  LoginMessage: [ // used to authenticate a login by the user
    {
      name: 'account', // address of the account logging in
      type: 'address',
    },
    {
      name: 'message', // e.g. "I want to log into rysk.finance"
      type: 'string',
    },
    {
      name: 'timestamp', // current timestamp in ms (will be rejected if older than 10s, easiest to send in a time in the future)
      type: 'uint64',
    },
  ],
  Withdraw: [ // used as authentication for a withdrawal request by the account
		{
			name: "account", // address of the account withdrawing
			type: "address",
		},
		{
			name: "subAccountId", // subAccountId of the subaccount to withdraw from
			type: "uint8",
		},
		{
			name: "asset", // address of the asset in the margin account to withdraw
			type: "address",
		},
		{
			name: "quantity", // quantity to withdraw in e18 format
			type: "uint128",
		},
		{
			name: "nonce", // unique nonce for the transaction, nonces are tracked globally across all actions and cannot be repeated
			type: "uint64",
		},
	],
  Order: [ // used as authentication for order creation by the subaccount
    {
			name: "account",  // address of the account making the order
			type: "address",
		},
		{
			name: "subAccountId", // subAccountId of the subaccount to execute the order on
			type: "uint8",
		},
		{
			name: "productId", // the productId of the product to trade
			type: "uint32",
		},
		{
			name: "isBuy", // whether the account is buying or selling
			type: "bool",
		},
		{
			name: "orderType", // e.g. limit, market, etc.
			type: "uint8",
		},
		{
			name: "timeInForce", // e.g. GTC, IOC, FOK
			type: "uint8",
		},
		{
			name: "expiration", // the expiration of the order, after this time the order is dropped from the orderbook
			type: "uint64",
		},
		{
			name: "price", // the price to execute the trade at represented in the quote asset in e18 format (the trade will be executed at this price or better)
			type: "uint128",
		},
		{
			name: "quantity", // the number of contracts to purchase at the given price
			type: "uint128",
		},
		{
			name: "nonce", // unique nonce for the transaction, nonces are tracked globally across all actions and cannot be repeated
			type: "uint64",
		},
  ],
 ApproveSigner: [ // used as authentication for approving a signer to sign transactions on behalf of your subaccount
		{
			name: "account",  // address of the account to approve the approvedSigner on
			type: "address",
		},
		{
			name: "subAccountId", // subAccountId of the subaccount to approve the signer on
			Type: "uint8",
		},
		{
			name: "approvedSigner", // the address of the account that will be an approvedSigner on the given subaccount
			type: "address",
		},
		{
			name: "isApproved", // boolean for whether to approve the approvedSigner on the account or not
			type: "bool",
		},
		{
			name: "nonce", // unique nonce for the transaction, nonces are tracked globally across all actions and cannot be repeated
			type: "uint64",
		},
	],
  CancelOrder: [ // used as authentication for cancelling an order
		{
			name: "account",  // address of the account to cancel the order on
			type: "address",
		},
		{
			name: "subAccountId", // subAccountId of the subaccount to cancel the order on
			type: "uint8",
		},
		{
			name: "productId", // the productId of the product to cancel the  order on (retrievable from list products)
			type: "uint32",
		},
		{
			name: "orderId", // id of the order to cancel
			type: "string",
		},
	],
   CancelOrders: [ // used as authentication for cancelling all orders
		{
			name: "account",  // address of the account to cancel all orders on
			type: "address",
		},
		{
			name: "subAccountId", // subAccountId of the subaccount to cancel all orders on
			type: "uint8",
		},
		{
			name: "productId", // the productId of the product to cancel all orders on (retrievable from list products)
			type: "uint32",
		},
  ]
 SignedAuthentication: [
		{
			name: "account",  // address to view
			type: "address",
		},
		{
			name: "subAccountId", // subAccountId of the subaccount to view
			type: "uint8",
		],
	},
}
var EIP712_TYPES = &apitypes.Types{
	"EIP712Domain": {
		{
			Name: "name", // Ciao
			Type: "string",
		},
		{
			Name: "version", // "0.0.0"
			Type: "string",
		},
		{
			Name: "chainId", // 168587773 - Blast testnet | 
			Type: "uint256",
		},
		{
			Name: "verifyingContract", // ORDER_DISPATCH_ADDRESS
			Type: "address",
		},
	},
	"LoginMessage": {
		{
			Name: "account",
			Type: "address",
		},
		{
			Name: "message",
			Type: "string",
		},
		{
			Name: "timestamp",
			Type: "uint64",
		},
	},
	"Order": {
		{
			Name: "account",
			Type: "address",
		},
		{
			Name: "subAccountId",
			Type: "uint8",
		},
		{
			Name: "productId",
			Type: "uint32",
		},
		{
			Name: "isBuy",
			Type: "bool",
		},
		{
			Name: "orderType",
			Type: "uint8",
		},
		{
			Name: "timeInForce",
			Type: "uint8",
		},
		{
			Name: "expiration",
			Type: "uint64",
		},
		{
			Name: "price",
			Type: "uint128",
		},
		{
			Name: "quantity",
			Type: "uint128",
		},
		{
			Name: "nonce",
			Type: "uint64",
		},
	},
	"CancelOrders": {
		{
			Name: "account",
			Type: "address",
		},
		{
			Name: "subAccountId",
			Type: "uint8",
		},
		{
			Name: "productId",
			Type: "uint32",
		},
	},
	"CancelOrder": {
		{
			Name: "account",
			Type: "address",
		},
		{
			Name: "subAccountId",
			Type: "uint8",
		},
		{
			Name: "productId",
			Type: "uint32",
		},
		{
			Name: "orderId",
			Type: "string",
		},
	},
	"ApproveSigner": {
		{
			Name: "account",
			Type: "address",
		},
		{
			Name: "subAccountId",
			Type: "uint8",
		},
		{
			Name: "approvedSigner",
			Type: "address",
		},
		{
			Name: "isApproved",
			Type: "bool",
		},
		{
			Name: "nonce",
			Type: "uint64",
		},
	},
	"Deposit": {
		{
			Name: "account",
			Type: "address",
		},
		{
			Name: "subAccountId",
			Type: "uint8",
		},
		{
			Name: "asset",
			Type: "address",
		},
		{
			Name: "quantity",
			Type: "uint256",
		},
		{
			Name: "nonce",
			Type: "uint64",
		},
	},
	"Withdraw": {
		{
			Name: "account",
			Type: "address",
		},
		{
			Name: "subAccountId",
			Type: "uint8",
		},
		{
			Name: "asset",
			Type: "address",
		},
		{
			Name: "quantity",
			Type: "uint128",
		},
		{
			Name: "nonce",
			Type: "uint64",
		},
	},
	"SignedAuthentication": {
		{
			Name: "account",
			Type: "address",
		},
		{
			Name: "subAccountId",
			Type: "uint8",
		},
	},

Signing for Authenticating HTTP requests - Tutorial

This tutorial will use Python as an example for constructing signatures for action authentication but the process is mostly the same for all languages, so just follow the comments, examples for other languages will be added upon request. The example goes through creating an order.

Required Python packages:

import eth_account # used for creating an account and signing the message using a stored private key
from eip712_structs import Address, EIP712Struct, String, Uint, make_domain # used for message construction https://pypi.org/project/eip712-structs/
from eth_account.messages import encode_structured_data # used for message construction
import time
  1. Defining the EIP712 type, EIP712 domain and private key

    # these data types can be seen in the type descriptions at the bottom of this page, 
    # make sure the datatypes are identical
    class Order(EIP712Struct):
      account      = Address()
      subAccountId = Uint(8)
      productId    = Uint(32)
      isBuy        = Boolean()
      orderType    = Uint(8)
      timeInForce  = Uint(8)
      expiration   = Uint(64)
      price        = Uint(128)
      quantity     = Uint(128)
      nonce        = Uint(64)
      
    def create_testnet_domain(): 
      # this is static for each chain so can be defined once for all time, always
      # the make_domain function from the eip712_structs constructs and returns the domain seperator
      return make_domain(name="rysk", version="0.0.0", chainId=421614, verifyingContract="0xInsertAddressHere")
    
    def create_wallet(private_key: str): 
      # this is the wallet used for signing, and must be derived from a private key,
      # typically presented in a .env
      return eth_account.Account.from_key(private_key)
    
  2. Generating the signature used for authentication takes two steps, building the hashed message and signing that message

def generate_signature(public_key: str, private_key: str):
  wallet = create_wallet(private_key)
  
  # we first want to build the message hash, we start by constructing the Order EIP712 struct we
  # made earlier passing in our desired data for the order in the struct, this is hardcoded here 
  # for ease of reading
  
    order = Order(account     = public_key, # make sure this address is checksumed
                  subAccountId= 217, # the subaccount you want to work with
                  productId   = 1002, # the numeric product id to trade, can be retrieved from list products
                  isBuy       = true, # buying
                  orderType   = 0, # Limit order
                  timeInForce = 0, # GTC
                  expiration  = int(time.time() * 1000) + 86400000, # order expires tomorrow
                  price       = 3000000000000000000000, # $3000 per contract or better
                  quantity    = 5000000000000000000, # 5 contracts
                  nonce       = 761398176 # we recommend using the current time in microseconds for this, a number less than uint64 that has never been used for any other action
                 )
    
  # we construct the order message using the to_message function which the Order class we made earlier
  # inherited from the EIP712Struct class, using the domain seperator as a parameter
  
    order_message = order.to_message(create_testnet_domain())
    
  # we hash/encode the data using eth_account.messages encode_structured_data, ready for signing
  
    signable_order_message =  encode_structured_data(order_message)
    
  # we sign the message 
  
    signed_order = wallet.sign_message(signable_order_message)
  
  # we extract the signature and return it (this signature is added to the body of the request,
  # the order details passed in the body of the request must be the same values you passed in to
  # generate the signature, so the hardcoded values we provided in this example
  # for the datatype, just pass in whatever the rest http request doc expects and the datatypes
  # will get properly converted for signature validation on the other side
  
    signature = signed_order.signature.hex()
    
  # this is the signature in hex format that should be added to your request and acts as order authentication
    return signature