🚨 Shai-Hulud Strikes Again:834 Packages Compromised.Technical Analysis β†’
Socket
Book a DemoInstallSign in
Socket

@shogun-sdk/money-legos

Package Overview
Dependencies
Maintainers
2
Versions
73
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@shogun-sdk/money-legos

Shogun Money Legos: clients and types for quotes, memes, prices, balances, fees, validations, etc.

latest
Source
npmnpm
Version
1.3.48-prod.3
Version published
Maintainers
2
Created
Source

@shogun-sdk/money-legos

Core SDK for cross-chain swaps, NFT purchases, token balances, and DeFi operations across multiple blockchains.

Quick Start

1. Install the Package

Choose your preferred package manager:

npm

npm install @shogun-sdk/money-legos

pnpm

pnpm add @shogun-sdk/money-legos

yarn

yarn add @shogun-sdk/money-legos

2. Initialize and Use

Set up the SDK and start making cross-chain operations:

import { init, LegoClient, OneShotClient, ShogunBalancesApiClient } from '@shogun-sdk/money-legos';

// 1. Initialize SDK with RPC URLs (recommended)
init({
  rpcUrls: {
    1: ['https://your-ethereum-rpc-url'],
    8453: ['https://your-base-rpc-url'],
    42161: ['https://your-arbitrum-rpc-url'],
    7565164: ['https://your-solana-rpc-url'],
  }
});

// 2. Initialize clients
const legoClient = new LegoClient({ 
  apiKey: 'YOUR_API_KEY' // Get from Shogun dashboard
});

const oneShotClient = new OneShotClient(
  'YOUR_API_KEY',
  'SHOGUN_API_URL'
);

const balancesClient = new ShogunBalancesApiClient('YOUR_API_KEY');

// 3. Example: Purchase NFT with any token
async function purchaseNFT() {
  const result = await legoClient.MagicEden.SwapForNFT({
    items: [{ 
      address: '0x7E0b0363804C6C79AAb9aB51850bF8F3D561f868', 
      tokenId: '42' 
    }],
    token: { 
      address: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', // WETH
      decimals: 18, 
      chainId: 1 
    },
    userAddress: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e'
  });
  
  if (result.status) {
    console.log('Success! Transaction steps:', result.data.steps);
  }
}

// 4. Example: Check token balances
async function checkBalances() {
  const evmBalances = await balancesClient.getEvmWalletBalance(
    '0x742d35Cc6634C0532925a3b844Bc454e4438f44e'
  );
  console.log('EVM Balances:', evmBalances);
}

3. Explore All Features

Check the sections below for comprehensive examples:

  • Configuration Options - RPC URLs, environment variables
  • Usage Examples - NFT purchases, cross-chain swaps, balance checking
  • Transaction Signing - EVM and Solana transaction execution
  • API Reference - Complete method documentation

Features

  • πŸ”„ Cross-Chain Swaps - Swap tokens between EVM chains and Solana
  • πŸ–ΌοΈ NFT Purchases - Buy NFTs on any chain using any supported token
  • πŸ’° Balance Checking - Check token balances across multiple chains
  • πŸ”— Multi-Chain Support - Ethereum, Base, Arbitrum, BSC, Bera Chain, Solana, Sonic
  • ⚑ Transaction Signing - Built-in support for EVM and Solana transactions
  • πŸ“Š Price Feeds - Real-time token pricing and market data
  • πŸ›‘οΈ Type Safety - Full TypeScript support with comprehensive types
  • πŸ”§ Flexible Configuration - Multiple RPC configuration options

Configuration Options

RPC URL Configuration

Best for Next.js and when you want full control over RPC URLs:

import { init } from '@shogun-sdk/money-legos';

init({
  rpcUrls: {
    1: ['https://your-ethereum-rpc-url'],
    8453: ['https://your-base-rpc-url'],
    42161: ['https://your-arbitrum-rpc-url'],
    56: ['https://your-bsc-rpc-url'],
    80094: ['https://your-berachain-rpc-url'],
    7565164: ['https://your-solana-rpc-url'],
    146: ['https://your-sonic-rpc-url'],
  }
});

Option 2: Environment Variables

Node.js (.env)

RPC_URL_1=https://your-ethereum-rpc-url
RPC_URL_8453=https://your-base-rpc-url
RPC_URL_42161=https://your-arbitrum-rpc-url
RPC_URL_56=https://your-bsc-rpc-url
RPC_URL_80094=https://your-berachain-rpc-url
RPC_URL_7565164=https://your-solana-rpc-url
RPC_URL_146=https://your-sonic-rpc-url

Next.js (.env.local)

NEXT_PUBLIC_RPC_URL_1=https://your-ethereum-rpc-url
NEXT_PUBLIC_RPC_URL_8453=https://your-base-rpc-url
NEXT_PUBLIC_RPC_URL_42161=https://your-arbitrum-rpc-url
NEXT_PUBLIC_RPC_URL_56=https://your-bsc-rpc-url
NEXT_PUBLIC_RPC_URL_80094=https://your-berachain-rpc-url
NEXT_PUBLIC_RPC_URL_7565164=https://your-solana-rpc-url
NEXT_PUBLIC_RPC_URL_146=https://your-sonic-rpc-url

Vite (.env)

VITE_RPC_URL_1=https://your-ethereum-rpc-url
VITE_RPC_URL_8453=https://your-base-rpc-url
VITE_RPC_URL_42161=https://your-arbitrum-rpc-url
# ... and so on

Option 3: Vite Plugin Configuration

For Vite applications, use the env-compatible plugin:

// vite.config.ts
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import envCompatible from 'vite-plugin-env-compatible';

export default defineConfig({
  plugins: [
    react(),
    envCompatible({
      prefix: 'RPC_URL_',
    }),
  ],
});

Supported Chains

