UNPKG

@ledgerhq/coin-stacks

Version:
217 lines 7.46 kB
import { getEnv } from "@ledgerhq/live-env"; import network from "@ledgerhq/live-network/network"; import { extractTokenTransferTransactions, extractSendManyTransactions, extractContractTransactions, } from "./transformers"; import { makeLRUCache, minutes } from "@ledgerhq/live-network/cache"; import { StacksMainnet, StacksTestnet } from "@stacks/network"; export const StacksNetwork = { mainnet: new StacksMainnet({ url: getEnv("API_STACKS_ENDPOINT") }), testnet: new StacksTestnet(), }; /** * Builds the Stacks API URL with an optional path */ const getStacksURL = (path) => { const baseUrl = getEnv("API_STACKS_ENDPOINT"); if (!baseUrl) throw new Error("API base URL not available"); return `${baseUrl}${path ? path : ""}`; }; /** * Basic GET request to the Stacks API */ const fetch = async (path) => { const url = getStacksURL(path); // We force data to this way as network func is not using the correct param type. Changing that func will generate errors in other implementations const opts = { method: "GET", url, }; const rawResponse = await network(opts); // We force data to this way as network func is not using the correct param type. Changing that func will generate errors in other implementations const { data } = rawResponse; return data; }; /** * Basic POST request with JSON data to the Stacks API */ const send = async (path, data) => { const url = getStacksURL(path); const opts = { method: "POST", url, data: JSON.stringify(data), headers: { "Content-Type": "application/json" }, }; const rawResponse = await network(opts); // We force data to this way as network func is not using generics. Changing that func will generate errors in other implementations const { data: responseData } = rawResponse; return responseData; }; /** * Basic POST request with raw binary data to the Stacks API */ const sendRaw = async (path, data) => { const url = getStacksURL(path); const opts = { method: "POST", url, data, headers: { "Content-Type": "application/octet-stream" }, }; const rawResponse = await network(opts); // We force data to this way as network func is not using generics. Changing that func will generate errors in other implementations const { data: responseData } = rawResponse; return responseData; }; /** * Fetches STX balance for an address */ export const fetchBalances = async (addr) => { const data = await fetch(`/extended/v1/address/${addr}/stx`); return data; }; /** * Fetches a page of token balances for an address */ export const fetchTokenBalancesPage = async (addr, offset = 0, limit = 50) => { try { const response = await fetch(`/extended/v2/addresses/${addr}/balances/ft?offset=${offset}&limit=${limit}`); return response; } catch { return { limit, offset, total: 0, results: [] }; } }; /** * Fetches all token balances for an address by paginating through results */ export const fetchAllTokenBalances = async (addr) => { const limit = 50; let offset = 0; let total = 0; const tokenBalanceMap = {}; do { const response = await fetchTokenBalancesPage(addr, offset, limit); // Map token balances to a more convenient format for (const item of response.results) { tokenBalanceMap[item.token.toLowerCase()] = item.balance; } offset += limit; total = response.total; } while (offset < total); return tokenBalanceMap; }; /** * Fetches estimated fees for a transfer */ export const fetchEstimatedFees = async (request) => { // Cast to Record<string, unknown> to satisfy type constraints const feeRate = await send(`/v2/fees/transfer`, request); return feeRate; }; /** * Fetches current blockchain status, including block height */ export const fetchBlockHeight = async () => { const data = await fetch("/extended"); return data; }; /** * Fetches a page of transactions for an address */ export const fetchTransactionsPage = async (addr, offset = 0, limit = 50) => { try { const response = await fetch(`/extended/v2/addresses/${addr}/transactions?offset=${offset}&limit=${limit}`); return response; } catch { return { limit, offset, total: 0, results: [] }; } }; /** * Fetches all transactions for an address */ export const fetchAllTransactions = async (addr) => { let qty; let offset = 0; const limit = 50; const allTransactions = []; // Fetch all transactions in pages do { const { results, total } = await fetchTransactionsPage(addr, offset, limit); allTransactions.push(...results); offset += limit; qty = total; } while (offset < qty); return allTransactions; }; /** * Fetches all transactions for an address and organizes them by type */ export const fetchFullTxs = async (addr) => { // 1. Fetch all transactions const allTransactions = await fetchAllTransactions(addr); // 2. Extract regular token transfers const tokenTransfers = extractTokenTransferTransactions(allTransactions); // 3. Extract and group contract calls const contractTransactions = await extractContractTransactions(allTransactions); // 4. Add send-many transactions to token transfers const sendManyTransactions = extractSendManyTransactions(allTransactions); tokenTransfers.push(...sendManyTransactions); return [tokenTransfers, contractTransactions]; }; /** * Broadcasts a signed transaction to the Stacks network */ export const broadcastTx = async (message) => { let response = await sendRaw(`/v2/transactions`, message); if (response !== "") response = `0x${response}`; return response; }; /** * Fetches a page of mempool transactions for an address */ export const fetchMempoolTransactionsPage = async (addr, offset = 0, limit = 50) => { const response = await fetch(`/extended/v1/tx/mempool?sender_address=${addr}&offset=${offset}&limit=${limit}`); return response; }; /** * Fetches all mempool transactions for an address */ export const fetchFullMempoolTxs = async (addr) => { let qty; let offset = 0; const limit = 50; let txs = []; do { const { results, total } = await fetchMempoolTransactionsPage(addr, offset, limit); txs = txs.concat(results); offset += limit; qty = total; } while (offset < qty); return txs; }; /** * Fetches the nonce for an address */ export const fetchNonce = async (addr) => { const response = await fetch(`/extended/v1/address/${addr}/nonces`); return response; }; /** * Fetches metadata for a fungible token by contract address * This can be used to extract the asset_identifier from a contract_id * @param contractAddress - The contract address in format: ADDRESS.CONTRACT_NAME * @returns The fungible token metadata including asset_identifier */ export const fetchFungibleTokenMetadata = async (contractAddress) => { const url = `/metadata/v1/ft?address=${contractAddress}`; const response = await fetch(url); return response; }; /** * Fetches metadata for a fungible token by contract address with caching */ export const fetchFungibleTokenMetadataCached = makeLRUCache(fetchFungibleTokenMetadata, args => args, minutes(60)); //# sourceMappingURL=api.js.map