@faktoryfun/styx-sdk
Version:
Bitcoin deposit SDK for Stacks applications, enabling trustless Bitcoin-to-sBTC deposits
330 lines (329 loc) • 11.6 kB
JavaScript
export class BitcoinDepositAPI {
constructor(baseUrl, apiKey) {
this.baseUrl = baseUrl;
// Use provided API key or fall back to a hardcoded one
this.apiKey = apiKey || "key";
// Create headers with authorization
this.headers = {
Authorization: `Bearer ${this.apiKey}`,
"Content-Type": "application/json",
};
}
async fetch(endpoint, options = {}) {
const url = `${this.baseUrl}${endpoint}`;
const response = await fetch(url, {
...options,
headers: {
...this.headers,
...options.headers,
},
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.json();
}
// ===========================================
// ALL YOUR EXISTING METHODS STAY EXACTLY THE SAME
// ===========================================
async getFeeEstimates() {
try {
const data = await this.fetch("/deposits/sdk/bitcoin-fees");
return data;
}
catch (error) {
console.error("Error fetching fee rates:", error);
// Default fallback values with proper separation
return { low: 1, medium: 2, high: 5 };
}
}
async createDeposit(data) {
var _a, _b, _c;
try {
const depositData = {
...data,
isBlaze: (_a = data.isBlaze) !== null && _a !== void 0 ? _a : false,
poolId: (_b = data.poolId) !== null && _b !== void 0 ? _b : "main",
swapType: (_c = data.swapType) !== null && _c !== void 0 ? _c : "sbtc", // Default to regular sBTC deposit
};
// Only add these fields if they exist (for AI BTC pool or swaps)
if (data.minTokenOut !== undefined) {
depositData.minTokenOut = data.minTokenOut;
}
if (data.dexId !== undefined) {
depositData.dexId = data.dexId;
}
if (data.aiAccountReceiver) {
depositData.aiAccountReceiver = data.aiAccountReceiver;
}
// Use the same endpoint as the frontend hook
const responseData = await this.fetch("/deposits", {
method: "POST",
body: JSON.stringify(depositData),
});
// Handle different response formats, matching the logic in your hook
const result = typeof responseData === "object" && "data" in responseData
? responseData.data
: responseData;
if (result && typeof result === "object" && "id" in result) {
return result.id;
}
if (typeof result === "string") {
return result;
}
console.error("Unexpected response format:", result);
throw new Error("Invalid response format from server");
}
catch (error) {
console.error("Error creating deposit:", error);
throw error;
}
}
async updateDeposit(data) {
try {
const responseData = await this.fetch(`/deposits/sdk/${data.id}`, {
method: "PATCH",
body: JSON.stringify(data.data),
});
return responseData;
}
catch (error) {
console.error("Error updating deposit:", error);
throw error;
}
}
async getDepositHistory(userAddress) {
try {
const responseData = await this.fetch(`/deposits/sdk/user/${userAddress}`);
return responseData;
}
catch (error) {
console.error("Error fetching deposit history:", error);
return [];
}
}
async getAllDepositsHistory(poolId) {
try {
// Add support for optional poolId parameter
let endpoint = "/deposits/sdk/list";
if (poolId) {
endpoint += `?poolId=${encodeURIComponent(poolId)}`;
}
const responseData = await this.fetch(endpoint);
return responseData;
}
catch (error) {
console.error("Error fetching all deposits:", error);
return {
aggregateData: {
totalDeposits: 0,
totalVolume: 0,
uniqueUsers: 0,
},
recentDeposits: [],
};
}
}
async prepareTransaction(params) {
var _a;
try {
const prepareParams = {
...params,
poolId: (_a = params.poolId) !== null && _a !== void 0 ? _a : "main",
};
// Only add these fields if they exist
if (params.swapType) {
prepareParams.swapType = params.swapType;
}
if (params.minTokenOut !== undefined) {
prepareParams.minTokenOut = params.minTokenOut;
}
if (params.dexId) {
prepareParams.dexId = params.dexId;
}
if (params.aiAccountReceiver) {
prepareParams.aiAccountReceiver = params.aiAccountReceiver;
}
const responseData = await this.fetch("/deposits/sdk/prepare-transaction", {
method: "POST",
body: JSON.stringify(prepareParams),
});
return responseData;
}
catch (error) {
console.error("Error preparing transaction:", error);
throw error;
}
}
async updateDepositStatus(params) {
try {
// Use PUT instead of PATCH to match the frontend hook
const responseData = await this.fetch(`/deposits/${params.id}`, {
method: "PUT",
body: JSON.stringify(params.data),
});
// Check if responseData is of type { data?: Deposit }
if (responseData &&
typeof responseData === "object" &&
"data" in responseData) {
if (responseData.data) {
return responseData.data;
}
throw new Error("Invalid response: missing deposit data");
}
// If we get here, responseData should be a Deposit
// We need to verify it has the required fields
if (responseData &&
typeof responseData === "object" &&
"id" in responseData &&
"btcAmount" in responseData &&
"stxReceiver" in responseData) {
return responseData;
}
throw new Error("Invalid response format from server");
}
catch (error) {
console.error("Error updating deposit status:", error);
throw error;
}
}
async executeTransaction(params) {
try {
const responseData = await this.fetch("/deposits/sdk/execute-transaction", {
method: "POST",
body: JSON.stringify(params),
});
return responseData;
}
catch (error) {
console.error("Error executing transaction:", error);
throw error;
}
}
async getPoolStatus(poolId) {
try {
let endpoint = "/pool-status";
if (poolId) {
endpoint += `?poolId=${encodeURIComponent(poolId)}`;
}
const responseData = await this.fetch(endpoint);
return responseData;
}
catch (error) {
console.error("Error fetching pool status:", error);
return {
realAvailable: 0,
estimatedAvailable: 0,
lastUpdated: Date.now(),
};
}
}
async getBTCPrice() {
try {
const responseData = await this.fetch("/fetchBtcPrice");
return responseData.price || null;
}
catch (error) {
console.error("Error fetching BTC price:", error);
return null;
}
}
async getDepositStatus(depositId) {
try {
const responseData = await this.fetch(`/deposits/sdk/${depositId}`);
return responseData;
}
catch (error) {
console.error(`Error fetching status for deposit ${depositId}:`, error);
throw error;
}
}
async getDepositStatusByTxId(btcTxId) {
try {
const responseData = await this.fetch(`/deposits/sdk/tx/${btcTxId}`);
return responseData;
}
catch (error) {
console.error(`Error fetching status for Bitcoin transaction ${btcTxId}:`, error);
throw error;
}
}
// ===========================================
// NEW: AI BTC Pool specific methods (minimal additions)
// ===========================================
async getAvailablePools() {
try {
const pools = await this.fetch("/pools/available");
return pools;
}
catch (error) {
console.error("Error fetching available pools:", error);
return [];
}
}
async getAllowlistedPairs(poolId = "aibtc") {
try {
const pairs = await this.fetch(`/pools/${poolId}/allowlisted-pairs`);
return pairs;
}
catch (error) {
console.error("Error fetching allowlisted pairs:", error);
return [];
}
}
async getSwapStatus(poolId = "aibtc") {
try {
const status = await this.fetch(`/pools/${poolId}/swap-status`);
return status;
}
catch (error) {
console.error("Error fetching swap status:", error);
return {
paused: false,
poolId,
network: "unknown",
checkedAt: Date.now(),
error: "Failed to fetch swap status",
};
}
}
// Admin method to check/add FT pairs
async checkFtPair(ftContract, poolId = "aibtc") {
try {
const response = await this.fetch(`/admin/pools/${poolId}/check-ft-pair`, {
method: "POST",
body: JSON.stringify({ ftContract }),
});
return response;
}
catch (error) {
console.error("Error checking FT pair:", error);
return {
success: false,
error: "Failed to check FT pair",
ftContract,
};
}
}
// Helper method to check if a specific FT/DEX pair is allowlisted
async isTokenAllowlisted(ftContract, poolId = "aibtc") {
try {
const pairs = await this.getAllowlistedPairs(poolId);
return pairs.some((pair) => pair.ftContract === ftContract && pair.isActive);
}
catch (error) {
console.error("Error checking if token is allowlisted:", error);
return false;
}
}
async areSwapsPaused(poolId = "aibtc") {
try {
const response = await this.fetch(`/pools/${poolId}/swap-status`);
return response.paused;
}
catch (error) {
console.error("Error checking swap status:", error);
return false; // Default to not paused
}
}
}