@moneygraph/sdk
Version:
AI-native SDK for global payouts powered by StratosPay
217 lines (216 loc) • 6.45 kB
JavaScript
;
/**
* MoneyGraph SDK - Liquidity Module
*
* FX quotes with Quote & Confirm pattern:
* 1. getQuote() - Get a rate quote (valid for 2 minutes)
* 2. confirmQuote() - Lock in the rate before executing payout
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.LiquidityModule = void 0;
const client_1 = require("../api/client");
// Mock FX rates for sandbox (USD as base)
const MOCK_RATES = {
// Major currencies
USD_EUR: 0.92,
USD_GBP: 0.79,
USD_CAD: 1.36,
USD_AUD: 1.53,
USD_JPY: 149.50,
USD_CHF: 0.88,
USD_CNY: 7.24,
USD_INR: 83.12,
USD_SGD: 1.34,
USD_HKD: 7.82,
USD_KRW: 1320.00,
USD_MXN: 17.15,
USD_BRL: 4.97,
// African currencies
USD_NGN: 1550.00,
USD_KES: 153.50,
USD_ZAR: 18.50,
USD_GHS: 12.50,
USD_UGX: 3750.00,
USD_TZS: 2510.00,
USD_XOF: 605.00,
USD_XAF: 605.00,
USD_EGP: 30.90,
USD_MAD: 10.05,
// Europe
USD_PLN: 4.02,
USD_CZK: 22.50,
USD_HUF: 355.00,
USD_RON: 4.58,
USD_NOK: 10.70,
USD_SEK: 10.55,
USD_DKK: 6.88,
// Middle East
USD_AED: 3.67,
USD_SAR: 3.75,
USD_ILS: 3.70,
// Asia
USD_PHP: 55.80,
USD_THB: 35.20,
USD_VND: 24500.00,
USD_IDR: 15650.00,
USD_MYR: 4.70,
USD_PKR: 278.00,
USD_BDT: 110.00,
// Crypto (relative to USD)
USD_USDC: 1.00,
USD_USDT: 1.00,
USD_ETH: 0.00030,
USD_BTC: 0.000015,
};
// Sandbox quote storage
const sandboxQuotes = new Map();
function generateQuoteId() {
return `quote_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
}
function getRate(from, to) {
// Direct rate
const directKey = `${from}_${to}`;
if (MOCK_RATES[directKey]) {
return MOCK_RATES[directKey];
}
// Reverse rate
const reverseKey = `${to}_${from}`;
if (MOCK_RATES[reverseKey]) {
return 1 / MOCK_RATES[reverseKey];
}
// Cross rate via USD
const fromUsdKey = `USD_${from}`;
const toUsdKey = `USD_${to}`;
if (from === 'USD' && MOCK_RATES[toUsdKey]) {
return MOCK_RATES[toUsdKey];
}
if (to === 'USD' && MOCK_RATES[fromUsdKey]) {
return 1 / MOCK_RATES[fromUsdKey];
}
// Cross via USD
const fromToUsd = MOCK_RATES[fromUsdKey] ? 1 / MOCK_RATES[fromUsdKey] : null;
const usdToTo = MOCK_RATES[toUsdKey] || null;
if (fromToUsd !== null && usdToTo !== null) {
return fromToUsd * usdToTo;
}
// Default fallback
return 1.0;
}
function calculateFee(amount) {
// 1% fee with $2 minimum
return Math.max(amount * 0.01, 2);
}
class LiquidityModule {
constructor(client) {
this.client = client;
}
/**
* Get an FX quote
* Quote is valid for 2 minutes
*/
async getQuote(params) {
if (this.client.isSandbox) {
const rate = getRate(params.from, params.to);
const fee = calculateFee(params.amount);
const toAmount = (params.amount - fee) * rate;
const quote = {
id: generateQuoteId(),
from_currency: params.from,
to_currency: params.to,
from_amount: params.amount,
to_amount: Math.round(toAmount * 100) / 100,
rate,
fee,
expires_at: new Date(Date.now() + 2 * 60 * 1000).toISOString(),
created_at: new Date().toISOString(),
};
sandboxQuotes.set(quote.id, { ...quote, confirmed: false });
return quote;
}
return this.client.post('/liquidity/quote', params);
}
/**
* Confirm a quote (lock in the rate)
* Must be called before the quote expires
*/
async confirmQuote(quoteId) {
if (this.client.isSandbox) {
const quote = sandboxQuotes.get(quoteId);
if (!quote) {
throw new client_1.MoneyGraphError('Quote not found', 'QUOTE_NOT_FOUND', 404);
}
if (new Date(quote.expires_at) < new Date()) {
throw new client_1.MoneyGraphError('Quote has expired', 'QUOTE_EXPIRED', 400);
}
if (quote.confirmed) {
throw new client_1.MoneyGraphError('Quote already confirmed', 'VALIDATION_ERROR', 400);
}
quote.confirmed = true;
sandboxQuotes.set(quoteId, quote);
return {
id: crypto.randomUUID(),
quote_id: quoteId,
locked_rate: quote.rate,
transaction_id: `txn_${Date.now()}`,
status: 'confirmed',
confirmed_at: new Date().toISOString(),
};
}
return this.client.post(`/liquidity/quote/${quoteId}/confirm`);
}
/**
* Check if a quote is still valid
*/
isQuoteValid(quote) {
return new Date(quote.expires_at) > new Date();
}
/**
* Get remaining time in seconds until quote expires
*/
getQuoteTimeRemaining(quote) {
const remaining = new Date(quote.expires_at).getTime() - Date.now();
return Math.max(0, Math.floor(remaining / 1000));
}
/**
* Get available exchange rates for a currency pair
*/
async getRates(fromCurrency, toCurrency) {
if (this.client.isSandbox) {
const rate = getRate(fromCurrency, toCurrency);
return {
rate,
inverse_rate: 1 / rate,
fee_percent: 1,
min_amount: 10,
max_amount: 100000,
};
}
// In live mode, get quote for $100 to determine rate
const quote = await this.getQuote({
from: fromCurrency,
to: toCurrency,
amount: 100,
});
return {
rate: quote.rate,
inverse_rate: 1 / quote.rate,
fee_percent: (quote.fee / quote.from_amount) * 100,
min_amount: 10,
max_amount: 100000,
};
}
/**
* Clean up expired quotes (sandbox only)
*/
cleanupExpiredQuotes() {
if (!this.client.isSandbox)
return;
const now = Date.now();
for (const [id, quote] of sandboxQuotes.entries()) {
if (new Date(quote.expires_at).getTime() < now) {
sandboxQuotes.delete(id);
}
}
}
}
exports.LiquidityModule = LiquidityModule;