Client
The KaizenClient is the main entry point for interacting with Kaizen Core.
Creating a Client
import { createClient, KaizenClient } from "@kaizen-core/sdk";
const client = createClient({
rpcUrl: "http://localhost:8546", // Required: JSON-RPC endpoint
wsUrl: "ws://localhost:8546/ws", // Optional: WebSocket endpoint
chainId: 1, // Optional: Chain ID (default: 1)
});:::tip Unified Proxy Endpoint
When using Docker Compose, connect to the proxy at localhost:8546 for both RPC and WebSocket.
The proxy automatically routes:
- Writes (
sendTransaction) → write-node - Reads (
getAccount,simulateTransaction, etc.) → read-node-aggressive - WebSocket subscriptions → read-node-aggressive :::
Signer Management
Connecting as Owner
When you connect directly with your own private key, sender and signer are the same:
// Connect as owner (sender == signer)
client.connectSigner(
"0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"
);
// Check connection
if (client.hasSigner) {
console.log(`Connected as: ${client.signerAddress}`);
console.log(`Account: ${client.accountAddress}`); // Same as signerAddress
}Connecting as API Wallet
When acting on behalf of an owner via an API wallet, the signer (API wallet) differs from the effective account (owner):
Important: API wallets have restricted permissions. They can only perform trading operations (RFQ submit/settle) and API wallet management. Transfer and Withdraw operations are not allowed for API wallets - these must be performed by the owner directly.
// Connect as API wallet acting on behalf of owner
client.connectAsApiWallet(
"0x...", // API wallet private key
"0xOwnerAddress..." // Owner address
);
// Check addresses
console.log(`Signer: ${client.signerAddress}`); // API wallet address
console.log(`Account: ${client.accountAddress}`); // Owner address
console.log(`Is API Wallet: ${client.isApiWallet}`); // true
// All "getMy..." methods now return owner's data
const balance = await client.getMyBalance(); // Owner's balance
const theses = await client.getMyTheses(); // Owner's theses
// ⚠️ API wallets cannot transfer or withdraw
// await client.transfer(...); // Will fail!
// await client.withdraw(...); // Will fail!
// ✅ API wallets can perform trading operations
await client.submitRfq(...); // OK
await client.settleRfq(...); // OKDisconnecting
client.disconnectSigner();Account Methods
Get Account Information
// Get any account
const account = await client.getAccount("0x...");
console.log({
address: account.address,
balance: account.balance,
apiWallets: account.apiWallets,
});
// Get connected signer's account
const myAccount = await client.getMyAccount();Get Balance
import { formatUSDC } from "@kaizen-core/sdk";
const balance = await client.getBalance("0x...");
console.log(`Balance: ${formatUSDC(balance.balance)} USDC`);
// Or get your balance
const myBalance = await client.getMyBalance();Thesis Methods
Get Thesis by ID
const thesis = await client.getThesis(1n);
if (thesis) {
console.log({
id: thesis.id,
user: thesis.user,
solver: thesis.solver,
status: thesis.status,
betAmount: thesis.betAmount,
payout: thesis.payout,
});
}Get Theses by User
// Get all theses for a user
const theses = await client.getThesesByUser(
"0x...",
100, // limit
0 // offset
);
// Get my theses (requires connected signer)
const myTheses = await client.getMyTheses();Get Theses by Solver
const solverTheses = await client.getThesesBySolver("0x...");Oracle Methods
Get Current Price
const BTC_ADDRESS = "0x0000000000000000000000000000000000000001";
const USDC_ADDRESS = "0x0000000000000000000000000000000000000002";
const price = await client.getOraclePrice(BTC_ADDRESS, USDC_ADDRESS);
console.log({
price: price.price,
timestamp: new Date(price.timestamp),
});Get Price History
const history = await client.getOraclePriceHistory(
BTC_ADDRESS,
USDC_ADDRESS,
Date.now() - 3600000, // from: 1 hour ago
Date.now(), // to: now
100 // limit
);
for (const point of history.prices) {
console.log(`${new Date(point.timestamp)}: ${point.price}`);
}Block Methods
Get Block by Height
const block = await client.getBlock(100n);
if (block) {
console.log({
height: block.height,
timestamp: block.timestamp,
stateRoot: block.stateRoot,
txCount: block.txCount,
});
}Get Current Block Height
const height = await client.getBlockHeight();
console.log(`Current height: ${height}`);Get State Root
const stateRoot = await client.getStateRoot();
console.log(`State root: ${stateRoot}`);Transaction Methods
Sign and Send Transaction
import { transfer, parseUSDC } from "@kaizen-core/sdk";
// Create payload and send in one call
const receipt = await client.sendTransaction(
transfer("0x...", parseUSDC("100.00")),
{
waitForConfirmation: true, // Wait for confirmation
confirmationTimeout: 30000, // Timeout in ms
}
);
console.log(`Transaction ${receipt.hash}: ${receipt.status}`);Manual Sign and Send
// Step 1: Sign transaction (timestamp is used for replay protection)
const signedTx = await client.signTransaction(
transfer("0x...", parseUSDC("100.00")),
{ timestamp: BigInt(Date.now()) } // Optional: auto-set if not provided
);
// Step 2: Send signed transaction
const receipt = await client.sendSignedTransaction(signedTx, {
waitForConfirmation: true,
});Simulate Transaction
const simulation = await client.simulateTransaction(
transfer("0x...", parseUSDC("100.00"))
);
if (simulation.success) {
console.log(`Gas used: ${simulation.gasUsed}`);
} else {
console.log(`Would fail: ${simulation.error}`);
}Get Transaction Status
const receipt = await client.getTransactionStatus("0x...");
if (receipt) {
console.log(`Status: ${receipt.status}`);
// "pending" | "confirmed" | "failed"
}Wait for Confirmation
try {
const receipt = await client.waitForConfirmation(
"0x...", // transaction hash
30000 // timeout in ms
);
console.log("Transaction confirmed!");
} catch (error) {
console.log("Transaction failed or timed out");
}High-Level Transaction APIs
The client provides convenient methods for common operations. These methods automatically handle payload creation, signing, and sending.
Transfer
// Simple transfer
const receipt = await client.transfer(
"0xRecipient...",
parseUSDC("100.00"),
{ waitForConfirmation: true }
);Withdraw
// Withdraw to external chain
const receipt = await client.withdraw(
parseUSDC("500.00"),
"0xExternalAddress...",
84532, // Base Sepolia chain ID
{ waitForConfirmation: true }
);Nominate API Wallet
// Authorize an API wallet for 24 hours
const receipt = await client.nominateApiWallet(
"0xApiWallet...",
Date.now() + 86400000, // 24 hours, 0 = no expiry
{ waitForConfirmation: true }
);Revoke API Wallet
// Remove API wallet authorization
const receipt = await client.revokeApiWallet(
"0xApiWallet...",
{ waitForConfirmation: true }
);Submit RFQ
The submitRfq method handles quote signing automatically:
// Get quote from solver
const quote = await client.requestQuote(solverUrl, { ... });
// Submit - quote signing is handled automatically
const receipt = await client.submitRfq(quote, {
waitForConfirmation: true
});Settle RFQ
// Settle a thesis (usually done by Settler service)
const receipt = await client.settleRfq(
1n, // thesisId
{ waitForConfirmation: true }
);:::tip High-Level vs Low-Level
Use high-level methods for simplicity. Use sendTransaction() with payload builders when you need more control (e.g., custom timestamps).
:::
Solver API Helper
Request Quote from Solver
const quote = await client.requestQuote(
"http://localhost:4000", // Solver API URL
{
user: client.signerAddress!,
thesisType: "box",
base: BTC_ADDRESS,
quote: USDC_ADDRESS,
lowerPrice: parsePrice("94000.00"),
upperPrice: parsePrice("96000.00"),
betAmount: parseUSDC("100.00"),
startTime: Date.now() + 5000,
endTime: Date.now() + 65000,
deadline: Date.now() + 30000,
}
);
console.log(`Quote payout: ${formatUSDC(quote.payout)}`);Sign Quote for Execution
Sign the solver quote hash to authorize an RFQ thesis. This signature is required for POST /execute on the solver API.
// Get quote from solver
const quote = await client.requestQuote("http://localhost:4000", { ... });
// Sign the quote hash (user authorizing the thesis)
const userSignature = await client.signQuote(quote);
// Execute via solver
const response = await fetch("http://localhost:4000/execute", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
quote: {
user: quote.user,
orderType: quote.orderType,
oraclePair: quote.oraclePair,
priceRange: {
lower: quote.priceRange.lower.toString(),
upper: quote.priceRange.upper.toString(),
},
betAmount: quote.betAmount.toString(),
payout: quote.payout.toString(),
startTime: quote.startTime,
endTime: quote.endTime,
deadline: quote.deadline,
signature: quote.signature,
},
userSignature,
}),
});
const result = await response.json();
console.log(`Tx Hash: ${result.result.txHash}`);Note: The quote hash is computed as keccak256(borsh_serialize(solverQuote)), matching the Rust implementation.
Error Handling
import { RpcError } from "@kaizen-core/sdk";
try {
await client.sendTransaction(transfer("0x...", parseUSDC("100.00")));
} catch (error) {
if (error instanceof RpcError) {
console.log(`RPC Error ${error.code}: ${error.message}`);
console.log(`Data: ${error.data}`);
} else {
throw error;
}
}Full Example
import { createClient, parseUSDC, formatUSDC } from "@kaizen-core/sdk";
async function main() {
// Setup
const client = createClient({ rpcUrl: "http://localhost:8546" });
client.connectSigner("0x...");
// Check balance
const balance = await client.getMyBalance();
console.log(`Balance: ${formatUSDC(balance.balance)} USDC`);
// Transfer funds (high-level API)
await client.transfer("0xRecipient...", parseUSDC("50.00"), {
waitForConfirmation: true,
});
// Nominate API wallet for trading
// API wallets can do everything EXCEPT withdraw and transfer
await client.nominateApiWallet(
"0xApiWallet...",
Date.now() + 86400000, // 24 hours, 0 = no expiry
{ waitForConfirmation: true }
);
// Withdraw to external address
await client.withdraw(
parseUSDC("25.00"),
"0xExternalAddress...",
84532, // Base Sepolia
{ waitForConfirmation: true }
);
}
main();