import { 
  ETHEREUM_CHAIN_ID,    // 1
  BASE_CHAIN_ID,        // 8453
  ARBITRUM_CHAIN_ID,    // 42161
  BSC_CHAIN_ID,         // 56
  BERA_CHAIN_ID,        // 80094
  SOLANA_CHAIN_ID,      // 7565164
  SONIC_CHAIN_ID,       // 146
  getSupportedChains,
  CHAIN_MAP 
} from '@shogun-sdk/money-legos';

// Get all supported chains
const supportedChains = getSupportedChains();
console.log('Supported chains:', supportedChains);

Usage Examples

NFT Purchase Examples

Basic NFT Purchase

import { LegoClient } from '@shogun-sdk/money-legos';

const legoClient = new LegoClient({ apiKey: 'YOUR_API_KEY' });

// Purchase single NFT with ETH
async function purchaseNFTWithETH() {
  const result = await legoClient.MagicEden.SwapForNFT({
    items: [{ 
      address: '0x7E0b0363804C6C79AAb9aB51850bF8F3D561f868', 
      tokenId: '42' 
    }],
    token: { 
      address: '0x0000000000000000000000000000000000000000', // ETH
      decimals: 18, 
      chainId: 1 
    },
    userAddress: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e'
  });

  if (result.status) {
    console.log('Success! Transaction steps:', result.data.steps);
    // Execute transaction steps...
  } else {
    console.error('Failed:', result.error);
  }
}

Multiple NFTs Purchase

// Purchase multiple NFTs in one transaction
async function purchaseMultipleNFTs() {
  const result = await legoClient.MagicEden.SwapForNFT({
    items: [
      { address: '0x7E0b0363804C6C79AAb9aB51850bF8F3D561f868', tokenId: '42' },
      { address: '0x7E0b0363804C6C79AAb9aB51850bF8F3D561f868', tokenId: '43' },
      { address: '0x7E0b0363804C6C79AAb9aB51850bF8F3D561f868', tokenId: '44' }
    ],
    token: { 
      address: '0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599', // WBTC
      decimals: 8, 
      chainId: 1 
    },
    userAddress: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e'
  });

  console.log('Multiple NFT purchase result:', result);
}

Different Token Purchases

// Using USDC
async function purchaseWithUSDC() {
  const result = await legoClient.MagicEden.SwapForNFT({
    items: [{ address: '0x...', tokenId: '1' }],
    token: { 
      address: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', // USDC
      decimals: 6, 
      chainId: 1 
    },
    userAddress: '0x...'
  });
}

// Using DAI
async function purchaseWithDAI() {
  const result = await legoClient.MagicEden.SwapForNFT({
    items: [{ address: '0x...', tokenId: '1' }],
    token: { 
      address: '0x6B175474E89094C44Da98b954EedeAC495271d0F', // DAI
      decimals: 18, 
      chainId: 1 
    },
    userAddress: '0x...'
  });
}

Balance Checking Examples

import { ShogunBalancesApiClient } from '@shogun-sdk/money-legos';

const balancesClient = new ShogunBalancesApiClient('YOUR_API_KEY');

// Check EVM wallet balances
async function checkEVMBalances() {
  const balances = await balancesClient.getEvmWalletBalance(
    '0x742d35Cc6634C0532925a3b844Bc454e4438f44e'
  );
  
  console.log('EVM Balances:', balances);
  
  // Access specific token balances
  balances.forEach(balance => {
    console.log(`${balance.symbol}: ${balance.balance} (${balance.usdValue} USD)`);
  });
}

// Check Solana token balances
async function checkSolanaBalances() {
  const balances = await balancesClient.getSolanaTokenBalances(
    'YourSolanaWalletAddress'
  );
  
  console.log('Solana Balances:', balances);
}

// Get token price information
async function getTokenPrice() {
  const tokenPrice = await balancesClient.getTokenUSDPrice(
    '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', // WETH
    1 // Ethereum mainnet
  );
  
  console.log('WETH Price:', tokenPrice);
}

// Get comprehensive token information
async function getTokenInfo() {
  const tokenInfo = await balancesClient.getTokenInfo(
    '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', // WETH
    1 // Ethereum mainnet
  );
  
  console.log('Token Info:', {
    name: tokenInfo.name,
    symbol: tokenInfo.symbol,
    decimals: tokenInfo.decimals,
    totalSupply: tokenInfo.totalSupply
  });
}

Cross-Chain Swap Examples

import { OneShotClient } from '@shogun-sdk/money-legos';

const oneShotClient = new OneShotClient('YOUR_API_KEY', 'SHOGUN_API_URL');

// Cross-chain swap: ETH to USDC on Base
async function swapETHToUSDCOnBase() {
  const quote = await oneShotClient.fetchQuote({
    srcChain: 1, // Ethereum mainnet
    destChain: 8453, // Base chain
    srcToken: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', // WETH
    destToken: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913', // USDC on Base
    amount: '1000000000000000000', // 1 ETH (in wei)
    senderAddress: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e',
    slippage: 0.5, // 0.5% slippage tolerance
  });

  if (quote.status) {
    console.log('Cross-chain swap quote:', quote.data);
    // Execute the swap...
  } else {
    console.error('Failed to get quote:', quote.error);
  }
}

// Solana to Ethereum swap
async function swapSOLToETH() {
  const quote = await oneShotClient.fetchQuote({
    srcChain: 7565164, // Solana
    destChain: 1, // Ethereum mainnet
    srcToken: 'So11111111111111111111111111111111111111112', // SOL
    destToken: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', // WETH
    amount: '1000000000', // 1 SOL (in lamports)
    senderAddress: 'YourSolanaAddress',
    slippage: 0.5,
  });

  console.log('SOL to ETH quote:', quote);
}

Transaction Signing

EVM Transaction Execution with Viem

import { createWalletClient, http, getPublicClient } from 'viem';
import { privateKeyToAccount } from 'viem/accounts';
import { base } from 'viem/chains';
import { LegoClient } from '@shogun-sdk/money-legos';

async function executeEVMTransactionWithViem() {
  // 1. Setup wallet and client
  const account = privateKeyToAccount('0x...'); // Your private key
  const client = createWalletClient({
    chain: base,
    transport: http(),
    account,
  });

  const legoClient = new LegoClient({ apiKey: 'YOUR_API_KEY' });

  try {
    // 2. Get transaction data
    const { data } = await legoClient.MagicEden.SwapForNFT({
      token: {
        address: '0x0000000000000000000000000000000000000000', // ETH
        decimals: 18,
        chainId: 8453, // Base chain
      },
      items: [{
        address: '0x72d876d9cdf4001b836f8e47254d0551eda2eebb',
        tokenId: '32',
      }],
      userAddress: account.address,
    });
    
    if (!data) {
      console.error('No transaction data returned');
      return;
    }
    
    // 3. Execute each transaction step
    for (const step of data.steps) {
      console.log(`Executing: ${step.description || 'Transaction step'}`);
      
      const txHash = await client.sendTransaction({
        to: step.to as `0x${string}`,
        data: step.data as `0x${string}`,
        value: BigInt(step.value),
        chainId: step.chainId,
        gas: BigInt(step.gas),
        maxFeePerGas: BigInt(step.maxFeePerGas),
        maxPriorityFeePerGas: BigInt(step.maxPriorityFeePerGas),
      });
      
      console.log(`Transaction successful! Hash: ${txHash}`);
    }
  } catch (error) {
    console.error('Transaction execution failed:', error);
  }
}

Advanced EVM Transaction Handling

import { QuoteTypes } from '@shogun-sdk/money-legos';

async function handleAdvancedEVMTransaction(
  quote: QuoteTypes, 
  signer: any, 
  chain: any
): Promise<{ hash: string; swapPlacementTimestamp: number; swapExecutionTimestamp: number } | null> {
  try {
    if (Array.isArray(quote.calldatas) || !signer) return null;
    
    const provider = getPublicClient({ chain });
    
    // Estimate gas with buffer
    const estimatedGas = await provider.estimateGas({
      to: quote.calldatas.to as `0x${string}`,
      data: quote.calldatas.data as `0x${string}`,
      value: BigInt(quote.calldatas?.value ?? 0),
      account: signer.account,
    });

    // Add 20% buffer to estimated gas
    const gas = BigInt(Math.ceil(Number(estimatedGas) * 1.2));

    const swapPlacementTimestamp = Date.now();

    const hash = await signer.sendTransaction({
      to: quote.calldatas.to as `0x${string}`,
      data: quote.calldatas.data as `0x${string}`,
      value: BigInt(quote.calldatas?.value ?? 0),
      account: signer.account,
      gas,
      chain,
    });

    // Wait for confirmation
    const tx = await provider.waitForTransactionReceipt({
      hash,
      retryCount: 5,
      confirmations: 1,
    });
    
    const swapExecutionTimestamp = Date.now();

    if (tx.status !== 'success') {
      throw new Error(`Transaction failed: ${tx.status}`);
    }

    return { hash, swapPlacementTimestamp, swapExecutionTimestamp };
  } catch (error) {
    console.error('EVM transaction error:', error);
    throw error;
  }
}

Solana Transaction Execution

import { VersionedTransaction } from '@solana/web3.js';
import bs58 from 'bs58';
import {
  sendTransactionUsingJito,
  sendBundleUsingJito,
  isValidSolanaSignature,
  checkTransactionConfirmation,
  confirmTransaction,
} from '@shogun-sdk/money-legos';

async function handleSolanaTransaction(
  quote: QuoteTypes,
  address: string,
  walletSigner: any
): Promise<{ transactionHash: string; swapPlacementTimestamp: number; swapExecutionTimestamp: number } | null> {
  if (!Array.isArray(quote.calldatas)) return null;
  
  try {
    // Deserialize transactions
    const transactions = await Promise.all(
      quote.calldatas.map(async (calldata) => {
        const messageBuffer = Buffer.from(calldata.data, 'base64');
        return VersionedTransaction.deserialize(messageBuffer);
      }),
    );

    // Sign all transactions
    const signedTransactions = await walletSigner.signAllTransactions!(
      transactions,
      address,
    );
    
    if (!signedTransactions) {
      throw new Error('Failed to sign transactions');
    }

    // Convert to base58 format
    const base58Transactions = signedTransactions.map((tx) => 
      bs58.encode(tx.serialize())
    );

    const swapPlacementTimestamp = Date.now();

    // Handle single vs bundle transactions
    let result;
    if (quote.calldatas.length === 1) {
      result = await handleSingleSolanaTransaction(base58Transactions[0]);
    } else {
      result = await handleSolanaBundleTransaction(
        base58Transactions, 
        transactions[0]
      );
    }

    const swapExecutionTimestamp = Date.now();

    return {
      transactionHash: result.transactionHash,
      swapPlacementTimestamp,
      swapExecutionTimestamp,
    };
  } catch (error) {
    console.error('Solana transaction error:', error);
    throw error;
  }
}

// Single transaction execution
async function handleSingleSolanaTransaction(
  base58Transaction: string
): Promise<{ transactionHash: string }> {
  const transactionHash = await sendTransactionUsingJito(base58Transaction);

  const confirmation = await confirmTransaction(transactionHash, {
    maxRetries: 50,
    commitment: 'confirmed',
    checkInterval: 200,
  });
  
  if (!confirmation.success) {
    throw new Error(`Transaction failed: ${confirmation.error}`);
  }

  return { transactionHash };
}

// Bundle transaction execution
async function handleSolanaBundleTransaction(
  base58Transactions: string[],
  firstTransaction: VersionedTransaction,
): Promise<{ transactionHash: string }> {
  // Extract signature from first transaction
  const transactionSignature = firstTransaction?.signatures?.[0];
  if (!transactionSignature) {
    throw new Error('Missing transaction signature');
  }
  
  const transactionHash = bs58.encode(transactionSignature);
  if (!isValidSolanaSignature(transactionHash)) {
    throw new Error('Invalid transaction signature format');
  }

  // Send bundle to Jito
  const bundleID = await sendBundleUsingJito(base58Transactions);
  
  // Wait for confirmation
  const confirmed = await checkTransactionConfirmation(
    provider, 
    firstTransaction, 
    transactionHash
  );

  if (!confirmed) {
    throw new Error('Bundle transaction failed to confirm');
  }

  return { transactionHash };
}

Error Handling

Comprehensive Error Handling

async function robustNFTPurchase() {
  try {
    const result = await legoClient.MagicEden.SwapForNFT({
      items: [{ address: '0x123...', tokenId: '1' }],
      token: { address: '0xabc...', decimals: 18, chainId: 1 },
      userAddress: '0xdef...'
    });
    
    if (!result.status) {
      // Handle specific API errors
      switch (result.error) {
        case 'INSUFFICIENT_BALANCE':
          console.error('❌ Not enough tokens to complete purchase');
          // Show user-friendly message to top up balance
          break;
          
        case 'INVALID_NFT':
          console.error('❌ NFT not found or unavailable');
          // Redirect to valid NFT selection
          break;
          
        case 'PRICE_CHANGED':
          console.error('❌ Price changed during transaction');
          // Offer to retry with new price
          break;
          
        case 'NETWORK_ERROR':
          console.error('❌ Network connection issues');
          // Suggest checking internet connection
          break;
          
        case 'API_RATE_LIMIT':
          console.error('❌ Too many requests, please wait');
          // Implement exponential backoff
          break;
          
        default:
          console.error(`❌ Purchase failed: ${result.error}`);
      }
      return { success: false, error: result.error };
    }

    // Success case
    console.log('βœ… Purchase data ready:', result.data);
    return { success: true, data: result.data };
    
  } catch (error) {
    // Handle unexpected errors
    if (error instanceof Error) {
      console.error('πŸ’₯ Unexpected error:', {
        message: error.message,
        stack: error.stack,
        name: error.name
      });
      
      // Network errors
      if (error.message.includes('fetch')) {
        console.error('Network issue detected');
      }
      
      // JSON parsing errors
      if (error.message.includes('JSON')) {
        console.error('API response format issue');
      }
    } else {
      console.error('πŸ’₯ Unknown error type:', error);
    }
    
    return { success: false, error: 'Unexpected error occurred' };
  }
}

// Retry mechanism with exponential backoff
async function purchaseWithRetry(maxRetries = 3) {
  let lastError;
  
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      console.log(`πŸ”„ Attempt ${attempt}/${maxRetries}`);
      
      const result = await robustNFTPurchase();
      if (result.success) {
        return result;
      }
      
      lastError = result.error;
      
      // Don't retry on certain errors
      if (['INSUFFICIENT_BALANCE', 'INVALID_NFT'].includes(result.error)) {
        break;
      }
      
    } catch (error) {
      lastError = error;
      console.error(`❌ Attempt ${attempt} failed:`, error);
    }
    
    // Exponential backoff delay
    if (attempt < maxRetries) {
      const delay = Math.pow(2, attempt) * 1000; // 2s, 4s, 8s...
      console.log(`⏳ Waiting ${delay}ms before retry...`);
      await new Promise(resolve => setTimeout(resolve, delay));
    }
  }
  
  throw new Error(`All ${maxRetries} attempts failed. Last error: ${lastError}`);
}

API Reference

LegoClient

Constructor

new LegoClient(config: LegoClientConfig)

Parameters:

  • config.apiKey (string): Your API key from Shogun dashboard
  • config.baseUrl (string, optional): Custom API base URL

Methods

MagicEden.SwapForNFT(props: FetchLegoProps): Promise<LegoResult>

Purchase NFTs using any supported token.

Parameters:

  • props.items (NFTItem[]): Array of NFT items to purchase
  • props.token (Token): Token to use for payment
  • props.userAddress (string): Buyer's wallet address

Returns:

interface LegoResult {
  status: boolean;
  error?: string;
  data?: {
    steps: TransactionStep[];
    details: PurchaseDetails;
    fees: FeeInformation;
  };
  isLoading: boolean;
  refetch: () => Promise<LegoResult>;
}

OneShotClient

Constructor

new OneShotClient(apiKey: string, baseUrl: string)

Methods

fetchQuote(params: QuoteParams): Promise<QuoteResponse>

Get a quote for cross-chain token swaps.

Parameters:

interface QuoteParams {
  srcChain: number;        // Source chain ID
  destChain: number;       // Destination chain ID
  srcToken: string;        // Source token address
  destToken: string;       // Destination token address
  amount: string;          // Amount to swap (in smallest unit)
  senderAddress: string;   // Sender's wallet address
  slippage: number;        // Slippage tolerance (0.5 = 0.5%)
}

Returns:

interface QuoteResponse {
  status: boolean;
  error?: string;
  data?: QuoteTypes;
}

ShogunBalancesApiClient

Constructor

new ShogunBalancesApiClient(apiKey: string)

Methods

getEvmWalletBalance(address: string): Promise<EvmBalance[]>

Get token balances for an EVM wallet.

Returns:

interface EvmBalance {
  tokenAddress: string;
  symbol: string;
  name: string;
  decimals: number;
  balance: string;
  usdValue: number;
  chainId: number;
}
getSolanaTokenBalances(address: string): Promise<SolanaBalance[]>

