Chapter 5: Phase 2 - API Interface
"The API is the contract between the blockchain and the world. Design it with clarity, secure it with rigor, and build it for billions."
— WIA Blockchain Finance Standard
In Phase 2 of the WIA Blockchain Finance Standard, we transition from foundational data structures to the critical API interface layer. This chapter explores how to design, implement, and secure APIs that bridge traditional finance applications with decentralized blockchain infrastructure. The API layer serves as the gateway through which developers interact with DeFi protocols, execute transactions, query blockchain state, and integrate Web3 functionality into their applications.
Modern blockchain applications demand APIs that are not only functional but also secure, scalable, and developer-friendly. This chapter provides comprehensive guidance on designing RESTful endpoints for blockchain operations, implementing Web3 wallet authentication, handling errors gracefully, and building SDKs that abstract complexity while maintaining flexibility.
1. DeFi API Design Philosophy
The design philosophy for blockchain finance APIs differs fundamentally from traditional REST APIs. While traditional APIs focus on CRUD operations against centralized databases, DeFi APIs must account for blockchain immutability, transaction finality delays, gas costs, network congestion, and the trustless nature of decentralized systems.
Core Design Principles
WIA Blockchain Finance APIs are built on five fundamental principles:
- Idempotency: Given the asynchronous nature of blockchain transactions, APIs must be designed to handle retries safely without creating duplicate transactions or state changes.
- Transparency: All API responses should provide clear visibility into transaction status, gas costs, block confirmations, and potential failure reasons.
- Composability: APIs should expose atomic operations that can be combined to create complex DeFi workflows, following the Unix philosophy of doing one thing well.
- Gas Optimization: API design should consider gas costs and provide endpoints that allow batching operations to minimize transaction fees.
- Security-First: Every endpoint must implement proper authentication, authorization, input validation, and protection against common attack vectors.
API Architecture Patterns
| Pattern | Use Case | Advantages | Considerations |
|---|---|---|---|
| REST | Standard CRUD operations, wallet queries | Simple, cacheable, stateless | Not ideal for real-time updates |
| GraphQL | Complex queries, subgraph data | Flexible queries, efficient data fetching | More complex to implement |
| WebSocket | Real-time price feeds, transaction monitoring | Low latency, bidirectional | Connection management overhead |
| gRPC | High-performance service-to-service | Fast, type-safe, efficient | Limited browser support |
| JSON-RPC | Direct blockchain node communication | Native blockchain protocol | Less developer-friendly |
Versioning Strategy
Blockchain APIs must support multiple versions simultaneously due to the immutable nature of deployed smart contracts. The WIA standard recommends URL-based versioning for clarity:
https://api.defi-platform.com/v1/swap
https://api.defi-platform.com/v2/swap
https://api.defi-platform.com/v3/swap
Each version should maintain backwards compatibility for a minimum of 12 months, with clear deprecation notices and migration guides provided to developers.
2. RESTful Endpoints for Blockchain
RESTful API design for blockchain applications requires careful consideration of resource modeling, HTTP method semantics, and the asynchronous nature of blockchain operations.
Resource Organization
WIA Blockchain Finance APIs organize resources into logical collections that map to DeFi primitives:
GET /v1/wallets/{address} # Get wallet details
GET /v1/wallets/{address}/balance # Get token balances
GET /v1/wallets/{address}/transactions # Get transaction history
POST /v1/swap # Create swap transaction
GET /v1/swap/{txHash} # Get swap status
GET /v1/swap/quote # Get swap quote
GET /v1/pools/{poolId} # Get liquidity pool info
POST /v1/pools/{poolId}/liquidity/add # Add liquidity
POST /v1/pools/{poolId}/liquidity/remove # Remove liquidity
GET /v1/tokens/{tokenAddress} # Get token metadata
GET /v1/tokens/{tokenAddress}/price # Get current price
GET /v1/tokens/{tokenAddress}/holders # Get token holders
POST /v1/lending/deposit # Deposit to lending protocol
POST /v1/lending/borrow # Borrow from protocol
POST /v1/lending/repay # Repay loan
GET /v1/lending/positions/{address} # Get lending positions
Request/Response Patterns
All blockchain transaction endpoints follow a consistent two-phase pattern to accommodate asynchronous blockchain confirmations:
Phase 1: Transaction Submission
// Request
POST /v1/swap
{
"fromToken": "0x6B175474E89094C44Da98b954EedeAC495271d0F",
"toToken": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
"amount": "1000000000000000000",
"slippage": "0.005",
"recipient": "0x742d35Cc6634C0532925a3b844Bc454e4438f44e"
}
// Response (202 Accepted)
{
"status": "pending",
"transactionId": "wia_tx_1a2b3c4d5e6f",
"txHash": null,
"estimatedGas": "150000",
"estimatedGasCost": "0.0045",
"submitTime": "2025-12-25T10:30:00Z",
"links": {
"status": "/v1/swap/wia_tx_1a2b3c4d5e6f",
"cancel": "/v1/swap/wia_tx_1a2b3c4d5e6f/cancel"
}
}
Phase 2: Transaction Confirmation
// Request
GET /v1/swap/wia_tx_1a2b3c4d5e6f
// Response (200 OK)
{
"status": "confirmed",
"transactionId": "wia_tx_1a2b3c4d5e6f",
"txHash": "0x8f3c...",
"blockNumber": 18950123,
"confirmations": 12,
"gasUsed": "142356",
"gasCost": "0.00427",
"fromToken": {
"address": "0x6B175474E89094C44Da98b954EedeAC495271d0F",
"symbol": "DAI",
"amount": "1000000000000000000"
},
"toToken": {
"address": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
"symbol": "USDC",
"amount": "998500000"
},
"effectivePrice": "0.9985",
"slippage": "0.0015",
"timestamp": "2025-12-25T10:31:45Z"
}
Pagination and Filtering
Blockchain data queries can return large result sets. The WIA standard implements cursor-based pagination for efficiency:
GET /v1/wallets/{address}/transactions?limit=50&cursor=eyJibG9jayI6MTg5NTAxMjN9
{
"data": [...],
"pagination": {
"limit": 50,
"hasMore": true,
"nextCursor": "eyJibG9jayI6MTg5NDk4NzZ9"
}
}
3. Authentication with Web3 Wallets
Traditional API authentication using API keys or OAuth is insufficient for DeFi applications. Web3 wallet authentication proves ownership of a blockchain address through cryptographic signatures, enabling trustless authentication without centralized credential storage.
Sign-In with Ethereum (SIWE)
The WIA standard implements the EIP-4361 Sign-In with Ethereum specification for wallet-based authentication:
// Step 1: Request challenge
POST /v1/auth/challenge
{
"address": "0x742d35Cc6634C0532925a3b844Bc454e4438f44e"
}
// Response
{
"message": "api.defi-platform.com wants you to sign in with your Ethereum account:\n0x742d35Cc6634C0532925a3b844Bc454e4438f44e\n\nSign in to DeFi Platform\n\nURI: https://api.defi-platform.com\nVersion: 1\nChain ID: 1\nNonce: 3j4k5l6m7n8o9p0q\nIssued At: 2025-12-25T10:30:00Z\nExpiration Time: 2025-12-25T11:00:00Z",
"nonce": "3j4k5l6m7n8o9p0q",
"expiresAt": "2025-12-25T11:00:00Z"
}
// Step 2: Sign message with wallet (client-side)
const signature = await wallet.signMessage(message);
// Step 3: Verify signature
POST /v1/auth/verify
{
"message": "...",
"signature": "0x8f3c2a1b...",
"address": "0x742d35Cc6634C0532925a3b844Bc454e4438f44e"
}
// Response
{
"accessToken": "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9...",
"refreshToken": "rt_a1b2c3d4e5f6...",
"expiresIn": 3600,
"tokenType": "Bearer"
}
JWT Token Structure
Authenticated sessions use JSON Web Tokens (JWT) with blockchain-specific claims:
{
"header": {
"alg": "ES256",
"typ": "JWT"
},
"payload": {
"iss": "api.defi-platform.com",
"sub": "0x742d35Cc6634C0532925a3b844Bc454e4438f44e",
"aud": "defi-platform",
"exp": 1735129800,
"iat": 1735126200,
"nbf": 1735126200,
"jti": "jwt_1a2b3c4d5e6f",
"chain": "ethereum",
"chainId": 1,
"permissions": [
"swap:execute",
"pool:add-liquidity",
"pool:remove-liquidity",
"lending:deposit",
"lending:borrow"
]
}
}
Multi-Chain Authentication
For applications supporting multiple blockchains, the WIA standard allows linking multiple wallet addresses to a single account:
| Chain | Address Format | Signature Algorithm | Verification |
|---|---|---|---|
| Ethereum | 0x + 40 hex chars | ECDSA (secp256k1) | ecrecover |
| Bitcoin | Base58Check | ECDSA (secp256k1) | Custom verification |
| Solana | Base58 (32 bytes) | Ed25519 | nacl.sign.detached.verify |
| Polkadot | SS58 | Sr25519 | Substrate verification |
| Cosmos | Bech32 | Secp256k1 | Cosmos SDK |
4. Request/Response Formats
Consistent data formats are essential for API usability. The WIA standard defines precise formats for common blockchain data types.
Numeric Formats
Blockchain operations involve very large numbers that exceed JavaScript's safe integer range. The WIA standard uses string representation for precision:
{
"balance": {
"raw": "1234567890123456789", // wei (18 decimals)
"formatted": "1.234567890123456789", // ETH
"display": "1.23 ETH" // User-friendly
},
"price": {
"value": "3847.50",
"currency": "USD",
"decimals": 2
},
"apr": {
"value": "0.0875",
"percentage": "8.75%",
"compounding": "continuous"
}
}
Address and Hash Formats
{
"addresses": {
"ethereum": "0x742d35Cc6634C0532925a3b844Bc454e4438f44e",
"checksum": true,
"ensName": "alice.eth"
},
"transaction": {
"hash": "0x8f3c2a1b4d5e6f7g8h9i0j1k2l3m4n5o6p7q8r9s0t1u2v3w4x5y6z",
"blockHash": "0xa1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0u1v2w3x4y5z6",
"blockNumber": 18950123
}
}
Timestamp Formats
All timestamps use ISO 8601 format in UTC:
{
"timestamp": "2025-12-25T10:30:00Z",
"blockTimestamp": "2025-12-25T10:29:45Z",
"expirationTime": "2025-12-25T11:30:00Z"
}
Error Response Format
Standardized error responses enable consistent error handling across the API:
{
"error": {
"code": "INSUFFICIENT_BALANCE",
"message": "Insufficient balance for transaction",
"details": {
"required": "1000000000000000000",
"available": "500000000000000000",
"token": "DAI"
},
"timestamp": "2025-12-25T10:30:00Z",
"requestId": "req_1a2b3c4d5e6f",
"documentation": "https://docs.api.com/errors/INSUFFICIENT_BALANCE"
}
}
5. Error Handling and Recovery
Blockchain APIs must handle a unique set of error conditions beyond traditional HTTP errors. The WIA standard defines comprehensive error taxonomies and recovery strategies.
Error Categories
| Category | HTTP Status | Example Codes | Recovery Strategy |
|---|---|---|---|
| Client Errors | 400-499 | INVALID_ADDRESS, INSUFFICIENT_BALANCE | Fix input, do not retry |
| Network Errors | 503 | NETWORK_CONGESTION, RPC_TIMEOUT | Retry with backoff |
| Transaction Errors | 400 | GAS_TOO_LOW, NONCE_TOO_LOW | Adjust parameters, retry |
| Contract Errors | 400 | SLIPPAGE_EXCEEDED, POOL_PAUSED | Adjust parameters or wait |
| Server Errors | 500-599 | INTERNAL_ERROR, DATABASE_ERROR | Retry with exponential backoff |
Blockchain-Specific Errors
// Gas estimation failure
{
"error": {
"code": "GAS_ESTIMATION_FAILED",
"message": "Unable to estimate gas for transaction",
"details": {
"reason": "execution reverted: Insufficient liquidity",
"suggestedAction": "Check pool liquidity before retrying"
}
}
}
// Transaction reverted
{
"error": {
"code": "TRANSACTION_REVERTED",
"message": "Transaction reverted on-chain",
"details": {
"txHash": "0x8f3c...",
"revertReason": "UniswapV2: INSUFFICIENT_OUTPUT_AMOUNT",
"gasUsed": "45000",
"blockNumber": 18950123
}
}
}
// Nonce mismatch
{
"error": {
"code": "NONCE_TOO_LOW",
"message": "Transaction nonce is too low",
"details": {
"expected": 157,
"provided": 155,
"suggestedAction": "Fetch latest nonce and retry"
}
}
}
Retry Logic
The WIA standard recommends exponential backoff with jitter for transient errors:
async function executeWithRetry(apiCall, maxRetries = 3) {
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
return await apiCall();
} catch (error) {
// Don't retry client errors
if (error.status >= 400 && error.status < 500) {
throw error;
}
// Last attempt
if (attempt === maxRetries - 1) {
throw error;
}
// Calculate backoff with jitter
const baseDelay = 1000;
const maxDelay = 32000;
const exponentialDelay = Math.min(baseDelay * Math.pow(2, attempt), maxDelay);
const jitter = Math.random() * 1000;
const delay = exponentialDelay + jitter;
console.log(`Retry attempt ${attempt + 1} after ${delay}ms`);
await sleep(delay);
}
}
}
6. Rate Limiting and Throttling
Blockchain APIs must implement rate limiting to prevent abuse, manage RPC node costs, and ensure fair resource allocation. The WIA standard defines a multi-tier rate limiting strategy.
Rate Limit Tiers
| Tier | Requests/Min | Burst Limit | Cost | Features |
|---|---|---|---|---|
| Free | 60 | 10 | $0 | Basic endpoints only |
| Developer | 600 | 50 | $49/mo | All endpoints, WebSocket |
| Professional | 3000 | 200 | $199/mo | Priority support, webhooks |
| Enterprise | Unlimited | 1000 | Custom | Dedicated infrastructure |
Rate Limit Headers
All API responses include standard rate limit headers:
HTTP/1.1 200 OK
X-RateLimit-Limit: 600
X-RateLimit-Remaining: 547
X-RateLimit-Reset: 1735127400
X-RateLimit-Bucket: developer
Retry-After: 45
Cost-Based Rate Limiting
Different endpoints consume different resources. The WIA standard assigns computational cost units to each operation:
{
"endpoints": {
"GET /v1/wallets/{address}/balance": 1,
"GET /v1/tokens/{token}/price": 1,
"POST /v1/swap/quote": 5,
"POST /v1/swap": 10,
"GET /v1/pools/{pool}/history": 20,
"POST /v1/lending/deposit": 15
},
"limits": {
"developer": {
"units_per_minute": 3000,
"burst": 500
}
}
}
WebSocket Rate Limiting
Real-time WebSocket connections use separate rate limits based on message frequency and subscription count:
{
"websocket": {
"max_connections_per_ip": 5,
"max_subscriptions_per_connection": 50,
"messages_per_second": 100,
"max_message_size_bytes": 65536
}
}
7. SDK Design and Implementation
While direct API access provides flexibility, SDKs dramatically improve developer experience by abstracting complexity, providing type safety, and handling common patterns automatically.
TypeScript SDK Architecture
import { WIABlockchainFinance } from '@wia/blockchain-finance';
// Initialize SDK
const wia = new WIABlockchainFinance({
apiKey: process.env.WIA_API_KEY,
network: 'ethereum',
provider: window.ethereum // Web3 provider
});
// Authenticate with wallet
await wia.auth.connectWallet();
// Get wallet balance
const balance = await wia.wallet.getBalance('0x742d35...');
console.log(balance.formatted); // "1.234 ETH"
// Execute swap with type safety
const swap = await wia.swap.execute({
fromToken: 'DAI',
toToken: 'USDC',
amount: '1000',
slippage: 0.005,
onStatusChange: (status) => console.log(status)
});
// Wait for confirmation
const receipt = await swap.wait(12); // 12 confirmations
console.log(`Swapped at price: ${receipt.effectivePrice}`);
SDK Core Components
| Component | Responsibility | Key Methods |
|---|---|---|
| Client | HTTP client, request/response handling | request(), retry(), handleError() |
| Auth | Wallet connection, signature generation | connectWallet(), signMessage(), getToken() |
| Wallet | Balance queries, transaction history | getBalance(), getTransactions(), getNFTs() |
| Swap | Token swaps, price quotes | quote(), execute(), estimateGas() |
| Pool | Liquidity provision | addLiquidity(), removeLiquidity(), getPoolInfo() |
| Lending | Deposit, borrow, repay | deposit(), borrow(), repay(), getPositions() |
| Events | WebSocket event streaming | subscribe(), unsubscribe(), on() |
Type Definitions
// types.ts
export interface SwapParams {
fromToken: string | TokenAddress;
toToken: string | TokenAddress;
amount: string | BigNumber;
slippage: number;
recipient?: string;
deadline?: number;
onStatusChange?: (status: TransactionStatus) => void;
}
export interface SwapQuote {
estimatedOutput: string;
priceImpact: number;
route: SwapRoute[];
estimatedGas: string;
estimatedGasCost: string;
minimumOutput: string;
}
export interface SwapReceipt {
transactionId: string;
txHash: string;
blockNumber: number;
confirmations: number;
gasUsed: string;
effectivePrice: string;
fromAmount: TokenAmount;
toAmount: TokenAmount;
timestamp: Date;
}
export type TransactionStatus =
| 'pending'
| 'submitted'
| 'confirming'
| 'confirmed'
| 'failed';
Error Handling in SDK
import {
WIAError,
InsufficientBalanceError,
SlippageExceededError,
NetworkError
} from '@wia/blockchain-finance';
try {
const swap = await wia.swap.execute({
fromToken: 'DAI',
toToken: 'USDC',
amount: '1000',
slippage: 0.001 // 0.1% slippage
});
const receipt = await swap.wait();
console.log('Swap successful:', receipt);
} catch (error) {
if (error instanceof InsufficientBalanceError) {
console.error(`Need ${error.required} but only have ${error.available}`);
} else if (error instanceof SlippageExceededError) {
console.error(`Price moved beyond ${error.allowedSlippage}% slippage`);
// Retry with higher slippage
} else if (error instanceof NetworkError) {
console.error('Network congestion, retrying...');
// Auto-retry handled by SDK
} else {
console.error('Unexpected error:', error);
}
}
8. Advanced SDK Features
Transaction Builder Pattern
For complex multi-step operations, the SDK provides a builder pattern:
// Complex DeFi strategy in one transaction
const tx = await wia.transaction()
.swap({
from: 'ETH',
to: 'USDC',
amount: '1.0'
})
.addLiquidity({
tokenA: 'USDC',
tokenB: 'DAI',
amountA: '1000',
amountB: '1000'
})
.stake({
pool: 'USDC-DAI-LP',
amount: 'max'
})
.estimateGas()
.execute();
await tx.wait();
Event Streaming
// Subscribe to real-time events
const subscription = wia.events.subscribe('swap', {
tokens: ['DAI', 'USDC'],
minAmount: '10000',
callback: (event) => {
console.log(`Large swap: ${event.amount} ${event.fromToken} -> ${event.toToken}`);
console.log(`Price: ${event.price}`);
}
});
// Unsubscribe when done
subscription.unsubscribe();
Multicall Support
Batch multiple read operations into a single RPC call:
const [balance, price, allowance] = await wia.multicall([
wia.wallet.getBalance('0x742d35...'),
wia.tokens.getPrice('DAI'),
wia.tokens.getAllowance('DAI', '0x742d35...', UNISWAP_ROUTER)
]);
console.log(`Balance: ${balance.formatted}`);
console.log(`Price: $${price.usd}`);
console.log(`Allowance: ${allowance}`);
9. Performance Optimization
Caching Strategy
The SDK implements intelligent caching for immutable and slowly-changing data:
// Cache configuration
const wia = new WIABlockchainFinance({
cache: {
tokenMetadata: { ttl: 86400 }, // 24 hours
prices: { ttl: 30 }, // 30 seconds
balances: { ttl: 10 }, // 10 seconds
transactions: { ttl: 3600 }, // 1 hour
blockNumbers: { ttl: 12 } // 12 seconds
}
});
Request Batching
Automatic request batching reduces API calls:
// These requests are automatically batched
const promises = tokens.map(async (token) => {
const price = await wia.tokens.getPrice(token);
const balance = await wia.wallet.getBalance(wallet, token);
return { token, price, balance };
});
// SDK batches into 2 API calls instead of 2N
const results = await Promise.all(promises);
10. Chapter Summary
5 Key Takeaways:
1. DeFi APIs must account for blockchain-specific challenges including asynchronous confirmations, gas costs, and trustless authentication through wallet signatures.
2. RESTful endpoints should follow a two-phase pattern (submission + confirmation) to handle the asynchronous nature of blockchain transactions gracefully.
3. Web3 wallet authentication using Sign-In with Ethereum (SIWE) provides trustless, cryptographically secure authentication without centralized credential storage.
4. Comprehensive error handling and retry logic are essential for managing blockchain-specific errors like gas estimation failures, nonce conflicts, and network congestion.
5. Well-designed SDKs dramatically improve developer experience by providing type safety, automatic retries, caching, request batching, and abstracting blockchain complexity while maintaining flexibility.
Review Questions
-
Why is idempotency critical for blockchain API design?
Due to the asynchronous nature of blockchain transactions and potential network issues, clients may retry requests. APIs must ensure that retrying the same request doesn't create duplicate transactions or state changes. This requires careful design of transaction IDs, duplicate detection, and stateless operation semantics.
-
Explain the two-phase pattern for blockchain transaction endpoints and why it's necessary.
Phase 1 accepts the transaction request and returns a transaction ID immediately (202 Accepted). Phase 2 allows clients to poll the transaction status as it moves through pending, submitted, confirming, and confirmed states. This pattern is necessary because blockchain confirmations take time (seconds to minutes), and synchronous waiting would cause HTTP timeouts and poor user experience.
-
How does Sign-In with Ethereum (SIWE) differ from traditional OAuth authentication?
SIWE is trustless and decentralized—users prove wallet ownership through cryptographic signatures without sharing private keys or passwords. There's no central identity provider; authentication is verified on-chain or through signature verification. This eliminates the risk of credential theft and aligns with blockchain's trustless philosophy.
-
What are the key differences between blockchain API errors and traditional REST API errors?
Blockchain APIs must handle unique errors like gas estimation failures, nonce conflicts, slippage exceeded, transaction reverts, network congestion, and RPC timeouts. Recovery strategies differ—some errors require parameter adjustment (slippage), others need exponential backoff (network issues), and some are non-retryable (insufficient balance). Error responses must include blockchain-specific details like gas costs, block numbers, and revert reasons.
-
Why is cost-based rate limiting more appropriate than simple request-count limiting for blockchain APIs?
Different blockchain operations have vastly different computational costs. A simple balance query is cheap, while complex historical analysis or simulation requires significant RPC resources. Cost-based limiting assigns units to each endpoint based on resource consumption, allowing fair usage across different operation types and preventing abuse while maximizing API utility.
-
Describe three key features that make blockchain SDKs more developer-friendly than raw API access.
(1) Type safety: TypeScript interfaces prevent errors at compile time and improve IDE autocomplete. (2) Automatic retry and error handling: SDKs implement exponential backoff, error classification, and recovery strategies transparently. (3) Transaction status management: SDKs abstract polling, confirmation waiting, and event streaming through simple async/await patterns and callbacks, hiding blockchain complexity.
Looking Ahead
In Chapter 6, we advance to Phase 3: Protocol, where we explore the deep architectural patterns that enable cross-chain communication. You'll learn how to design bridge mechanisms that securely transfer value between different blockchains, implement message formats for inter-chain communication, build real-time event streaming systems, and integrate with multiple consensus mechanisms. We'll examine the security considerations unique to cross-chain protocols and study battle-tested patterns from leading bridge implementations like LayerZero, Wormhole, and Chainlink CCIP.
The protocol layer represents the cutting edge of blockchain interoperability, enabling the vision of a multi-chain future where assets and data flow freely across blockchain boundaries while maintaining security and trustlessness.