Engineering Principles & Design
This document outlines the key engineering principles, architectural decisions, and trade-offs made during the development of EarnKit.
Part 1: Core SDK Philosophy & Best Practices
We started with a clear philosophy for the earnkit-sdk
: it should be lightweight, flexible, and completely focused on providing a best-in-class developer experience (DX).
A Focus on Developer Experience (DX)
-
Performant and Reliable: The EarnKit Backend is designed to be extremely performant and reliable, making sure the developer’s application is not hindered by the backend.
-
Lightweight and Dependency-Free: A core tenet of the SDK’s design is its minimal footprint. It is intentionally built with zero production dependencies, ensuring it is lean, easy to integrate, and will not bloat a developer’s application. The final production bundle is a mere ~15 KB (and ~4 KB gzipped), making its impact on load times negligible.
-
Decoupled & Wallet-Agnostic: To maximize compatibility across the web3 ecosystem, the SDK is completely wallet-agnostic. Instead of bundling a specific wallet connector, it requires the developer to pass in the walletAddress. This crucial design choice grants developers the freedom to use any wallet connection library they prefer (e.g., Privy, RainbowKit, Web3Modal).
-
Instant Agent Configuration Change: The SDK is designed in such a way that the developer doesn’t need to redeploy their application to change the agent configuration.
-
Clear & Actionable Errors: The SDK features a set of custom error classes (
EarnKitInitializationError
,EarnKitInputError
,EarnKitApiError
). This empowers developers to move beyond generic error handling and build robust, programmatic responses to specific failures, such as prompting a user to top up when anEarnKitApiError
with a status of 402 is caught. -
Resilience Through Automatic Retries: The SDK is built to be resilient against transient network and server issues. It incorporates a built-in retry mechanism with exponential backoff. If a request fails with a server-side error (5xx), the SDK will automatically retry the request up to two more times, preventing temporary glitches from impacting the user experience.
-
Configurable & Transparent:
- To maintain a clean developer console, all internal SDK logging is disabled by default and can be enabled via a
debug: true
flag. - To handle varying network conditions, the global API request timeout is configurable (
requestTimeoutMs
), making the developer’s application more resilient.
- To maintain a clean developer console, all internal SDK logging is disabled by default and can be enabled via a
-
Client-Server Separation: The SDK’s sole responsibility is to act as a secure and efficient messenger to the EarnKit backend. All business logic (fee calculations, balance updates) resides on the server. This is a paramount design choice, as it allows for pricing models and features to be updated without requiring developers to update their installed SDK package.
-
Support for Multiple Instances: The SDK was explicitly designed as a standard class, not a singleton. This allows developers to instantiate multiple EarnKit clients in a single application, each configured for a different agent. This is essential for applications that need to manage multiple distinct AI agents simultaneously.
-
High-Level Helpers for Common Workflows: Beyond providing core API methods, the SDK includes high-level utility functions to simplify complex tasks. The
pollForBalanceUpdate
method, for instance, encapsulates the entire asynchronous workflow of checking for a balance change after a top-up, saving the developer from writing boilerplate polling code.
Part 2: Architectural Deep Dive
These core principles directly informed the key architectural decisions of the platform.
1. Hybrid Billing Model: On-Chain vs. Off-Chain
To provide a seamless user experience with instant, gas-free interactions, EarnKit uses a hybrid billing model. This approach addresses the challenge of processing high-frequency, low-value transactions for each AI interaction, which would be slow and costly if handled purely on-chain.
- Off-Chain Micro-transactions: Per-prompt fees and credit deductions are handled as atomic updates in our own database. This allows for instantaneous transactions without gas fees.
- On-Chain Macro-transactions: User top-ups are standard blockchain transactions (e.g., sending ETH on Base Sepolia) directly to the developer’s wallet, ensuring security and user control over their funds.
2. The track/capture/release
Transaction Lifecycle
To guarantee that a user is never charged for a failed AI call and to ensure the developer is credited for a successful one, EarnKit implements a two-step transaction flow.
track()
is called before the AI operation to place a temporary hold on the user’s balance.capture()
is called only after a successful AI response to finalize the charge.- If the AI call fails,
release()
is called to cancel the hold and refund the user.
This pattern adds a minor complexity to the SDK integration, as the developer must remember to call capture
or release
. However, this is a necessary trade-off for achieving a fair and reliable billing system, which is fundamental to user and developer trust.
3. Asynchronous Top-Up Confirmation
On-chain transactions can take time to confirm. To avoid forcing the user to wait on a loading screen, the top-up flow is fully asynchronous. The client submits the txHash
to the backend, and the UI can respond immediately. A separate background process on the server monitors the blockchain for confirmation and updates the user’s balance once verified.
4. Idempotency for Safe Retries
Network requests can time out, and clients may retry a request. To prevent accidental double-charging in such scenarios, the track
endpoint accepts an optional idempotencyKey
. If a developer provides a unique key for an operation (e.g., a UUID), our backend can recognize and discard any duplicate requests, guaranteeing that the user is only charged once for the same operation.