Get token balances for a Solana wallet.

getTokenUSDPrice(tokenAddress: string, chainId: number): Promise<TokenPrice>

Get current USD price for a token.

getTokenInfo(tokenAddress: string, chainId: number): Promise<TokenInfo>

Get comprehensive token information.

Core Types

interface NFTItem {
  address: string;  // NFT contract address
  tokenId: string;  // NFT token ID
}

interface Token {
  address: string;  // Token contract address (use '0x0000...' for native ETH)
  decimals: number; // Token decimals (18 for ETH, 6 for USDC, etc.)
  chainId: number;  // Chain ID (1 for Ethereum, 8453 for Base, etc.)
}

interface TransactionStep {
  to: string;                    // Contract address to call
  data: string;                  // Transaction data
  value: string;                 // ETH value to send
  chainId: number;               // Chain to execute on
  gas: string;                   // Gas limit
  maxFeePerGas: string;          // Max fee per gas
  maxPriorityFeePerGas: string;  // Max priority fee
  description?: string;          // Human-readable description
}

Best Practices

1. Always Validate Response Status

const result = await legoClient.MagicEden.SwapForNFT({...});
if (!result.status) {
  console.error('Operation failed:', result.error);
  return;
}
// Proceed with result.data

2. Use TypeScript for Type Safety

import { LegoClient, NFTItem, Token, TransactionStep } from '@shogun-sdk/money-legos';

const items: NFTItem[] = [
  { address: '0x...', tokenId: '42' }
];

const token: Token = {
  address: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2',
  decimals: 18,
  chainId: 1
};

3. Secure API Key Management

// ❌ Don't hardcode API keys
const client = new LegoClient({ apiKey: 'sk_live_abc123...' });

// βœ… Use environment variables
const client = new LegoClient({ 
  apiKey: process.env.SHOGUN_API_KEY! 
});

// βœ… Validate API key exists
if (!process.env.SHOGUN_API_KEY) {
  throw new Error('SHOGUN_API_KEY environment variable is required');
}

4. Implement Proper Error Boundaries

class ShogunSDKError extends Error {
  constructor(
    message: string,
    public code: string,
    public originalError?: any
  ) {
    super(message);
    this.name = 'ShogunSDKError';
  }
}

async function safeExecute<T>(operation: () => Promise<T>): Promise<T> {
  try {
    return await operation();
  } catch (error) {
    if (error instanceof ShogunSDKError) {
      throw error;
    }
    
    throw new ShogunSDKError(
      'Unexpected error in Shogun SDK operation',
      'UNKNOWN_ERROR',
      error
    );
  }
}

5. Gas Optimization

// Add buffer to gas estimates
const estimatedGas = await provider.estimateGas({...});
const gasWithBuffer = BigInt(Math.ceil(Number(estimatedGas) * 1.2)); // 20% buffer

// Monitor gas prices for optimal timing
const gasPrice = await provider.getGasPrice();
console.log(`Current gas price: ${gasPrice} gwei`);

Common Use Cases

1. Cross-Chain NFT Marketplace

class CrossChainNFTMarketplace {
  constructor(private apiKey: string) {}

  async purchaseNFTFromAnyChain(
    nftContract: string,
    tokenId: string,
    paymentToken: Token,
    buyerAddress: string
  ) {
    const legoClient = new LegoClient({ apiKey: this.apiKey });
    
    const result = await legoClient.MagicEden.SwapForNFT({
      items: [{ address: nftContract, tokenId }],
      token: paymentToken,
      userAddress: buyerAddress
    });
    
    if (!result.status) {
      throw new Error(`Purchase failed: ${result.error}`);
    }
    
    return result.data;
  }
}

2. Portfolio Tracker

class PortfolioTracker {
  constructor(private apiKey: string) {
    this.balancesClient = new ShogunBalancesApiClient(apiKey);
  }

  async getFullPortfolio(evmAddress: string, solanaAddress: string) {
    const [evmBalances, solanaBalances] = await Promise.all([
      this.balancesClient.getEvmWalletBalance(evmAddress),
      this.balancesClient.getSolanaTokenBalances(solanaAddress)
    ]);

    const totalUSDValue = [
      ...evmBalances,
      ...solanaBalances
    ].reduce((sum, balance) => sum + balance.usdValue, 0);

    return {
      evmBalances,
      solanaBalances,
      totalUSDValue,
      lastUpdated: new Date().toISOString()
    };
  }
}

3. Cross-Chain DeFi Aggregator

class DeFiAggregator {
  constructor(private apiKey: string, private baseUrl: string) {
    this.oneShotClient = new OneShotClient(apiKey, baseUrl);
  }

  async findBestSwapRoute(
    fromToken: Token,
    toToken: Token,
    amount: string,
    userAddress: string
  ) {
    // Get quotes for different routes
    const routes = [
      { srcChain: fromToken.chainId, destChain: toToken.chainId },
      { srcChain: fromToken.chainId, destChain: 1 }, // Via Ethereum
      { srcChain: fromToken.chainId, destChain: 8453 }, // Via Base
    ];

    const quotes = await Promise.allSettled(
      routes.map(route => 
        this.oneShotClient.fetchQuote({
          srcChain: route.srcChain,
          destChain: route.destChain,
          srcToken: fromToken.address,
          destToken: toToken.address,
          amount,
          senderAddress: userAddress,
          slippage: 0.5
        })
      )
    );

    // Find the best quote (highest output amount)
    const validQuotes = quotes
      .filter((result): result is PromiseFulfilledResult<any> => 
        result.status === 'fulfilled' && result.value.status
      )
      .map(result => result.value.data);

    return validQuotes.sort((a, b) => 
      parseFloat(b.outputAmount) - parseFloat(a.outputAmount)
    )[0];
  }
}

Hyperliquid Integration

