// API client for Lux MPC backend (all routes from pkg/api/server.go) import { getToken, getRefreshToken, setTokens, clearTokens } from './auth' import type { AuthResponse, RegisterRequest, LoginRequest, MFARequiredResponse, Vault, CreateVaultRequest, UpdateVaultRequest, Wallet, WalletAddresses, CreateWalletRequest, ReshareWalletRequest, Transaction, TransactionFilters, CreateTransactionRequest, RejectTransactionRequest, Policy, CreatePolicyRequest, UpdatePolicyRequest, AddressWhitelist, AddWhitelistRequest, Webhook, CreateWebhookRequest, UpdateWebhookRequest, Subscription, CreateSubscriptionRequest, UpdateSubscriptionRequest, PaymentRequest, CreatePaymentRequestRequest, PayPaymentRequestRequest, SmartWallet, DeploySmartWalletRequest, ProposeSafeTxRequest, ExecuteSafeTxRequest, UserOperationRequest, User, InviteUserRequest, UpdateUserRequest, APIKey, APIKeyCreateResponse, CreateAPIKeyRequest, AuditEntry, ClusterStatus, InfoResponse, Intent, CreateIntentRequest, SignIntentRequest, CoSignIntentRequest, IntentFilters, Settlement, SettlementFilters, WalletBackup, CreateWalletBackupRequest, } from './types' export class APIError extends Error { constructor( public status: number, public body: { error: string }, ) { super(body.error) this.name = 'APIError' } } export class APIClient { private baseURL: string private refreshing: Promise | null = null constructor(baseURL?: string) { this.baseURL = baseURL ?? (process.env.NEXT_PUBLIC_API_URL || '/api/v1') } private async request( method: string, path: string, body?: unknown, retry = true, ): Promise { const headers: Record = { 'Content-Type': 'application/json', } const token = getToken() if (token) { headers['Authorization'] = `Bearer ${token}` } const res = await fetch(`${this.baseURL}${path}`, { method, headers, body: body !== undefined ? JSON.stringify(body) : undefined, }) // Handle 401 with automatic token refresh if (res.status === 401 && retry) { await this.tryRefresh() return this.request(method, path, body, false) } if (res.status === 204) { return undefined as T } const data = await res.json() if (!res.ok) { throw new APIError(res.status, data) } return data as T } private async tryRefresh(): Promise { // Deduplicate concurrent refresh attempts if (this.refreshing) { await this.refreshing return } this.refreshing = (async () => { const refreshToken = getRefreshToken() if (!refreshToken) { clearTokens() throw new APIError(401, { error: 'no refresh token' }) } try { const res = await fetch(`${this.baseURL}/auth/refresh`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ refresh_token: refreshToken }), }) if (!res.ok) { clearTokens() throw new APIError(401, { error: 'refresh failed' }) } const data: AuthResponse = await res.json() setTokens(data.access_token, data.refresh_token) } catch (err) { clearTokens() throw err } })() try { await this.refreshing } finally { this.refreshing = null } } private get(path: string): Promise { return this.request('GET', path) } private post(path: string, body?: unknown): Promise { return this.request('POST', path, body) } private patch(path: string, body?: unknown): Promise { return this.request('PATCH', path, body) } private del(path: string): Promise { return this.request('DELETE', path) } // --- Auth --- async register(req: RegisterRequest): Promise { return this.post('/auth/register', req) } async oidcExchange(accessToken: string, providerUrl: string): Promise { return this.request('POST', '/auth/oidc', { access_token: accessToken, provider_url: providerUrl, }, false) } async login(req: LoginRequest): Promise { return this.post('/auth/login', req) } async refresh(refreshToken: string): Promise { return this.post('/auth/refresh', { refresh_token: refreshToken }) } async mfaSetup(): Promise<{ secret: string; otpauth: string }> { return this.post('/auth/mfa/setup') } async mfaVerify(code: string): Promise<{ status: string }> { return this.post('/auth/mfa/verify', { code }) } // --- Vaults --- async listVaults(): Promise { return this.get('/vaults') } async createVault(req: CreateVaultRequest): Promise { return this.post('/vaults', req) } async getVault(id: string): Promise { return this.get(`/vaults/${id}`) } async updateVault(id: string, req: UpdateVaultRequest): Promise { return this.patch(`/vaults/${id}`, req) } async deleteVault(id: string): Promise { return this.del(`/vaults/${id}`) } // --- Wallets --- async listWallets(vaultId: string): Promise { return this.get(`/vaults/${vaultId}/wallets`) } async createWallet(vaultId: string, req: CreateWalletRequest): Promise { return this.post(`/vaults/${vaultId}/wallets`, req) } async getWallet(id: string): Promise { return this.get(`/wallets/${id}`) } async getWalletAddresses(id: string): Promise { return this.get(`/wallets/${id}/addresses`) } async reshareWallet(id: string, req: ReshareWalletRequest): Promise<{ status: string }> { return this.post(`/wallets/${id}/reshare`, req) } async getWalletHistory(id: string): Promise { return this.get(`/wallets/${id}/history`) } // --- Transactions --- async createTransaction(req: CreateTransactionRequest): Promise { return this.post('/transactions', req) } async listTransactions(filters?: TransactionFilters): Promise { const params = new URLSearchParams() if (filters?.status) params.set('status', filters.status) if (filters?.chain) params.set('chain', filters.chain) const qs = params.toString() return this.get(`/transactions${qs ? `?${qs}` : ''}`) } async getTransaction(id: string): Promise { return this.get(`/transactions/${id}`) } async approveTransaction(id: string): Promise<{ status: string }> { return this.post(`/transactions/${id}/approve`) } async rejectTransaction(id: string, req?: RejectTransactionRequest): Promise<{ status: string }> { return this.post(`/transactions/${id}/reject`, req) } // --- Policies --- async listPolicies(): Promise { return this.get('/policies') } async createPolicy(req: CreatePolicyRequest): Promise { return this.post('/policies', req) } async updatePolicy(id: string, req: UpdatePolicyRequest): Promise { return this.patch(`/policies/${id}`, req) } async deletePolicy(id: string): Promise { return this.del(`/policies/${id}`) } // --- Whitelist --- async listWhitelist(): Promise { return this.get('/whitelist') } async addWhitelist(req: AddWhitelistRequest): Promise { return this.post('/whitelist', req) } async deleteWhitelist(id: string): Promise { return this.del(`/whitelist/${id}`) } // --- Webhooks --- async listWebhooks(): Promise { return this.get('/webhooks') } async createWebhook(req: CreateWebhookRequest): Promise { return this.post('/webhooks', req) } async updateWebhook(id: string, req: UpdateWebhookRequest): Promise { return this.patch(`/webhooks/${id}`, req) } async deleteWebhook(id: string): Promise { return this.del(`/webhooks/${id}`) } async testWebhook(id: string): Promise<{ status: string }> { return this.post(`/webhooks/${id}/test`) } // --- Subscriptions --- async listSubscriptions(): Promise { return this.get('/subscriptions') } async createSubscription(req: CreateSubscriptionRequest): Promise { return this.post('/subscriptions', req) } async getSubscription(id: string): Promise { return this.get(`/subscriptions/${id}`) } async updateSubscription(id: string, req: UpdateSubscriptionRequest): Promise { return this.patch(`/subscriptions/${id}`, req) } async deleteSubscription(id: string): Promise { return this.del(`/subscriptions/${id}`) } async payNow(id: string): Promise<{ status: string; tx_id: string }> { return this.post(`/subscriptions/${id}/pay-now`) } // --- Payment Requests --- async createPaymentRequest(req: CreatePaymentRequestRequest): Promise<{ payment_request: PaymentRequest payment_url: string }> { return this.post('/payment-requests', req) } async listPaymentRequests(): Promise { return this.get('/payment-requests') } async getPaymentRequest(id: string): Promise { return this.get(`/payment-requests/${id}`) } async payPaymentRequest(id: string, req: PayPaymentRequestRequest): Promise<{ status: string; tx_id: string }> { return this.post(`/payment-requests/${id}/pay`, req) } // --- Smart Wallets --- async deploySmartWallet(walletId: string, req: DeploySmartWalletRequest): Promise { return this.post(`/wallets/${walletId}/smart-wallet`, req) } async listSmartWallets(walletId: string): Promise { return this.get(`/wallets/${walletId}/smart-wallets`) } async getSmartWallet(id: string): Promise { return this.get(`/smart-wallets/${id}`) } async proposeSafeTx(id: string, req: ProposeSafeTxRequest): Promise<{ status: string; smart_wallet_id: string; to: string; value: string }> { return this.post(`/smart-wallets/${id}/propose`, req) } async executeSafeTx(id: string, req: ExecuteSafeTxRequest): Promise<{ status: string; safe_tx_hash: string }> { return this.post(`/smart-wallets/${id}/execute`, req) } async userOperation(id: string, req: UserOperationRequest): Promise<{ status: string; smart_wallet_id: string }> { return this.post(`/smart-wallets/${id}/user-operation`, req) } // --- Users & Teams --- async listUsers(): Promise { return this.get('/users') } async inviteUser(req: InviteUserRequest): Promise { return this.post('/users', req) } async updateUser(id: string, req: UpdateUserRequest): Promise { return this.patch(`/users/${id}`, req) } async deleteUser(id: string): Promise { return this.del(`/users/${id}`) } // --- API Keys --- async listAPIKeys(): Promise { return this.get('/api-keys') } async createAPIKey(req: CreateAPIKeyRequest): Promise { return this.post('/api-keys', req) } async deleteAPIKey(id: string): Promise { return this.del(`/api-keys/${id}`) } // --- Audit --- async listAudit(): Promise { return this.get('/audit') } // --- WebAuthn / Biometric --- async webauthnRegisterBegin(): Promise { return this.post('/webauthn/register/begin') } async webauthnRegisterComplete(data: any): Promise { return this.post('/webauthn/register/complete', data) } async webauthnVerify(txId: string, assertion: any): Promise { return this.post('/webauthn/verify', { tx_id: txId, ...assertion }) } async listWebAuthnCredentials(): Promise { return this.get('/webauthn/credentials') } async deleteWebAuthnCredential(id: string): Promise { return this.del(`/webauthn/credentials/${id}`) } // --- Bridge Admin --- async getBridgeConfig(): Promise { return this.get('/bridge/config') } async updateBridgeConfig(config: any): Promise { return this.patch('/bridge/config', config) } async listBridgeNetworks(): Promise { return this.get('/bridge/networks') } // --- Intents --- async createIntent(req: CreateIntentRequest): Promise { return this.post('/intents', req) } async listIntents(filters?: IntentFilters): Promise { const params = new URLSearchParams() if (filters?.status) params.set('status', filters.status) const qs = params.toString() return this.get(`/intents${qs ? '?' + qs : ''}`) } async getIntent(id: string): Promise { return this.get(`/intents/${id}`) } async signIntent(id: string, req: SignIntentRequest): Promise { return this.post(`/intents/${id}/sign`, req) } async coSignIntent(id: string, req: CoSignIntentRequest): Promise { return this.post(`/intents/${id}/co-sign`, req) } // --- Settlements --- async listSettlements(filters?: SettlementFilters): Promise { const params = new URLSearchParams() if (filters?.status) params.set('status', filters.status) const qs = params.toString() return this.get(`/settlements${qs ? '?' + qs : ''}`) } async getSettlement(id: string): Promise { return this.get(`/settlements/${id}`) } // --- Wallet Backup --- async createWalletBackup(walletId: string, req?: CreateWalletBackupRequest): Promise { return this.post(`/wallets/${walletId}/backup`, req ?? {}) } async getWalletBackups(walletId: string): Promise { return this.get(`/wallets/${walletId}/backup`) } // --- Status & Info --- async getStatus(): Promise { return this.get('/status') } async getInfo(): Promise { return this.get('/info') } } // Singleton instance export const api = new APIClient()