@faktoryfun/styx-sdk
Version:
Bitcoin deposit SDK for Stacks applications, enabling trustless Bitcoin-to-sBTC deposits
517 lines (453 loc) • 12.9 kB
JavaScript
// patch/sdk-patch.js - Updated with regtest support
// BitcoinDepositAPI class
class BitcoinDepositAPI {
constructor(baseUrl, apiKey) {
this.baseUrl = baseUrl;
this.apiKey = apiKey || "key";
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();
}
async getFeeEstimates() {
try {
const data = await this.fetch("/deposits/sdk/bitcoin-fees");
return data;
} catch (error) {
console.error("Error fetching fee rates:", error);
return { low: 1, medium: 2, high: 5 };
}
}
// Updated createDeposit method with backward compatibility
async createDeposit(data) {
try {
const depositData = {
...data,
isBlaze: data.isBlaze ?? false,
poolId: data.poolId ?? "main",
swapType: data.swapType ?? "sbtc",
};
// 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;
}
const responseData = await this.fetch("/deposits", {
method: "POST",
body: JSON.stringify(depositData),
});
const result = 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 {
let endpoint = "/deposits/sdk/list";
if (poolId) {
endpoint += `?poolId=${encodeURIComponent(poolId)}`;
}
const responseData = await this.fetch(endpoint);
return (
responseData || {
aggregateData: {
totalDeposits: 0,
totalVolume: 0,
uniqueUsers: 0,
},
recentDeposits: [],
}
);
} catch (error) {
console.error("Error fetching all deposits:", error);
return {
aggregateData: {
totalDeposits: 0,
totalVolume: 0,
uniqueUsers: 0,
},
recentDeposits: [],
};
}
}
// Updated prepareTransaction with backward compatibility
async prepareTransaction(params) {
try {
const requestData = {
...params,
poolId: params.poolId ?? "main",
};
// Only add these fields if they exist
if (params.swapType) {
requestData.swapType = params.swapType;
}
if (params.minTokenOut !== undefined) {
requestData.minTokenOut = params.minTokenOut;
}
if (params.dexId !== undefined) {
requestData.dexId = params.dexId;
}
if (params.aiAccountReceiver) {
requestData.aiAccountReceiver = params.aiAccountReceiver;
}
const responseData = await this.fetch(
"/deposits/sdk/prepare-transaction",
{
method: "POST",
body: JSON.stringify(requestData),
}
);
return responseData;
} catch (error) {
console.error("Error preparing transaction:", error);
throw error;
}
}
async updateDepositStatus(params) {
try {
const responseData = await this.fetch(`/deposits/${params.id}`, {
method: "PUT",
body: JSON.stringify(params.data),
});
return responseData.data || responseData;
} 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
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",
};
}
}
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;
}
}
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 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,
};
}
}
}
// Constants
const MIN_DEPOSIT_SATS = 10000;
const MAX_DEPOSIT_SATS = 400000; // Legacy pool limit
const MAX_DEPOSIT_SATS_AIBTC = 1000000; // AI BTC pool limit
// Pool-specific limits
const POOL_LIMITS = {
main: { min: 10000, max: 400000 },
aibtc: { min: 10000, max: 1000000 },
};
function getPoolLimits(poolId = "main") {
return POOL_LIMITS[poolId] || POOL_LIMITS.main;
}
// Network configurations - UPDATED with regtest support
const NETWORK_CONFIGS = {
mainnet: {
apiUrl: "https://styx-be.vercel.app/api",
},
testnet: {
apiUrl: "https://backend-styx-testnet.vercel.app/api",
},
regtest: {
apiUrl: "https://backend-styx-testnet.vercel.app/api", // Your local backend for regtest
},
};
function getApiUrl(network = "mainnet") {
return NETWORK_CONFIGS[network]?.apiUrl || NETWORK_CONFIGS.mainnet.apiUrl;
}
// Default API credentials - now points to mainnet
const DEFAULT_API_URL = getApiUrl("mainnet");
const DEFAULT_API_KEY =
"jc_e4d2e10396eef95215a7afd492f42d743a3325739d29200c2a28b256f778be01";
// StyxSDK class - UPDATED with regtest support
class StyxSDK {
constructor(
baseUrl = DEFAULT_API_URL,
apiKey = DEFAULT_API_KEY,
network = "mainnet"
) {
this.network = network;
const apiUrl = baseUrl || getApiUrl(network);
this.api = new BitcoinDepositAPI(apiUrl, apiKey);
}
getCurrentNetwork() {
return this.network;
}
async getFeeEstimates() {
return this.api.getFeeEstimates();
}
async updateDeposit(data) {
return this.api.updateDeposit(data);
}
async getDepositHistory(userAddress) {
return this.api.getDepositHistory(userAddress);
}
async getAllDepositsHistory(poolId) {
return this.api.getAllDepositsHistory(poolId);
}
async prepareTransaction(params) {
return this.api.prepareTransaction(params);
}
async createDeposit(params) {
return this.api.createDeposit(params);
}
async updateDepositStatus(params) {
return this.api.updateDepositStatus(params);
}
async executeTransaction(params) {
return this.api.executeTransaction(params);
}
async getPoolStatus(poolId) {
return this.api.getPoolStatus(poolId);
}
async getBTCPrice() {
return this.api.getBTCPrice();
}
async getDepositStatus(depositId) {
return this.api.getDepositStatus(depositId);
}
async getDepositStatusByTxId(btcTxId) {
return this.api.getDepositStatusByTxId(btcTxId);
}
// NEW: AI BTC Pool methods
async getAvailablePools() {
return this.api.getAvailablePools();
}
async getAllowlistedPairs(poolId) {
return this.api.getAllowlistedPairs(poolId);
}
async areSwapsPaused(poolId) {
return this.api.areSwapsPaused(poolId);
}
async isTokenAllowlisted(ftContract, poolId) {
return this.api.isTokenAllowlisted(ftContract, poolId);
}
async checkFtPair(ftContract, poolId) {
return this.api.checkFtPair(ftContract, poolId);
}
// Convenience method for AI BTC deposits
async createAIBTCDeposit(data) {
return this.api.createDeposit({
...data,
poolId: "aibtc",
swapType: data.swapType || "aibtc",
});
}
// Helper to check DEX pair compatibility
async isDexPairAllowed(ftContract, dexContract, poolId = "aibtc") {
try {
const allowlistedPairs = await this.getAllowlistedPairs(poolId);
return allowlistedPairs.some(
(pair) =>
pair.ftContract === ftContract &&
pair.dexContract === dexContract &&
pair.isActive
);
} catch (error) {
console.error("Error checking DEX pair allowlist:", error);
return false;
}
}
}
// Transaction priorities
const TransactionPriority = {
Low: "low",
Medium: "medium",
High: "high",
};
// Create SDK instances - UPDATED with regtest support
const sdk = new StyxSDK();
const mainnetStyxSDK = new StyxSDK(undefined, DEFAULT_API_KEY, "mainnet");
const testnetStyxSDK = new StyxSDK(undefined, DEFAULT_API_KEY, "testnet");
const regtestStyxSDK = new StyxSDK(undefined, DEFAULT_API_KEY, "regtest"); // NEW
// Exports
module.exports = {
BitcoinDepositAPI,
StyxSDK,
MIN_DEPOSIT_SATS,
MAX_DEPOSIT_SATS,
MAX_DEPOSIT_SATS_AIBTC,
POOL_LIMITS,
getPoolLimits,
TransactionPriority,
styxSDK: sdk,
mainnetStyxSDK,
testnetStyxSDK,
regtestStyxSDK, // NEW
getApiUrl,
default: sdk,
};