The Hyperliquid integration provides comprehensive trading capabilities for perpetual futures and spot trading on the Hyperliquid exchange.

Quick Start

import { Hyperliquid } from '@shogun-sdk/money-legos';

// Initialize Hyperliquid client
const hyperliquid = new Hyperliquid({
  privateKey: 'YOUR_PRIVATE_KEY', // Optional, required for trading
  testnet: false, // Set to true for testnet
  walletAddress: '0x...', // Optional, if different from private key address
  vaultAddress: '0x...', // Optional, for vault operations
});

// Connect and initialize
await hyperliquid.connect();

Configuration Options

interface HyperliquidConfig {
  privateKey?: string;           // Private key for trading operations
  testnet?: boolean;            // Use testnet environment
  walletAddress?: string;       // Wallet address if different from private key
  vaultAddress?: string;        // Vault address for vault operations
  maxReconnectAttempts?: number; // Max reconnection attempts
}

Trading Operations

Place Orders

// Market order (IoC with slippage)
const marketOrder = await hyperliquid.custom.marketOpen(
  'BTC',      // Symbol
  true,       // is_buy
  0.1,        // Size
  undefined,  // Price (optional, uses market price)
  0.05,       // Slippage (5%)
  'cloid-123' // Client order ID (optional)
);

// Limit order
const limitOrder = await hyperliquid.exchange.placeOrder({
  coin: 'BTC',
  is_buy: true,
  sz: 0.1,
  limit_px: 45000,
  order_type: { limit: { tif: 'Gtc' } }, // Good-til-canceled
  reduce_only: false,
  cloid: 'my-order-123'
});

// Stop-loss order
const stopLossOrder = await hyperliquid.exchange.placeOrder({
  coin: 'BTC',
  is_buy: false,
  sz: 0.1,
  limit_px: 43000,
  order_type: { 
    trigger: { 
      triggerPx: 44000,
      isMarket: true,
      tpsl: 'sl'
    }
  },
  reduce_only: true
});

// Take-profit order
const takeProfitOrder = await hyperliquid.exchange.placeOrder({
  coin: 'BTC',
  is_buy: false,
  sz: 0.1,
  limit_px: 47000,
  order_type: { 
    trigger: { 
      triggerPx: 46000,
      isMarket: false,
      tpsl: 'tp'
    }
  },
  reduce_only: true
});

Order Types and Time-in-Force

// Available order types
type OrderType = {
  limit?: { tif: 'Alo' | 'Ioc' | 'Gtc' };    // Add liquidity only, Immediate or cancel, Good til canceled
  trigger?: {
    triggerPx: string | number;
    isMarket: boolean;
    tpsl: 'tp' | 'sl';                        // Take profit or stop loss
  };
};

// Multiple orders in one transaction
const multipleOrders = await hyperliquid.exchange.placeOrder({
  orders: [
    {
      coin: 'BTC',
      is_buy: true,
      sz: 0.1,
      limit_px: 45000,
      order_type: { limit: { tif: 'Gtc' } },
      reduce_only: false
    },
    {
      coin: 'ETH',
      is_buy: true,
      sz: 1.0,
      limit_px: 3000,
      order_type: { limit: { tif: 'Gtc' } },
      reduce_only: false
    }
  ],
  grouping: 'na' // 'na' | 'normalTpsl' | 'positionTpsl'
});

Cancel Orders

// Cancel specific order
const cancelResponse = await hyperliquid.exchange.cancelOrder({
  coin: 'BTC',
  o: 123456789 // Order ID
});

// Cancel by client order ID
const cancelByCloid = await hyperliquid.exchange.cancelOrderByCloid(
  'BTC',
  'my-order-123'
);

// Cancel multiple orders
const cancelMultiple = await hyperliquid.exchange.cancelOrder([
  { coin: 'BTC', o: 123456789 },
  { coin: 'ETH', o: 987654321 }
]);

// Cancel all orders for a symbol
const cancelAllBTC = await hyperliquid.custom.cancelAllOrders('BTC');

// Cancel all orders for all symbols
const cancelAll = await hyperliquid.custom.cancelAllOrders();

Modify Orders

// Modify single order
const modifyResponse = await hyperliquid.exchange.modifyOrder(
  123456789, // Order ID
  {
    coin: 'BTC',
    is_buy: true,
    sz: 0.2,        // New size
    limit_px: 45500, // New price
    order_type: { limit: { tif: 'Gtc' } },
    reduce_only: false
  }
);

// Batch modify orders
const batchModify = await hyperliquid.exchange.batchModifyOrders([
  {
    oid: 123456789,
    order: {
      coin: 'BTC',
      is_buy: true,
      sz: 0.2,
      limit_px: 45500,
      order_type: { limit: { tif: 'Gtc' } },
      reduce_only: false
    }
  },
  {
    oid: 987654321,
    order: {
      coin: 'ETH',
      is_buy: true,
      sz: 2.0,
      limit_px: 3100,
      order_type: { limit: { tif: 'Gtc' } },
      reduce_only: false
    }
  }
]);

Position Management

Close Positions

// Close specific position
const closePosition = await hyperliquid.custom.marketClose(
  'BTC',      // Symbol
  0.1,        // Size (optional, closes entire position if not specified)
  undefined,  // Price (optional)
  0.05,       // Slippage
  'close-123' // Client order ID (optional)
);

// Close all positions
const closeAllPositions = await hyperliquid.custom.closeAllPositions(0.05);

Update Leverage

// Update leverage for a symbol
const updateLeverage = await hyperliquid.exchange.updateLeverage(
  'BTC',      // Symbol
  'cross',    // 'cross' or 'isolated'
  10          // Leverage amount
);

// Update isolated margin
const updateMargin = await hyperliquid.exchange.updateIsolatedMargin(
  'BTC',  // Symbol
  true,   // is_buy
  1000    // New margin amount
);

Market Data and Information

Get Market Data

// Get all market prices
const allMids = await hyperliquid.info.getAllMids();
console.log('BTC Price:', allMids['BTC']);

// Get order book
const orderBook = await hyperliquid.info.getL2Book('BTC');
console.log('Bids:', orderBook.levels[0]);
console.log('Asks:', orderBook.levels[1]);

// Get candlestick data
const candles = await hyperliquid.info.getCandleSnapshot(
  'BTC',
  '1h',        // Interval: '1m', '5m', '15m', '1h', '4h', '1d'
  Date.now() - 86400000, // Start time (24 hours ago)
  Date.now()   // End time
);

// Get perpetual market info
const perpMeta = await hyperliquid.info.perpetuals.getPerpMeta();
console.log('Available perpetuals:', perpMeta.universe);

// Get spot market info
const spotMeta = await hyperliquid.info.spot.getSpotMeta();
console.log('Available spot pairs:', spotMeta.universe);

Get User Data

const userAddress = '0x...';

// Get user positions
const positions = await hyperliquid.info.perpetuals.getClearinghouseState(userAddress);
console.log('Positions:', positions.assetPositions);

// Get open orders
const openOrders = await hyperliquid.info.getUserOpenOrders(userAddress);
console.log('Open orders:', openOrders);

// Get trade history
const fills = await hyperliquid.info.getUserFills(userAddress);
console.log('Recent fills:', fills);

// Get fills by time range
const fillsByTime = await hyperliquid.info.getUserFillsByTime(
  userAddress,
  Date.now() - 86400000, // Start time
  Date.now()             // End time
);

// Get user portfolio
const portfolio = await hyperliquid.info.getUserPortfolio(userAddress);
console.log('Portfolio data:', portfolio);

// Get spot balances
const spotBalances = await hyperliquid.info.spot.getSpotClearinghouseState(userAddress);
console.log('Spot balances:', spotBalances.balances);

TWAP Orders

// Place TWAP order
const twapOrder = await hyperliquid.exchange.placeTwapOrder({
  coin: 'BTC',
  is_buy: true,
  sz: 1.0,           // Total size
  reduce_only: false,
  minutes: 30,       // Execute over 30 minutes
  randomize: true    // Randomize execution timing
});

console.log('TWAP ID:', twapOrder.response.data.status.running.twapId);

// Cancel TWAP order
const cancelTwap = await hyperliquid.exchange.cancelTwapOrder({
  coin: 'BTC',
  twap_id: twapOrder.response.data.status.running.twapId
});

// Get TWAP fills
const twapFills = await hyperliquid.info.getUserTwapSliceFills(userAddress);
console.log('TWAP fills:', twapFills);

Transfers and Withdrawals

USDC Transfers

// Transfer USDC to another address
const usdcTransfer = await hyperliquid.exchange.usdTransfer(
  '0x742d35Cc6634C0532925a3b844Bc454e4438f44e', // Destination
  100 // Amount in USDC
);

// Initiate withdrawal
const withdrawal = await hyperliquid.exchange.initiateWithdrawal(
  '0x742d35Cc6634C0532925a3b844Bc454e4438f44e', // Destination
  100 // Amount in USDC
);

Spot Token Transfers

// Transfer spot tokens
const spotTransfer = await hyperliquid.exchange.spotTransfer(
  '0x742d35Cc6634C0532925a3b844Bc454e4438f44e', // Destination
  'PURR',  // Token symbol
  '1000'   // Amount
);

// Transfer between spot and perp accounts
const spotPerpTransfer = await hyperliquid.exchange.transferBetweenSpotAndPerp(
  100,  // USDC amount
  true  // true = to perp, false = to spot
);

Vault Operations

// Initialize with vault address
const vaultHyperliquid = new Hyperliquid({
  privateKey: 'YOUR_PRIVATE_KEY',
  vaultAddress: '0x...'
});

// Deposit to vault
const vaultDeposit = await hyperliquid.exchange.vaultTransfer(
  '0x...', // Vault address
  true,    // is_deposit
  1000     // Amount in USDC
);

// Withdraw from vault
const vaultWithdraw = await hyperliquid.exchange.vaultTransfer(
  '0x...', // Vault address
  false,   // is_deposit
  500      // Amount in USDC
);

// Get vault details
const vaultDetails = await hyperliquid.info.getVaultDetails(
  '0x...', // Vault address
  userAddress // User address (optional)
);

// Get user vault equities
const vaultEquities = await hyperliquid.info.getUserVaultEquities(userAddress);

Advanced Features

Spot Trading

// Get spot assets
const spotAssets = await hyperliquid.custom.getAllAssets();
console.log('Spot assets:', spotAssets.spot);

// Spot market buy
const spotBuy = await hyperliquid.exchange.placeOrder({
  coin: 'PURR/USDC',
  is_buy: true,
  sz: 1000,
  limit_px: 0.001,
  order_type: { limit: { tif: 'Gtc' } },
  reduce_only: false
});

Batch Operations

// Schedule order cancellation
const scheduleCancel = await hyperliquid.exchange.scheduleCancel(
  Date.now() + 3600000 // Cancel all orders in 1 hour
);

// Set referrer code
const setReferrer = await hyperliquid.exchange.setReferrer('YOUR_REFERRER_CODE');

// Approve agent for trading
const approveAgent = await hyperliquid.exchange.approveAgent({
  agentAddress: '0x...',
  agentName: 'Trading Bot'
});

Rate Limiting and Status

// Get user rate limit status
const rateLimit = await hyperliquid.info.getUserRateLimit(userAddress);
console.log('Rate limit status:', rateLimit);

// Get order status
const orderStatus = await hyperliquid.info.getOrderStatus(
  userAddress,
  123456789 // Order ID
);
console.log('Order status:', orderStatus);

// Check if authenticated
const isAuthenticated = hyperliquid.isAuthenticated();
console.log('Authenticated:', isAuthenticated);

Error Handling

async function safeTrading() {
  try {
    const order = await hyperliquid.exchange.placeOrder({
      coin: 'BTC',
      is_buy: true,
      sz: 0.1,
      limit_px: 45000,
      order_type: { limit: { tif: 'Gtc' } },
      reduce_only: false
    });
    
    if (order.status === 'ok') {
      console.log('Order placed successfully:', order.response);
    } else {
      console.error('Order failed:', order.response);
    }
  } catch (error) {
    if (error.message.includes('Invalid or missing private key')) {
      console.error('Authentication required for trading operations');
    } else if (error.message.includes('Unknown asset')) {
      console.error('Invalid symbol provided');
    } else if (error.message.includes('Invalid price')) {
      console.error('Price does not meet tick size requirements');
    } else {
      console.error('Trading error:', error);
    }
  }
}

Best Practices

1. Price Precision

// Hyperliquid has specific tick sizes for each asset
// Use the built-in price validation
const isValidPrice = await hyperliquid.custom.isValidPrice('BTC', 45000.5);
if (!isValidPrice) {
  // Adjust price to valid tick size
  const adjustedPrice = await hyperliquid.custom.adjustPrice('BTC', 45000.5);
}

2. Order Management

// Always use client order IDs for tracking
const cloid = `order-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;

const order = await hyperliquid.exchange.placeOrder({
  coin: 'BTC',
  is_buy: true,
  sz: 0.1,
  limit_px: 45000,
  order_type: { limit: { tif: 'Gtc' } },
  reduce_only: false,
  cloid: cloid
});

// Store the mapping for later reference
orderTracker.set(cloid, order);

3. Position Sizing

// Check account balance before placing orders
const positions = await hyperliquid.info.perpetuals.getClearinghouseState(userAddress);
const accountValue = parseFloat(positions.marginSummary.accountValue);
const availableBalance = parseFloat(positions.withdrawable);

// Calculate position size based on risk management
const riskPercentage = 0.02; // 2% risk per trade
const stopLossDistance = 1000; // $1000 stop loss
const maxPositionSize = (accountValue * riskPercentage) / stopLossDistance;

4. Symbol Conversion

// The SDK handles symbol conversion automatically
// Internal symbols may differ from display symbols
const internalSymbol = await hyperliquid.info.getInternalName('BTC-PERP');
const displaySymbol = await hyperliquid.symbolConversion.convertSymbol(internalSymbol, 'forward');

Types and Interfaces

// Key interfaces for trading
interface HyperliquidOrder {
  coin: string;           // Asset symbol
  is_buy: boolean;        // Buy or sell
  sz: number;            // Size
  limit_px: number;      // Limit price
  order_type: OrderType; // Order type and TIF
  reduce_only: boolean;  // Reduce only flag
  cloid?: string;        // Client order ID
}

interface OrderResponse {
  status: string;
  response: {
    type: string;
    data: {
      statuses: Array<{
        resting?: { oid: number };
        filled?: { oid: number; totalSz: string; avgPx: string };
      }>;
    };
  };
}

// Position data
interface Position {
  coin: string;
  entryPx: string;
  leverage: { type: string; value: number };
  liquidationPx: string;
  marginUsed: string;
  positionValue: string;
  szi: string;            // Signed size
  unrealizedPnl: string;
}

Troubleshooting

Common Issues and Solutions

1. API Key Issues

// Validate API key format
const isValidAPIKey = (key: string) => {
  return key.startsWith('sk_') && key.length > 20;
};

if (!isValidAPIKey(process.env.SHOGUN_API_KEY!)) {
  throw new Error('Invalid API key format');
}

2. RPC Configuration Issues

// Test RPC connectivity
async function testRPCConnection(chainId: number) {
  try {
    const provider = getPublicClient({ 
      chain: { id: chainId } as any,
      transport: http()
    });
    
    const blockNumber = await provider.getBlockNumber();
    console.log(`βœ… Chain ${chainId} connected, latest block: ${blockNumber}`);
    return true;
  } catch (error) {
    console.error(`❌ Chain ${chainId} connection failed:`, error);
    return false;
  }
}

3. Transaction Failures

// Debug transaction failures
async function debugTransaction(txHash: string, chainId: number) {
  const provider = getPublicClient({ chain: { id: chainId } as any });
  
  try {
    const receipt = await provider.getTransactionReceipt({ hash: txHash as `0x${string}` });
    
    if (receipt.status === 'reverted') {
      console.error('Transaction reverted');
      // Try to get revert reason
      const tx = await provider.getTransaction({ hash: txHash as `0x${string}` });
      console.log('Transaction data:', tx);
    }
  } catch (error) {
    console.error('Failed to get transaction receipt:', error);
  }
}

4. Balance Issues

// Check if user has sufficient balance
async function validateBalance(
  userAddress: string,
  tokenAddress: string,
  requiredAmount: string,
  chainId: number
) {
  const balancesClient = new ShogunBalancesApiClient(process.env.SHOGUN_API_KEY!);
  
  if (chainId === 7565164) { // Solana
    const balances = await balancesClient.getSolanaTokenBalances(userAddress);
    const tokenBalance = balances.find(b => b.tokenAddress === tokenAddress);
    return tokenBalance && parseFloat(tokenBalance.balance) >= parseFloat(requiredAmount);
  } else { // EVM
    const balances = await balancesClient.getEvmWalletBalance(userAddress);
    const tokenBalance = balances.find(b => b.tokenAddress === tokenAddress);
    return tokenBalance && parseFloat(tokenBalance.balance) >= parseFloat(requiredAmount);
  }
}

Support

License

ISC

Keywords

shogun

FAQs

Package last updated on 09 Oct 2025

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts