Skip to content

L1 Methods

L1 methods rely on EIP-712 signatures produced by your private key. Their sole purpose is initial setup — generating the L2 credentials (API key, secret, passphrase) you need for all trading operations going forward.

L1 methods need a client with a signer attached, but no pre-existing API credentials.

use std::str::FromStr;
use openfish_client_sdk::POLYGON;
use openfish_client_sdk::auth::{LocalSigner, Signer};
use openfish_client_sdk::clob::{Client, Config};
let private_key = std::env::var("OPENFISH_PRIVATE_KEY")?;
let signer = LocalSigner::from_str(&private_key)?
.with_chain_id(Some(POLYGON));
// The unauthenticated client can call L1 methods directly
let client = Client::new("https://api.openfish.fun", Config::default())?;

Never commit private keys to version control. Always use environment variables or a secrets manager.


Generates a fresh API key (L2 credentials) for the wallet signer. Only one active API key per wallet — generating a new one invalidates the previous.

let creds = client.create_api_key(&signer, None).await?;
println!("API Key: {}", creds.key());
// Save creds.secret() and creds.passphrase() securely
ParameterTypeDescription
signer&impl SignerWallet signer for EIP-712 signature
nonceOption<u32>Optional custom nonce for deterministic generation

Response (Credentials):

FieldTypeDescription
keyUUIDAPI key identifier
secretSecretStringHMAC signing secret (base64url-encoded)
passphraseSecretStringAuthentication passphrase

Recovers an existing API key using a known nonce. If credentials were previously created with that nonce, the same credentials come back.

let creds = client.derive_api_key(&signer, Some(0)).await?;

This is handy for restoring credentials on a new machine when you remember the nonce used at creation time.

A convenience method that attempts to derive using the default nonce, falling back to creating a new key if none exists. This is the recommended entry point for initial setup.

let creds = client.create_or_derive_api_key(&signer, None).await?;

This is the method authenticate() calls internally when no credentials are supplied.


The simplest path combines L1 key derivation and client initialization in a single call:

let client = Client::new("https://api.openfish.fun", Config::default())?
.authentication_builder(&signer)
.authenticate()
.await?;

Under the hood, authenticate():

  1. Calls create_or_derive_api_key() using L1 EIP-712 authentication
  2. Stores the returned credentials in the client state
  3. Elevates the client from Unauthenticated to Authenticated<Normal>
  4. If the heartbeats feature is enabled, starts automatic heartbeat sending

If you already have credentials (saved from a prior session, for instance), skip the derivation step:

use openfish_client_sdk::auth::Credentials;
let creds = Credentials::new(
"your-api-key-uuid".parse()?,
"your-secret".to_owned(),
"your-passphrase".to_owned(),
);
let client = Client::new("https://api.openfish.fun", Config::default())?
.authentication_builder(&signer)
.credentials(creds)
.authenticate()
.await?;

You cannot supply both credentials and nonce — doing so returns a validation error.


L1 headers are produced by signing a ClobAuth typed struct:

// The struct signed via EIP-712:
struct ClobAuth {
address: Address,
timestamp: String,
nonce: U256,
message: String, // "This message attests that I control the given wallet"
}
// Domain:
Eip712Domain {
name: "ClobAuthDomain",
version: "1",
chain_id: <your_chain_id>,
}

The resulting signature is sent in the OPENFISH_SIGNATURE header, alongside OPENFISH_ADDRESS, OPENFISH_TIMESTAMP, and OPENFISH_NONCE.


When authenticating, you can specify how the funder address is determined:

TypeWhen to UseFunder
EOA (default)Standalone walletYour wallet address directly
ProxyOpenfish proxy walletAuto-derived via CREATE2
GnosisSafeOpenfish Safe walletAuto-derived via CREATE2
use openfish_client_sdk::clob::types::SignatureType;
let client = Client::new("https://api.openfish.fun", Config::default())?
.authentication_builder(&signer)
.signature_type(SignatureType::GnosisSafe)
.authenticate()
.await?;

For Proxy and GnosisSafe types, the SDK automatically derives the funder address using CREATE2 address computation. You can also provide an explicit funder address with .funder(address).


Your private key is wrong or the chain ID does not match.

  • Verify the key is a valid hex string (with or without 0x prefix)
  • Ensure chain ID is set: .with_chain_id(Some(POLYGON))
  • Only Polygon (137) and Amoy testnet (80002) are supported

The nonce was already consumed to create an API key.

  • Use derive_api_key() with the same nonce to recover existing credentials
  • Or use a different nonce with create_api_key()
let recovered = client.derive_api_key(&signer, Some(original_nonce)).await?;

Create new credentials. The old ones are invalidated:

let new_creds = client.create_api_key(&signer, None).await?;

  • Public Methods — Read market data without auth -> public
  • L2 Methods — Place orders with API credentials -> l2
  • Overview — Authentication architecture -> ../overview