satsterminal-sdk v1.6.3
SatsTerminal SDK
A TypeScript/JavaScript SDK for interacting with the SatsTerminal ecosystem. This SDK allows developers to integrate SatsTerminal functionalities into their Node.js applications easily.
Version Changelog
v1.6.3
- Added RBF protection support for rune transactions
v1.6.2
- Added support for multiple marketplaces
- Performance improvements and bug fixes
Table of Contents
Installation
Install the SDK via npm:
npm install satsterminal-sdk@latest
Environment Variables
The SDK requires configuration including an API key (which you can get from the SatsTerminal Admin Dashboard). You can set this up using environment variables or pass it directly to the constructor.
Setting Environment Variables
Create a .env
file in your project root:
SATSTERMINAL_API_KEY=your_api_key_here
Load it using dotenv:
require('dotenv').config();
Quick Start
import { SatsTerminal } from 'satsterminal-sdk';
const satsTerminal = new SatsTerminal({
apiKey: 'your_api_key_here'
});
// Example: Searching for runes
const searchResult = await satsTerminal.search({ rune_name: 'DOG•GO•TO•THE•MOON' });
API Reference
Configuration
The SDK requires configuration when initializing:
interface SatsTerminalConfig {
apiKey: string;
}
Methods
signIn
Register and authenticate a user with their Bitcoin and Ordinals addresses.
async signIn(params: SignInParams): Promise<SignInResponse>
Parameters:
ord_address
(string): Ordinals addressbtc_address
(string): Bitcoin addressord_public_key
(string): Ordinals public keybtc_public_key
(string): Bitcoin public keyprovider
(string): Wallet provider (e.g., 'xverse', 'unisat')
Example:
const signIn = await satsTerminal.signIn({
ord_address: "bc1...",
btc_address: "3Pc...",
ord_public_key: "...",
btc_public_key: "...",
provider: "xverse"
});
bind
Bind a user's wallet to Unisat and DotSwap. This is required only once before performing transactions.
async bind(params: BindParams): Promise<BindResponse>
Parameters:
btcAddress
(string): Bitcoin addressnftAddress
(string): Ordinals addresssign
(string): User's signature of the bind message
Example:
const bind = await satsTerminal.bind({
btcAddress: "3Pc...",
nftAddress: "bc1...",
sign: "user_signature_here"
});
points
Get the user's accumulated Amber points balance.
async points(params: PointsParams): Promise<PointsResponse>
Parameters:
ord_address
(string): Ordinals address
Example:
const points = await satsTerminal.points({
ord_address: "bc1..."
});
search
Search for runes by name.
async search(params: SearchParams): Promise<SearchResponse>
Parameters:
rune_name
(string): The name of the rune to search forsell
(boolean, optional): Whether to search for sell orders
Example:
const result = await satsTerminal.search({
rune_name: 'LOBO•THE•WOLF•PUP',
sell: false
});
popularCollections
Fetch popular rune collections.
async popularCollections(params: PopularCollectionsParams): Promise<PopularCollectionsResponse>
Example:
const collections = await satsTerminal.popularCollections();
fetchQuote
Fetch a quote for a rune purchase.
async fetchQuote(params: QuoteParams): Promise<QuoteResponse>
Parameters:
btcAmount
(number/string): Amount of BTC to spendruneName
(string): Name of the runeaddress
(string): Bitcoin addressthemeID?
(string): Optional widget theme IDsell?
(boolean): Optional flag for sell orders (default: false)marketplaces?
(string[]): Optional list of marketplaces to use (default: empty, uses all available)rbfProtection?
(boolean): Optional RBF protection flag (default: false)
Example:
const quote = await satsTerminal.fetchQuote({
btcAmount: 0.0001,
runeName: "LOBO•THE•WOLF•PUP",
address: "bc1...",
marketplaces: ["MagicEden"],
rbfProtection: true
});
Example Response:
{
bestMarketplace: 'MagicEden',
selectedOrders: [
{
amount: '2039350000000',
formattedAmount: '20393.5',
id: '5b35441a-297b-45b4-8277-a3048309837b',
isPending: false,
rune: 'LOBOTHEWOLFPUP',
maker: 'bc1p97mujnqerk098gnd9actkpev6waeauvzk2yt3zznllshz5evz66q55vjw6',
makerReceiveAddress: '393PuAEiJ7JTFmVvkJJSG42s7241gG1ncC',
makerFeeBps: 0,
price: 10000,
side: 'sell',
status: 'valid',
formattedUnitPrice: '0.490352',
utxoIds: [
'f99df727b7268bd4662798a8893d965940a63a68b77bdcc92d924ccecee66035:0'
],
filledAmount: '0',
formattedFilledAmount: '0',
rbfProtectionShield: true,
rbfProtectedSacAddress: 'bc1pg5lj78n75nhmvv47xq8y0kf6wzzed6ewesuq9p4qu6hmsnketz9sgmfdeh',
market: 'MagicEden'
}
],
totalFormattedAmount: '20393.5',
totalPrice: '0.0001',
metrics: { /* metrics data */ },
marketplaces: { /* marketplace data */ }
}
getPSBT
Generate a Partially Signed Bitcoin Transaction (PSBT).
async getPSBT(params: GetPSBTParams): Promise<PSBTResponse>
Parameters:
orders
(RuneOrder[]): Array of orders to include (from fetchQuote)address
(string): Bitcoin addresspublicKey
(string): Bitcoin public keypaymentAddress
(string): Payment addresspaymentPublicKey
(string): Payment public keyutxos?
(UTXO[]): Optional UTXOs to usefeeRate?
(number): Optional fee rateruneName?
(string): Optional rune nameslippage?
(number): Optional slippage percentagethemeID?
(string): Optional widget theme IDsell?
(boolean): Optional flag for sell orders (default: false)rbfProtection?
(boolean): Optional RBF protection flag (default: false)
Example:
const psbtResponse = await satsTerminal.getPSBT({
orders: quote.selectedOrders,
address: "bc1...",
publicKey: "...",
paymentAddress: "3Pc...",
paymentPublicKey: "...",
feeRate: 5,
runeName: "LOBO•THE•WOLF•PUP",
slippage: 9,
rbfProtection: true
});
Example Response:
{
marketplace: 'magiceden',
swapId: '0b0fef9zmab9-bocn3afkmr',
validOrders: [ '5b35441a-297b-45b4-8277-a3048309837b' ],
rbfProtected: {
base64: 'cHNidP8BAKkCAAAAASdY5eq28qE1btcTbldfKCNVaaBNEBo6EYpvVKrVBaGqAQAAAAD/////AyMqAAAAAAAAIlEg1pNbOdpsqSDkdCcK+uN7LUaajvtMQqFlLaugrMI2cvSSBwAAAAAAACJRINaTWznabKkg5HQnCvrjey1Gmo77TEKhZS2roKzCNnL0VbIAAAAAAAAXqRTwcmaf/igf0hSlNZd3ss2y7lyJpYcAAAAACPwCbWUDc2lnQHpbBxAVPvbM3I975/SZDbOrmdw+3KjUk0gxbYeYxwVqAHdIYFxtfXbHHW5lMHU51KNrFa+h0OFOfVHVpSHUL/gJ/AJtZQRwZmVlAzU4MAv8Am1lBnNpZ2V4cAhCeVg6nAVwABT8Am1lD3RyYW5zYWN0aW9uVHlwZQdyYmYtYnV5GPwCbWUTdGFrZXJQYXltZW50QWRkcmVzcyIzUGNQOUo2QzlaTlFwVFJIcnRxRkJoUDNMb0JqU21Na1pxGPwCbWUTdGFrZXJSZWNlaXZlQWRkcmVzcz5iYzFwdXhhMmN3dmZ3NDd2YWFkdDRsZnh3Z2w0emxuOWVwcXRmc3EzeTdjbDd2cjV3dmZheXo2czIybW13cAABASAS5QAAAAAAABepFPByZp/+KB/SFKU1l3eyzbLuXImlhwEEFgAUY9wnP/vXXCJ9d3tDwzvzOL1cNV8ACfwCbWUEbWZlZQQAAAAACfwCbWUEdGZlZQQAAABkEPwCbWULaXRlbUZlZVRpcHMFMTAwLDAQ/AJtZQtydW5lT3JkZXJJZCQ1YjM1NDQxYS0yOTdiLTQ1YjQtODI3Ny1hMzA0ODMwOTgzN2IAAAA=',
hex: '70736274ff0100a902000000012758e5eab6f2a1356ed7136e575f28235569a04d101a3a118a6f54aad505a1aa0100000000ffffffff03232a000000000000225120d6935b39da6ca920e474270afae37b2d469a8efb4c42a1652daba0acc23672f49207000000000000225120d6935b39da6ca920e474270afae37b2d469a8efb4c42a1652daba0acc23672f455b200000000000017a914f072669ffe281fd214a5359777b2cdb2ee5c89a5870000000008fc026d6503736967407a5b0710153ef6ccdc8f7be7f4990db3ab99dc3edca8d49348316d8798c7056a007748605c6d7d76c71d6e65307539d4a36b15afa1d0e14e7d51d5a521d42ff809fc026d650470666565033538300bfc026d6506736967657870084279583a9c05700014fc026d650f7472616e73616374696f6e54797065077262662d62757918fc026d651374616b65725061796d656e74416464726573732233506350394a3643395a4e517054524872747146426850334c6f426a536d4d6b5a7118fc026d651374616b657252656365697665416464726573733e6263317075786132637776667734377661616474346c667877676c347a6c6e3965707174667371337937636c3776723577766661797a367332326d6d77700001012012e500000000000017a914f072669ffe281fd214a5359777b2cdb2ee5c89a587010416001463dc273ffbd75c227d777b43c33bf338bd5c355f0009fc026d65046d666565040000000009fc026d650474666565040000006410fc026d650b6974656d46656554697073053130302c3010fc026d650b72756e654f7264657249642435623335343431612d323937622d343562342d383237372d613330343833303938333762000000',
inputs: [ 0 ]
}
}
confirmPSBT
Confirm and broadcast a signed PSBT.
async confirmPSBT(params: ConfirmPSBTParams): Promise<ConfirmPSBTResponse>
Parameters:
orders
(RuneOrder[]): Array of orders (from fetchQuote)address
(string): Bitcoin addresspublicKey
(string): Bitcoin public keypaymentAddress
(string): Payment addresspaymentPublicKey
(string): Payment public keysignedPsbtBase64
(string): Signed PSBT in base64 formatswapId
(string): Swap ID (from getPSBT)themeID?
(string): Optional widget theme IDsell?
(boolean): Optional flag for sell orders (default: false)runeName?
(string): Optional rune namemarketplaces?
(string[]): Optional list of marketplaces to userbfProtection?
(boolean): Optional RBF protection flag (default: false)signedRbfPsbtBase64?
(string): Optional signed RBF protection PSBT in base64 format
Example:
const confirmation = await satsTerminal.confirmPSBT({
orders: quote.selectedOrders,
address: "bc1...",
publicKey: "...",
paymentAddress: "3Pc...",
paymentPublicKey: "...",
signedPsbtBase64: "cHNid...",
signedRbfPsbtBase64: "cHNid...", // Only if rbfProtection is true
swapId: psbtResponse.swapId,
runeName: "LOBO•THE•WOLF•PUP",
rbfProtection: true
});
Example Response:
{
marketplace: 'magiceden',
txid: '4fb3ae350ae4366687a1e069b1d4de528f3dc9dc097382de041e979a8187312b',
rbfProtection: {
fundsPreparationTxId: '4fb3ae350ae4366687a1e069b1d4de528f3dc9dc097382de041e979a8187312b',
fulfillmentId: 'bd85a36b-7a54-458a-8585-e3af2b1a8fe1'
},
isRbfTxid: true
}
Example Flow
Below is a complete example flow for buying runes with RBF protection, including waiting for user input for signed PSBTs:
import { SatsTerminal } from 'satsterminal-sdk';
import readlineSync from 'readline-sync';
// Configuration
const CONFIG = {
apiKey: 'your_api_key_here',
baseURL: 'https://api.satsterminal.com/v1',
timeout: 30000 // 30 seconds timeout
};
// Trade Parameters
const TRADE_PARAMS = {
runeName: 'LOBO•THE•WOLF•PUP',
address: 'bc1p...',
publicKey: '...',
paymentAddress: '3Pc...',
paymentPublicKey: '...',
btcAmount: 0.0001,
rbfProtection: true,
marketplaces: ["MagicEden"]
};
// Initialize the SDK
const satsTerminal = new SatsTerminal(CONFIG);
// Function to get user input for signed PSBT
const getSignedPSBT = () => {
console.log('\nPlease sign the PSBT and enter the signed PSBT base64 string:');
return readlineSync.question('Signed PSBT: ');
};
// Function to get user input for signed RBF PSBT
const getSignedRbfPSBT = () => {
console.log('\nPlease sign the RBF protection PSBT and enter the signed RBF PSBT base64 string:');
return readlineSync.question('Signed RBF PSBT: ');
};
// Main execution
(async () => {
try {
// 1. Get a quote for the trade
console.log('\n1. Fetching quote...');
const quote = await satsTerminal.fetchQuote({
btcAmount: TRADE_PARAMS.btcAmount,
runeName: TRADE_PARAMS.runeName,
address: TRADE_PARAMS.address,
marketplaces: TRADE_PARAMS.marketplaces,
rbfProtection: TRADE_PARAMS.rbfProtection
});
console.log('Best marketplace:', quote.bestMarketplace);
console.log('Quote:', quote.selectedOrders);
// 2. Create a PSBT
console.log('\n2. Creating PSBT...');
const psbtResponse = await satsTerminal.getPSBT({
orders: quote.selectedOrders,
address: TRADE_PARAMS.address,
publicKey: TRADE_PARAMS.publicKey,
paymentAddress: TRADE_PARAMS.paymentAddress,
paymentPublicKey: TRADE_PARAMS.paymentPublicKey,
utxos: [],
feeRate: 5,
runeName: TRADE_PARAMS.runeName,
themeID: null,
sell: false,
slippage: 9,
rbfProtection: TRADE_PARAMS.rbfProtection
});
console.log('PSBT created:', psbtResponse);
// Wait for user to input the signed PSBT
const signedPsbtBase64 = getSignedPSBT();
// Get signed RBF PSBT if RBF protection is enabled
let signedRbfPsbtBase64 = null;
if (TRADE_PARAMS.rbfProtection && psbtResponse.rbfProtected.base64) {
console.log('\nRBF Protection PSBT:', psbtResponse.rbfProtected.base64);
signedRbfPsbtBase64 = getSignedRbfPSBT();
}
// 3. Confirm the PSBT after signing
console.log('\n3. Confirming PSBT...');
const confirmation = await satsTerminal.confirmPSBT({
orders: quote.selectedOrders,
address: TRADE_PARAMS.address,
publicKey: TRADE_PARAMS.publicKey,
paymentAddress: TRADE_PARAMS.paymentAddress,
paymentPublicKey: TRADE_PARAMS.paymentPublicKey,
signedRbfPsbtBase64: signedRbfPsbtBase64,
swapId: psbtResponse.swapId,
runeName: TRADE_PARAMS.runeName,
rbfProtection: TRADE_PARAMS.rbfProtection,
signedPsbtBase64: signedPsbtBase64
});
console.log('Confirmation:', confirmation);
} catch (error) {
console.error('Error:', error.message);
// Provide more helpful information for network errors
if (error.code === 'ECONNREFUSED') {
console.error('Connection refused. Make sure the API server is running and accessible.');
} else if (error.code === 'ECONNRESET' || error.message.includes('socket hang up')) {
console.error('Connection was reset or timed out. This could be due to:');
console.error('- Network connectivity issues');
console.error('- Server overload or maintenance');
console.error('- Invalid API key or authentication');
console.error('- Request timeout (the operation might take longer than expected)');
}
if (process.env.NODE_ENV === 'development') {
console.error('Full error:', error);
}
}
})();
Error Handling
The SDK throws errors with descriptive messages. Always wrap API calls in try-catch blocks:
try {
const result = await satsTerminal.search({ rune_name: 'PEPE' });
} catch (error) {
if (error.message.includes('API key')) {
console.error('Invalid API key');
} else if (error.code === 'ECONNRESET' || error.message.includes('socket hang up')) {
console.error('Network connection issue. Please try again.');
} else {
console.error('An error occurred:', error);
}
}
Common Issues
Invalid API Key
Ensure your API key is valid and properly configured. Only approved partners can use the SDK.
Network Errors
- Check your internet connection
- Verify the API endpoint is accessible
- Check if your firewall is blocking requests
- Consider increasing the timeout value in the configuration
PSBT Errors
- Ensure all required parameters are provided
- Ensure the PSBT is valid and signed
- Verify the format of public keys and addresses
- Check if UTXOs are valid and confirmed
RBF Protection Issues
- Make sure both the main PSBT and RBF protection PSBT are properly signed
- Verify that the RBF protection flag is set to true in all relevant method calls
- Ensure the signed RBF PSBT is passed to the confirmPSBT method
For additional support, contact our support team.
8 months ago
9 months ago
9 months ago
9 months ago
9 months ago
8 months ago
4 months ago
8 months ago
4 months ago
8 months ago
4 months ago
8 months ago
4 months ago
7 months ago
8 months ago
7 months ago
8 months ago
7 months ago
8 months ago
8 months ago
7 months ago
7 months ago
8 months ago
7 months ago
4 months ago
5 months ago
5 months ago
7 months ago
6 months ago
7 months ago
8 months ago
8 months ago
8 months ago
8 months ago
6 months ago
7 months ago
8 months ago
6 months ago
8 months ago
8 months ago
6 months ago
8 months ago
8 months ago
7 months ago
8 months ago
8 months ago
7 months ago
8 months ago
7 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago