feat(finance): BTC basket flows, client scoping, and jewelry-box store
- Finance API: baskets, holdings, rebalances, deposits, bridge withdrawals, vault checks. - Schemas: btc-basket; api-client finance types; workspace lockfile update. - Vitest config for finance service; expanded tests. Made-with: Cursor
This commit is contained in:
@@ -21,6 +21,81 @@ export interface LedgerEntry {
|
||||
reference?: string;
|
||||
}
|
||||
|
||||
export interface BasketAllocation {
|
||||
symbol: string;
|
||||
targetWeightBps: number;
|
||||
routeHint?: string;
|
||||
}
|
||||
|
||||
export interface BasketMandate {
|
||||
id: string;
|
||||
clientId: string;
|
||||
mandateName: string;
|
||||
chain138VaultAddress: string;
|
||||
baseAssetSymbol: string;
|
||||
status: 'draft' | 'active' | 'rebalancing' | 'closed';
|
||||
allocations: BasketAllocation[];
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
}
|
||||
|
||||
export interface BtcDeposit {
|
||||
id: string;
|
||||
clientId: string;
|
||||
basketId: string;
|
||||
chain138VaultAddress: string;
|
||||
depositAddress: string;
|
||||
expectedAmountSats?: number;
|
||||
confirmationsRequired: number;
|
||||
currentConfirmations: number;
|
||||
status:
|
||||
| 'instruction_created'
|
||||
| 'pending_confirmations'
|
||||
| 'confirmed'
|
||||
| 'minted'
|
||||
| 'frozen';
|
||||
observedTxId?: string;
|
||||
freezeReason?: string;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
}
|
||||
|
||||
export interface Holding {
|
||||
clientId: string;
|
||||
basketId: string;
|
||||
symbol: string;
|
||||
allocationWeightBps: number;
|
||||
bookValueSats: number;
|
||||
status: 'pending_funding' | 'funded';
|
||||
updatedAt: string;
|
||||
}
|
||||
|
||||
export interface Rebalance {
|
||||
id: string;
|
||||
clientId: string;
|
||||
basketId: string;
|
||||
sourceSymbol: string;
|
||||
targetSymbols: string[];
|
||||
reason: string;
|
||||
status: 'planned' | 'queued' | 'executed' | 'failed';
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
}
|
||||
|
||||
export interface BridgeWithdrawal {
|
||||
id: string;
|
||||
clientId: string;
|
||||
basketId: string;
|
||||
sourceSymbol: string;
|
||||
destinationSymbol: string;
|
||||
destinationChainId: number;
|
||||
destinationAddress: string;
|
||||
amount: string;
|
||||
status: 'pending' | 'approved' | 'submitted' | 'failed';
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
}
|
||||
|
||||
export class FinanceClient {
|
||||
protected client: AxiosInstance;
|
||||
|
||||
@@ -28,8 +103,8 @@ export class FinanceClient {
|
||||
const apiBaseURL =
|
||||
baseURL ||
|
||||
(typeof window !== 'undefined'
|
||||
? process.env.NEXT_PUBLIC_FINANCE_SERVICE_URL || 'http://localhost:4005'
|
||||
: 'http://localhost:4005');
|
||||
? process.env.NEXT_PUBLIC_FINANCE_SERVICE_URL || 'http://localhost:4003'
|
||||
: 'http://localhost:4003');
|
||||
|
||||
this.client = axios.create({
|
||||
baseURL: apiBaseURL,
|
||||
@@ -38,7 +113,6 @@ export class FinanceClient {
|
||||
},
|
||||
});
|
||||
|
||||
// Set up request interceptor for authentication
|
||||
this.client.interceptors.request.use(
|
||||
(config) => {
|
||||
const token = this.getAuthToken();
|
||||
@@ -47,7 +121,7 @@ export class FinanceClient {
|
||||
}
|
||||
return config;
|
||||
},
|
||||
(error) => Promise.reject(error)
|
||||
(error) => Promise.reject(error),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -74,8 +148,8 @@ export class FinanceClient {
|
||||
paymentMethod: string;
|
||||
description?: string;
|
||||
}): Promise<Payment> {
|
||||
const response = await this.client.post<Payment>('/api/v1/payments', data);
|
||||
return response.data;
|
||||
const response = await this.client.post<{ payment: Payment }>('/api/v1/payments', data);
|
||||
return response.data.payment;
|
||||
}
|
||||
|
||||
async getPayment(paymentId: string): Promise<Payment> {
|
||||
@@ -104,8 +178,71 @@ export class FinanceClient {
|
||||
}): Promise<{ entries: LedgerEntry[]; total: number }> {
|
||||
const response = await this.client.get<{ entries: LedgerEntry[]; total: number }>(
|
||||
'/api/v1/ledger',
|
||||
{ params: filters }
|
||||
{ params: filters },
|
||||
);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
async createBasket(data: {
|
||||
clientId: string;
|
||||
mandateName: string;
|
||||
chain138VaultAddress: string;
|
||||
allocations: BasketAllocation[];
|
||||
baseAssetSymbol?: string;
|
||||
}): Promise<BasketMandate> {
|
||||
const response = await this.client.post<{ basket: BasketMandate }>('/api/v1/baskets', data);
|
||||
return response.data.basket;
|
||||
}
|
||||
|
||||
async createBtcDeposit(data: {
|
||||
clientId: string;
|
||||
basketId?: string;
|
||||
mandateName?: string;
|
||||
chain138VaultAddress: string;
|
||||
allocations?: BasketAllocation[];
|
||||
expectedAmountSats?: number;
|
||||
clientReference?: string;
|
||||
}): Promise<{ deposit: BtcDeposit; basket: BasketMandate; createdBasket: boolean }> {
|
||||
const response = await this.client.post<{
|
||||
deposit: BtcDeposit;
|
||||
basket: BasketMandate;
|
||||
createdBasket: boolean;
|
||||
}>('/api/v1/btc-deposits', data);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
async getBtcDeposit(id: string): Promise<BtcDeposit> {
|
||||
const response = await this.client.get<{ deposit: BtcDeposit }>(`/api/v1/btc-deposits/${id}`);
|
||||
return response.data.deposit;
|
||||
}
|
||||
|
||||
async getHoldings(filters?: { clientId?: string; basketId?: string }): Promise<Holding[]> {
|
||||
const response = await this.client.get<{ holdings: Holding[] }>('/api/v1/holdings', {
|
||||
params: filters,
|
||||
});
|
||||
return response.data.holdings;
|
||||
}
|
||||
|
||||
async getRebalances(filters?: { clientId?: string; basketId?: string }): Promise<Rebalance[]> {
|
||||
const response = await this.client.get<{ rebalances: Rebalance[] }>('/api/v1/rebalances', {
|
||||
params: filters,
|
||||
});
|
||||
return response.data.rebalances;
|
||||
}
|
||||
|
||||
async requestBridgeWithdrawal(data: {
|
||||
clientId: string;
|
||||
basketId: string;
|
||||
sourceSymbol?: string;
|
||||
destinationSymbol?: string;
|
||||
destinationChainId: number;
|
||||
destinationAddress: string;
|
||||
amount: string;
|
||||
}): Promise<BridgeWithdrawal> {
|
||||
const response = await this.client.post<{ withdrawal: BridgeWithdrawal }>(
|
||||
'/api/v1/withdrawals/bridge',
|
||||
data,
|
||||
);
|
||||
return response.data.withdrawal;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user